Skip to content

Forward ICLRSymbolProvider calls to a host-supplied symbol resolver#5877

Open
max-charlamb wants to merge 1 commit into
dotnet:mainfrom
max-charlamb:max-charlamb/symbol-provider
Open

Forward ICLRSymbolProvider calls to a host-supplied symbol resolver#5877
max-charlamb wants to merge 1 commit into
dotnet:mainfrom
max-charlamb:max-charlamb/symbol-provider

Conversation

@max-charlamb

Copy link
Copy Markdown
Member

Requires: microsoft/clrmd#1482

Plumbs ClrMD's IClrSymbolProvider hook through the diagnostics host so the (c)DAC can map runtime addresses back to module-qualified symbols via the host's IModuleService / IModuleSymbols. Also unifies SOS's existing legacy-DAC symbol-provider thunks onto the same shared adapter.

  • HostSymbolProvider (new): IClrSymbolProvider adapter over IModuleService; registered as a per-target service via [ServiceExport].

  • RuntimeProvider: sets DataTargetOptions.SymbolProvider from the registered service so ClrMD's COM data target wrapper forwards ICLRSymbolProvider calls to the host.

  • SOS.Hosting/DataTargetWrapper: exposes ICLRSymbolProvider on the legacy-DAC CCW (was missing), delegating to the same IClrSymbolProvider service for a single resolver code path.

  • Runtime: extracts the ClrFlavor -> RuntimeType mapping into a single GetRuntimeType helper; adds a generic GetDacFilePath fallback so runtimes that ship only a cDAC (no legacy DAC binary) load via the cDAC without the user having to set ForceUseContractReader.

  • ServiceManager: invokes RuntimeHelpers.RunModuleConstructor on each loaded extension so module initializers fire at load time, allowing extensions to register process-wide state (e.g. ClrMD IClrInfoProvider) before any provider factory is invoked.

Depends on a ClrMD package that contains IClrSymbolProvider and DataTargetOptions.SymbolProvider (microsoft/clrmd PR pending; the corresponding package-version bump in eng/Version.Details.xml will land via darc once the upstream PR merges).

Plumbs ClrMD's IClrSymbolProvider hook through the diagnostics host so the (c)DAC can map runtime addresses back to module-qualified symbols via the host's IModuleService / IModuleSymbols. Also unifies SOS's existing legacy-DAC symbol-provider thunks onto the same shared adapter.

* HostSymbolProvider (new): IClrSymbolProvider adapter over IModuleService; registered as a per-target service via [ServiceExport].

* RuntimeProvider: sets DataTargetOptions.SymbolProvider from the registered service so ClrMD's COM data target wrapper forwards ICLRSymbolProvider calls to the host.

* SOS.Hosting/DataTargetWrapper: exposes ICLRSymbolProvider on the legacy-DAC CCW (was missing), delegating to the same IClrSymbolProvider service for a single resolver code path.

* Runtime: extracts the ClrFlavor -> RuntimeType mapping into a single GetRuntimeType helper; adds a generic GetDacFilePath fallback so runtimes that ship only a cDAC (no legacy DAC binary) load via the cDAC without the user having to set ForceUseContractReader.

* ServiceManager: invokes RuntimeHelpers.RunModuleConstructor on each loaded extension so module initializers fire at load time, allowing extensions to register process-wide state (e.g. ClrMD IClrInfoProvider) before any provider factory is invoked.

Depends on a ClrMD package that contains IClrSymbolProvider and DataTargetOptions.SymbolProvider (microsoft/clrmd PR pending; the corresponding package-version bump in eng/Version.Details.xml will land via darc once the upstream PR merges).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb max-charlamb requested a review from hoyosjs June 12, 2026 15:46
@max-charlamb max-charlamb requested a review from a team as a code owner June 12, 2026 15:46
Copilot AI review requested due to automatic review settings June 12, 2026 15:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR plumbs ClrMD’s symbol-resolution hook through the diagnostics host so both ClrMD’s data target and SOS’s legacy-DAC COM data target can resolve module-qualified symbols via a host-provided implementation. It also adjusts extension loading to proactively run module initializers and refactors some runtime-type/DAC selection logic.

Changes:

  • Add a per-target IClrSymbolProvider adapter over the host’s module/symbol services and pass it into ClrMD via DataTargetOptions.SymbolProvider.
  • Expose ICLRSymbolProvider on SOS’s legacy-DAC data-target CCW and forward calls to the shared IClrSymbolProvider service.
  • Run extension module initializers at load time; refactor runtime flavor→type mapping and add a CDAC fallback in GetDacFilePath.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/SOS/SOS.Hosting/DataTargetWrapper.cs Implements ICLRSymbolProvider on the legacy-DAC data target CCW and forwards symbol queries to a managed provider.
src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceManager.cs Forces module initializers to run when registering an extension assembly.
src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeProvider.cs Supplies DataTargetOptions.SymbolProvider from the host service container when creating ClrMD DataTarget.
src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs Centralizes flavor→RuntimeType mapping and adds a CDAC fallback path in GetDacFilePath.
src/Microsoft.Diagnostics.DebugServices.Implementation/HostSymbolProvider.cs New adapter implementing ClrMD’s IClrSymbolProvider on top of IModuleService/IModuleSymbols.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +399 to +403
if (!_symbolProvider.TryGetSymbolName(address, out string symbolName, out ulong displacement)
|| string.IsNullOrEmpty(symbolName))
{
return HResult.S_FALSE;
}
Comment on lines +405 to +424
if (pcchNameActual != null)
{
*pcchNameActual = (uint)symbolName.Length;
}
if (pDisplacement != null)
{
*pDisplacement = displacement;
}

if (cchName == 0 || pName == null)
{
return HResult.S_OK;
}

int copy = Math.Min(symbolName.Length, (int)cchName);
for (int i = 0; i < copy; i++)
{
pName[i] = symbolName[i];
}
return HResult.S_OK;
return HResult.S_FALSE;
}

if (_symbolProvider.TryGetSymbolAddress(name, out ulong address) && address != 0)
Comment on lines +59 to +63
string moduleName = !string.IsNullOrEmpty(module.FileName)
? Path.GetFileName(module.FileName)
: bareName;
symbolName = bareName.IndexOf('!') >= 0 ? bareName : $"{moduleName}!{bareName}";
return true;
Comment on lines +173 to +179
// Force-run the assembly's module initializer ([ModuleInitializer]-marked
// methods) so extensions can register process-wide state before any
// provider factory is invoked.
foreach (System.Reflection.Module module in assembly.GetModules())
{
System.Runtime.CompilerServices.RuntimeHelpers.RunModuleConstructor(module.ModuleHandle);
}
Comment on lines +124 to +132
if (_dacFilePath is null)
{
_cdacFilePath ??= GetLibraryPath(DebugLibraryKind.CDac);
if (_cdacFilePath is not null)
{
verifySignature = false;
return _cdacFilePath;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants