///////////////////////////////////////////////////////////////////////////// // // 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; } }