Uh oh!
There was an error while loading. Please reload this page.
macOS SOS-in-lldb: speed up ReadVirtual and fix arm64 SpecialDiagInfo - #5823
Conversation
The legacy SpecialDiagInfo address 0x7fffffff10000000 is beyond Apple Silicon's 47-bit user-space VM limit. While createdump writes this address into the core file's segment list, lldb's MachO core reader rejects reads above 0x7FFF_FFFFFFFF, so SOS reports 'Special diagnostics info read failed' and falls back to the slower managed-side path. Use 0x00007ffffff10000 on arm64 macOS (matching the Linux/non-Apple 64-bit address) and probe the legacy x86_64 address as a fallback so older dumps continue to be recognized on platforms where the legacy address is readable. Mirrored on the managed side (SpecialDiagInfo.cs) for the DataTarget-based path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
LLDBServices::ReadVirtual falls back to reading directly from native module sections when lldb's process.ReadMemory cannot satisfy a read. For dumps that don't include code/data segments (notably MachO core files on macOS, where the .text segments aren't in the dump) this is hit on the vast majority of reads. The previous fallback iterated numModules x numSections per call (~3500 entries on a typical .NET process; ~44 microseconds per fallback) making clrthreads -managedexception and similar DAC-heavy commands take ~100s on macOS arm64. Build a sorted SectionRange table on first use and binary-search it with std::upper_bound. The cache is invalidated when the target's module count changes, and through the existing ClearCache() path. Measured on macOS arm64 SOS LineNums tests with prebuilt runtime configs: ~118s -> ~28s for net11 (~4x), and total LineNums wall time from ~13 minutes to ~3.7 minutes (~3.5x). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Fixes SOS-in-lldb behavior on macOS arm64 by moving SpecialDiagInfo to a 47-bit-valid address (with legacy fallback) and by significantly reducing ReadVirtual fallback overhead via a cached, binary-searched section table.
Changes:
- Update
SpecialDiagInfoaddress on Apple Silicon macOS and add legacy-address probing for older dumps. - Add a cached
SectionRangetable to speed upLLDBServices::ReadVirtualsection-backed fallback reads. - Mirror the
SpecialDiagInfoaddress fallback behavior in the managedSpecialDiagInfoimplementation.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| src/SOS/lldbplugin/services.h | Introduces section-range cache fields and helpers for faster section fallback reads. |
| src/SOS/lldbplugin/services.cpp | Implements legacy SpecialDiagInfo fallback and the section-range cache (sort + upper_bound) for ReadVirtual. |
| src/SOS/inc/specialdiaginfo.h | Adjusts arm64 macOS SpecialDiagInfo address and defines legacy address constant. |
| src/Microsoft.Diagnostics.DebugServices.Implementation/SpecialDiagInfo.cs | Adds macOS arm64-valid address probing + legacy fallback in managed reader. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
* Section cache: guard endAddr/endOffset against uint64 overflow when building the SectionRange table and when computing the containment check, so a pathologically large section can't wrap and produce spurious cache hits. * GetExceptionRecord: when the SpecialDiagInfo signature matches but the exception record can't be read (Version too low, ExceptionRecordAddress=0, or read fails), continue to the next candidate address instead of returning eagerly. Cheap and avoids pinning behavior to the first matching address. * specialdiaginfo.h: fix cross-reference comment to point at the actual reader (LLDBServices::GetLastEventInformation), not a non-existent SOSReadDiagInfoHeader symbol. * ExtensionCommands SpecialDiagInfoHeader: add GetCandidateAddresses alongside the existing GetAddress so the OSX path probes the 47-bit-valid address first then falls back to the legacy x86_64 address. CommandFormatHelpers.DisplaySpecialInfo now iterates the candidates so 'runtimes' / extension commands recognize Apple Silicon dumps the same way the lldb plugin and managed reader already do. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| return SpecialDiagInfoAddressMacOS64; | ||
| // Try the arm64-valid address first (also valid on x86_64 macOS for newer | ||
| // createdump output); fall back to the legacy x86_64 address. | ||
| yield return SpecialDiagInfoAddressMacOSArm64; |
There was a problem hiding this comment.
Follow up change - lets remove the fallback. 8.0 already had this address.
| { | ||
| Span<byte> headerBuffer = stackalloc byte[Unsafe.SizeOf<SpecialDiagInfoHeader>()]; | ||
| if (_memoryService.ReadMemory(SpecialDiagInfoAddress, headerBuffer, out int bytesRead) && bytesRead == headerBuffer.Length) | ||
| Span<byte> exceptionRecordBuffer = stackalloc byte[Unsafe.SizeOf<EXCEPTION_RECORD64>()]; |
There was a problem hiding this comment.
Follow up - encapsulate getting the SpecialDiagInfoHeader into a helper that this and HasDiagnosticInfo can use
Uh oh!
There was an error while loading. Please reload this page.
609e546 to
38d8dbaCompareThis reverts commit 38d8dba.
The flag was load-bearing only for the degenerate 'target with 0 modules is a valid cached state' case. Module-count equality alone is sufficient to detect cache freshness, and matches the previous behavior in every real-world scenario. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 6b75fa0.
Two independent fixes to SOS-in-lldb on macOS. They surfaced together
while debugging extremely slow / failing
clrthreads -managedexceptionon Apple Silicon, but each stands on its own.
1. Speed up
LLDBServices::ReadVirtualwith a cached section table — applies to both arm64 and x86_64 macOSWhen lldb's
process.ReadMemorycan't satisfy a read, the plugin fallsback to reading from on-disk module sections. For MachO core dumps this
is the common path, not the rare one —
.text/code segments aren'tin the dump, so the DAC's metadata reads almost all hit the fallback.
The previous implementation iterated
numModules × numSectionspercall. Measured on a typical .NET process: ~3500 entries × multiple SWIG
calls each = ~44 µs per fallback. With ~5M ReadVirtual calls during a
single
clrthreads -managedexception, that's ~200 s of pure iteration.This change builds a sorted
SectionRangetable on first use andbinary-searches it with
std::upper_bound. The table is invalidatedwhen the target's module count changes, and through the existing
ClearCache()path. The fix is arch-agnostic — x86_64 macOS sees thesame speedup since the underlying lldb MachO core behavior is identical.
Measured impact (macOS arm64,
SOS.LineNums)singlefile.*(live debug)prebuilt.11(dump load)prebuilt.{10,9,8}(dump load)Not yet measured on x86_64 macOS, but the same code path is exercised
and the same root cause applies.
2. Fix
SpecialDiagInfoaddress for arm64 macOS — arm64 onlyThe legacy address
0x7fffffff10000000is beyond Apple Silicon's47-bit user-space VM limit. createdump still writes a segment at that
address into the core file, but lldb's MachO core reader refuses reads
above
0x7FFF_FFFFFFFF, so SOS reportsSpecial diagnostics info read failedand falls through to the slower managed-side path (or fails outright on
older builds without that fallback). On x86_64 macOS this address
remains valid, so the legacy constant is preserved there — no behavior
change for Intel Macs.
This change uses
0x00007ffffff10000on arm64 macOS — the same addressalready used on Linux/non-Apple 64-bit — and probes the legacy x86_64
address as a fallback so dumps produced by older createdump on x86_64
Macs continue to be recognized. The managed
SpecialDiagInfo.cspathis mirrored.
CI on Windows/Linux is unaffected — the section cache only kicks in
when lldb's primary read fails (rare on those platforms), and the
SpecialDiagInfo legacy fallback preserves the previous address on every
non-arm64-macOS configuration.