Skip to content

Commit e734fde

Browse files
author
Mike McLaughlin
authored
Fix VS4Mac crash report and core dump generation perf problems (#60205)
Refactor the DAC enumerate memory region phase out of gather crash info This is so the crash report json is written and available before the core dump which for VS4Mac currently takes 4 minutes. Since on both Linux and MacOS all the RW regions have been already added by createdump itself for heap dumps, then the sometimes slow (4 minutes for VS4Mac) heap enum memory region is changed to the faster normal one. It adds necessary DAC globals, etc. without the costly assembly, module, class, type runtime data structure enumeration. This fast heap dumps is opt'ed in with COMPlus_DbgEnableFastHeapDumps=1 env var to mitigate the risk of something missing from these dumps. Move current_c_gc_state out of !MULTIPLE_HEAPS
1 parent 3d8a6e6 commit e734fde

File tree

6 files changed

+142
-96
lines changed

6 files changed

+142
-96
lines changed

src/coreclr/debug/createdump/crashinfo.cpp

Lines changed: 119 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ CrashInfo::CrashInfo(pid_t pid, bool gatherFrames, pid_t crashThread, uint32_t s
1313
m_pid(pid),
1414
m_ppid(-1),
1515
m_hdac(nullptr),
16+
m_pClrDataEnumRegions(nullptr),
17+
m_pClrDataProcess(nullptr),
1618
m_gatherFrames(gatherFrames),
1719
m_crashThread(crashThread),
1820
m_signal(signal),
@@ -44,6 +46,15 @@ CrashInfo::~CrashInfo()
4446
}
4547
m_moduleInfos.clear();
4648

49+
// Clean up DAC interfaces
50+
if (m_pClrDataEnumRegions != nullptr)
51+
{
52+
m_pClrDataEnumRegions->Release();
53+
}
54+
if (m_pClrDataProcess != nullptr)
55+
{
56+
m_pClrDataProcess->Release();
57+
}
4758
// Unload DAC module
4859
if (m_hdac != nullptr)
4960
{
@@ -190,8 +201,16 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType)
190201
thread->GetThreadStack();
191202
}
192203
}
193-
// Gather all the useful memory regions from the DAC
194-
if (!EnumerateMemoryRegionsWithDAC(minidumpType))
204+
// Load and initialize DAC interfaces
205+
if (!InitializeDAC())
206+
{
207+
return false;
208+
}
209+
if (!EnumerateManagedModules())
210+
{
211+
return false;
212+
}
213+
if (!UnwindAllThreads())
195214
{
196215
return false;
197216
}
@@ -204,19 +223,15 @@ CrashInfo::GatherCrashInfo(MINIDUMP_TYPE minidumpType)
204223
// Enumerate all the memory regions using the DAC memory region support given a minidump type
205224
//
206225
bool
207-
CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType)
226+
CrashInfo::InitializeDAC()
208227
{
209228
ReleaseHolder<DumpDataTarget> dataTarget = new DumpDataTarget(*this);
210229
PFN_CLRDataCreateInstance pfnCLRDataCreateInstance = nullptr;
211-
ICLRDataEnumMemoryRegions* pClrDataEnumRegions = nullptr;
212-
IXCLRDataProcess* pClrDataProcess = nullptr;
213-
HRESULT hr = S_OK;
214230
bool result = false;
231+
HRESULT hr = S_OK;
215232

216233
if (!m_coreclrPath.empty())
217234
{
218-
TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration STARTED\n");
219-
220235
// We assume that the DAC is in the same location as the libcoreclr.so module
221236
std::string dacPath;
222237
dacPath.append(m_coreclrPath);
@@ -235,140 +250,156 @@ CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType)
235250
fprintf(stderr, "GetProcAddress(CLRDataCreateInstance) FAILED %d\n", GetLastError());
236251
goto exit;
237252
}
238-
if ((minidumpType & MiniDumpWithFullMemory) == 0)
239-
{
240-
hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&pClrDataEnumRegions);
241-
if (FAILED(hr))
242-
{
243-
fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr);
244-
goto exit;
245-
}
246-
// Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC
247-
hr = pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT);
248-
if (FAILED(hr))
249-
{
250-
fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr);
251-
goto exit;
252-
}
253-
}
254-
hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&pClrDataProcess);
253+
hr = pfnCLRDataCreateInstance(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&m_pClrDataEnumRegions);
255254
if (FAILED(hr))
256255
{
257-
fprintf(stderr, "CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n", hr);
256+
fprintf(stderr, "CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED %08x\n", hr);
258257
goto exit;
259258
}
260-
TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration FINISHED\n");
261-
if (!EnumerateManagedModules(pClrDataProcess))
259+
hr = pfnCLRDataCreateInstance(__uuidof(IXCLRDataProcess), dataTarget, (void**)&m_pClrDataProcess);
260+
if (FAILED(hr))
262261
{
262+
fprintf(stderr, "CLRDataCreateInstance(IXCLRDataProcess) FAILED %08x\n", hr);
263263
goto exit;
264264
}
265265
}
266-
else {
267-
TRACE("EnumerateMemoryRegionsWithDAC: coreclr not found; not using DAC\n");
268-
}
269-
if (!UnwindAllThreads(pClrDataProcess))
266+
else
270267
{
271-
goto exit;
268+
TRACE("InitializeDAC: coreclr not found; not using DAC\n");
272269
}
273270
result = true;
274271
exit:
275-
if (pClrDataEnumRegions != nullptr)
276-
{
277-
pClrDataEnumRegions->Release();
278-
}
279-
if (pClrDataProcess != nullptr)
272+
return result;
273+
}
274+
275+
//
276+
// Enumerate all the memory regions using the DAC memory region support given a minidump type
277+
//
278+
bool
279+
CrashInfo::EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType)
280+
{
281+
if (m_pClrDataEnumRegions != nullptr && (minidumpType & MiniDumpWithFullMemory) == 0)
280282
{
281-
pClrDataProcess->Release();
283+
TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration STARTED\n");
284+
285+
// Since on both Linux and MacOS all the RW regions will be added for heap
286+
// dumps by createdump, the only thing differentiating a MiniDumpNormal and
287+
// a MiniDumpWithPrivateReadWriteMemory is that the later uses the EnumMemory
288+
// APIs. This is kind of expensive on larger applications (4 minutes, or even
289+
// more), and this should already be in RW pages. Change the dump type to the
290+
// faster normal one. This one already ensures necessary DAC globals, etc.
291+
// without the costly assembly, module, class, type runtime data structures
292+
// enumeration.
293+
if (minidumpType & MiniDumpWithPrivateReadWriteMemory)
294+
{
295+
char* fastHeapDumps = getenv("COMPlus_DbgEnableFastHeapDumps");
296+
if (fastHeapDumps != nullptr && strcmp(fastHeapDumps, "1") == 0)
297+
{
298+
minidumpType = MiniDumpNormal;
299+
}
300+
}
301+
// Calls CrashInfo::EnumMemoryRegion for each memory region found by the DAC
302+
HRESULT hr = m_pClrDataEnumRegions->EnumMemoryRegions(this, minidumpType, CLRDATA_ENUM_MEM_DEFAULT);
303+
if (FAILED(hr))
304+
{
305+
fprintf(stderr, "EnumMemoryRegions FAILED %08x\n", hr);
306+
return false;
307+
}
308+
TRACE("EnumerateMemoryRegionsWithDAC: Memory enumeration FINISHED\n");
282309
}
283-
return result;
310+
return true;
284311
}
285312

