Skip to content

Commit

Permalink
Add support for multiple signals (-sig) and specifying core dump cont…
Browse files Browse the repository at this point in the history
…ents (-mc) (#226)

* Check-point

* Fix compilation error

* Cleanup

* Add DEBUG env variable to build to enable/disable optimizations
  • Loading branch information
MarioHewardt authored Jan 23, 2024
1 parent 3df0ee2 commit c368649
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 59 deletions.
18 changes: 16 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,14 @@ add_executable(procdump
${PROJECT_BINARY_DIR}/ProcDumpProfiler.o
)

target_compile_options(procdump PRIVATE -g -pthread -fstack-protector-all -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -Werror -D_GNU_SOURCE -std=c++11 -O2)
target_compile_options(procdump PRIVATE -g -pthread -fstack-protector-all -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -Werror -D_GNU_SOURCE -std=c++11)
if (NOT DEFINED ENV{DEBUG})
message(STATUS "procdump DEBUG disabled. Enabling optimizations.")
add_compile_options(procdump -O2)
else()
message(STATUS "procdump DEBUG enabled. Disabling optimizations.")
endif()


target_include_directories(procdump PUBLIC
${procdump_INC}
Expand Down Expand Up @@ -210,7 +217,14 @@ add_executable(ProcDumpTestApplication
${procdump_Test}/ProcDumpTestApplication.c
)

target_compile_options(ProcDumpTestApplication PRIVATE -g -pthread -std=gnu99 -fstack-protector-all -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -Werror -O2)
target_compile_options(ProcDumpTestApplication PRIVATE -g -pthread -std=gnu99 -fstack-protector-all -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -Werror)
if (NOT DEFINED ENV{DEBUG})
message(STATUS "ProcDumpTestApplication DEBUG disabled. Enabling optimizations.")
add_compile_options(ProcDumpTestApplication -O2)
else()
message(STATUS "ProcDumpTestApplication DEBUG enabled. Disabling optimizations.")
endif()


target_include_directories(ProcDumpTestApplication PUBLIC
/usr/include
Expand Down
3 changes: 3 additions & 0 deletions include/GenHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ static inline void cancel_pthread(unsigned long* val)

int* GetSeparatedValues(char* src, char* separator, int* numValues);
bool ConvertToInt(const char* src, int* conv);
bool ConvertToIntHex(const char* src, int* conv);
bool IsValidNumberArg(const char *arg);
bool CheckKernelVersion(int major, int minor);
uint16_t* GetUint16(char* buffer);
Expand All @@ -121,6 +122,8 @@ char* GetSocketPath(char* prefix, pid_t pid, pid_t targetPid);
int send_all(int socket, void *buffer, size_t length);
int recv_all(int socket, void* buffer, size_t length);
pid_t gettid() noexcept;
unsigned long GetCoreDumpFilter(int pid);
bool SetCoreDumpFilter(int pid, unsigned long filter);

#endif // GENHELPERS_H

4 changes: 3 additions & 1 deletion include/ProcDumpConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ struct ProcDumpConfiguration
bool DiagnosticsLoggingEnabled; // -log
int ThreadThreshold; // -tc
int FileDescriptorThreshold; // -fc
int SignalNumber; // -sig
int* SignalNumber; // -sig
int SignalCount;
int PollingInterval; // -pf
char *CoreDumpPath; //
char *CoreDumpName; //
Expand All @@ -115,6 +116,7 @@ struct ProcDumpConfiguration
bool bRestrackEnabled; // -restrack
bool bLeakReportInProgress;
int SampleRate; // Record every X resource allocation in restrack
int CoreDumpMask; // -mc (core dump mask)

//
// Keeps track of the memory allocations when -restrack is specified.
Expand Down
3 changes: 2 additions & 1 deletion include/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,8 @@ struct ProcessStatus {
// -----------------------------------------------------------

bool GetProcessStat(pid_t pid, struct ProcessStat *proc);
char * GetProcessName(pid_t pid);
char* GetProcessName(pid_t pid);
char* GetProcessNameFromCmdLine(char* cmdLine);
pid_t GetProcessPgid(pid_t pid);
bool LookupProcessByPid(pid_t pid);
bool LookupProcessByPgid(pid_t pid);
Expand Down
36 changes: 30 additions & 6 deletions src/CoreDumpWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,32 +106,54 @@ char* WriteCoreDump(struct CoreDumpWriter *self)

// Enter critical section (block till we decrement semaphore)
rc = WaitForQuitOrEvent(self->Config, &self->Config->semAvailableDumpSlots, INFINITE_WAIT);
if(rc == 0){
if(rc == 0)
{
Log(error, INTERNAL_ERROR);
Trace("WriteCoreDump: failed WaitForQuitOrEvent.");
exit(-1);
}
if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0){
if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
{
Log(error, INTERNAL_ERROR);
Trace("WriteCoreDump: failed pthread_setcanceltype.");
exit(-1);
}
switch (rc) {
switch (rc)
{
case WAIT_OBJECT_0: // QUIT! Time for cleanup, no dump
break;
case WAIT_OBJECT_0+1: // We got a dump slot!
{
char* socketName = NULL;
IsCoreClrProcess(self->Config->ProcessId, &socketName);
if ((dumpFileName = WriteCoreDumpInternal(self, socketName)) != NULL) {
unsigned int currentCoreDumpFilter = -1;
if(self->Config->CoreDumpMask != -1)
{
currentCoreDumpFilter = GetCoreDumpFilter(self->Config->ProcessId);
SetCoreDumpFilter(self->Config->ProcessId, self->Config->CoreDumpMask);
}
if ((dumpFileName = WriteCoreDumpInternal(self, socketName)) != NULL)
{
// We're done here, unlock (increment) the sem
if(sem_post(&self->Config->semAvailableDumpSlots.semaphore) == -1){
if(sem_post(&self->Config->semAvailableDumpSlots.semaphore) == -1)
{
Log(error, INTERNAL_ERROR);
Trace("WriteCoreDump: failed sem_post.");
if(socketName) free(socketName);
if(self->Config->CoreDumpMask != -1 && currentCoreDumpFilter != -1)
{
SetCoreDumpFilter(self->Config->ProcessId, currentCoreDumpFilter);
}

exit(-1);
}
}

if(self->Config->CoreDumpMask != -1 && currentCoreDumpFilter != -1)
{
SetCoreDumpFilter(self->Config->ProcessId, currentCoreDumpFilter);
}

if(socketName) free(socketName);
}
break;
Expand All @@ -141,7 +163,9 @@ char* WriteCoreDump(struct CoreDumpWriter *self)
Trace("WriteCoreDump: Error in default case");
break;
}
if(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) != 0){

if(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) != 0)
{
Log(error, INTERNAL_ERROR);
Trace("WriteCoreDump: failed pthread_setcanceltype.");
exit(-1);
Expand Down
91 changes: 91 additions & 0 deletions src/GenHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,45 @@ bool ConvertToInt(const char* src, int* conv)
return true;
}

//--------------------------------------------------------------------
//
// ConvertToIntHex - Helper to convert from a char* (hex) to int
//
//--------------------------------------------------------------------
bool ConvertToIntHex(const char* src, int* conv)
{
int temp = 0;

for (size_t i=0; src[i] != '\0'; i++)
{
if ((src[i] >= '0') && (src[i] <= '9'))
{
// Shift left by 0x10 (16) and add the digit using an ASCII delta
temp *= 0x10;
temp += src[i] - '0';
}
else if ((src[i] >= 'A') && (src[i] <= 'F'))
{
// Shift left by 0x10 (16) and add the digit using an ASCII delta
temp *= 0x10;
temp += 10 + (src[i] - 'A');
}
else if ((src[i] >= 'a') && (src[i] <= 'f'))
{
// Shift left by 0x10 (16) and add the digit using an ASCII delta
temp *= 0x10;
temp += 10 + (src[i] - 'a');
}
else
{
return false;
}
}

*conv = temp;
return true;
}

//--------------------------------------------------------------------
//
// CheckKernelVersion - Check to see if current kernel is greater than
Expand Down Expand Up @@ -571,3 +610,55 @@ pid_t gettid() noexcept

return 0;
}

//--------------------------------------------------------------------
//
// GetCoreDumpFilter
//
// Returns the core dump filter for the specified process id.
//--------------------------------------------------------------------
unsigned long GetCoreDumpFilter(int pid)
{
unsigned long filter = -1;

char filepath[PATH_MAX];
snprintf(filepath, sizeof(filepath), "/proc/%d/coredump_filter", pid);

FILE* file = fopen(filepath, "r");
if (file != NULL)
{
int itemsRead = fscanf(file, "%lx", &filter);
if (itemsRead != 1)
{
filter = -1;
}
}

fclose(file);
return filter;
}

//--------------------------------------------------------------------
//
// SetCoreDumpFilter
//
// Sets the core dump filter for the specified process id.
//--------------------------------------------------------------------
bool SetCoreDumpFilter(int pid, unsigned long filter)
{
bool ret = false;
char filepath[PATH_MAX];
snprintf(filepath, sizeof(filepath), "/proc/%d/coredump_filter", pid);

FILE *file = fopen(filepath, "w");
if (file != NULL)
{
if(fprintf(file, "%ld", filter) > 0)
{
ret = true;
}
}

fclose(file);
return ret;
}
37 changes: 19 additions & 18 deletions src/Monitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void* SignalThread(void *input)
// access to the signal path (in SignalMonitoringThread). Note, there is still a race but
// acceptable since it is very unlikely to occur. We also cancel the SignalMonitorThread to
// break it out of waitpid call.
if(it->second->SignalNumber != -1)
if(it->second->SignalCount > 0)
{
for(int i=0; i<it->second->nThreads; i++)
{
Expand Down Expand Up @@ -537,7 +537,7 @@ int CreateMonitorThreads(struct ProcDumpConfiguration *self)
}
}

if (self->SignalNumber != -1 && !tooManyTriggers)
if (self->SignalCount > 0 && !tooManyTriggers)
{
if ((rc = CreateMonitorThread(self, Signal, SignalMonitoringThread, (void *)self)) != 0 )
{
Expand Down Expand Up @@ -1152,7 +1152,17 @@ void* SignalMonitoringThread(void *thread_args /* struct ProcDumpConfiguration*
// We are now in a signal-stop state

signum = WSTOPSIG(wstatus);
if(signum == config->SignalNumber)
bool found = false;
for(int i = 0; i < config->SignalCount; i++)
{
if(signum == config->SignalNumber[i])
{
found = true;
break;
}
}

if(found == true)
{
// We have to detach in a STOP state so we can invoke gcore
if(ptrace(PTRACE_DETACH, config->ProcessId, 0, SIGSTOP) == -1)
Expand All @@ -1167,7 +1177,9 @@ void* SignalMonitoringThread(void *thread_args /* struct ProcDumpConfiguration*
dumpFileName = WriteCoreDump(writer);
if(dumpFileName == NULL)
{
SetQuit(config, 1);
ptrace(PTRACE_CONT, config->ProcessId, NULL, signum);
ptrace(PTRACE_DETACH, config->ProcessId, 0, 0);
break;
}

//
Expand All @@ -1176,28 +1188,22 @@ void* SignalMonitoringThread(void *thread_args /* struct ProcDumpConfiguration*
if(config->bRestrackEnabled == true)
{
pthread_t id = WriteRestrackSnapshot(config, (std::string(dumpFileName) + ".restrack").c_str());
if (id == 0)
{
SetQuit(config, 1);
}
else
if (id != 0)
{
leakReportThreads.push_back(id);
}
}

kill(config->ProcessId, SIGCONT);
ptrace(PTRACE_CONT, config->ProcessId, NULL, signum);

if(config->NumberOfDumpsCollected >= config->NumberOfDumpsToCollect)
{
// If we are over the max number of dumps to collect, send the original signal we intercepted.
kill(config->ProcessId, signum);
pthread_mutex_unlock(&config->ptrace_mutex);
ptrace(PTRACE_DETACH, config->ProcessId, 0, 0);
break;
}

ptrace(PTRACE_CONT, config->ProcessId, NULL, signum);

// Re-attach to the target process
if (ptrace(PTRACE_SEIZE, config->ProcessId, NULL, NULL) == -1)
{
Expand All @@ -1213,11 +1219,6 @@ void* SignalMonitoringThread(void *thread_args /* struct ProcDumpConfiguration*
// Resume execution of the target process
ptrace(PTRACE_CONT, config->ProcessId, NULL, signum);
pthread_mutex_unlock(&config->ptrace_mutex);

if(dumpFileName == NULL)
{
break;
}
}
}
}
Expand Down
Loading

0 comments on commit c368649

Please sign in to comment.