macOS SOS-in-lldb: speed up ReadVirtual and fix arm64 SpecialDiagInfo#5823
Merged
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>
Contributor
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.
* 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>
hoyosjs
reviewed
May 9, 2026
| 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; |
Member
There was a problem hiding this comment.
Follow up change - lets remove the fallback. 8.0 already had this address.
hoyosjs
reviewed
May 9, 2026
| { | ||
| 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>()]; |
Member
There was a problem hiding this comment.
Follow up - encapsulate getting the SpecialDiagInfoHeader into a helper that this and HasDiagnosticInfo can use
hoyosjs
reviewed
May 11, 2026
609e546 to
38d8dba
Compare
This 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.
hoyosjs
approved these changes
May 11, 2026
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.
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.