Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wildcard filtering support when dump on exceptions #154

Merged
merged 3 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions profiler/inc/ProcDumpProfiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class CorProfiler : public ICorProfilerCallback8
void SendCatastrophicFailureStatus();
int send_all(int socket, void* buffer, size_t length);
int recv_all(int socket, void* buffer, size_t length);
bool WildcardSearch(WCHAR*, WCHAR*);

public:
CorProfiler();
Expand Down
36 changes: 36 additions & 0 deletions profiler/inc/profilerstring.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,30 @@ inline int wcscmp(const char16_t *lhs, const char16_t *rhs)
return lhs[i] - rhs[i];
}

inline int wcscpy(char16_t *dst, size_t dmax,const char16_t *src)
{
while (dmax > 0) {
*dst = *src;
if (*dst == u'\0') {
return 0;
}

dmax--;
dst++;
src++;
}
return 0;
}
inline int wcslwr(char16_t *str, size_t len)
{
for (size_t i = 0; i < len; ++i)
{
if ((str[i] >= u'A') && (str[i] <= u'Z'))
str[i] = str[i] + (u'a' - u'A');
}
return 0;
}

#endif // defined(__WIN32)

// 16 bit string type that works cross plat and doesn't require changing widths
Expand Down Expand Up @@ -333,3 +357,15 @@ inline bool EndsWith(const String &lhs, const String &rhs)

return true;
}

inline WCHAR *getWCHARs(const String &src)
{
size_t len = src.Length();
WCHAR* tempbuff = new WCHAR[len+1];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check allocation result

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Mario! I will modify the other two and commit, but I have question for this allocation checking with the below two thoughts:

  1. There is no checking for other new allocations in the same file.
  2. The post below says seems we only need to check new allocation with no-throw
    https://stackoverflow.com/questions/5197732/should-one-always-check-if-the-new-operator-worked

for (size_t i = 0; i < len; ++i)
{
tempbuff[i] = src[i];
}
tempbuff[len] = u'\0';
return tempbuff;
}
147 changes: 143 additions & 4 deletions profiler/src/ProcDumpProfiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

INITIALIZE_EASYLOGGINGPP


//------------------------------------------------------------------------------------------------------------------------------------------------------
// HealthThread
//
Expand Down Expand Up @@ -52,6 +51,144 @@ void* HealthThread(void* args)
return NULL;
}

//--------------------------------------------------------------------
//
// CorProfiler::WildcardSearch
// Search string supports '*' anywhere and any number of times
//
//--------------------------------------------------------------------
bool CorProfiler::WildcardSearch(WCHAR* szClassName, WCHAR* szSearch)
{
if ((szClassName == NULL) || (szSearch == NULL))
return false;

WCHAR* szClassLowerMalloc = (WCHAR*)malloc(sizeof(WCHAR)*(wcslen(szClassName)+1));
if (szClassLowerMalloc == NULL)
return false;

WCHAR* szSearchLowerMalloc = (WCHAR*)malloc(sizeof(WCHAR)*(wcslen(szSearch)+1));
if (szSearchLowerMalloc == NULL)
{
free(szClassLowerMalloc);
return false;
}

WCHAR* szClassLower = szClassLowerMalloc;
wcscpy(szClassLower, (wcslen(szClassName)+1), szClassName);
wcslwr(szClassLower, (wcslen(szClassName)+1));

WCHAR* szSearchLower = szSearchLowerMalloc;
wcscpy(szSearchLower, (wcslen(szSearch)+1), szSearch);
wcslwr(szSearchLower, (wcslen(szSearch)+1));

while ((*szSearchLower != u'\0') && (*szClassLower != u'\0'))
{
if (*szSearchLower != u'*')
{
// Straight (case insensitive) compare
if (*szSearchLower != *szClassLower)
{
free(szClassLowerMalloc);
szClassLowerMalloc = NULL;

free(szSearchLowerMalloc);
szSearchLowerMalloc = NULL;

return false;
}

szSearchLower++;
szClassLower++;
continue;
}

//
// Wildcard processing
//

ContinueWildcard:
szSearchLower++;

// The wildcard is on the end; e.g. '*' 'blah*' or '*blah*'
// Must be a match
if (*szSearchLower == u'\0')
{
free(szClassLowerMalloc);
szClassLowerMalloc = NULL;

free(szSearchLowerMalloc);
szSearchLowerMalloc = NULL;
return true;
}

// Double Wildcard; e.g. '**' 'blah**' or '*blah**'
if (*szSearchLower == u'*')
goto ContinueWildcard;

// Find the length of the sub-string to search for
int endpos = 0;
while ((szSearchLower[endpos] != u'\0') && (szSearchLower[endpos] != u'*'))
endpos++;

// Find a match of the sub-search string anywhere within the class string
int cc = 0; // Offset in to the Class
int ss = 0; // Offset in to the Sub-Search
while (ss < endpos)
{
if (szClassLower[ss+cc] == u'\0')
{
free(szClassLowerMalloc);
szClassLowerMalloc = NULL;

free(szSearchLowerMalloc);
szSearchLowerMalloc = NULL;

return false;
}

if (szSearchLower[ss] != szClassLower[ss+cc])
{
cc++;
ss = 0;
continue;
}
ss++;
}

// If we get here, we found a match; move each string forward
szSearchLower += ss;
szClassLower += (ss + cc);
}

// Do we have a trailing wildcard?
// This happens when Class = ABC.XYZ and Search = *XYZ*
// Needed as the trailing wildcard code (above) doesn't run after the ss/cc search as Class is null
while (*szSearchLower == u'*')
{
szSearchLower++;
}

// If Class and Search have no residual, this is a match.
if ((*szSearchLower == u'\0') && (*szClassLower == u'\0'))
{
free(szClassLowerMalloc);
szClassLowerMalloc = NULL;

free(szSearchLowerMalloc);
szSearchLowerMalloc = NULL;

return true;
}

free(szClassLowerMalloc);
szClassLowerMalloc = NULL;

free(szSearchLowerMalloc);
szSearchLowerMalloc = NULL;

return false;
}

