-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Fix VS4Mac crash report and core dump generation perf problems #60205
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
Changes from all commits
a14b23c
e8bd4c0
8eea698
96ee054
f93a60e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,8 @@ CrashInfo::CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t s | |
| m_pid(pid), | ||
| m_ppid(-1), | ||
| m_hdac(nullptr), | ||
| m_pClrDataEnumRegions(nullptr), | ||
| m_pClrDataProcess(nullptr), | ||
| m_gatherFrames(gatherFrames), | ||
| m_crashThread(crashThread), | ||
| m_signal(signal), | ||
|
|
@@ -44,6 +46,15 @@ CrashInfo::~CrashInfo() | |
| } | ||
| m_moduleInfos.clear(); | ||
|
|
||
| // Clean up DAC interfaces | ||
| if (m_pClrDataEnumRegions != nullptr) | ||
| { | ||
| m_pClrDataEnumRegions->Release(); | ||
| } | ||
| if (m_pClrDataProcess != nullptr) | ||
| { | ||
| m_pClrDataProcess->Release(); | ||
| } | ||
| // Unload DAC module | ||
| if (m_hdac != nullptr) | ||
| { | ||
|
|
@@ -190,8 +201,16 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) | |
| thread->GetThreadStack(); | ||
| } | ||
| } | ||
| // Gather all the useful memory regions from the DAC | ||
| if (!EnumerateMemoryRegionsWithDAC(minidumpType)) | ||
| // Load and initialize DAC interfaces | ||
| if (!InitializeDAC()) | ||
| { | ||
| return false; | ||
| } | ||
| if (!EnumerateManagedModules()) | ||
| { | ||
| return false; | ||
| } | ||
| if (!UnwindAllThreads()) | ||
| { | ||
| return false; | ||
| } | ||
|
|
@@ -204,19 +223,15 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType) | |
| // Enumerate all the memory regions using the DAC memory region support given a minidump type | ||
| // | ||
| bool | ||
| CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) | ||
| CrashInfo::InitializeDAC() | ||
| { | ||
| ReleaseHolder<DumpDataTarget> dataTarget = new DumpDataTarget(*this); | ||
| PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr; | ||
| ICLRDataEnumMemoryRegions* pClrDataEnumRegions = nullptr; | ||
| IXCLRDataProcess* pClrDataProcess = nullptr; | ||
| HRESULT hr = S_OK; | ||
| bool result = false; | ||
| HRESULT hr = S_OK; | ||
|
|
||
| if (!m_coreclrPath.empty()) | ||
| { | ||
| TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration STARTED\n"); | ||
|
|
||
| // We assume that the DAC is in the same location as the libcoreclr.so module | ||
| std::string dacPath; | ||
| dacPath.append(m_coreclrPath); | ||
|
|
@@ -235,140 +250,156 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) | |
| fprintf(stderr, "GetProcAddress(CLRDataCreateInstance) FAILED %d\n", GetLastError()); | ||
| goto exit; | ||
| } | ||
| if ((minidumpType & MiniDumpWithFullMemory) == 0) | ||
| { | ||
| hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&pClrDataEnumRegions); | ||
| if (FAILED(hr)) | ||
| { | ||
| fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr); | ||
| goto exit; | ||
| } | ||
| // Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC | ||
| hr = pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT); | ||
| if (FAILED(hr)) | ||
| { | ||
| fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr); | ||
| goto exit; | ||
| } | ||
| } | ||
| hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&pClrDataProcess); | ||
| hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&m_pClrDataEnumRegions); | ||
| if (FAILED(hr)) | ||
| { | ||
| fprintf(stderr, "CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n", hr); | ||
| fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr); | ||
| goto exit; | ||
| } | ||
| TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration FINISHED\n"); | ||
| if (!EnumerateManagedModules(pClrDataProcess)) | ||
| hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&m_pClrDataProcess); | ||
| if (FAILED(hr)) | ||
| { | ||
| fprintf(stderr, "CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n", hr); | ||
| goto exit; | ||
| } | ||
| } | ||
| else { | ||
| TRACE("EnumerateMemoryRegionsWithDAC: coreclr not found; not using DAC\n"); | ||
| } | ||
| if (!UnwindAllThreads(pClrDataProcess)) | ||
| else | ||
| { | ||
| goto exit; | ||
| TRACE("InitializeDAC: coreclr not found; not using DAC\n"); | ||
| } | ||
| result = true; | ||
| exit: | ||
| if (pClrDataEnumRegions != nullptr) | ||
| { | ||
| pClrDataEnumRegions->Release(); | ||
| } | ||
| if (pClrDataProcess != nullptr) | ||
| return result; | ||
| } | ||
|
|
||
| // | ||
| // Enumerate all the memory regions using the DAC memory region support given a minidump type | ||
| // | ||
| bool | ||
| CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType) | ||
| { | ||
| if (m_pClrDataEnumRegions != nullptr && (minidumpType & MiniDumpWithFullMemory) == 0) | ||
| { | ||
| pClrDataProcess->Release(); | ||
| TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration STARTED\n"); | ||
|
|
||
| // Since on both Linux and MacOS all the RW regions will be added for heap | ||
| // dumps by createdump, the only thing differentiating a MiniDumpNormal and | ||
| // a MiniDumpWithPrivateReadWriteMemory is that the later uses the EnumMemory | ||
| // APIs. This is kind of expensive on larger applications (4 minutes, or even | ||
| // more), and this should already be in RW pages. Change the dump type to the | ||
| // faster normal one. This one already ensures necessary DAC globals, etc. | ||
| // without the costly assembly, module, class, type runtime data structures | ||
| // enumeration. | ||
| if (minidumpType & MiniDumpWithPrivateReadWriteMemory) | ||
| { | ||
| char* fastHeapDumps = getenv("COMPlus_DbgEnableFastHeapDumps"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only part I don't like about this is we are back to adding debt around the COMPlus_ vs DOTNET_. It's fixed in main - and I don't want to regress this. Granted - this is in 7.0 and I don't know if it's better in a follow up PR or on this one and just ignore that commit on the cherrypick. Basically
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I don't like that either. After this gets backported to 6.0 I'm going to either move it to a command option and do the env var in the pal launch code or use the clrconfigNoCache directly. |
||
| if (fastHeapDumps != nullptr && strcmp(fastHeapDumps, "1") == 0) | ||
| { | ||
| minidumpType = MiniDumpNormal; | ||
| } | ||
| } | ||
| // Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC | ||
| HRESULT hr = m_pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT); | ||
| if (FAILED(hr)) | ||
| { | ||
| fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr); | ||
| return false; | ||
| } | ||
| TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration FINISHED\n"); | ||
| } | ||
| return result; | ||
| return true; | ||
| } | ||
|
|
||
| // | ||
| // Enumerate all the managed modules and replace the module mapping with the module name found. | ||
| // | ||
| bool | ||
| CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess) | ||
| CrashInfo::EnumerateManagedModules() | ||
| { | ||
| CLRDATA_ENUM enumModules = 0; | ||
| bool result = true; | ||
| HRESULT hr = S_OK; | ||
|
|
||
| if (FAILED(hr = pClrDataProcess->StartEnumModules(&enumModules))) { | ||
| fprintf(stderr, "StartEnumModules FAILED %08x\n", hr); | ||
| return false; | ||
| } | ||
|
|
||
| while (true) | ||
| if (m_pClrDataProcess != nullptr) | ||
| { | ||
| ReleaseHolder<IXCLRDataModule> pClrDataModule; | ||
| if ((hr = pClrDataProcess->EnumModule(&enumModules, &pClrDataModule)) != S_OK) { | ||
| break; | ||
| } | ||
| TRACE("EnumerateManagedModules: Module enumeration STARTED\n"); | ||
|
|
||
| // Skip any dynamic modules. The Request call below on some DACs crashes on dynamic modules. | ||
| ULONG32 flags; | ||
| if ((hr = pClrDataModule->GetFlags(&flags)) != S_OK) { | ||
| TRACE("MODULE: GetFlags FAILED %08x\n", hr); | ||
| continue; | ||
| } | ||
| if (flags & CLRDATA_MODULE_IS_DYNAMIC) { | ||
| TRACE("MODULE: Skipping dynamic module\n"); | ||
| continue; | ||
| if (FAILED(hr = m_pClrDataProcess->StartEnumModules(&enumModules))) { | ||
| fprintf(stderr, "StartEnumModules FAILED %08x\n", hr); | ||
| return false; | ||
| } | ||
|
|
||
| DacpGetModuleData moduleData; | ||
| if (SUCCEEDED(hr = moduleData.Request(pClrDataModule.GetPtr()))) | ||
| while (true) | ||
| { | ||
| TRACE("MODULE: %" PRIA PRIx64 " dyn %d inmem %d file %d pe %" PRIA PRIx64 " pdb %" PRIA PRIx64, (uint64_t)moduleData.LoadedPEAddress, moduleData.IsDynamic, | ||
| moduleData.IsInMemory, moduleData.IsFileLayout, (uint64_t)moduleData.PEAssembly, (uint64_t)moduleData.InMemoryPdbAddress); | ||
| ReleaseHolder<IXCLRDataModule> pClrDataModule; | ||
| if ((hr = m_pClrDataProcess->EnumModule(&enumModules, &pClrDataModule)) != S_OK) { | ||
| break; | ||
| } | ||
|
|
||
| if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0) | ||
| // Skip any dynamic modules. The Request call below on some DACs crashes on dynamic modules. | ||
| ULONG32 flags; | ||
| if ((hr = pClrDataModule->GetFlags(&flags)) != S_OK) { | ||
| TRACE("MODULE: GetFlags FAILED %08x\n", hr); | ||
| continue; | ||
| } | ||
| if (flags & CLRDATA_MODULE_IS_DYNAMIC) { | ||
| TRACE("MODULE: Skipping dynamic module\n"); | ||
| continue; | ||
| } | ||
|
|
||
| DacpGetModuleData moduleData; | ||
| if (SUCCEEDED(hr = moduleData.Request(pClrDataModule.GetPtr()))) | ||
| { | ||
| ArrayHolder<WCHAR> wszUnicodeName = new WCHAR[MAX_LONGPATH + 1]; | ||
| if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName))) | ||
| TRACE("MODULE: %" PRIA PRIx64 " dyn %d inmem %d file %d pe %" PRIA PRIx64 " pdb %" PRIA PRIx64, (uint64_t)moduleData.LoadedPEAddress, moduleData.IsDynamic, | ||
| moduleData.IsInMemory, moduleData.IsFileLayout, (uint64_t)moduleData.PEAssembly, (uint64_t)moduleData.InMemoryPdbAddress); | ||
|
|
||
| if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0) | ||
| { | ||
| std::string moduleName = FormatString("%S", wszUnicodeName.GetPtr()); | ||
| ArrayHolder<WCHAR> wszUnicodeName = new WCHAR[MAX_LONGPATH + 1]; | ||
| if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName))) | ||
| { | ||
| std::string moduleName = FormatString("%S", wszUnicodeName.GetPtr()); | ||
|
|
||
| // Change the module mapping name | ||
| ReplaceModuleMapping(moduleData.LoadedPEAddress, moduleData.LoadedPESize, moduleName); | ||
| // Change the module mapping name | ||
| ReplaceModuleMapping(moduleData.LoadedPEAddress, moduleData.LoadedPESize, moduleName); | ||
|
|
||
| // Add managed module info | ||
| AddModuleInfo(true, moduleData.LoadedPEAddress, pClrDataModule, moduleName); | ||
| // Add managed module info | ||
| AddModuleInfo(true, moduleData.LoadedPEAddress, pClrDataModule, moduleName); | ||
| } | ||
| else { | ||
| TRACE("\nModule.GetFileName FAILED %08x\n", hr); | ||
| } | ||
| } | ||
| else { | ||
| TRACE("\nModule.GetFileName FAILED %08x\n", hr); | ||
| TRACE("\n"); | ||
| } | ||
| } | ||
| else { | ||
| TRACE("\n"); | ||
| TRACE("moduleData.Request FAILED %08x\n", hr); | ||
| } | ||
| } | ||
| else { | ||
| TRACE("moduleData.Request FAILED %08x\n", hr); | ||
| } | ||
| } | ||
|
|
||
| if (enumModules != 0) { | ||
| pClrDataProcess->EndEnumModules(enumModules); | ||
| if (enumModules != 0) { | ||
| m_pClrDataProcess->EndEnumModules(enumModules); | ||
| } | ||
| TRACE("EnumerateManagedModules: Module enumeration FINISHED\n"); | ||
| } | ||
|
|
||
| return result; | ||
| return true; | ||
| } | ||
|
|
||
| // | ||
| // Unwind all the native threads to ensure that the dwarf unwind info is added to the core dump. | ||
| // | ||
| bool | ||
| CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess) | ||
| CrashInfo::UnwindAllThreads() | ||
| { | ||
| ReleaseHolder<ISOSDacInterface> pSos = nullptr; | ||
| if (pClrDataProcess != nullptr) { | ||
| pClrDataProcess->QueryInterface(__uuidof(ISOSDacInterface), (void**)&pSos); | ||
| if (m_pClrDataProcess != nullptr) { | ||
| m_pClrDataProcess->QueryInterface(__uuidof(ISOSDacInterface), (void**)&pSos); | ||
| } | ||
| // For each native and managed thread | ||
| for (ThreadInfo* thread : m_threads) | ||
| { | ||
| if (!thread->UnwindThread(pClrDataProcess, pSos)) { | ||
| if (!thread->UnwindThread(m_pClrDataProcess, pSos)) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
@@ -827,5 +858,5 @@ FormatGuid(const GUID* guid) | |
| { | ||
| uint8_t* bytes = (uint8_t*)guid; | ||
| return FormatString("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", | ||
| bytes[3], bytes[2], bytes[1], bytes[0], bytes[5], bytes[4], bytes[7], bytes[6], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); | ||
| bytes[3], bytes[2], bytes[1], bytes[0], bytes[5], bytes[4], bytes[7], bytes[6], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -122,8 +122,11 @@ ClrDataAccess::ServerGCHeapDetails(CLRDATA_ADDRESS heapAddr, DacpGcHeapDetails * | |
|
|
||
| detailsData->lowest_address = PTR_CDADDR(g_lowest_address); | ||
| detailsData->highest_address = PTR_CDADDR(g_highest_address); | ||
| detailsData->current_c_gc_state = (CLRDATA_ADDRESS)*g_gcDacGlobals->current_c_gc_state; | ||
|
|
||
| detailsData->current_c_gc_state = c_gc_state_free; | ||
| if (g_gcDacGlobals->current_c_gc_state != NULL) | ||
| { | ||
| detailsData->current_c_gc_state = (CLRDATA_ADDRESS)*g_gcDacGlobals->current_c_gc_state; | ||
|
||
| } | ||
| // now get information specific to this heap (server mode gives us several heaps; we're getting | ||
| // information about only one of them. | ||
| detailsData->alloc_allocated = (CLRDATA_ADDRESS)pHeap->alloc_allocated; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.