286313
//
287314
// Enumerate all the managed modules and replace the module mapping with the module name found.
288315
//
289316
bool
290-
CrashInfo::EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess)
317+
CrashInfo::EnumerateManagedModules()
291318
{
292319
CLRDATA_ENUM enumModules = 0;
293-
bool result = true;
294320
HRESULT hr = S_OK;
295321

296-
if (FAILED(hr = pClrDataProcess->StartEnumModules(&enumModules))) {
297-
fprintf(stderr, "StartEnumModules FAILED %08x\n", hr);
298-
return false;
299-
}
300-
301-
while (true)
322+
if (m_pClrDataProcess != nullptr)
302323
{
303-
ReleaseHolder<IXCLRDataModule> pClrDataModule;
304-
if ((hr = pClrDataProcess->EnumModule(&enumModules, &pClrDataModule)) != S_OK) {
305-
break;
306-
}
324+
TRACE("EnumerateManagedModules: Module enumeration STARTED\n");
307325

308-
// Skip any dynamic modules. The Request call below on some DACs crashes on dynamic modules.
309-
ULONG32 flags;
310-
if ((hr = pClrDataModule->GetFlags(&flags)) != S_OK) {
311-
TRACE("MODULE: GetFlags FAILED %08x\n", hr);
312-
continue;
313-
}
314-
if (flags & CLRDATA_MODULE_IS_DYNAMIC) {
315-
TRACE("MODULE: Skipping dynamic module\n");
316-
continue;
326+
if (FAILED(hr = m_pClrDataProcess->StartEnumModules(&enumModules))) {
327+
fprintf(stderr, "StartEnumModules FAILED %08x\n", hr);
328+
return false;
317329
}
318330

319-
DacpGetModuleData moduleData;
320-
if (SUCCEEDED(hr = moduleData.Request(pClrDataModule.GetPtr())))
331+
while (true)
321332
{
322-
TRACE("MODULE: %" PRIA PRIx64 " dyn %d inmem %d file %d pe %" PRIA PRIx64 " pdb %" PRIA PRIx64, (uint64_t)moduleData.LoadedPEAddress, moduleData.IsDynamic,
323-
moduleData.IsInMemory, moduleData.IsFileLayout, (uint64_t)moduleData.PEAssembly, (uint64_t)moduleData.InMemoryPdbAddress);
333+
ReleaseHolder<IXCLRDataModule> pClrDataModule;
334+
if ((hr = m_pClrDataProcess->EnumModule(&enumModules, &pClrDataModule)) != S_OK) {
335+
break;
336+
}
324337

325-
if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0)
338+
// Skip any dynamic modules. The Request call below on some DACs crashes on dynamic modules.
339+
ULONG32 flags;
340+
if ((hr = pClrDataModule->GetFlags(&flags)) != S_OK) {
341+
TRACE("MODULE: GetFlags FAILED %08x\n", hr);
342+
continue;
343+
}
344+
if (flags & CLRDATA_MODULE_IS_DYNAMIC) {
345+
TRACE("MODULE: Skipping dynamic module\n");
346+
continue;
347+
}
348+
349+
DacpGetModuleData moduleData;
350+
if (SUCCEEDED(hr = moduleData.Request(pClrDataModule.GetPtr())))
326351
{
327-
ArrayHolder<WCHAR> wszUnicodeName = new WCHAR[MAX_LONGPATH + 1];
328-
if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName)))
352+
TRACE("MODULE: %" PRIA PRIx64 " dyn %d inmem %d file %d pe %" PRIA PRIx64 " pdb %" PRIA PRIx64, (uint64_t)moduleData.LoadedPEAddress, moduleData.IsDynamic,
353+
moduleData.IsInMemory, moduleData.IsFileLayout, (uint64_t)moduleData.PEAssembly, (uint64_t)moduleData.InMemoryPdbAddress);
354+
355+
if (!moduleData.IsDynamic && moduleData.LoadedPEAddress != 0)
329356
{
330-
std::string moduleName = FormatString("%S", wszUnicodeName.GetPtr());
357+
ArrayHolder<WCHAR> wszUnicodeName = new WCHAR[MAX_LONGPATH + 1];
358+
if (SUCCEEDED(hr = pClrDataModule->GetFileName(MAX_LONGPATH, nullptr, wszUnicodeName)))
359+
{
360+
std::string moduleName = FormatString("%S", wszUnicodeName.GetPtr());
331361

332-
// Change the module mapping name
333-
ReplaceModuleMapping(moduleData.LoadedPEAddress, moduleData.LoadedPESize, moduleName);
362+
// Change the module mapping name
363+
ReplaceModuleMapping(moduleData.LoadedPEAddress, moduleData.LoadedPESize, moduleName);
334364

335-
// Add managed module info
336-
AddModuleInfo(true, moduleData.LoadedPEAddress, pClrDataModule, moduleName);
365+
// Add managed module info
366+
AddModuleInfo(true, moduleData.LoadedPEAddress, pClrDataModule, moduleName);
367+
}
368+
else {
369+
TRACE("\nModule.GetFileName FAILED %08x\n", hr);
370+
}
337371
}
338372
else {
339-
TRACE("\nModule.GetFileName FAILED %08x\n", hr);
373+
TRACE("\n");
340374
}
341375
}
342376
else {
343-
TRACE("\n");
377+
TRACE("moduleData.Request FAILED %08x\n", hr);
344378
}
345379
}
346-
else {
347-
TRACE("moduleData.Request FAILED %08x\n", hr);
348-
}
349-
}
350380

