[prototype] cdac-lite: native crash-dump memory enumeration#130277
Draft
max-charlamb wants to merge 13 commits into
Draft
[prototype] cdac-lite: native crash-dump memory enumeration#130277max-charlamb wants to merge 13 commits into
max-charlamb wants to merge 13 commits into
Conversation
Prototype of a small standalone native component (cdac-lite) that reproduces the DAC's crash-dump memory enumeration by reading the runtime's contract/data descriptors directly, without a version-matched mscordaccore. - src/coreclr/debug/cdaclite: exports CLRDataCreateInstance + ICLRDataEnumMemoryRegions; contract walks (gc, thread, loader, handles, jit, statics, syncblock, stresslog, interop) + conservative stack scan for the Normal tier; size-optimized build (~114 KB on windows-x64). - createdump integration (env-gated DOTNET_DbgUseCdacLite), Normal + Heap tiers. - cdac DumpTests: add Mini (MiniDumpNormal) tier + MiniDumpTests covering the clrstack/clrthreads scenarios supported by a normal minidump. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…numerators, CI flag Builds on the base cdac-lite prototype commit with this session's work: - Refactor stackscan into a CodeEnumerator class (one deduped Emit + per-type Enum* methods mirroring the DAC EnumMemoryRegions pattern). - Rearchitect the driver into a uniform table of tier-aware enumerators; each self-selects by target.Tier() (Target now carries ClrBase/ContractDescriptorAddr). New contracts/bootstrap.cpp for the export dir + global singletons. - Close the JIT stack-walk gap (native-code-slot re-resolution, frame-chain transition frames, unwind info) and emit GC info + full dumpdomain state (global LoaderAllocator + assembly-list arrays) so SOS clrthreads/clrstack/ dumpmd/ip2md/dumpmt/dumpmodule/dumpdomain work on cdac-lite normal dumps. - Add MiniDumpSosDacTests exercising the ISOSDacInterface APIs those commands use. - CI: UseCdacLite flag (DumpTests.targets + RunDumpTests.ps1) and an opt-in cdacUseCdacLite parameter wiring the runtime-diagnostics pipeline to generate + validate cdac-lite dumps on Helix (Windows). 30/30 MiniDumpTests pass; cdac-lite builds clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR prototypes a new native cdac-lite component under CoreCLR debug infrastructure and wires it into Windows createdump behind environment-variable gates, then expands the managed cDAC dump-test suite (including new MiniDumpNormal coverage) and runtime-diagnostics pipeline wiring to exercise the new dump-generation mode.
Changes:
- Add
src/coreclr/debug/cdaclite/**: a nativeCLRDataCreateInstance+ICLRDataEnumMemoryRegionsimplementation plus contract-driven region enumeration. - Add an env-gated Windows
createdumppath (DOTNET_DbgUseCdacLite/DOTNET_DbgCdacLitePath) that uses cdac-lite to feed managed regions toMiniDumpWriteDump. - Extend cDAC DumpTests to generate/use
Minidumps and add Helix/pipeline parameters to run the suite in “use cdac-lite for dump generation” mode (Windows).
Reviewed changes
Copilot reviewed 62 out of 62 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/native/managed/cdac/tests/TestInfrastructure/DumpTestBase.cs | Extend net10.0 skip logic to cover mini dumps and improve skip message. |
| src/native/managed/cdac/tests/TestInfrastructure/ClrMdDumpHost.cs | Add PE-header fallback reads to satisfy mini/normal dump scenarios. |
| src/native/managed/cdac/tests/DumpTests/RunDumpTests.ps1 | Add -UseCdacLite switch and plumb it into dump-generation build properties. |
| src/native/managed/cdac/tests/DumpTests/README.md | Document Mini dumps, supported SOS scenarios, and new mini-tier test classes. |
| src/native/managed/cdac/tests/DumpTests/MiniDumpTests.cs | New integration tests for clrstack / clrthreads expectations on MiniDumpNormal. |
| src/native/managed/cdac/tests/DumpTests/DumpTests.targets | Add Mini dump tier support; add UseCdacLite env-var injection for dump generation. |
| src/native/managed/cdac/tests/DumpTests/Debuggees/StackWalk/StackWalk.csproj | Request both Heap;Mini dumps for StackWalk debuggee. |
| src/native/managed/cdac/tests/DumpTests/Debuggees/Directory.Build.props | Update dump-tier documentation to include Mini and combinations. |
| src/native/managed/cdac/tests/DumpTests/cdac-dump-helix.proj | Add Helix precommand to enable cdac-lite mode via env var. |
| src/native/managed/cdac/scripts/ThreadsCommand.cs | Fix OS ID formatting to use .Value. |
| src/native/managed/cdac/scripts/StacksCommand.cs | Fix OS ID formatting to use .Value. |
| src/native/managed/cdac/scripts/DumpHelpers.cs | Add dump-read fallback to on-disk PE images for missing module bytes. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader/ContractDescriptorTarget.cs | Add optional read-failure logging (env gated) and route read failures through helper. |
| src/coreclr/debug/createdump/createdumpwindows.cpp | Add Windows createdump integration to use cdac-lite region enumeration (env gated). |
| src/coreclr/debug/CMakeLists.txt | Add cdaclite subdirectory to the debug build (non-cross-components builds). |
| src/coreclr/debug/cdaclite/CMakeLists.txt | New build definition for the cdac-lite shared library and helper static libs. |
| src/coreclr/debug/cdaclite/cdaclite.h | New cdac-lite COM type definition (ICLRDataEnumMemoryRegions). |
| src/coreclr/debug/cdaclite/cdaclite.cpp | Implement CLRDataCreateInstance, symbol lookup, COM lifetime, and logging. |
| src/coreclr/debug/cdaclite/cdaclite.src | Export definition for Windows build. |
| src/coreclr/debug/cdaclite/cdaclite_unixexports.src | Export list for Unix builds. |
| src/coreclr/debug/cdaclite/enumerate.cpp | Implement tier-aware enumeration driver over contract enumerators. |
| src/coreclr/debug/cdaclite/datatarget.h | Define ICLRDataTarget adapter / read callback plumbing. |
| src/coreclr/debug/cdaclite/datatarget.cpp | Implement target memory read callback. |
| src/coreclr/debug/cdaclite/cdaclitetest.cpp | New Windows-only standalone harness to exercise cdac-lite against a live process. |
| src/coreclr/debug/cdaclite/json/CMakeLists.txt | Build definition for the JSON parser static lib. |
| src/coreclr/debug/cdaclite/json/json.h | Define tiny JSON value model and parsing API. |
| src/coreclr/debug/cdaclite/json/json.cpp | Implement tiny JSON parser (with jsonc comments support). |
| src/coreclr/debug/cdaclite/data/CMakeLists.txt | Build definition for descriptor/target data-layer static lib. |
| src/coreclr/debug/cdaclite/data/datatype.h | Infrastructure for descriptor-driven struct field reads (CDAC_* macros). |
| src/coreclr/debug/cdaclite/data/runtimetypes.h | Descriptor-driven “Data types” for runtime structures used by contracts. |
| src/coreclr/debug/cdaclite/data/datadescriptor.h | Data descriptor model (types/globals/contracts) and merge/resolve APIs. |
| src/coreclr/debug/cdaclite/data/datadescriptor.cpp | Read/parse/merge logic for in-memory descriptor + sub-descriptors. |
| src/coreclr/debug/cdaclite/data/target.h | Target abstraction for typed reads and enum-mem recording. |
| src/coreclr/debug/cdaclite/data/target.cpp | Implement Target raw reads, field/global helpers, and enum-mem emission. |
| src/coreclr/debug/cdaclite/contracts/CMakeLists.txt | Build definition for contract enumerators library. |
| src/coreclr/debug/cdaclite/contracts/contracts.h | Shared RegionCallback contract type. |
| src/coreclr/debug/cdaclite/contracts/bootstrap.h | Bootstrap enumerator declarations. |
| src/coreclr/debug/cdaclite/contracts/bootstrap.cpp | Emit PE headers/export dir and key singleton structs needed for bootstrap. |
| src/coreclr/debug/cdaclite/contracts/thread.h | Thread enumerator declarations. |
| src/coreclr/debug/cdaclite/contracts/thread.cpp | Enumerate thread stacks and thread-locals emission. |
| src/coreclr/debug/cdaclite/contracts/loader.h | Loader/module enumeration declarations. |
| src/coreclr/debug/cdaclite/contracts/loader.cpp | Enumerate modules and emit metadata locator structs / in-memory PDB streams. |
| src/coreclr/debug/cdaclite/contracts/loaderheaps.h | Loader-heap enumeration declarations. |
| src/coreclr/debug/cdaclite/contracts/loaderheaps.cpp | Enumerate loader-allocator heaps and report heap blocks. |
| src/coreclr/debug/cdaclite/contracts/jit.h | JIT code heap enumeration declarations. |
| src/coreclr/debug/cdaclite/contracts/jit.cpp | Enumerate JIT code heaps and capture associated heap structs. |
| src/coreclr/debug/cdaclite/contracts/stackscan.h | Normal-tier stack-scan enumeration declarations. |
| src/coreclr/debug/cdaclite/contracts/stackscan.cpp | Implement conservative stack scanning and method/code emission (normal tier). |
| src/coreclr/debug/cdaclite/contracts/gc.h | GC heap enumeration declarations. |
| src/coreclr/debug/cdaclite/contracts/gc.cpp | Enumerate GC heap segments and supporting GC bookkeeping/free-region data. |
| src/coreclr/debug/cdaclite/contracts/handles.h | GC handle-table enumeration declarations. |
| src/coreclr/debug/cdaclite/contracts/handles.cpp | Enumerate handle-table segments and required pointer arrays. |
| src/coreclr/debug/cdaclite/contracts/syncblock.h | Syncblock enumeration declarations. |
| src/coreclr/debug/cdaclite/contracts/syncblock.cpp | Enumerate sync table/cache and in-use syncblocks (heap tier). |
| src/coreclr/debug/cdaclite/contracts/stresslog.h | Stresslog enumeration declarations. |
| src/coreclr/debug/cdaclite/contracts/stresslog.cpp | Enumerate stresslog chunks and emit chunk buffers (heap tier). |
| src/coreclr/debug/cdaclite/contracts/interop.h | COM interop enumeration declarations. |
| src/coreclr/debug/cdaclite/contracts/interop.cpp | Enumerate RCW cleanup structures (heap tier). |
| src/coreclr/debug/cdaclite/contracts/statics.h | “Statics” / bootstrap descriptor emission declarations. |
| src/coreclr/debug/cdaclite/contracts/statics.cpp | Emit contract descriptor structs/JSON/pointer_data and indirect global storage. |
| eng/pipelines/runtime-diagnostics.yml | Add pipeline parameter and pass it through Helix send to enable cdac-lite mode. |
| eng/pipelines/cdac/prepare-cdac-helix-steps.yml | Add opt-in step to copy cdaclite.dll into testhost payload next to coreclr.dll (Windows). |
Comment on lines
+264
to
+277
| HMODULE cdacLite = LoadLibraryA(cdacLitePath.c_str()); | ||
| if (cdacLite == nullptr) | ||
| { | ||
| printf_error("cdac-lite: LoadLibrary(%s) FAILED - %s\n", cdacLitePath.c_str(), GetLastErrorString().c_str()); | ||
| return false; | ||
| } | ||
|
|
||
| bool result = false; | ||
| PFN_CLRDataCreateInstance pfnCreate = (PFN_CLRDataCreateInstance)GetProcAddress(cdacLite, "CLRDataCreateInstance"); | ||
| if (pfnCreate == nullptr) | ||
| { | ||
| printf_error("cdac-lite: GetProcAddress(CLRDataCreateInstance) FAILED\n"); | ||
| return false; | ||
| } |
Comment on lines
+64
to
+82
| STDMETHOD(GetMachineType)(ULONG32* machine) | ||
| { | ||
| #if defined(_M_ARM64) | ||
| *machine = IMAGE_FILE_MACHINE_ARM64; | ||
| #elif defined(_M_ARM) | ||
| *machine = IMAGE_FILE_MACHINE_ARMNT; | ||
| #elif defined(_M_IX86) | ||
| *machine = IMAGE_FILE_MACHINE_I386; | ||
| #else | ||
| *machine = IMAGE_FILE_MACHINE_AMD64; | ||
| #endif | ||
| return S_OK; | ||
| } | ||
|
|
||
| STDMETHOD(GetPointerSize)(ULONG32* size) | ||
| { | ||
| *size = sizeof(void*); | ||
| return S_OK; | ||
| } |
Comment on lines
+88
to
+89
| int rva = (int)(current - info.ImageBase); | ||
| PEMemoryBlock block = peReader.GetSectionData(rva); |
Comment on lines
+100
to
+104
| while (filled < buffer.Length) | ||
| { | ||
| int rva = (int)(current - info.ImageBase); | ||
| PEMemoryBlock block = peReader.GetSectionData(rva); | ||
| if (block.Length > 0) |
Comment on lines
+35
to
+37
| // Stress log is opt-in; if disabled there is nothing to capture. | ||
| // COPILOT TODO: We shouldn't need this fallback for different sized things. Either it is pointer sized or a set size. | ||
| uint64_t enabled = 0; |
Comment on lines
+379
to
+383
| value.type = Type::Number; | ||
| value.rawNumber.assign(start, (size_t)(m_cur - start)); | ||
|
|
||
| value.number = strtod(value.rawNumber.c_str(), nullptr); | ||
| if (!isFloat) |
Comment on lines
+182
to
+191
| int count = 0; | ||
| HashSet<uint> seenIds = new(); | ||
| TargetPointer currentThread = storeData.FirstThread; | ||
| while (currentThread != TargetPointer.Null) | ||
| { | ||
| ThreadData threadData = threadContract.GetThreadData(currentThread); | ||
| count++; | ||
| Assert.True(seenIds.Add(threadData.Id), $"Duplicate thread ID: {threadData.Id}"); | ||
| currentThread = threadData.NextThread; | ||
| } |
…acOS -dead_strip) CI runtime-diagnostics (build 1496310) surfaced two prototype build breaks on platforms I hadn't built locally (windows-x64 + linux were fine): - windows-x86: cdaclitetest.cpp(588) 'wmain must be __cdecl' (C4007) -- x86 enforces the calling convention on wmain; add __cdecl. - macOS: 'ld: unknown options: --gc-sections' -- Apple's ld doesn't support --gc-sections; use -dead_strip on APPLE, keep --gc-sections on other Unix. The windows-x64 CdacDumpTest and windows-arm64 CdacDumpTest legs PASSED with cdacUseCdacLite=true, so cdac-lite dump generation + validation works in CI on Windows. (The windows-x64 AllSubsets failure is the known 'VSIX Auto Update' infra error -- it fails identically in the DAC baseline run and has no compile error.) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Removes the per-platform CDACLITE_SIZE_* block. It only bought size-over-speed (/O1, -Os) + /OPT:ICF folding -- negligible for a small component and not important yet -- while dead-code stripping (/OPT:REF, --gc-sections, -dead_strip) is already applied per-platform by configurecompiler.cmake. Removing it also eliminates the hand-rolled platform flags that broke the macOS build. cdac-lite now just uses coreclr's default Release optimization. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines
+28
to
+43
| bool Value::TryGetUInt64(uint64_t& out) const | ||
| { | ||
| if (type == Type::Number) | ||
| { | ||
| if (isInteger) | ||
| { | ||
| out = (uint64_t)integer; | ||
| return true; | ||
| } | ||
| if (number >= 0.0) | ||
| { | ||
| out = (uint64_t)number; | ||
| return true; | ||
| } | ||
| return false; | ||
| } |
Comment on lines
+379
to
+394
| value.type = Type::Number; | ||
| value.rawNumber.assign(start, (size_t)(m_cur - start)); | ||
|
|
||
| value.number = strtod(value.rawNumber.c_str(), nullptr); | ||
| if (!isFloat) | ||
| { | ||
| char* end = nullptr; | ||
| errno = 0; | ||
| long long parsed = strtoll(value.rawNumber.c_str(), &end, 10); | ||
| if (errno == 0 && end != value.rawNumber.c_str() && *end == '\0') | ||
| { | ||
| value.integer = (int64_t)parsed; | ||
| value.isInteger = true; | ||
| } | ||
| } | ||
| return true; |
Comment on lines
+264
to
+277
| HMODULE cdacLite = LoadLibraryA(cdacLitePath.c_str()); | ||
| if (cdacLite == nullptr) | ||
| { | ||
| printf_error("cdac-lite: LoadLibrary(%s) FAILED - %s\n", cdacLitePath.c_str(), GetLastErrorString().c_str()); | ||
| return false; | ||
| } | ||
|
|
||
| bool result = false; | ||
| PFN_CLRDataCreateInstance pfnCreate = (PFN_CLRDataCreateInstance)GetProcAddress(cdacLite, "CLRDataCreateInstance"); | ||
| if (pfnCreate == nullptr) | ||
| { | ||
| printf_error("cdac-lite: GetProcAddress(CLRDataCreateInstance) FAILED\n"); | ||
| return false; | ||
| } |
Comment on lines
+352
to
+356
| if (depth == 0) | ||
| { | ||
| const char* dumpPath = getenv("CDACLITE_DUMP_JSON"); | ||
| if (dumpPath != nullptr) | ||
| { |
3 tasks
cdaclite is installed into the CoreCLR runtime pack (install_clr ... DESTINATIONS sharedFramework COMPONENT debug), so the shared-framework manifest validation (sharedfx.targets) requires a PlatformManifestFileEntry for it, the same way mscordaccore/mscordbi are listed. Without it the product build (SOSTests / AllSubsets_CoreCLR legs) fails with 'files are missing entries in the templated manifest: cdaclite.dll'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…asses
After adding cdaclite.dll to the platform manifest, sharedfx.targets(519)
additionally requires each Windows shared-framework file to carry a FileVersion
resource ('Missing FileVersion in 1 shared framework files: cdaclite.dll').
Add a Native.rc (FX_VER_FILEDESCRIPTION_STR + fxver.h/fxver.rc) and, on Windows,
define FX_VER_INTERNALNAME_STR=cdaclite.dll and compile Native.rc into the DLL --
the same pattern mscordaccore/mscordbi use. Verified locally: cdaclite.dll now
reports FileVersion 42.42.42.42424. The resource is Windows-only; Unix .so/.dylib
ship without one, matching the peer debug components.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines
+32
to
+36
| if (isInteger) | ||
| { | ||
| out = (uint64_t)integer; | ||
| return true; | ||
| } |
Comment on lines
+382
to
+395
| value.number = strtod(value.rawNumber.c_str(), nullptr); | ||
| if (!isFloat) | ||
| { | ||
| char* end = nullptr; | ||
| errno = 0; | ||
| long long parsed = strtoll(value.rawNumber.c_str(), &end, 10); | ||
| if (errno == 0 && end != value.rawNumber.c_str() && *end == '\0') | ||
| { | ||
| value.integer = (int64_t)parsed; | ||
| value.isInteger = true; | ||
| } | ||
| } | ||
| return true; | ||
| } |
Comment on lines
+333
to
+337
| if (descriptorSize == 0 || descriptorSize > MaxDescriptorSize) | ||
| { | ||
| error = "descriptor_size out of range"; | ||
| return false; | ||
| } |
Comment on lines
+37
to
+44
| void CDacLite::RegionSinkThunk(void* context, const char* kind, uint64_t start, uint64_t size) | ||
| { | ||
| RegionSinkState* state = (RegionSinkState*)context; | ||
| state->callback->EnumMemoryRegion((CLRDATA_ADDRESS)start, (ULONG32)size); | ||
| state->count++; | ||
| state->owner->Log(state->callback, "region %s [0x%llx, 0x%llx)", | ||
| kind, (unsigned long long)start, (unsigned long long)(start + size)); | ||
| } |
Comment on lines
+49
to
+54
| void CDacLite::EnumMemThunk(void* context, uint64_t address, uint32_t size) | ||
| { | ||
| RegionSinkState* state = (RegionSinkState*)context; | ||
| state->callback->EnumMemoryRegion((CLRDATA_ADDRESS)address, (ULONG32)size); | ||
| state->count++; | ||
| } |
Comment on lines
+64
to
+76
| STDMETHOD(GetMachineType)(ULONG32* machine) | ||
| { | ||
| #if defined(_M_ARM64) | ||
| *machine = IMAGE_FILE_MACHINE_ARM64; | ||
| #elif defined(_M_ARM) | ||
| *machine = IMAGE_FILE_MACHINE_ARMNT; | ||
| #elif defined(_M_IX86) | ||
| *machine = IMAGE_FILE_MACHINE_I386; | ||
| #else | ||
| *machine = IMAGE_FILE_MACHINE_AMD64; | ||
| #endif | ||
| return S_OK; | ||
| } |
Comment on lines
+78
to
+82
| STDMETHOD(GetPointerSize)(ULONG32* size) | ||
| { | ||
| *size = sizeof(void*); | ||
| return S_OK; | ||
| } |
Comment on lines
+264
to
+277
| HMODULE cdacLite = LoadLibraryA(cdacLitePath.c_str()); | ||
| if (cdacLite == nullptr) | ||
| { | ||
| printf_error("cdac-lite: LoadLibrary(%s) FAILED - %s\n", cdacLitePath.c_str(), GetLastErrorString().c_str()); | ||
| return false; | ||
| } | ||
|
|
||
| bool result = false; | ||
| PFN_CLRDataCreateInstance pfnCreate = (PFN_CLRDataCreateInstance)GetProcAddress(cdacLite, "CLRDataCreateInstance"); | ||
| if (pfnCreate == nullptr) | ||
| { | ||
| printf_error("cdac-lite: GetProcAddress(CLRDataCreateInstance) FAILED\n"); | ||
| return false; | ||
| } |
Comment on lines
+310
to
+313
| MINIDUMP_TYPE dumpType = heapTier | ||
| ? (MINIDUMP_TYPE)(MiniDumpNormal | MiniDumpWithPrivateReadWriteMemory) | ||
| : (MINIDUMP_TYPE)(MiniDumpNormal | MiniDumpWithoutAuxiliaryState); | ||
|
|
Comment on lines
11
to
17
| Debuggees are auto-discovered from the Debuggees/ subdirectory. Each debuggee csproj | ||
| can set a DumpTypes property to control which dump types are generated: | ||
| "Mini" — normal minidump only (type 1) | ||
| "Heap" — heap dump only (type 2, default) | ||
| "Full" — full dump only (type 4) | ||
| "Heap;Full" — both heap and full dumps | ||
| "Heap;Full" — any combination separated by ';' | ||
|
|
The createdump cdac-lite integration previously existed only on Windows (createdumpwindows.cpp), so the CI runtime-diagnostics runs exercised cdac-lite dump collection on Windows only. Wire it into the Unix createdump so the Linux/macOS CdacDumpTest legs exercise it too and can uncover cross-platform bugs. - crashinfo.cpp InitializeDAC: when DOTNET_DbgUseCdacLite=1 (non-full dump), load libcdaclite.so/.dylib (from DOTNET_DbgCdacLitePath or next to coreclr) instead of mscordaccore and create ICLRDataEnumMemoryRegions from it. cdac-lite does not implement IXCLRDataProcess, so it is left null; the two consumers (EnumerateManagedModules, UnwindAllThreads) are already null-guarded. - prepare-cdac-helix-steps.yml: copy the platform cdac-lite library (cdaclite.dll / libcdaclite.so / libcdaclite.dylib) into the testhost payload on all platforms, not just Windows. - cdac-dump-helix.proj: add the Unix 'export DOTNET_DbgUseCdacLite=1' pre-command. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
3 tasks
Open
3 tasks
Comment on lines
+73
to
+75
| Currently wired for Windows only (the createdump cdac-lite path is Windows-only). Use a clean | ||
| dump directory when switching modes (the tests key off the dump directory, not how it was made). | ||
| --> |
Comment on lines
+80
to
+84
| <_CdacLiteArch>$(TargetArchitecture)</_CdacLiteArch> | ||
| <_CdacLiteArch Condition="'$(_CdacLiteArch)' == ''">x64</_CdacLiteArch> | ||
| <CdacLitePath Condition="'$(UseCdacLite)' == 'true' AND '$(CdacLitePath)' == ''">$([MSBuild]::NormalizePath('$(RepoRoot)', 'artifacts', 'bin', 'coreclr', '$(HostOS).$(_CdacLiteArch).$(_CdacLiteConfig)', 'cdaclite.dll'))</CdacLitePath> | ||
| <!-- Extra env vars appended to the dump-generation command when cdac-lite mode is enabled. --> | ||
| <_CdacLiteEnvVars Condition="'$(UseCdacLite)' == 'true'">;DOTNET_DbgUseCdacLite=1;DOTNET_DbgCdacLitePath=$(CdacLitePath)</_CdacLiteEnvVars> |
Comment on lines
+68
to
+71
| - name: cdacUseCdacLite | ||
| displayName: Use cdac-lite for dump generation (Windows only) | ||
| type: boolean | ||
| default: false |
Comment on lines
+64
to
+69
| When set, generates the crash dumps with cdac-lite selecting the managed memory | ||
| instead of the legacy DAC (createdump honors DOTNET_DbgUseCdacLite and loads the | ||
| cdac-lite DLL from the coreclr build output). The cDAC DumpTests then run unchanged | ||
| against the cdac-lite-generated dumps. Windows only. Use a clean dump directory | ||
| (delete artifacts\dumps\cdac or pass -Force) when switching between DAC and cdac-lite | ||
| modes, since existing dumps are reused as-is. |
Comment on lines
+64
to
+66
| STDMETHOD(GetMachineType)(ULONG32* machine) | ||
| { | ||
| #if defined(_M_ARM64) |
Comment on lines
+292
to
+297
| hr = enumRegions->EnumMemoryRegions(&collector, enumFlags, CLRDATA_ENUM_MEM_DEFAULT); | ||
| if (FAILED(hr)) | ||
| { | ||
| printf_error("cdac-lite: EnumMemoryRegions FAILED (%08x)\n", hr); | ||
| return false; | ||
| } |
Comment on lines
+319
to
+328
| if (MiniDumpWriteDump(hProcess, pid, hFile, dumpType, NULL, NULL, &callbackInfo)) | ||
| { | ||
| result = true; | ||
| } | ||
| else | ||
| { | ||
| printf_error("cdac-lite: MiniDumpWriteDump - %s\n", GetLastErrorString().c_str()); | ||
| } | ||
|
|
||
| return result; |
Comment on lines
+35
to
+50
| // Stress log is opt-in; if disabled there is nothing to capture. | ||
| // COPILOT TODO: We shouldn't need this fallback for different sized things. Either it is pointer sized or a set size. | ||
| uint64_t enabled = 0; | ||
| if (!target.TryGetGlobalValue(GlobalStressLogEnabled, enabled)) | ||
| { | ||
| // Fall back to reading it as a byte at the global's address. | ||
| uint32_t enabled32 = 0; | ||
| if (target.TryReadGlobalUInt32(GlobalStressLogEnabled, enabled32)) | ||
| { | ||
| enabled = enabled32 & 0xFF; | ||
| } | ||
| } | ||
| if (enabled == 0) | ||
| { | ||
| return 0; | ||
| } |
Comment on lines
+9
to
+13
| #include "json.h" | ||
|
|
||
| #include <stdlib.h> | ||
| #include <string.h> | ||
|
|
Comment on lines
+121
to
+135
| // Indeterminate types (offsets only): max(field offset) + 8 covers every scalar/pointer. | ||
| const TypeInfo* type = m_descriptor->FindType(typeName); | ||
| if (type == nullptr || type->fields.empty()) | ||
| { | ||
| return false; | ||
| } | ||
| uint32_t maxEnd = 0; | ||
| for (std::map<std::string, FieldInfo>::const_iterator it = type->fields.begin(); it != type->fields.end(); ++it) | ||
| { | ||
| uint32_t end = it->second.offset + (uint32_t)sizeof(uint64_t); | ||
| if (end > maxEnd) | ||
| { | ||
| maxEnd = end; | ||
| } | ||
| } |
CI (runtime-diagnostics on Linux arm64/arm32) revealed the Unix createdump cdac-lite path failing at load: GetProcAddress(CLRDataCreateInstance) FAILED -- libcdaclite.so: undefined symbol: CLRDataCreateInstance. libcdaclite.so dlopen'd fine (no PAL-init problem), but CLRDataCreateInstance was not in its dynamic symbol table. coreclr compiles with -fvisibility=hidden, so the extern C symbol was hidden; the --version-script cannot un-hide a compile-time hidden symbol. The legacy DAC avoids this by declaring the entry point STDAPI DLLEXPORT (default visibility on Unix). Add an equivalent default-visibility attribute, guarded so it is a no-op on Windows (export comes from cdaclite.def). Verified cdaclite.dll still builds + links on Windows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines
+64
to
+76
| STDMETHOD(GetMachineType)(ULONG32* machine) | ||
| { | ||
| #if defined(_M_ARM64) | ||
| *machine = IMAGE_FILE_MACHINE_ARM64; | ||
| #elif defined(_M_ARM) | ||
| *machine = IMAGE_FILE_MACHINE_ARMNT; | ||
| #elif defined(_M_IX86) | ||
| *machine = IMAGE_FILE_MACHINE_I386; | ||
| #else | ||
| *machine = IMAGE_FILE_MACHINE_AMD64; | ||
| #endif | ||
| return S_OK; | ||
| } |
Comment on lines
+78
to
+82
| STDMETHOD(GetPointerSize)(ULONG32* size) | ||
| { | ||
| *size = sizeof(void*); | ||
| return S_OK; | ||
| } |
Comment on lines
+272
to
+277
| PFN_CLRDataCreateInstance pfnCreate = (PFN_CLRDataCreateInstance)GetProcAddress(cdacLite, "CLRDataCreateInstance"); | ||
| if (pfnCreate == nullptr) | ||
| { | ||
| printf_error("cdac-lite: GetProcAddress(CLRDataCreateInstance) FAILED\n"); | ||
| return false; | ||
| } |
Comment on lines
+281
to
+286
| HRESULT hr = pfnCreate(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&enumRegions); | ||
| if (FAILED(hr) || enumRegions == nullptr) | ||
| { | ||
| printf_error("cdac-lite: CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED (%08x)\n", hr); | ||
| return false; | ||
| } |
Comment on lines
+292
to
+297
| hr = enumRegions->EnumMemoryRegions(&collector, enumFlags, CLRDATA_ENUM_MEM_DEFAULT); | ||
| if (FAILED(hr)) | ||
| { | ||
| printf_error("cdac-lite: EnumMemoryRegions FAILED (%08x)\n", hr); | ||
| return false; | ||
| } |
Comment on lines
+347
to
+395
| bool ParseNumber(Value& value) | ||
| { | ||
| const char* start = m_cur; | ||
| bool isFloat = false; | ||
|
|
||
| if (Peek() == '-') | ||
| { | ||
| m_cur++; | ||
| } | ||
| while (!AtEnd()) | ||
| { | ||
| char c = *m_cur; | ||
| if (c >= '0' && c <= '9') | ||
| { | ||
| m_cur++; | ||
| } | ||
| else if (c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-') | ||
| { | ||
| isFloat = (c == '.' || c == 'e' || c == 'E') ? true : isFloat; | ||
| m_cur++; | ||
| } | ||
| else | ||
| { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (m_cur == start) | ||
| { | ||
| return Fail("invalid number"); | ||
| } | ||
|
|
||
| value.type = Type::Number; | ||
| value.rawNumber.assign(start, (size_t)(m_cur - start)); | ||
|
|
||
| value.number = strtod(value.rawNumber.c_str(), nullptr); | ||
| if (!isFloat) | ||
| { | ||
| char* end = nullptr; | ||
| errno = 0; | ||
| long long parsed = strtoll(value.rawNumber.c_str(), &end, 10); | ||
| if (errno == 0 && end != value.rawNumber.c_str() && *end == '\0') | ||
| { | ||
| value.integer = (int64_t)parsed; | ||
| value.isInteger = true; | ||
| } | ||
| } | ||
| return true; | ||
| } |
Comment on lines
+372
to
+374
| // Read pointer_data and resolve this descriptor's indirect globals. | ||
| std::vector<uint64_t> pointerData(pointerDataCount, 0); | ||
| for (uint32_t i = 0; i < pointerDataCount; i++) |
Comment on lines
+37
to
+44
| void CDacLite::RegionSinkThunk(void* context, const char* kind, uint64_t start, uint64_t size) | ||
| { | ||
| RegionSinkState* state = (RegionSinkState*)context; | ||
| state->callback->EnumMemoryRegion((CLRDATA_ADDRESS)start, (ULONG32)size); | ||
| state->count++; | ||
| state->owner->Log(state->callback, "region %s [0x%llx, 0x%llx)", | ||
| kind, (unsigned long long)start, (unsigned long long)(start + size)); | ||
| } |
Comment on lines
+49
to
+54
| void CDacLite::EnumMemThunk(void* context, uint64_t address, uint32_t size) | ||
| { | ||
| RegionSinkState* state = (RegionSinkState*)context; | ||
| state->callback->EnumMemoryRegion((CLRDATA_ADDRESS)address, (ULONG32)size); | ||
| state->count++; | ||
| } |
Comment on lines
+30
to
+43
| if (type == Type::Number) | ||
| { | ||
| if (isInteger) | ||
| { | ||
| out = (uint64_t)integer; | ||
| return true; | ||
| } | ||
| if (number >= 0.0) | ||
| { | ||
| out = (uint64_t)number; | ||
| return true; | ||
| } | ||
| return false; | ||
| } |
…eadability)
CI on Linux/macOS revealed the cdac-lite createdump path generating dumps the
managed cDAC reader could not read: 133 VirtualReadException failures across
frames, stackwalk, appdomain, and metadata tests (Windows cdac-lite passed 220/257,
Linux/macOS ~116/257).
Root-caused with a local WSL repro + ELF-core analysis: a managed assembly on Linux
is mapped as multiple non-contiguous file-backed segments, and createdump's ELF/
Mach-O core omits read-only file-backed segments (metadata). The managed cDAC reader
(EcmaMetadata_1.GetReadOnlyMetadataAddress) reads read-only metadata directly from
target memory with no on-disk fallback (unlike the legacy DAC), so the metadata bytes
must be in the dump. On Windows they happen to be captured; on Linux/macOS they are not.
loader.cpp previously emitted only the PEImageLayout locator struct (comment: "the
metadata bytes themselves come from the on-disk image"), which holds on Windows but
not for the managed reader on Linux/macOS. Emit the bytes the reader touches:
- mapped image: PE headers [Base,SizeOfHeaders] + CLI(COR20) header + ECMA metadata
(RVA == offset-from-base), parsed from the module's PE headers
- flat image: the whole (small) file
Managed assemblies are PE on every platform, so the PE parse is valid on Linux/macOS.
Verified in a local WSL build: the CoreLib and debuggee metadata regions the reader
reads flipped from OUT to IN in the ELF core, with only a lean (~4MB) dump-size
increase (vs ~17MB for whole-image emission). Builds clean on Windows and Linux/clang.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…data from disk Replace the Linux/macOS metadata-embedding approach with DAC-faithful PE header emission. PEImage::EnumMemoryRegions enumerates both m_pLayouts[IMAGE_FLAT] and [IMAGE_LOADED], and PEDecoder::EnumMemoryRegions emits the DOS header, NT headers, section table, COR (CLI) header and R2R header; the loaded layout additionally emits the debug directory. cdac-lite now mirrors this exactly for each layout and does NOT emit the ECMA metadata bytes -- a reader reads those from the on-disk image, just like the DAC. createdump excludes module image content from heap dumps by design (only full dumps write module mappings), so re-emitting these headers is required for the reader's on-disk metadata fallback to resolve the module. Expose PEImage::m_pLayouts[IMAGE_FLAT] as cdac_data<PEImage>::FlatImageLayout in the data descriptor so the flat layout's headers can be emitted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines
+64
to
+76
| STDMETHOD(GetMachineType)(ULONG32* machine) | ||
| { | ||
| #if defined(_M_ARM64) | ||
| *machine = IMAGE_FILE_MACHINE_ARM64; | ||
| #elif defined(_M_ARM) | ||
| *machine = IMAGE_FILE_MACHINE_ARMNT; | ||
| #elif defined(_M_IX86) | ||
| *machine = IMAGE_FILE_MACHINE_I386; | ||
| #else | ||
| *machine = IMAGE_FILE_MACHINE_AMD64; | ||
| #endif | ||
| return S_OK; | ||
| } |
Comment on lines
+78
to
+82
| STDMETHOD(GetPointerSize)(ULONG32* size) | ||
| { | ||
| *size = sizeof(void*); | ||
| return S_OK; | ||
| } |
Comment on lines
+264
to
+286
| HMODULE cdacLite = LoadLibraryA(cdacLitePath.c_str()); | ||
| if (cdacLite == nullptr) | ||
| { | ||
| printf_error("cdac-lite: LoadLibrary(%s) FAILED - %s\n", cdacLitePath.c_str(), GetLastErrorString().c_str()); | ||
| return false; | ||
| } | ||
|
|
||
| bool result = false; | ||
| PFN_CLRDataCreateInstance pfnCreate = (PFN_CLRDataCreateInstance)GetProcAddress(cdacLite, "CLRDataCreateInstance"); | ||
| if (pfnCreate == nullptr) | ||
| { | ||
| printf_error("cdac-lite: GetProcAddress(CLRDataCreateInstance) FAILED\n"); | ||
| return false; | ||
| } | ||
|
|
||
| ReleaseHolder<ProcessDataTarget> dataTarget = new ProcessDataTarget(hProcess); | ||
| ReleaseHolder<ICLRDataEnumMemoryRegions> enumRegions; | ||
| HRESULT hr = pfnCreate(__uuidof(ICLRDataEnumMemoryRegions), dataTarget, (void**)&enumRegions); | ||
| if (FAILED(hr) || enumRegions == nullptr) | ||
| { | ||
| printf_error("cdac-lite: CLRDataCreateInstance(ICLRDataEnumMemoryRegions) FAILED (%08x)\n", hr); | ||
| return false; | ||
| } |
Comment on lines
+11
to
+13
| #include <stdlib.h> | ||
| #include <string.h> | ||
|
|
Comment on lines
+32
to
+36
| if (isInteger) | ||
| { | ||
| out = (uint64_t)integer; | ||
| return true; | ||
| } |
Comment on lines
+379
to
+395
| value.type = Type::Number; | ||
| value.rawNumber.assign(start, (size_t)(m_cur - start)); | ||
|
|
||
| value.number = strtod(value.rawNumber.c_str(), nullptr); | ||
| if (!isFloat) | ||
| { | ||
| char* end = nullptr; | ||
| errno = 0; | ||
| long long parsed = strtoll(value.rawNumber.c_str(), &end, 10); | ||
| if (errno == 0 && end != value.rawNumber.c_str() && *end == '\0') | ||
| { | ||
| value.integer = (int64_t)parsed; | ||
| value.isInteger = true; | ||
| } | ||
| } | ||
| return true; | ||
| } |
Comment on lines
+35
to
+37
| // Stress log is opt-in; if disabled there is nothing to capture. | ||
| // COPILOT TODO: We shouldn't need this fallback for different sized things. Either it is pointer sized or a set size. | ||
| uint64_t enabled = 0; |
Comment on lines
+82
to
+84
| <CdacLitePath Condition="'$(UseCdacLite)' == 'true' AND '$(CdacLitePath)' == ''">$([MSBuild]::NormalizePath('$(RepoRoot)', 'artifacts', 'bin', 'coreclr', '$(HostOS).$(_CdacLiteArch).$(_CdacLiteConfig)', 'cdaclite.dll'))</CdacLitePath> | ||
| <!-- Extra env vars appended to the dump-generation command when cdac-lite mode is enabled. --> | ||
| <_CdacLiteEnvVars Condition="'$(UseCdacLite)' == 'true'">;DOTNET_DbgUseCdacLite=1;DOTNET_DbgCdacLitePath=$(CdacLitePath)</_CdacLiteEnvVars> |
The dump-test memory host resolved on-disk module images via ClrMD's raw module list. For a managed ReadyToRun assembly on Linux/macOS that has two mappings -- a flat/file layout and a separate loaded/converted layout far apart in the address space -- ClrMD registers the module at the flat base only. Reads of loaded-layout addresses (e.g. ECMA metadata, which heap/mini dumps do not capture) then fail their on-disk fallback, non-deterministically depending on ASLR ordering. Source managed module mappings from the cDAC Loader contract instead, mirroring dotnet/diagnostics' ManagedModuleService: DumpTestBase enumerates modules via ILoader (loaded base + size via TryGetLoadedImageContents, path via GetPath, and the PE identity key via GetFileHeadersInfo) and registers them with the host. ClrMdDumpHost's read fallback now resolves the owning module solely from this table and no longer consults ClrMD's module list. ClrMD is retained only for raw memory reads, thread context, and the one-time contract-descriptor export lookup needed to bootstrap the Target. Each module's on-disk file is located and verified against its PE identity key (COFF TimeDateStamp + optional-header SizeOfImage) once at registration, so a stale or mismatched same-named assembly is rejected rather than read silently. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
A managed module's loaded image can be either a mapped (loaded/converted) layout or a flat (file) layout. For a flat layout -- e.g. an IL-only assembly whose loaded layout is the raw file mapping -- the runtime reports metadata and other data as base + file offset, not base + RVA. The on-disk fallback assumed a mapped layout and translated the offset via GetSectionData, which fails for a flat module (the file offset is not a section RVA), so reads that crossed out of the captured header page into the assembly's metadata failed. Thread the module's FLAG_MAPPED bit from the Loader contract through to the fallback and branch on it: mapped layouts translate the RVA via the section table, flat layouts read the raw image at the offset directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cdac-lite previously emitted DAC-faithful PE header regions (DOS/NT/COR/R2R headers, section table, and debug directory) for both the flat and loaded image layouts of every managed module. Now that the reader pages a module's data in from the on-disk assembly (located and key-verified via the Loader contract), the dump only needs enough to identify the module and reach its loaded image. Emit just the Module -> PEAssembly -> PEImage -> PEImageLayout locator chain (auto- emitted as the walk reads each struct) plus the loaded image's PE header page ([Base, SizeOfHeaders]) -- enough for a reader to read the module's identity key (COFF TimeDateStamp + OptionalHeader SizeOfImage) and section table. The COR/R2R headers, ECMA metadata, debug directory, code, and the flat layout are no longer emitted; a reader reads those from the on-disk image, exactly like the DAC. Also drops the now-unused PEImage.FlatImageLayout data descriptor field. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines
+99
to
+103
| foreach (ResolvedModule module in _modules) | ||
| { | ||
| if (address >= module.Base && address < module.Base + module.Size) | ||
| return module; | ||
| } |
Comment on lines
+100
to
+104
| while (filled < buffer.Length) | ||
| { | ||
| int rva = (int)(current - info.ImageBase); | ||
| PEMemoryBlock block = peReader.GetSectionData(rva); | ||
| if (block.Length > 0) |
Comment on lines
+78
to
+84
| <_CdacLiteConfig>$(_DebuggeeRuntimeConfig)</_CdacLiteConfig> | ||
| <_CdacLiteConfig Condition="'$(_CdacLiteConfig)' == ''">Release</_CdacLiteConfig> | ||
| <_CdacLiteArch>$(TargetArchitecture)</_CdacLiteArch> | ||
| <_CdacLiteArch Condition="'$(_CdacLiteArch)' == ''">x64</_CdacLiteArch> | ||
| <CdacLitePath Condition="'$(UseCdacLite)' == 'true' AND '$(CdacLitePath)' == ''">$([MSBuild]::NormalizePath('$(RepoRoot)', 'artifacts', 'bin', 'coreclr', '$(HostOS).$(_CdacLiteArch).$(_CdacLiteConfig)', 'cdaclite.dll'))</CdacLitePath> | ||
| <!-- Extra env vars appended to the dump-generation command when cdac-lite mode is enabled. --> | ||
| <_CdacLiteEnvVars Condition="'$(UseCdacLite)' == 'true'">;DOTNET_DbgUseCdacLite=1;DOTNET_DbgCdacLitePath=$(CdacLitePath)</_CdacLiteEnvVars> |
Comment on lines
+35
to
+50
| // Stress log is opt-in; if disabled there is nothing to capture. | ||
| // COPILOT TODO: We shouldn't need this fallback for different sized things. Either it is pointer sized or a set size. | ||
| uint64_t enabled = 0; | ||
| if (!target.TryGetGlobalValue(GlobalStressLogEnabled, enabled)) | ||
| { | ||
| // Fall back to reading it as a byte at the global's address. | ||
| uint32_t enabled32 = 0; | ||
| if (target.TryReadGlobalUInt32(GlobalStressLogEnabled, enabled32)) | ||
| { | ||
| enabled = enabled32 & 0xFF; | ||
| } | ||
| } | ||
| if (enabled == 0) | ||
| { | ||
| return 0; | ||
| } |
Comment on lines
+9
to
+13
| #include "json.h" | ||
|
|
||
| #include <stdlib.h> | ||
| #include <string.h> | ||
|
|
Comment on lines
+64
to
+82
| STDMETHOD(GetMachineType)(ULONG32* machine) | ||
| { | ||
| #if defined(_M_ARM64) | ||
| *machine = IMAGE_FILE_MACHINE_ARM64; | ||
| #elif defined(_M_ARM) | ||
| *machine = IMAGE_FILE_MACHINE_ARMNT; | ||
| #elif defined(_M_IX86) | ||
| *machine = IMAGE_FILE_MACHINE_I386; | ||
| #else | ||
| *machine = IMAGE_FILE_MACHINE_AMD64; | ||
| #endif | ||
| return S_OK; | ||
| } | ||
|
|
||
| STDMETHOD(GetPointerSize)(ULONG32* size) | ||
| { | ||
| *size = sizeof(void*); | ||
| return S_OK; | ||
| } |
Comment on lines
+379
to
+395
| value.type = Type::Number; | ||
| value.rawNumber.assign(start, (size_t)(m_cur - start)); | ||
|
|
||
| value.number = strtod(value.rawNumber.c_str(), nullptr); | ||
| if (!isFloat) | ||
| { | ||
| char* end = nullptr; | ||
| errno = 0; | ||
| long long parsed = strtoll(value.rawNumber.c_str(), &end, 10); | ||
| if (errno == 0 && end != value.rawNumber.c_str() && *end == '\0') | ||
| { | ||
| value.integer = (int64_t)parsed; | ||
| value.isInteger = true; | ||
| } | ||
| } | ||
| return true; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prototype: cdac-lite — native crash-dump memory enumeration
What's here
src/coreclr/debug/cdaclite/**— the cdac-lite component: descriptor model,Target, per-subsystem memory enumerators (tier-aware, uniform table), conservative stack scan + JIT/R2R method resolution, GC-info + dumpdomain emission.src/coreclr/debug/createdump/**— env-gated cdac-lite path (DOTNET_DbgUseCdacLite,DOTNET_DbgCdacLitePath).src/native/managed/cdac/tests/DumpTests/**—MiniDumpTests(walk/thread/SOS-DAC coverage) + aUseCdacLitedump-generation flag.eng/pipelines/**— opt-incdacUseCdacLiteparameter wiring the runtime-diagnostics pipeline to generate + validate cdac-lite dumps on Helix (Windows).Status
30/30
MiniDumpTestspass against cdac-lite-generated normal dumps (clrthreads / clrstack / dumpmd / ip2md / dumpmt / dumpmodule / dumpdomain viaISOSDacInterface). cdac-lite normal dumps are ~3.4–8x smaller than the DAC's.This PR exists primarily to exercise the runtime-diagnostics pipeline with the new
cdacUseCdacLitemode.Note
This PR description was generated with GitHub Copilot (AI-assisted).