Skip to content

Commit 33db161

Browse files
max-charlambMax CharlambCopilot
authored
[cDAC] Enumerate R2R hashmap pages into triage dumps for odd entry points (#129931)
## Summary Fix the cDAC's R2R stack walk aborting a frame past a `SoftwareExceptionFrame` when reading macOS triage minidumps. The root cause is an odd-entry-point optimization in the legacy DAC that skips a hashmap probe, so the bucket pages that probe would touch are never enumerated into the triage dump -- and the cDAC consumer then faults reading those not-in-dump pages. Tracking issue: dotnet/diagnostics#5910 -- full repro, captured failure trace, and dump-file page-map evidence are there. ## Root cause `ReadyToRunInfo::GetMethodDescForEntryPointInNativeImage` looks up an entry point in the `EntryPointToMethodDescMap` hashmap. It has an x64/x86 fast path that returns early when the entry point is odd: ```cpp #if defined(TARGET_AMD64) || defined(TARGET_X86) // A normal method entry point is always 8 byte aligned, but a funclet can start at an odd address. if ((entryPoint & 0x1) != 0) return NULL; #endif ``` This is purely a **performance optimization**: the map only ever contains true method entry points (4-byte aligned on every architecture), so a lookup for an odd (funclet) address is always a miss. (`PtrHashMap` handles odd keys fine -- they're just never present.) The problem is the side effect in **DAC builds**. When `createdump` enumerates memory for a triage minidump (`DOTNET_DbgMiniDumpType=3`), it drives stack walking through this same path. The fast path makes it return without ever probing the hashmap for odd (funclet) entry points, so the bucket pages that probe would touch are never read and never captured into the dump. The cDAC consumer, walking the same stack later, probes the hashmap for that odd entry point, lands in a bucket page absent from the dump, throws `VirtualReadException`, and aborts the walk -- SOS reports `<failed>` one frame past the `SoftwareExceptionFrame`. ## Fix Make the producer enumerate the pages instead of mirroring the bail on the consumer: - **`src/coreclr/vm/readytoruninfo.cpp`**: gate the odd-entry-point optimization with `!defined(DACCESS_COMPILE)`. The live runtime keeps the perf win; DAC builds perform the lookup so the bucket pages are enumerated into triage minidumps. Also removed the inaccurate "PtrHashMap can't handle odd pointers" comment. - **cDAC `ExecutionManagerCore.ReadyToRunJitManager`**: removed the bail entirely. With the DAC enumerating the pages, the consumer reads them naturally. This is a root-cause fix (complete dump enumeration) rather than masking the symptom on the consumer side, and it benefits the legacy DAC consumer too, not just the cDAC. ## Validation - **Native build**: rebuilt `coreclr.dll` (non-DAC) and `mscordaccore.dll` (DAC); both compilations of `readytoruninfo.cpp` succeed with 0 warnings / 0 errors, confirming the `#if` change is valid in both configurations. - **cDAC unit tests** (`Microsoft.Diagnostics.DataContractReader.Tests`): all green. - **End to end**: the failing diagnostics CI tests create fresh triage dumps, so once this change rides in via the runtime -> diagnostics flow, the dumps will contain the previously-missing bucket pages and the cDAC walk completes. (Dumps captured by an older, still-bailing DAC won't contain those pages; this fix applies to newly created dumps.) > [!NOTE] > This PR description was drafted with assistance from GitHub Copilot. --------- Co-authored-by: Max Charlamb <maxcharlamb@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 19a803d commit 33db161

1 file changed

Lines changed: 7 additions & 2 deletions

File tree

src/coreclr/vm/readytoruninfo.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,14 @@ PTR_MethodDesc ReadyToRunInfo::GetMethodDescForEntryPointInNativeImage(PCODE ent
368368
}
369369
CONTRACTL_END;
370370

371-
#if defined(TARGET_AMD64) || defined(TARGET_X86)
371+
#if (defined(TARGET_AMD64) || defined(TARGET_X86)) && !defined(DACCESS_COMPILE)
372372
// A normal method entry point is always 8 byte aligned, but a funclet can start at an odd address.
373-
// Since PtrHashMap can't handle odd pointers, check for this case and return NULL.
373+
// The map only contains true method entry points, so a lookup for an odd (funclet) address is
374+
// always a miss. Skip the guaranteed-miss lookup as a performance optimization.
375+
//
376+
// This is intentionally limited to non-DAC builds. The DAC must perform the lookup so that the
377+
// hashmap bucket pages it touches are enumerated into triage minidumps; otherwise a consumer
378+
// (such as the cDAC) faults when it later probes those not-in-dump pages. See dotnet/diagnostics#5910.
374379
if ((entryPoint & 0x1) != 0)
375380
return NULL;
376381
#endif

0 commit comments

Comments
 (0)