351-
if (enumModules != 0) {
352-
pClrDataProcess->EndEnumModules(enumModules);
381+
if (enumModules != 0) {
382+
m_pClrDataProcess->EndEnumModules(enumModules);
383+
}
384+
TRACE("EnumerateManagedModules: Module enumeration FINISHED\n");
353385
}
354-
355-
return result;
386+
return true;
356387
}
357388

358389
//
359390
// Unwind all the native threads to ensure that the dwarf unwind info is added to the core dump.
360391
//
361392
bool
362-
CrashInfo::UnwindAllThreads(IXCLRDataProcess* pClrDataProcess)
393+
CrashInfo::UnwindAllThreads()
363394
{
364395
ReleaseHolder<ISOSDacInterface> pSos = nullptr;
365-
if (pClrDataProcess != nullptr) {
366-
pClrDataProcess->QueryInterface(__uuidof(ISOSDacInterface), (void**)&pSos);
396+
if (m_pClrDataProcess != nullptr) {
397+
m_pClrDataProcess->QueryInterface(__uuidof(ISOSDacInterface), (void**)&pSos);
367398
}
368399
// For each native and managed thread
369400
for (ThreadInfo* thread : m_threads)
370401
{
371-
if (!thread->UnwindThread(pClrDataProcess, pSos)) {
402+
if (!thread->UnwindThread(m_pClrDataProcess, pSos)) {
372403
return false;
373404
}
374405
}
@@ -827,5 +858,5 @@ FormatGuid(const GUID* guid)
827858
{
828859
uint8_t* bytes = (uint8_t*)guid;
829860
return FormatString("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
830-
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]);
861+
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]);
831862
}

