singrdk/base/Windows/singx86/GCLog.cpp

564 lines
15 KiB
C++

/////////////////////////////////////////////////////////////////////////////
//
// GCLog.cpp - GC diagnosis extension support
//
// Copyright Microsoft Corporation. All rights reserved.
//
#include "singx86.h"
#include "diagnose.h"
static char szSymbol[512];
class GCTracing : public EventingEnumerator
{
// High level diagnosis enumeration. Each override will skip walking through actuall entries
public:
GCTracing ();
// Override callouts
bool virtual TypeCallout(EventTypeEntry * entryDescriptor);
bool virtual ControllerCallout(ControllerObject *controller, bool finished);
bool virtual EntryCallout(EntryHeader *header, EventTypeEntry * entryDescriptor);
bool virtual MedataCallout(ControllerObject *controller);
void virtual SourcesCallout(SourceEntry * source);
bool virtual StorageCallout(UINT64 storageAddress, bool finished);
private:
void InitFile();
FILE * f;
ControllerObject * controller;
char fname[128];
UINT64 RootsHandle;
UINT64 GenerationsHandle;
UINT64 ObjectHandle;
UINT64 StackHandle;
UINT64 TypeHandle;
UINT64 IntervalHandle;
UINT64 AllocationHandle;
UINT64 FunctionHandle;
UINT64 EventStorageHandle;
UINT64 TypeStorageHandle;
bool ParsingMetadata;
};
#define MAX_PAGE_SIZE 4096
char PageBuffer[MAX_PAGE_SIZE];
void DumpMemory(PCSTR fname)
{
HRESULT status;
ULONG64 pageTable;
ULONG64 pageCount;
ULONG64 pageSize = MAX_PAGE_SIZE;
EXT_CHECK(FindBound("FlatPages::pageTable", &pageTable));
EXT_CHECK(FindBound("FlatPages::pageCount", &pageCount));
// EXT_CHECK(FindBound("nt!MemoryManager::PageSize", &pageSize)); not available
ExtOut("%p %p %p", pageTable, pageCount, pageSize);
FILE * f;
f = fopen( fname, "wb" );
if (f != NULL)
{
for (ULONG64 i = 0; i < pageCount; i++) {
ULONG Descriptor;
if ((i % 1000) == 0) {
ExtOut(".", pageSize * i, Descriptor);
}
if (g_ExtData->ReadVirtual(pageTable + i * sizeof(Descriptor),
&Descriptor, sizeof(Descriptor), NULL) != S_OK)
{
ExtOut("Error reading descriptor\n");
break;
}
if (g_ExtData->ReadVirtual(pageSize * i, PageBuffer, (ULONG)pageSize, NULL) == S_OK)
{
// ExtOut("%p %lx\n", pageSize * i, Descriptor);
fwrite(PageBuffer, (ULONG)pageSize, 1, f);
} else {
memset(PageBuffer, 0, (ULONG)pageSize);
fwrite(PageBuffer, (ULONG)pageSize, 1, f);
ExtOut("%p %lx error\n", pageSize * i, Descriptor);
}
}
fclose(f);
}
return;
Exit:
// Error path
ExtOut("Invalid symbols information\n");
return;
}
void WriteFileRange (int Base, int Size, PCSTR fname) {
FILE * f;
f = fopen( fname, "wb" );
if (f != NULL)
{
char buff[2048];
while (Size)
{
int readsize = Size;
if (readsize > sizeof(buff))
{
readsize = sizeof(buff);
}
if (g_ExtData->ReadVirtual(Base, buff, readsize, NULL) == S_OK)
{
fwrite(buff, readsize, 1, f);
}
Size -= readsize;
Base += readsize;
}
fclose(f);
}
}
void
EnableKernelGCProfiler(ULONG64 MemorySize, bool breakOnRecycle)
{
HRESULT status;
ULONG64 ExistingKernelBufferSize;
ULONG64 KernelBufferAddress;
BOOL OKSymbols = FALSE;
EXT_CHECK(FindBound("Class_Microsoft_Singularity_GCProfilerLogger::c_StorageHandle",
&KernelBufferAddress));
if (KernelBufferAddress != 0)
{
ULONG64 isProfiling;
EXT_CHECK(FindBound("Microsoft_Singularity_V1_Services_DiagnosisService::KernelBufferSize", &ExistingKernelBufferSize));
OKSymbols = TRUE;
ExtOut("GC Profiling is already in use for kernel (%ld MBytes buffer)\n",
ExistingKernelBufferSize/ 1024 / 1024);
ExtOut("The default value cannot be changed dynamically without rebooting the system\n");
EXT_CHECK(FindBound("System_GC::isProfiling", &isProfiling));
if (isProfiling == 0)
{
EXT_CHECK(SetBound("System_GC::isProfiling", 1));
ExtOut("Re-enabling with the existing settings\n");
}
goto Exit;
}
ExtOut("Enabling kernel GC profiler with %ld MBytes buffer size and %sbreak on recycle\n",
(long)(MemorySize/ 1024 / 1024),
(breakOnRecycle ? "" : "NO "));
EXT_CHECK(SetBound("Microsoft_Singularity_V1_Services_DiagnosisService::DeferedCommand", 1));
EXT_CHECK(SetBound("Microsoft_Singularity_V1_Services_DiagnosisService::KernelBufferSize", MemorySize));
EXT_CHECK(SetBound("Microsoft_Singularity_V1_Services_DiagnosisService::KernelProfileOptions", breakOnRecycle));
OKSymbols = TRUE;
Exit:
if (!OKSymbols)
{
ExtOut("Error accessing variables. Please check symbols for kernel\n");
}
return;
}
void
DisableKernelGCProfiler()
{
HRESULT status;
ULONG64 KernelBufferAddress;
BOOL OKSymbols = FALSE;
EXT_CHECK(FindBound("gKernelBufferStart", &KernelBufferAddress));
OKSymbols = TRUE;
if (KernelBufferAddress == 0)
{
ExtOut("GC profiling is not enabled\n");
goto Exit;
}
EXT_CHECK(SetBound("System_GC::isProfiling", 0));
OKSymbols = TRUE;
ExtOut("Kernel GC profiling disabled\n");
Exit:
if (!OKSymbols)
{
ExtOut("Error accessing variables. Please check symbols for kernel\n");
}
return;
}
void
EnableSIPGCProfiler(ULONG64 MemorySize, bool breakOnRecycle)
{
HRESULT status;
BOOL OKSymbols = FALSE;
EXT_CHECK(SetBound("Microsoft_Singularity_V1_Services_DiagnosisService::SIPBufferSize", MemorySize));
EXT_CHECK(SetBound("Microsoft_Singularity_V1_Services_DiagnosisService::SIPProfileOptions", breakOnRecycle));
if (MemorySize == 0)
{
ExtOut("Disabling SIP GC profiler for new processes\n");
}
else
{
ExtOut("Enabling SIP GC profiler with %ld MBytes buffer size and %sbreak on recycle\n",
(long)(MemorySize/ 1024 / 1024),
(breakOnRecycle ? "" : "NO "));
ExtOut("New processes starting from now on will have GC profiler enabled\n");
}
OKSymbols = TRUE;
Exit:
if (!OKSymbols)
{
ExtOut("Error accessing variables. Please check symbols for kernel\n");
}
return;
}
//
// GC CLR writter
//
PCSTR prefixName;
GCTracing::GCTracing ()
{
f = NULL;
EventStorageHandle = 0;
TypeStorageHandle = 0;
}
void GCTracing::InitFile()
{
if (f == NULL) {
char fname[128];
char * controllerName = controller->GetControllerName();
_snprintf(fname, sizeof(fname), "%s_%s.LOG", prefixName, controllerName);
free(controllerName);
f = fopen( fname, "wt" );
}
}
bool GCTracing::TypeCallout(EventTypeEntry * entryDescriptor)
{
if (!EventingEnumerator::TypeCallout(entryDescriptor)) {
if (!EventingEnumerator::TypeCallout(SystemHeaderType)) {
return FALSE;
}
}
if (_stricmp(entryDescriptor->Name, "System.GC_ROOTS") == 0) {
RootsHandle = (UINT64)entryDescriptor->Key;
} else if (_stricmp(entryDescriptor->Name, "System.GC_GENERATIONS") == 0) {
GenerationsHandle = (UINT64)entryDescriptor->Key;
} else if (_stricmp(entryDescriptor->Name, "System.GC_OBJECT") == 0) {
ObjectHandle = (UINT64)entryDescriptor->Key;
} else if (_stricmp(entryDescriptor->Name, "System.GC_STACK") == 0) {
StackHandle = (UINT64)entryDescriptor->Key;
} else if (_stricmp(entryDescriptor->Name, "System.GC_TYPE") == 0) {
TypeHandle = (UINT64)entryDescriptor->Key;
} else if (_stricmp(entryDescriptor->Name, "System.GC_INTERVAL") == 0) {
IntervalHandle = (UINT64)entryDescriptor->Key;
} else if (_stricmp(entryDescriptor->Name, "System.GC_ALLOCATION") == 0) {
AllocationHandle = (UINT64)entryDescriptor->Key;
} else if (_stricmp(entryDescriptor->Name, "System.GC_FUNCTION") == 0) {
FunctionHandle = (UINT64)entryDescriptor->Key;
}
return TRUE;
}
bool GCTracing::ControllerCallout( ControllerObject *ctrl, bool finished)
{
if (finished) {
controller = NULL;
} else {
controller = ctrl;
}
if (f != NULL) fclose(f);
f = NULL;
EventStorageHandle = 0;
TypeStorageHandle = 0;
ParsingMetadata = TRUE;
return TRUE;
}
bool GCTracing::MedataCallout(ControllerObject *controller)
{
ParsingMetadata = FALSE;
if (TypeStorageHandle == 0) return FALSE;
return TRUE;
}
bool GCTracing::StorageCallout(UINT64 storageAddress, bool finished)
{
if (ParsingMetadata) return TRUE;
if (EventStorageHandle == storageAddress) {
ExtOut("Walking event storage %p\n", storageAddress);
return TRUE;
}
if (TypeStorageHandle == storageAddress) {
ExtOut("Walking type storage %p\n", storageAddress);
return TRUE;
}
return FALSE;
}
void GCTracing::SourcesCallout(SourceEntry * source)
{
if (MatchPatternString(exact, source->Name, "GC.Events")) {
EventStorageHandle = source->StorageHandle;
ExtOut("GC source: %s: (Key=%p, Storage=%p, ControlFlags=%lx)\n",
source->Name,
(UINT64)source->Key,
source->StorageHandle,
source->ControlFlags);
} else if (MatchPatternString(exact, source->Name, "GC.TypeDefinitions")) {
TypeStorageHandle = source->StorageHandle;
ExtOut("GC source: %s: (Key=%p, Storage=%p, ControlFlags=%lx)\n",
source->Name,
(UINT64)source->Key,
source->StorageHandle,
source->ControlFlags);
}
}
bool GCTracing::EntryCallout(EntryHeader *header, EventTypeEntry * entryDescriptor)
{
HRESULT status;
if (entryDescriptor == NULL) {
return FALSE;
}
if (entryDescriptor->Key == TypeHandle) {
InitFile();
PCHAR str = entryDescriptor->GetExtendedString(1);
fprintf(f, "t 0x%p 0 %s\n", (ULONG)entryDescriptor->GetField("TypeId")->GetFieldNumericValue(), str);
} else if (entryDescriptor->Key == FunctionHandle) {
InitFile();
ULONG64 displacement = 0;
ULONG64 funcPtr = entryDescriptor->GetField("IP")->GetFieldNumericValue();
status = g_ExtSymbols->GetNameByOffset(funcPtr,
szSymbol,
arrayof(szSymbol),
NULL,
&displacement);
if (status == S_OK) {
fprintf(f, "f 0x%p %s+0x%I64x_0x%p (UNKNOWN ARGUMENTS) 0 0\n",
(ULONG)entryDescriptor->GetField("FnIdx")->GetFieldNumericValue(),
szSymbol,
displacement,
(ULONG)funcPtr);
} else {
fprintf(f, "f 0x%p FUNCTION_EIP 0x%p (UNKNOWN ARGUMENTS) 0 0\n",
(ULONG)entryDescriptor->GetField("FnIdx")->GetFieldNumericValue(),
(ULONG)funcPtr);
}
} else if (entryDescriptor->Key == IntervalHandle) {
InitFile();
fprintf(f, "i 0x%I64x\n", entryDescriptor->GetField("Delta")->GetFieldNumericValue());
} else if (entryDescriptor->Key == AllocationHandle) {
InitFile();
ULONG tid;
ULONG_PTR ctx = SWITCH_TO_METADATA_CURSOR();
FieldEntry * field = SystemHeaderType->GetField("TID");
tid = (ULONG)field->GetFieldNumericValue();
RESTORE_CURSOR(ctx);
fprintf(f, "! 0x%p 0x%p 0x%p\n",
tid,
(ULONG)entryDescriptor->GetField("Object")->GetFieldNumericValue(),
(ULONG)entryDescriptor->GetField("StkNo")->GetFieldNumericValue());
} else if (entryDescriptor->Key == StackHandle) {
InitFile();
fprintf(f, "n 0x%lx 1 0x%lx 0x%lx",
(ULONG)entryDescriptor->GetField("StkNo")->GetFieldNumericValue(),
(ULONG)entryDescriptor->GetField("TypeId")->GetFieldNumericValue(),
(ULONG)entryDescriptor->GetField("Size")->GetFieldNumericValue());
FieldEntry * array = entryDescriptor->GetField("fncList");
void * buffer = entryDescriptor->GetFieldArray((int)array->GetFieldNumericValue());
int count = array->GetArraySize(buffer);
if (count >= 2) {
for (int i = 0; i < count - 2; i++) {
ULONG fnc = (ULONG)array->GetFieldNumericValue(buffer, i);
fprintf(f, " 0x%lx", fnc);
}
}
fprintf(f, "\n");
} else if (entryDescriptor->Key == RootsHandle) {
InitFile();
fprintf(f, "r");
FieldEntry * array = entryDescriptor->GetField("Roots");
void * buffer = entryDescriptor->GetFieldArray((int)array->GetFieldNumericValue());
int count = array->GetArraySize(buffer);
for (int i = 0; i < count; i++) {
ULONG Address = (ULONG)array->GetFieldNumericValue(buffer, i);
fprintf(f, " 0x%lx", Address);
}
fprintf(f, "\n");
} else if (entryDescriptor->Key == GenerationsHandle) {
InitFile();
fprintf(f, "g");
FieldEntry * array = entryDescriptor->GetField("Genrations");
void * buffer = entryDescriptor->GetFieldArray((int)array->GetFieldNumericValue());
int count = array->GetArraySize(buffer);
for (int i = 0; i < count; i++) {
ULONG Address = (ULONG)array->GetFieldNumericValue(buffer, i);
fprintf(f, " %ld", Address);
}
fprintf(f, "\n");
} else if (entryDescriptor->Key == ObjectHandle) {
InitFile();
fprintf(f, "o");
FieldEntry * array = entryDescriptor->GetField("ParameterList");
void * buffer = entryDescriptor->GetFieldArray((int)array->GetFieldNumericValue());
int count = array->GetArraySize(buffer);
for (int i = 0; i < count; i++) {
ULONG Address = (ULONG)array->GetFieldNumericValue(buffer, i);
fprintf(f, " 0x%lx", Address);
}
fprintf(f, "\n");
}
return TRUE;
}
void
WriteCLRProfileFile(PCSTR fname, bool Kernel)
{
prefixName = fname;
TypeEntryCollector * collector = new TypeEntryCollector(0, "Timestamp");
if (collector) {
GCTracing * dump = new GCTracing();
if (dump) {
collector->TypeHandle = 0;
collector->GroupByController = TRUE;
collector->cascadeEnumerator = dump;
WalkTracingDatabase(collector, FALSE);
delete dump;
}
delete collector;
}
}