Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.IO;
using Microsoft.Diagnostics.Runtime;

namespace Microsoft.Diagnostics.DebugServices.Implementation
{
/// <summary>
/// Adapter exposing the host's <see cref="IModuleService"/> /
/// <see cref="IModuleSymbols"/> through ClrMD's
/// <see cref="IClrSymbolProvider"/> contract. Registered as a per-target
/// service so both ClrMD (via <see cref="DataTargetOptions.SymbolProvider"/>)
/// and SOS's data-target CCW can resolve symbols through one shared
/// implementation. Returns symbols in <c>"Module!Symbol"</c> form.
/// </summary>
[ServiceExport(Type = typeof(IClrSymbolProvider), Scope = ServiceScope.Target)]
public sealed class HostSymbolProvider : IClrSymbolProvider
{
private readonly IModuleService _moduleService;

public HostSymbolProvider(IModuleService moduleService)
{
_moduleService = moduleService ?? throw new ArgumentNullException(nameof(moduleService));
}

public bool TryGetSymbolName(ulong address, out string symbolName, out ulong displacement)
{
symbolName = null;
displacement = 0;

IModule module;
try
{
module = _moduleService.GetModuleFromAddress(address);
}
catch (DiagnosticsException)
{
return false;
}
if (module is null)
{
return false;
}

IModuleSymbols symbols = module.Services.GetService<IModuleSymbols>();
if (symbols is null)
{
return false;
}

if (!symbols.TryGetSymbolName(address, out string bareName, out displacement)
|| string.IsNullOrEmpty(bareName))
{
return false;
}

string moduleName = !string.IsNullOrEmpty(module.FileName)
? Path.GetFileName(module.FileName)
: bareName;
symbolName = bareName.IndexOf('!') >= 0 ? bareName : $"{moduleName}!{bareName}";
return true;
Comment on lines +59 to +63
}

public bool TryGetSymbolAddress(string name, out ulong address)
{
address = 0;
if (string.IsNullOrEmpty(name))
{
return false;
}

string targetModule = null;
string symbolName = name;
int bang = name.IndexOf('!');
if (bang > 0 && bang + 1 < name.Length)
{
targetModule = name.Substring(0, bang);
symbolName = name.Substring(bang + 1);
}

foreach (IModule module in _moduleService.EnumerateModules())
{
if (targetModule is not null
&& !string.Equals(
Path.GetFileNameWithoutExtension(module.FileName),
targetModule,
StringComparison.OrdinalIgnoreCase))
{
continue;
}

IModuleSymbols symbols = module.Services.GetService<IModuleSymbols>();
if (symbols is null)
{
continue;
}

if (symbols.TryGetSymbolAddress(symbolName, out ulong addr) && addr != 0)
{
address = addr;
return true;
}
}

return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,7 @@ public Runtime(IServiceProvider services, int id, ClrInfo clrInfo)
_settingsService = services.GetService<ISettingsService>() ?? throw new ArgumentException("ISettingsService required");
_symbolService = services.GetService<ISymbolService>() ?? throw new ArgumentException("ISymbolService required");

RuntimeType = RuntimeType.Unknown;
if (clrInfo.Flavor == ClrFlavor.Core)
{
RuntimeType = RuntimeType.NetCore;
}
else if (clrInfo.Flavor == ClrFlavor.Desktop)
{
RuntimeType = RuntimeType.Desktop;
}
RuntimeType = GetRuntimeType(clrInfo.Flavor);
RuntimeModule = services.GetService<IModuleService>().GetModuleFromBaseAddress(clrInfo.ModuleInfo.ImageBase);

ServiceContainerFactory containerFactory = services.GetService<IServiceManager>().CreateServiceContainerFactory(ServiceScope.Runtime, services);
Expand Down Expand Up @@ -129,6 +121,15 @@ public string GetDacFilePath(out bool verifySignature)
_verifySignature = _settingsService.DacSignatureVerificationEnabled;
}
}
if (_dacFilePath is null)
{
_cdacFilePath ??= GetLibraryPath(DebugLibraryKind.CDac);
if (_cdacFilePath is not null)
{
verifySignature = false;
return _cdacFilePath;
}
}
Comment on lines +124 to +132
verifySignature = _verifySignature;
return _dacFilePath;
}
Expand Down Expand Up @@ -321,6 +322,13 @@ public override int GetHashCode()
"Other"
};

private static RuntimeType GetRuntimeType(ClrFlavor flavor) => flavor switch
{
ClrFlavor.Core => RuntimeType.NetCore,
ClrFlavor.Desktop => RuntimeType.Desktop,
_ => RuntimeType.Unknown,
};