src/coreclr/debug/createdump/crashinfo.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
4747
pid_t m_ppid; // parent pid
4848
pid_t m_tgid; // process group
4949
HMODULE m_hdac; // dac module handle when loaded
50+
ICLRDataEnumMemoryRegions* m_pClrDataEnumRegions; // dac enumerate memory interface instance
51+
IXCLRDataProcess* m_pClrDataProcess; // dac process interface instance
5052
bool m_gatherFrames; // if true, add the native and managed stack frames to the thread info
5153
pid_t m_crashThread; // crashing thread id or 0 if none
5254
uint32_t m_signal; // crash signal code or 0 if none
@@ -84,6 +86,7 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
8486
void CleanupAndResumeProcess();
8587
bool EnumerateAndSuspendThreads();
8688
bool GatherCrashInfo(MINIDUMP_TYPE minidumpType);
89+
bool EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType);
8790
bool ReadMemory(void* address, void* buffer, size_t size); // read memory and add to dump
8891
bool ReadProcessMemory(void* address, void* buffer, size_t size, size_t* read); // read raw memory
8992
uint64_t GetBaseAddressFromAddress(uint64_t address);
@@ -137,9 +140,9 @@ class CrashInfo : public ICLRDataEnumMemoryRegionsCallback,
137140
void VisitProgramHeader(uint64_t loadbias, uint64_t baseAddress, ElfW(Phdr)* phdr);
138141
bool EnumerateModuleMappings();
139142
#endif
140-
bool EnumerateMemoryRegionsWithDAC(MINIDUMP_TYPE minidumpType);
141-
bool EnumerateManagedModules(IXCLRDataProcess* pClrDataProcess);
142-
bool UnwindAllThreads(IXCLRDataProcess* pClrDataProcess);
143+
bool InitializeDAC();
144+
bool EnumerateManagedModules();
145+
bool UnwindAllThreads();
143146
void ReplaceModuleMapping(CLRDATA_ADDRESS baseAddress, ULONG64 size, const std::string& pszName);
144147
void InsertMemoryBackedRegion(const MemoryRegion& region);
145148
void InsertMemoryRegion(const MemoryRegion& region);