//------------------------------------------------------------------------------------------------------------------------------------------------------
// CorProfiler::CorProfiler
//------------------------------------------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -512,6 +649,8 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionThrown(ObjectID thrownObjectId)
}

LOG(TRACE) << "CorProfiler::ExceptionThrown: exception name: " << exceptionName.ToCStr();

WCHAR* exceptionWCHARs = getWCHARs(exceptionName);

// Check to see if we have any matches on exceptions
for (auto & element : exceptionMonitorList)
Expand All @@ -524,9 +663,7 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionThrown(ObjectID thrownObjectId)
return E_FAIL;
}

String exc(exception);
free(exception);
if((exceptionName==exc || element.exception.compare("<any>") == 0) && element.exceptionID != thrownObjectId)
if(WildcardSearch(exceptionWCHARs,exception) && element.exceptionID != thrownObjectId)
{
//
// We have to serialize calls to the diag pipe to avoid concurrency issues
Expand Down Expand Up @@ -597,7 +734,9 @@ HRESULT STDMETHODCALLTYPE CorProfiler::ExceptionThrown(ObjectID thrownObjectId)
UnloadProfiler();
}
}
free(exception);
}
free(exceptionWCHARs);

LOG(TRACE) << "CorProfiler::ExceptionThrown: Exit";
return S_OK;
Expand Down
2 changes: 1 addition & 1 deletion src/ProcDumpConfiguration.c
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,8 @@ int GetOptions(struct ProcDumpConfiguration *self, int argc, char *argv[])
0 == strcasecmp( argv[i], "-f" ))
{
if( i+1 >= argc ) return PrintUsage();

self->ExceptionFilter = strdup(argv[i+1]);
if( tolower( self->ExceptionFilter[0] ) > 'z' || ( self->ExceptionFilter[0] != '*' && tolower( self->ExceptionFilter[0] ) < 'a' ) ) return PrintUsage();
i++;
}