public override string ToString()
{
StringBuilder sb = new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public IEnumerable<IRuntime> EnumerateRuntimes(int startingRuntimeId, RuntimeEnu
DataTarget dataTarget = new(_services.GetService<IDataReader>(), new DataTargetOptions()
{
ForceCompleteRuntimeEnumeration = (flags & RuntimeEnumerationFlags.All) != 0,
VerifyDacOnWindows = settingsService?.DacSignatureVerificationEnabled ?? true
VerifyDacOnWindows = settingsService?.DacSignatureVerificationEnabled ?? true,
SymbolProvider = _services.GetService<IClrSymbolProvider>(),
});
for (int i = 0; i < dataTarget.ClrVersions.Length; i++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ public void RegisterAssembly(Assembly assembly)
}
try
{
// 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 +173 to +179
RegisterExportedServices(assembly);
NotifyExtensionLoad.Fire(assembly);
}
Expand Down
91 changes: 91 additions & 0 deletions src/SOS/SOS.Hosting/DataTargetWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal sealed unsafe class DataTargetWrapper : COMCallableIUnknown
private static readonly Guid IID_ICLRMetadataLocator = new("aa8fa804-bc05-4642-b2c5-c353ed22fc63");
private static readonly Guid IID_ICLRRuntimeLocator = new("b760bf44-9377-4597-8be7-58083bdc5146");
private static readonly Guid IID_ICLRContractLocator = new("17d5b8c6-34a9-407f-af4f-a930201d4e02");
private static readonly Guid IID_ICLRSymbolProvider = new("c4f8b7e2-9d3a-4f6c-b1e5-8a2d7c3f9b1e");

// For ClrMD's magic hand shake
private const ulong MagicCallbackConstant = 0x43;
Expand All @@ -31,6 +32,7 @@ internal sealed unsafe class DataTargetWrapper : COMCallableIUnknown
private readonly IModuleService _moduleService;
private readonly IThreadUnwindService _threadUnwindService;
private readonly IRemoteMemoryService _remoteMemoryService;
private readonly IClrSymbolProvider _symbolProvider;
private readonly ulong _ignoreAddressBitsMask;

public IntPtr IDataTarget { get; }
Expand All @@ -47,6 +49,7 @@ public DataTargetWrapper(IServiceProvider services, IRuntime runtime)
_threadUnwindService = services.GetService<IThreadUnwindService>();
_moduleService = services.GetService<IModuleService>();
_remoteMemoryService = services.GetService<IRemoteMemoryService>();
_symbolProvider = services.GetService<IClrSymbolProvider>();
_ignoreAddressBitsMask = _memoryService.SignExtensionMask();

VTableBuilder builder = AddInterface(IID_ICLRDataTarget, false);
Expand All @@ -73,6 +76,11 @@ public DataTargetWrapper(IServiceProvider services, IRuntime runtime)
builder.AddMethod(new GetContractDescriptorDelegate(GetContractDescriptor));
builder.Complete();

builder = AddInterface(IID_ICLRSymbolProvider, false);
builder.AddMethod(new TryGetSymbolNameDelegate(TryGetSymbolName));
builder.AddMethod(new TryGetSymbolAddressDelegate(TryGetSymbolAddress));
builder.Complete();

AddRef();
}

Expand Down Expand Up @@ -378,6 +386,70 @@ private int GetContractDescriptor(

#endregion

#region ICLRSymbolProvider

private int TryGetSymbolName(
IntPtr self,
ulong address,
uint cchName,
char* pName,
uint* pcchNameActual,
ulong* pDisplacement)
{
if (!_symbolProvider.TryGetSymbolName(address, out string symbolName, out ulong displacement)
|| string.IsNullOrEmpty(symbolName))
{
return HResult.S_FALSE;
}
Comment on lines +399 to +403

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;
Comment on lines +405 to +424
}

private int TryGetSymbolAddress(
IntPtr self,
string name,
ulong* pAddress)
{
if (pAddress == null)
{
return HResult.E_INVALIDARG;
}
*pAddress = 0;

if (string.IsNullOrEmpty(name))
{
return HResult.S_FALSE;
}

if (_symbolProvider.TryGetSymbolAddress(name, out ulong address) && address != 0)
{
*pAddress = address;
return HResult.S_OK;
}
return HResult.S_FALSE;
}

#endregion

#region ICLRDataTarget delegates

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
Expand Down Expand Up @@ -522,5 +594,24 @@ private delegate int GetContractDescriptorDelegate(
[Out] out ulong address);

#endregion

#region ICLRSymbolProvider delegates

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate int TryGetSymbolNameDelegate(
[In] IntPtr self,
[In] ulong address,
[In] uint cchName,
[Out] char* pName,
[Out] uint* pcchNameActual,
[Out] ulong* pDisplacement);

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate int TryGetSymbolAddressDelegate(
[In] IntPtr self,
[In][MarshalAs(UnmanagedType.LPWStr)] string name,
[Out] ulong* pAddress);

#endregion
}
}
Loading