src/coreclr/debug/createdump/createdumpunix.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ CreateDump(const char* dumpPathTemplate, int pid, const char* dumpType, MINIDUMP
4747
CrashReportWriter crashReportWriter(*crashInfo);
4848
crashReportWriter.WriteCrashReport(dumpPath);
4949
}
50-
printf("Writing %s to file %s\n", dumpType, dumpPath.c_str());
50+
// Gather all the useful memory regions from the DAC
51+
if (!crashInfo->EnumerateMemoryRegionsWithDAC(minidumpType))
52+
{
53+
goto exit;
54+
}
55+
fprintf(stdout, "Writing %s to file %s\n", dumpType, dumpPath.c_str());
5156

5257
// Write the actual dump file
5358
if (!dumpWriter.OpenDump(dumpPath.c_str()))

src/coreclr/debug/daccess/enummem.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,7 @@ HRESULT ClrDataAccess::EnumMemoryRegionsWorkerSkinny(IN CLRDataEnumMemoryFlags f
15971597
//
15981598
// collect CLR static
15991599
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRStatic(flags); )
1600+
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemCLRHeapCrticalStatic(flags); );
16001601

16011602
// Dump AppDomain-specific info needed for MiniDumpNormal.
16021603
CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED( status = EnumMemDumpAppDomainInfo(flags); )

src/coreclr/debug/daccess/request_svr.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,11 @@ ClrDataAccess::ServerGCHeapDetails(CLRDATA_ADDRESS heapAddr, DacpGcHeapDetails *
122122

123123
detailsData->lowest_address = PTR_CDADDR(g_lowest_address);
124124
detailsData->highest_address = PTR_CDADDR(g_highest_address);
125-
detailsData->current_c_gc_state = (CLRDATA_ADDRESS)*g_gcDacGlobals->current_c_gc_state;
126-
125+
detailsData->current_c_gc_state = c_gc_state_free;
126+
if (g_gcDacGlobals->current_c_gc_state != NULL)
127+
{
128+
detailsData->current_c_gc_state = (CLRDATA_ADDRESS)*g_gcDacGlobals->current_c_gc_state;
129+
}
127130
// now get information specific to this heap (server mode gives us several heaps; we're getting
128131
// information about only one of them.
129132
detailsData->alloc_allocated = (CLRDATA_ADDRESS)pHeap->alloc_allocated;

src/coreclr/gc/gc.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46708,11 +46708,15 @@ void PopulateDacVars(GcDacVars *gcDacVars)
4670846708
gcDacVars->generation_size = sizeof(generation);
4670946709
gcDacVars->total_generation_count = total_generation_count;
4671046710
gcDacVars->max_gen = &g_max_generation;
46711+
#ifdef BACKGROUND_GC
46712+
gcDacVars->current_c_gc_state = const_cast<c_gc_state*>(&gc_heap::current_c_gc_state);
46713+
#else //BACKGROUND_GC
46714+
gcDacVars->current_c_gc_state = 0;
46715+
#endif //BACKGROUND_GC
4671146716
#ifndef MULTIPLE_HEAPS
4671246717
gcDacVars->ephemeral_heap_segment = reinterpret_cast<dac_heap_segment**>(&gc_heap::ephemeral_heap_segment);
4671346718
#ifdef BACKGROUND_GC
4671446719
gcDacVars->mark_array = &gc_heap::mark_array;
46715-
gcDacVars->current_c_gc_state = const_cast<c_gc_state*>(&gc_heap::current_c_gc_state);
4671646720
gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address;
4671746721
gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address;
4671846722
gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj;
@@ -46725,7 +46729,6 @@ void PopulateDacVars(GcDacVars *gcDacVars)
4672546729
#endif //USE_REGIONS
4672646730
#else //BACKGROUND_GC
4672746731
gcDacVars->mark_array = 0;
46728-
gcDacVars->current_c_gc_state = 0;
4672946732
gcDacVars->background_saved_lowest_address = 0;
4673046733
gcDacVars->background_saved_highest_address = 0;
4673146734
gcDacVars->next_sweep_obj = 0;

0 commit comments

Comments
 (0)