Expand Down
25 changes: 21 additions & 4 deletions src/ProfilerHelpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,10 @@ char* GetEncodedExceptionFilter(char* exceptionFilterCmdLine, unsigned int numDu
char* exceptionFilter = NULL;
char* exceptionFilterCur = NULL;
char tmp[10];
size_t len = 0;

// If no exceptions were specified using -f we should dump on any exception (hence we add <any>)
char* cpy = exceptionFilterCmdLine ? strdup(exceptionFilterCmdLine) : strdup("<any>");
char* cpy = exceptionFilterCmdLine ? strdup(exceptionFilterCmdLine) : strdup("*");

numberOfDumpsLen = sprintf(tmp, "%d", numDumps);

Expand All @@ -313,11 +314,11 @@ char* GetEncodedExceptionFilter(char* exceptionFilterCmdLine, unsigned int numDu

free(cpy);

cpy = exceptionFilterCmdLine ? strdup(exceptionFilterCmdLine) : strdup("<any>");
cpy = exceptionFilterCmdLine ? strdup(exceptionFilterCmdLine) : strdup("*");

totalExceptionNameLen++; // NULL terminator

exceptionFilter = malloc(totalExceptionNameLen+numExceptions*(numberOfDumpsLen+2)); // +1 for : seperator +1 for ; seperator
exceptionFilter = malloc(totalExceptionNameLen+numExceptions*(numberOfDumpsLen+2+2)); // +1 for : seperator +1 for ; seperator +2 for 2 '*' wildcard
if(exceptionFilter==NULL)
{
return NULL;
Expand All @@ -328,7 +329,23 @@ char* GetEncodedExceptionFilter(char* exceptionFilterCmdLine, unsigned int numDu
token = strtok(cpy, ",");
while(token!=NULL)
{
exceptionFilterCur += sprintf(exceptionFilterCur, "%s:%d;", token, numDumps);
len = strlen(token);
if(token[0] != '*' && token[len-1] != '*')
{
exceptionFilterCur += sprintf(exceptionFilterCur, "*%s*:%d;", token, numDumps);
}
else if(token[0] != '*')
{
exceptionFilterCur += sprintf(exceptionFilterCur, "*%s:%d;", token, numDumps);
}
else if(token[len-1] != '*')
{
exceptionFilterCur += sprintf(exceptionFilterCur, "%s*:%d;", token, numDumps);
}
else
{
exceptionFilterCur += sprintf(exceptionFilterCur, "%s:%d;", token, numDumps);
}
token = strtok(NULL, ",");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";
PROCDUMPPATH=$(readlink -m "$DIR/../../../bin/procdump");
TESTWEBAPIPATH=$(readlink -m "$DIR/../TestWebApi");

pushd .
cd $TESTWEBAPIPATH
dotnet run --urls=http://localhost:5032&
TESTPID=$!
sleep 10s
sudo $PROCDUMPPATH -e -f invalid TestWebApi testdump&
sleep 5s
wget http://localhost:5032/throwinvalidoperation
sleep 5s
if [ -f "testdump_0" ]; then
rm -rf testdump_0
popd

#check to make sure profiler so is unloaded
PROF="$(cat /proc/${TESTPID}/maps | awk '{print $6}' | grep '\procdumpprofiler.so' | uniq)"
pkill -9 TestWebApi
if [[ "$PROF" == "procdumpprofiler.so" ]]; then
exit 1
else
exit 0
fi
else
pkill -9 TestWebApi
popd
exit 1
fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";
PROCDUMPPATH=$(readlink -m "$DIR/../../../bin/procdump");
TESTWEBAPIPATH=$(readlink -m "$DIR/../TestWebApi");

pushd .
cd $TESTWEBAPIPATH
dotnet run --urls=http://localhost:5032&
TESTPID=$!
sleep 10s
sudo $PROCDUMPPATH -e -f Invali*xception TestWebApi testdump&
sleep 5s
wget http://localhost:5032/throwinvalidoperation
sleep 5s
if [ -f "testdump_0" ]; then
rm -rf testdump_0
popd

#check to make sure profiler so is unloaded
PROF="$(cat /proc/${TESTPID}/maps | awk '{print $6}' | grep '\procdumpprofiler.so' | uniq)"
pkill -9 TestWebApi
if [[ "$PROF" == "procdumpprofiler.so" ]]; then
exit 1
else
exit 0
fi
else
pkill -9 TestWebApi
popd
exit 1
fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )";
PROCDUMPPATH=$(readlink -m "$DIR/../../../bin/procdump");
TESTWEBAPIPATH=$(readlink -m "$DIR/../TestWebApi");

pushd .
cd $TESTWEBAPIPATH
dotnet run --urls=http://localhost:5032&
TESTPID=$!
sleep 10s
sudo $PROCDUMPPATH -n 2 -e -f In*rat*ption TestWebApi testdump&
sleep 5s
wget http://localhost:5032/throwinvalidoperation
wget http://localhost:5032/throwinvalidoperation
sleep 5s
if [[ -f "testdump_0" && -f "testdump_1" ]]; then
rm -rf testdump_0
rm -rf testdump_1
popd

#check to make sure profiler so is unloaded
PROF="$(cat /proc/${TESTPID}/maps | awk '{print $6}' | grep '\procdumpprofiler.so' | uniq)"
pkill -9 TestWebApi
if [[ "$PROF" == "procdumpprofiler.so" ]]; then
exit 1
else
exit 0
fi
else
pkill -9 TestWebApi
popd
exit 1
fi
Loading