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

188 - Generate a dump when a .NET gen collection starts and ends #196

Merged
merged 4 commits into from
Jul 14, 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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ procdump [-n Count]
[-c|-cl CPU_Usage]
[-m|-ml Commit_Usage1[,Commit_Usage2,...]]
[-gcm Memory_Usage1[,Memory_Usage2...]]
[-gcgen Generation]
[-tc Thread_Threshold]
[-fc FileDescriptor_Threshold]
[-sig Signal_Number]
Expand All @@ -46,6 +47,7 @@ Options:
-m Memory commit threshold(s) (MB) above which to create dumps.
-ml Memory commit threshold(s) (MB) below which to create dumps.
-gcm [.NET] GC memory threshold(s) (MB) above which to create dumps.
-gcgen [.NET] Create dump when the garbage collection of the specified generation starts and finishes.
-tc Thread count threshold above which to create a dump of the process.
-fc File descriptor count threshold above which to create a dump of the process.
-sig Signal number to intercept to create a dump of the process.
Expand Down Expand Up @@ -96,6 +98,10 @@ The following will create a core dump when .NET memory usage is >= 100 MB follow
```
sudo procdump -gcm 100,200 1234
```
The following will create a core dump at the start and end of a .NET generation 1 garbage collection.
```
sudo procdump -gcgen 1
```
The following will create a core dump in the `/tmp` directory immediately.
```
sudo procdump 1234 /tmp
Expand Down
1 change: 1 addition & 0 deletions include/GenHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ bool createDir(const char *dir, mode_t perms);
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();

#endif // GENHELPERS_H

3 changes: 3 additions & 0 deletions include/Monitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ bool BeginMonitoring(struct ProcDumpConfiguration *self);
bool MonitorDotNet(struct ProcDumpConfiguration *self);
char* GetThresholds(struct ProcDumpConfiguration *self);
char* GetClientData(struct ProcDumpConfiguration *self, char* fullDumpPath);
char* GetClientDataInt(enum TriggerType triggerType, char* path, int value);
char* GetClientDataString(enum TriggerType triggerType, char* path, char* value);
bool ExitProcessMonitor(struct ProcDumpConfiguration* config, pthread_t processMonitor);

// Monitor worker threads
void *CommitMonitoringThread(void *thread_args /* struct ProcDumpConfiguration* */);
Expand Down
2 changes: 2 additions & 0 deletions include/ProcDumpConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct ProcDumpConfiguration {
int NumberOfDumpsCollected; // Number of dumps we have collected
bool bTerminated; // Do we know whether the process has terminated and subsequently whether we are terminating?
char* socketPath;
bool bExitProcessMonitor;

// Quit
int nQuit; // if not 0, then quit
Expand All @@ -89,6 +90,7 @@ struct ProcDumpConfiguration {
int MemoryCurrentThreshold;
bool bMemoryTriggerBelowValue; // -m or -ml
bool bMonitoringGCMemory; // -gcm
int DumpGCGeneration; // -gcgen
int ThresholdSeconds; // -s
bool bTimerThreshold; // -s
int NumberOfDumpsToCollect; // -n
Expand Down
3 changes: 2 additions & 1 deletion include/ProfilerCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ enum TriggerType
ThreadCount,
FileDescriptorCount,
Exception,
GCThreshold
GCThreshold,
GCGeneration
};

#endif // PROFILERCOMMON_H
4 changes: 4 additions & 0 deletions procdump.1
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ procdump [-n Count]
[-s Seconds]
[-c|-cl CPU_Usage]
[-m|-ml Commit_Usage1[,Commit_Usage2,...]]
[-gcm Memory_Usage1[,Memory_Usage2...]]
[-gcgen Generation]
[-tc Thread_Threshold]
[-fc FileDescriptor_Threshold]
[-sig Signal_Number]
Expand All @@ -26,6 +28,8 @@ Options:
-cl CPU threshold below which to create a dump of the process.
-m Memory commit thresholds (MB) above which to create dumps.
-ml Memory commit thresholds (MB) below which to create dumps.
-gcm [.NET] GC memory threshold(s) (MB) above which to create dumps.
-gcgen [.NET] Create dump when the garbage collection of the specified generation starts and finishes.
-tc Thread count threshold above which to create a dump of the process.
-fc File descriptor count threshold above which to create a dump of the process.
-sig Signal number to intercept to create a dump of the process.
Expand Down
3 changes: 3 additions & 0 deletions profiler/inc/ProcDumpProfiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ class CorProfiler : public ICorProfilerCallback8
enum TriggerType triggerType;
int currentThresholdIndex;
bool gen2Collection;
int gcGeneration;
bool gcGenStarted;

String GetExceptionName(ObjectID objectId);
String GetExceptionMessage(ObjectID objectId);
Expand All @@ -129,6 +131,7 @@ class CorProfiler : public ICorProfilerCallback8
bool WildcardSearch(WCHAR*, WCHAR*);
u_int64_t GetGCHeapSize();
bool WriteDumpHelper(std::string dumpName);
bool IsHighPerfBasicGC();

public:
CorProfiler();
Expand Down
66 changes: 64 additions & 2 deletions profiler/src/ProcDumpProfiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,25 @@ bool CorProfiler::WildcardSearch(WCHAR* szClassName, WCHAR* szSearch)
return false;
}

//------------------------------------------------------------------------------------------------------------------------------------------------------
// CorProfiler::IsHighPerfBasicGC
//------------------------------------------------------------------------------------------------------------------------------------------------------
bool CorProfiler::IsHighPerfBasicGC()
{
if(triggerType == GCThreshold || triggerType == GCGeneration)
{
return true;
}

return false;
}

//------------------------------------------------------------------------------------------------------------------------------------------------------
// CorProfiler::CorProfiler
//------------------------------------------------------------------------------------------------------------------------------------------------------
CorProfiler::CorProfiler() : refCount(0), corProfilerInfo8(nullptr), corProfilerInfo3(nullptr), corProfilerInfo(nullptr), procDumpPid(0), currentThresholdIndex(0), gen2Collection(false)
CorProfiler::CorProfiler() :
refCount(0), corProfilerInfo8(nullptr), corProfilerInfo3(nullptr), corProfilerInfo(nullptr),
procDumpPid(0), currentThresholdIndex(0), gen2Collection(false), gcGeneration(-1), gcGenStarted(false)
{
// Configure logging
el::Loggers::reconfigureAllLoggers(el::ConfigurationType::Filename, LOG_FILE);
Expand Down Expand Up @@ -273,7 +288,7 @@ HRESULT STDMETHODCALLTYPE CorProfiler::InitializeForAttach(IUnknown *pCorProfile
return E_FAIL;
}
}
else if(triggerType == GCThreshold)
else if(IsHighPerfBasicGC() == true)
{
HRESULT hr = this->corProfilerInfo8->SetEventMask2(0, COR_PRF_HIGH_BASIC_GC);
if(FAILED(hr))
Expand Down Expand Up @@ -332,6 +347,7 @@ WCHAR* CorProfiler::GetUint16(char* buffer)
//
// DOTNET_EXCEPTION_TRIGGER;<fullpathtodumplocation>;<pidofprocdump>;<exception>:<numdumps>;<exception>:<numdumps>,...
// DOTNET_GC_THRESHOLD_TRIGGER;<fullpathtodumplocation>;<pidofprocdump>;Threshold1;Threshold2,...
// DOTNET_GC_GEN_TRIGGER;<fullpathtodumplocation>;<pidofprocdump>;GCGeneration
//
//------------------------------------------------------------------------------------------------------------------------------------------------------
bool CorProfiler::ParseClientData(char* clientData)
Expand Down Expand Up @@ -401,6 +417,12 @@ bool CorProfiler::ParseClientData(char* clientData)
// GC threshold list
gcMemoryThresholdMonitorList.push_back(std::stoi(dataItem) << 20);
}
else if (triggerType == GCGeneration)
{
// GC generation
gcGeneration = std::stoi(dataItem);
break;
}
else
{
LOG(TRACE) << "CorProfiler::ParseClientData Unrecognized trigger type";
Expand All @@ -418,6 +440,11 @@ bool CorProfiler::ParseClientData(char* clientData)
LOG(TRACE) << "CorProfiler::ParseClientData:GCMemoryThreshold " << element;
}

if(gcGeneration != -1)
{
LOG(TRACE) << "CorProfiler::ParseClientData:GCGeneration " << gcGeneration;
}

LOG(TRACE) << "CorProfiler::ParseClientData: Exit";
return true;
}
Expand Down Expand Up @@ -802,8 +829,26 @@ HRESULT STDMETHODCALLTYPE CorProfiler::GarbageCollectionStarted(int cGenerations

if(gcMemoryThresholdMonitorList.size() > 0 && generationCollected[2] == true)
{
// GC memory threshold dump
gen2Collection = true;
}
else if(gcGeneration != -1 && gcGenStarted == false && gcGeneration < cGenerations && generationCollected[gcGeneration] == true)
{
// GC Generation dump
LOG(TRACE) << "CorProfiler::GarbageCollectionStarted: Dump on generation: " << gcGeneration << " and cGenerations = " << cGenerations;
std::string dump = GetDumpName(1, convertString<std::string,std::wstring>(L"gc_gen"));
if(WriteDumpHelper(dump) == false)
{
SendCatastrophicFailureStatus();
return E_FAIL;
}

gcGenStarted = true;
}
else
{
LOG(TRACE) << "CorProfiler::GarbageCollectionStarted: Invalid trigger data, trigger = " << triggerType << " cGenerations " << cGenerations;
}

LOG(TRACE) << "CorProfiler::GarbageCollectionStarted: Exit";
return S_OK;
Expand Down Expand Up @@ -849,6 +894,23 @@ HRESULT STDMETHODCALLTYPE CorProfiler::GarbageCollectionFinished()
}
}
}
else if(gcGenStarted == true)
{
// GC Generation dump
LOG(TRACE) << "CorProfiler::GarbageCollectionFinished: Dump on generation: " << gcGeneration;
gcGenStarted = false;

std::string dump = GetDumpName(2, convertString<std::string,std::wstring>(L"gc_gen"));
if(WriteDumpHelper(dump) == false)
{
SendCatastrophicFailureStatus();
return E_FAIL;
}

// Stop monitoring
CleanupProfiler();
UnloadProfiler();
}

LOG(TRACE) << "CorProfiler::GarbageCollectionFinished: Exit";
return S_OK;
Expand Down
21 changes: 21 additions & 0 deletions src/GenHelpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
// General purpose helpers
//
//--------------------------------------------------------------------
#define _GNU_SOURCE
#include "Includes.h"
#include <syscall.h>

//--------------------------------------------------------------------
//
Expand Down Expand Up @@ -547,5 +549,24 @@ int recv_all(int socket, void* buffer, size_t length)
length -= i;
}

return 0;
}

//--------------------------------------------------------------------
//
// gettid
//
// Returns the current thread ID. Useful to add in trace statements
// when threads are created to match to a debug session.
//
// Note: SYS_gettid is not POSIX compliant.
//
//--------------------------------------------------------------------
pid_t gettid()
{
#ifdef SYS_gettid
return syscall(SYS_gettid);
#endif

return 0;
}
Loading