Misc service and implementation cleanup (#5129)

Clean up and add useful functions to IModuleService, IThreadService,
IHost, ITarget
This commit is contained in:
Mike McLaughlin 2025-01-22 16:53:00 -08:00 коммит произвёл GitHub
Родитель a83544eeba
Коммит 4d0d682cfd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
32 изменённых файлов: 390 добавлений и 255 удалений

Просмотреть файл

@ -30,7 +30,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
// apply any existing policy
// Apply any existing policy
AssemblyName referenceName = new(AppDomain.CurrentDomain.ApplyPolicy(args.Name));
string fileName = referenceName.Name + ".dll";
string assemblyPath;
@ -39,10 +39,10 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
// Look next to the executing assembly
probingPath = Path.Combine(_defaultAssembliesPath, fileName);
Debug.WriteLine($"Considering {probingPath} based on ExecutingAssembly");
Trace.TraceInformation($"Considering {probingPath} based on ExecutingAssembly");
if (Probe(probingPath, referenceName.Version, out assembly))
{
Debug.WriteLine($"Matched {probingPath} based on ExecutingAssembly");
Trace.TraceInformation($"Matched {probingPath} based on ExecutingAssembly");
return assembly;
}
@ -51,10 +51,10 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
if (!string.IsNullOrEmpty(assemblyPath))
{
probingPath = Path.Combine(Path.GetDirectoryName(assemblyPath), fileName);
Debug.WriteLine($"Considering {probingPath} based on RequestingAssembly");
Trace.TraceInformation($"Considering {probingPath} based on RequestingAssembly");
if (Probe(probingPath, referenceName.Version, out assembly))
{
Debug.WriteLine($"Matched {probingPath} based on RequestingAssembly");
Trace.TraceInformation($"Matched {probingPath} based on RequestingAssembly");
return assembly;
}
}

Просмотреть файл

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using Microsoft.Diagnostics.DebugServices;
@ -14,6 +15,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
private readonly ServiceManager _serviceManager;
private ServiceContainer _serviceContainer;
private readonly List<ITarget> _targets = new();
private string _tempDirectory;
private int _targetIdFactory;
public Host(HostType type)
@ -61,6 +63,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
target.Destroy();
}
_targets.Clear();
CleanupTempDirectory();
}
#region IHost
@ -84,6 +87,35 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
return _targetIdFactory++;
}
public string GetTempDirectory()
{
if (_tempDirectory == null)
{
// Use the SOS process's id if can't get the target's
uint processId = (uint)Process.GetCurrentProcess().Id;
// SOS depends on that the temp directory ends with "/".
_tempDirectory = Path.Combine(Path.GetTempPath(), "sos" + processId.ToString()) + Path.DirectorySeparatorChar;
Directory.CreateDirectory(_tempDirectory);
}
return _tempDirectory;
}
#endregion
private void CleanupTempDirectory()
{
if (_tempDirectory != null)
{
try
{
Directory.Delete(_tempDirectory, recursive: true);
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
{
}
_tempDirectory = null;
}
}
}
}

Просмотреть файл

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.Runtime;
using Microsoft.FileFormats;
@ -73,7 +74,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
public virtual uint? IndexTimeStamp { get; protected set; }
public bool IsPEImage
public virtual bool IsPEImage
{
get
{
@ -184,6 +185,31 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
public abstract string LoadSymbols();
/// <summary>
/// Downloads and returns the metadata for the assembly.
/// </summary>
/// <returns>metadata bytes</returns>
public ImmutableArray<byte> GetMetadata()
{
try
{
PEReader reader = Services.GetService<PEReader>();
if (reader is not null && reader.HasMetadata)
{
PEMemoryBlock metadataInfo = reader.GetMetadata();
return metadataInfo.GetContent();
}
}
catch (Exception ex) when
(ex is InvalidOperationException ||
ex is BadImageFormatException ||
ex is IOException)
{
Trace.TraceError($"GetMetaData: {ex.Message}");
}
return ImmutableArray<byte>.Empty;
}
#endregion
#region IExportSymbols

Просмотреть файл

@ -69,6 +69,8 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
#endregion
internal void AddService<T>(T service) => _serviceContainer.AddService(service);
protected override ModuleService ModuleService { get; }
}
}

Просмотреть файл

@ -172,6 +172,19 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
}
}
/// <summary>
/// Create a module instance
/// </summary>
/// <param name="moduleIndex">artificial index</param>
/// <param name="imageBase">module base address</param>
/// <param name="imageSize">module size</param>
/// <param name="imageName">module name</param>
/// <returns>IModule</returns>
IModule IModuleService.CreateModule(int moduleIndex, ulong imageBase, ulong imageSize, string imageName)
{
return new ModuleFromAddress(this, moduleIndex, imageBase, imageSize, imageName);
}
#endregion
/// <summary>
@ -220,7 +233,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
// First try getting the PE info as loaded layout (native Windows DLLs and most managed PEs).
peFile = GetPEInfo(isVirtual: true, address, size, out List<PdbFileInfo> pdbs, out Module.Flags flags);
// Continue only if marked as a PE. This bit regardless of the layout if the module has a PE header/signature.
// Continue only if marked as a PE. This bit is set regardless of the layout if the module has a PE header/signature.
if ((flags & Module.Flags.IsPEImage) != 0)
{
if (peFile is null || pdbs.Count == 0)
@ -288,9 +301,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
/// <returns>build id or null</returns>
internal byte[] GetBuildId(ulong address)
{
// This code is called by the image mapping memory service so it needs to use the
// original or raw memory service to prevent recursion so it can't use the ELFFile
// or MachOFile instance that is available from the IModule.Services provider.
Stream stream = MemoryService.CreateMemoryStream();
byte[] buildId = null;
try

Просмотреть файл

@ -47,6 +47,8 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
public override uint? IndexTimeStamp => _moduleInfo.IndexTimeStamp == InvalidTimeStamp ? null : unchecked((uint)_moduleInfo.IndexTimeStamp);
public override bool IsPEImage => _moduleInfo.Kind == ModuleKind.PortableExecutable;
public override ImmutableArray<byte> BuildId
{
get

Просмотреть файл

@ -51,7 +51,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
public IDisposable RegisterOneShot(Action callback) => Register(oneshot: true, callback);
#pragma warning disable CA1859
// Use concrete types when possible for improved performance. Explicitly obscure any functionality other than a duspose token.
// Use concrete types when possible for improved performance. Explicitly obscure any functionality other than a dispose token.
private IDisposable Register(bool oneshot, Action callback)
#pragma warning restore CA1859
{

Просмотреть файл

@ -72,7 +72,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
/// <summary>
/// The default symbol cache path:
/// * dbgeng on Windows uses the dbgeng symbol cache path: %PROGRAMDATA%\dbg\sym
/// * dotnet-dump on Windows uses the VS symbol cache path: %TEMPDIR%\SymbolCache
/// * VS or dotnet-dump on Windows use the VS symbol cache path: %TEMPDIR%\SymbolCache
/// * dotnet-dump/lldb on Linux/MacOS uses: $HOME/.dotnet/symbolcache
/// </summary>
public string DefaultSymbolCache
@ -86,8 +86,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
_defaultSymbolCache = _host.HostType switch
{
HostType.DbgEng => Path.Combine(Environment.GetEnvironmentVariable("PROGRAMDATA"), "dbg", "sym"),
HostType.DotnetDump => Path.Combine(Path.GetTempPath(), "SymbolCache"),
_ => throw new NotSupportedException($"Host type not supported {_host.HostType}"),
_ => Path.Combine(Path.GetTempPath(), "SymbolCache"),
};
}
else

Просмотреть файл

@ -17,7 +17,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
public abstract class Target : ITarget
{
private readonly string _dumpPath;
private string _tempDirectory;
private ServiceContainer _serviceContainer;
protected readonly ServiceContainerFactory _serviceContainerFactory;
@ -82,23 +81,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
/// </summary>
public uint? ProcessId { get; protected set; }
/// <summary>
/// Returns the unique temporary directory for this instance of SOS
/// </summary>
public string GetTempDirectory()
{
if (_tempDirectory == null)
{
// Use the SOS process's id if can't get the target's
uint processId = ProcessId.GetValueOrDefault((uint)Process.GetCurrentProcess().Id);
// SOS depends on that the temp directory ends with "/".
_tempDirectory = Path.Combine(Path.GetTempPath(), "sos" + processId.ToString()) + Path.DirectorySeparatorChar;
Directory.CreateDirectory(_tempDirectory);
}
return _tempDirectory;
}
/// <summary>
/// The per target services.
/// </summary>
@ -132,7 +114,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
OnDestroyEvent.Fire();
_serviceContainer.RemoveService(typeof(ITarget));
_serviceContainer.DisposeServices();
CleanupTempDirectory();
}
#endregion
@ -154,25 +135,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
return new Reader(new StreamAddressSpace(stream), layoutManager);
}
private void CleanupTempDirectory()
{
if (_tempDirectory != null)
{
try
{
foreach (string file in Directory.EnumerateFiles(_tempDirectory))
{
File.Delete(file);
}
Directory.Delete(_tempDirectory);
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
{
}
_tempDirectory = null;
}
}
public override bool Equals(object obj)
{
return Id == ((ITarget)obj).Id;
@ -192,10 +154,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
{
sb.Append($" {_dumpPath}");
}
if (_tempDirectory != null)
{
sb.Append($" {_tempDirectory}");
}
return sb.ToString();
}
}

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Microsoft.Diagnostics.DebugServices.Implementation
{
@ -23,7 +24,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
_serviceContainer.AddService<IThread>(this);
}
void IDisposable.Dispose()
public void Dispose()
{
_serviceContainer.RemoveService(typeof(IThread));
_serviceContainer.DisposeServices();
@ -58,22 +59,47 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
{
if (_threadContext.IsEmpty)
{
_threadContext = _threadService.GetThreadContext(this);
byte[] threadContext = new byte[_threadService.ContextSize];
if (!GetThreadContextInner(_threadService.ContextFlags, threadContext))
{
throw new DiagnosticsException($"Unable to get the context for thread {ThreadId:X8} with flags {_threadService.ContextFlags:X8}");
}
_threadContext = threadContext;
}
return _threadContext.Span;
}
/// <summary>
/// Get the thread context
/// </summary>
/// <param name="contextFlags">Windows context flags</param>
/// <param name="context">Context buffer</param>
/// <returns>true succeeded, false failed</returns>
protected virtual bool GetThreadContextInner(uint contextFlags, byte[] context) => _threadService.GetThreadContext(ThreadId, contextFlags, context);
public ulong GetThreadTeb()
{
if (!_teb.HasValue)
{
_teb = _threadService.GetThreadTeb(this);
_teb = GetThreadTebInner();
}
return _teb.Value;
}
/// <summary>
/// Returns the Windows TEB pointer for the thread
/// </summary>
/// <returns>TEB pointer or 0 if not implemented or thread id not found</returns>
protected virtual ulong GetThreadTebInner() => _threadService.GetThreadTeb(ThreadId);
#endregion
protected void SetContextFlags(uint contextFlags, Span<byte> context)
{
Span<byte> threadSpan = context.Slice(_threadService.ContextFlagsOffset, sizeof(uint));
MemoryMarshal.Write<uint>(threadSpan, ref contextFlags);
}
public override bool Equals(object obj)
{
IThread thread = (IThread)obj;

Просмотреть файл

@ -16,12 +16,13 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation {
/// </summary>
public abstract class ThreadService : IThreadService, IDisposable
{
private readonly int _contextSize;
private readonly uint _contextFlags;
private readonly Dictionary<string, RegisterInfo> _lookupByName;
private readonly Dictionary<int, RegisterInfo> _lookupByIndex;
private Dictionary<uint, IThread> _threads;
protected internal readonly int ContextSize;
protected internal readonly uint ContextFlags;
protected internal readonly int ContextFlagsOffset;
protected internal readonly IServiceProvider Services;
protected internal readonly ITarget Target;
@ -36,38 +37,38 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation {
{
case Architecture.X64:
// Dumps generated with newer dbgeng have bigger context buffers and clrmd requires the context size to at least be that size.
_contextSize = Target.OperatingSystem == OSPlatform.Windows ? 0x700 : AMD64Context.Size;
_contextFlags = AMD64Context.ContextControl | AMD64Context.ContextInteger | AMD64Context.ContextSegments | AMD64Context.ContextFloatingPoint;
ContextSize = Target.Host.HostType != HostType.Vs && Target.OperatingSystem == OSPlatform.Windows ? 0x700 : AMD64Context.Size;
ContextFlags = AMD64Context.ContextControl | AMD64Context.ContextInteger | AMD64Context.ContextSegments | AMD64Context.ContextFloatingPoint;
contextType = typeof(AMD64Context);
break;
case Architecture.X86:
_contextSize = X86Context.Size;
_contextFlags = X86Context.ContextControl | X86Context.ContextInteger | X86Context.ContextSegments | X86Context.ContextFloatingPoint;
ContextSize = X86Context.Size;
ContextFlags = X86Context.ContextControl | X86Context.ContextInteger | X86Context.ContextSegments | X86Context.ContextFloatingPoint;
contextType = typeof(X86Context);
break;
case Architecture.Arm64:
_contextSize = Arm64Context.Size;
_contextFlags = Arm64Context.ContextControl | Arm64Context.ContextInteger | Arm64Context.ContextFloatingPoint;
ContextSize = Arm64Context.Size;
ContextFlags = Arm64Context.ContextControl | Arm64Context.ContextInteger | Arm64Context.ContextFloatingPoint;
contextType = typeof(Arm64Context);
break;
case Architecture.Arm:
_contextSize = ArmContext.Size;
_contextFlags = ArmContext.ContextControl | ArmContext.ContextInteger | ArmContext.ContextFloatingPoint;
ContextSize = ArmContext.Size;
ContextFlags = ArmContext.ContextControl | ArmContext.ContextInteger | ArmContext.ContextFloatingPoint;
contextType = typeof(ArmContext);
break;
case (Architecture)6 /* Architecture.LoongArch64 */:
_contextSize = LoongArch64Context.Size;
_contextFlags = LoongArch64Context.ContextControl | LoongArch64Context.ContextInteger | LoongArch64Context.ContextFloatingPoint;
ContextSize = LoongArch64Context.Size;
ContextFlags = LoongArch64Context.ContextControl | LoongArch64Context.ContextInteger | LoongArch64Context.ContextFloatingPoint;
contextType = typeof(LoongArch64Context);
break;
case (Architecture)9 /* Architecture.RiscV64 */:
_contextSize = RiscV64Context.Size;
_contextFlags = RiscV64Context.ContextControl | RiscV64Context.ContextInteger | RiscV64Context.ContextFloatingPoint;
ContextSize = RiscV64Context.Size;
ContextFlags = RiscV64Context.ContextControl | RiscV64Context.ContextInteger | RiscV64Context.ContextFloatingPoint;
contextType = typeof(RiscV64Context);
break;
@ -81,6 +82,11 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation {
FieldInfo[] fields = contextType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
FieldOffsetAttribute offsetAttribute = field.GetCustomAttributes<FieldOffsetAttribute>(inherit: false).Single();
if (field.Name.Equals("contextflags", StringComparison.InvariantCultureIgnoreCase))
{
ContextFlagsOffset = offsetAttribute.Value;
}
RegisterAttribute registerAttribute = field.GetCustomAttributes<RegisterAttribute>(inherit: false).SingleOrDefault();
if (registerAttribute is null)
{
@ -108,7 +114,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation {
{
FramePointerIndex = index;
}
FieldOffsetAttribute offsetAttribute = field.GetCustomAttributes<FieldOffsetAttribute>(inherit: false).Single();
RegisterInfo registerInfo = new(index, offsetAttribute.Value, Marshal.SizeOf(field.FieldType), registerAttribute.Name ?? field.Name.ToLowerInvariant());
registers.Add(registerInfo);
index++;
@ -187,43 +192,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation {
return _lookupByIndex.TryGetValue(registerIndex, out info);
}
/// <summary>
/// Returns the register value for the thread context and register index. This function
/// can only return register values that are 64 bits or less and currently the clrmd data
/// targets don't return any floating point or larger registers.
/// </summary>
/// <param name="context">thread context</param>
/// <param name="registerIndex">register index</param>
/// <param name="value">value returned</param>
/// <returns>true if value found</returns>
public bool TryGetRegisterValue(ReadOnlySpan<byte> context, int registerIndex, out ulong value)
{
if (TryGetRegisterInfo(registerIndex, out RegisterInfo info))
{
ReadOnlySpan<byte> threadSpan = context.Slice(info.RegisterOffset, info.RegisterSize);
switch (info.RegisterSize)
{
case 1:
value = MemoryMarshal.Read<byte>(threadSpan);
return true;
case 2:
value = MemoryMarshal.Read<ushort>(threadSpan);
return true;
case 4:
value = MemoryMarshal.Read<uint>(threadSpan);
return true;
case 8:
value = MemoryMarshal.Read<ulong>(threadSpan);
return true;
default:
Trace.TraceError($"GetRegisterValue: {info.RegisterName} invalid size {info.RegisterSize}");
break;
}
}
value = 0;
return false;
}
/// <summary>
/// Enumerate all the native threads
/// </summary>
@ -268,32 +236,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation {
#endregion
/// <summary>
/// Get the thread context
/// </summary>
/// <param name="thread">thread instance</param>
/// <returns>context array</returns>
/// <exception cref="DiagnosticsException">invalid thread id</exception>
internal byte[] GetThreadContext(Thread thread)
{
byte[] threadContext = new byte[_contextSize];
if (!GetThreadContext(thread.ThreadId, _contextFlags, (uint)_contextSize, threadContext))
{
throw new DiagnosticsException();
}
return threadContext;
}
/// <summary>
/// Get the thread TEB
/// </summary>
/// <param name="thread">thread instance</param>
/// <returns>TEB</returns>
internal ulong GetThreadTeb(Thread thread)
{
return GetThreadTeb(thread.ThreadId);
}
/// <summary>
/// Get/create the thread dictionary.
/// </summary>
@ -313,17 +255,16 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation {
/// </summary>
/// <param name="threadId">OS thread id</param>
/// <param name="contextFlags">Windows context flags</param>
/// <param name="contextSize">Context size</param>
/// <param name="context">Context buffer</param>
/// <returns>true succeeded, false failed</returns>
/// <exception cref="DiagnosticsException">invalid thread id</exception>
protected abstract bool GetThreadContext(uint threadId, uint contextFlags, uint contextSize, byte[] context);
protected internal virtual bool GetThreadContext(uint threadId, uint contextFlags, byte[] context) => throw new NotImplementedException();
/// <summary>
/// Returns the Windows TEB pointer for the thread
/// </summary>
/// <param name="threadId">OS thread id</param>
/// <returns>TEB pointer or 0</returns>
protected abstract ulong GetThreadTeb(uint threadId);
/// <returns>TEB pointer or 0 if not implemented or thread id not found</returns>
protected internal virtual ulong GetThreadTeb(uint threadId) => throw new NotImplementedException();
}
}

Просмотреть файл

@ -25,11 +25,16 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
_threadReader = (IThreadReader)dataReader;
}
protected override bool GetThreadContext(uint threadId, uint contextFlags, uint contextSize, byte[] context)
protected override IEnumerable<IThread> GetThreadsInner()
{
return _threadReader.EnumerateOSThreadIds().Select((uint id, int index) => new Thread(this, index, id)).Cast<IThread>();
}
protected internal override bool GetThreadContext(uint threadId, uint contextFlags, byte[] context)
{
try
{
return _dataReader.GetThreadContext(threadId, contextFlags, new Span<byte>(context, 0, unchecked((int)contextSize)));
return _dataReader.GetThreadContext(threadId, contextFlags, new Span<byte>(context));
}
catch (ClrDiagnosticsException ex)
{
@ -38,12 +43,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
}
}
protected override IEnumerable<IThread> GetThreadsInner()
{
return _threadReader.EnumerateOSThreadIds().Select((uint id, int index) => new Thread(this, index, id)).Cast<IThread>();
}
protected override ulong GetThreadTeb(uint threadId)
protected internal override ulong GetThreadTeb(uint threadId)
{
return _threadReader.GetThreadTeb(threadId);
}

Просмотреть файл

@ -28,6 +28,29 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
/// </summary>
public static string ToHex(this ImmutableArray<byte> array) => string.Concat(array.Select((b) => b.ToString("x2")));
/// <summary>
/// Returns the pointer size for a given processor type
/// </summary>
/// <param name="architecture">processor type</param>
/// <returns>pointer size</returns>
/// <exception cref="NotSupportedException"></exception>
public static int GetPointerSizeFromArchitecture(Architecture architecture)
{
switch (architecture)
{
case Architecture.X64:
case Architecture.Arm64:
case (Architecture)6 /* Architecture.LoongArch64 */:
case (Architecture)9 /* Architecture.RiscV64 */:
return 8;
case Architecture.X86:
case Architecture.Arm:
return 4;
default:
throw new NotSupportedException("Architecture not supported");
}
}
/// <summary>
/// Combines two hash codes into a single hash code, in an order-dependent manner.
/// </summary>

Просмотреть файл

@ -55,5 +55,10 @@ namespace Microsoft.Diagnostics.DebugServices
/// <param name="target">target instance</param>
/// <returns>target id</returns>
int AddTarget(ITarget target);
/// <summary>
/// Returns the unique temporary directory for this debug session
/// </summary>
string GetTempDirectory();
}
}

Просмотреть файл

@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
namespace Microsoft.Diagnostics.DebugServices
{
/// <summary>
/// Attaches to live processes.
/// </summary>
public interface ILiveTargetFactory
{
/// <summary>
/// Attaches to a live process and suspends it until the target is destroyed/closed.
/// </summary>
/// <returns>target instance</returns>
/// <exception cref="DiagnosticsException">can not construct target instance</exception>
ITarget Attach(int processId);
}
}

Просмотреть файл

@ -97,5 +97,11 @@ namespace Microsoft.Diagnostics.DebugServices
/// </summary>
/// <returns>the symbol file name</returns>
string LoadSymbols();
/// <summary>
/// Downloads and returns the metadata for the assembly.
/// </summary>
/// <returns>metadata bytes</returns>
ImmutableArray<byte> GetMetadata();
}
}

Просмотреть файл

@ -45,5 +45,15 @@ namespace Microsoft.Diagnostics.DebugServices
/// <param name="moduleName">module name to find</param>
/// <returns>matching modules</returns>
IEnumerable<IModule> GetModuleFromModuleName(string moduleName);
/// <summary>
/// Create a module instance from a stream (memory or file).
/// </summary>
/// <param name="moduleIndex">artifical index</param>
/// <param name="imageBase">module base address</param>
/// <param name="imageSize">module size</param>
/// <param name="imageName">module name</param>
/// <returns>IModule</returns>
IModule CreateModule(int moduleIndex, ulong imageBase, ulong imageSize, string imageName);
}
}

Просмотреть файл

@ -43,11 +43,6 @@ namespace Microsoft.Diagnostics.DebugServices
/// </summary>
uint? ProcessId { get; }
/// <summary>
/// Returns the unique temporary directory for this instance of SOS
/// </summary>
string GetTempDirectory();
/// <summary>
/// The per target services.
/// </summary>

Просмотреть файл

@ -47,17 +47,6 @@ namespace Microsoft.Diagnostics.DebugServices
/// <returns>true if index found</returns>
bool TryGetRegisterInfo(int registerIndex, out RegisterInfo info);
/// <summary>
/// Returns the register value for the thread context and register index. This function
/// can only return register values that are 64 bits or less and currently the clrmd data
/// targets don't return any floating point or larger registers.
/// </summary>
/// <param name="context">thread context</param>
/// <param name="registerIndex">register index</param>
/// <param name="value">value returned</param>
/// <returns>true if value found</returns>
bool TryGetRegisterValue(ReadOnlySpan<byte> context, int registerIndex, out ulong value);
/// <summary>
/// Enumerate all the native threads
/// </summary>

Просмотреть файл

@ -0,0 +1,88 @@
// 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.Diagnostics;
using System.Runtime.InteropServices;
namespace Microsoft.Diagnostics.DebugServices
{
public static class ThreadServiceExtensions
{
/// <summary>
/// Returns the register value for the thread context and register index. This function
/// can only return register values that are 64 bits or less and currently the clrmd data
/// targets don't return any floating point or larger registers.
/// </summary>
/// <param name="threadService">thread service instance</param>
/// <param name="context">thread context</param>
/// <param name="registerIndex">register index</param>
/// <param name="value">value returned</param>
/// <returns>true if value found</returns>
public static bool TryGetRegisterValue(this IThreadService threadService, ReadOnlySpan<byte> context, int registerIndex, out ulong value)
{
if (threadService.TryGetRegisterInfo(registerIndex, out RegisterInfo info))
{
ReadOnlySpan<byte> threadSpan = context.Slice(info.RegisterOffset, info.RegisterSize);
switch (info.RegisterSize)
{
case 1:
value = MemoryMarshal.Read<byte>(threadSpan);
return true;
case 2:
value = MemoryMarshal.Read<ushort>(threadSpan);
return true;
case 4:
value = MemoryMarshal.Read<uint>(threadSpan);
return true;
case 8:
value = MemoryMarshal.Read<ulong>(threadSpan);
return true;
default:
Trace.TraceError($"GetRegisterValue: {info.RegisterName} invalid size {info.RegisterSize}");
break;
}
}
value = 0;
return false;
}
/// <summary>
/// Change a specific register.
/// </summary>
/// <param name="threadService">thread service instance</param>
/// <param name="context">writeable context span</param>
/// <param name="registerIndex">register index</param>
/// <param name="value">value to write</param>
/// <returns></returns>
public static bool TrySetRegisterValue(this IThreadService threadService, Span<byte> context, int registerIndex, ulong value)
{
if (threadService.TryGetRegisterInfo(registerIndex, out RegisterInfo info))
{
Span<byte> threadSpan = context.Slice(info.RegisterOffset, info.RegisterSize);
switch (info.RegisterSize)
{
case 1:
byte byteValue = (byte)value;
MemoryMarshal.Write<byte>(threadSpan, ref byteValue);
return true;
case 2:
ushort ushortValue = (ushort)value;
MemoryMarshal.Write<ushort>(threadSpan, ref ushortValue);
return true;
case 4:
uint uintValue = (uint)value;
MemoryMarshal.Write<uint>(threadSpan, ref uintValue);
return true;
case 8:
MemoryMarshal.Write<ulong>(threadSpan, ref value);
return true;
default:
Trace.TraceError($"SetRegisterValue: {info.RegisterName} invalid size {info.RegisterSize}");
break;
}
}
return false;
}
}
}

Просмотреть файл

@ -33,7 +33,7 @@ namespace Microsoft.Diagnostics.TestHelpers
{
ITarget target = services.GetService<ITarget>();
Debug.Assert(target is not null);
AddMembers(Target, typeof(ITarget), target, nameof(ITarget.Id), nameof(ITarget.GetTempDirectory));
AddMembers(Target, typeof(ITarget), target, nameof(ITarget.Id));
XElement modulesElement = new("Modules");
Target.Add(modulesElement);

Просмотреть файл

@ -5,6 +5,7 @@ using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.DebugServices.Implementation;
using Microsoft.Diagnostics.Runtime.Utilities;
namespace SOS.Extensions
@ -26,19 +27,7 @@ namespace SOS.Extensions
Debug.Assert(target != null);
Debug.Assert(debuggerServices != null);
_debuggerServices = debuggerServices;
switch (target.Architecture)
{
case Architecture.X64:
case Architecture.Arm64:
case (Architecture)6 /* Architecture.LoongArch64 */:
PointerSize = 8;
break;
case Architecture.X86:
case Architecture.Arm:
PointerSize = 4;
break;
}
PointerSize = Utilities.GetPointerSizeFromArchitecture(target.Architecture);
}
#region IMemoryService

Просмотреть файл

@ -24,11 +24,6 @@ namespace SOS.Extensions
_debuggerServices = debuggerServices;
}
protected override bool GetThreadContext(uint threadId, uint contextFlags, uint contextSize, byte[] context)
{
return _debuggerServices.GetThreadContext(threadId, contextFlags, contextSize, context) == HResult.S_OK;
}
protected override IEnumerable<IThread> GetThreadsInner()
{
HResult hr = _debuggerServices.GetNumberThreads(out uint number);
@ -55,6 +50,11 @@ namespace SOS.Extensions
}
}
protected override bool GetThreadContext(uint threadId, uint contextFlags, byte[] context)
{
return _debuggerServices.GetThreadContext(threadId, contextFlags, (uint)context.Length, context).IsOK;
}
protected override ulong GetThreadTeb(uint threadId)
{
_debuggerServices.GetThreadTeb(threadId, out ulong teb);

Просмотреть файл

@ -27,6 +27,7 @@ namespace SOS.Hosting
builder.AddMethod(new GetHostTypeDelegate(GetHostType));
builder.AddMethod(new GetServiceDelegate(ServiceWrapper.GetService));
builder.AddMethod(new GetCurrentTargetDelegate(GetCurrentTarget));
builder.AddMethod(new GetTempDirectoryDelegate(GetTempDirectory));
IHost = builder.Complete();
AddRef();
@ -65,6 +66,12 @@ namespace SOS.Hosting
return HResult.S_OK;
}
private string GetTempDirectory(
IntPtr self)
{
return _host.GetTempDirectory();
}
#endregion
#region IHost delegates
@ -84,6 +91,11 @@ namespace SOS.Hosting
[In] IntPtr self,
[Out] out IntPtr target);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.LPStr)]
private delegate string GetTempDirectoryDelegate(
[In] IntPtr self);
#endregion
}
}

Просмотреть файл

@ -6,6 +6,7 @@ using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.Runtime.Utilities;
@ -70,21 +71,49 @@ namespace SOS.Hosting
{
return HResult.E_INVALIDARG;
}
int hr = HResult.S_OK;
int hr = HResult.E_FAIL;
int dataSize = 0;
ImmutableArray<byte> metadata = symbolService.GetMetadata(imagePath, imageTimestamp, imageSize);
if (!metadata.IsEmpty)
if (symbolService.IsSymbolStoreEnabled)
{
dataSize = metadata.Length;
int size = Math.Min((int)bufferSize, dataSize);
Marshal.Copy(metadata.ToArray(), 0, pMetadata, size);
try
{
SymbolStoreKey key = PEFileKeyGenerator.GetKey(imagePath, imageTimestamp, imageSize);
string localFilePath = symbolService.DownloadFile(key.Index, key.FullPathName);
if (!string.IsNullOrWhiteSpace(localFilePath))
{
Stream peStream = TryOpenFile(localFilePath);
if (peStream != null)
{
using PEReader peReader = new(peStream, PEStreamOptions.Default);
if (peReader.HasMetadata)
{
PEMemoryBlock metadataInfo = peReader.GetMetadata();
ImmutableArray<byte> metadata = metadataInfo.GetContent();
if (!metadata.IsEmpty)
{
dataSize = metadata.Length;
int size = Math.Min((int)bufferSize, dataSize);
Marshal.Copy(metadata.ToArray(), 0, pMetadata, size);
hr = HResult.S_OK;
}
}
}
}
else
{
Trace.TraceError($"GetMetaDataLocator: file not download {key.Index}");
}
}
catch (Exception ex) when (ex is InvalidOperationException or BadImageFormatException or IOException)
{
Trace.TraceError($"GetMetaDataLocator: {ex.Message}");
}
}
else
{
hr = HResult.E_FAIL;
Trace.TraceError($"GetMetadataLocator: {imagePath} {imageTimestamp:X8} {imageSize:X8} symbol store not enabled");
}
if (pMetadataSize != IntPtr.Zero)
{
Marshal.WriteInt32(pMetadataSize, dataSize);
@ -115,9 +144,9 @@ namespace SOS.Hosting
int actualSize = 0;
Debug.Assert(pwszPathBuffer != IntPtr.Zero);
try
if (symbolService.IsSymbolStoreEnabled)
{
if (symbolService.IsSymbolStoreEnabled)
try
{
SymbolStoreKey key = PEFileKeyGenerator.GetKey(imagePath, imageTimestamp, imageSize);
string localFilePath = symbolService.DownloadFile(key.Index, key.FullPathName);
@ -143,19 +172,19 @@ namespace SOS.Hosting
hr = HResult.E_FAIL;
}
}
else
catch (Exception ex) when
(ex is UnauthorizedAccessException
or BadImageFormatException
or InvalidVirtualAddressException
or IOException)
{
Trace.TraceError($"GetICorDebugMetadataLocator: {imagePath} {imageTimestamp:X8} {imageSize:X8} symbol store not enabled");
Trace.TraceError($"GetICorDebugMetadataLocator: {imagePath} {imageTimestamp:X8} {imageSize:X8} ERROR {ex.Message}");
hr = HResult.E_FAIL;
}
}
catch (Exception ex) when
(ex is UnauthorizedAccessException or
BadImageFormatException or
InvalidVirtualAddressException or
IOException)
else
{
Trace.TraceError($"GetICorDebugMetadataLocator: {imagePath} {imageTimestamp:X8} {imageSize:X8} ERROR {ex.Message}");
Trace.TraceError($"GetICorDebugMetadataLocator: {imagePath} {imageTimestamp:X8} {imageSize:X8} symbol store not enabled");
hr = HResult.E_FAIL;
}
if (pPathBufferSize != IntPtr.Zero)
@ -164,5 +193,27 @@ namespace SOS.Hosting
}
return hr;
}
/// <summary>
/// Attempt to open a file stream.
/// </summary>
/// <param name="path">file path</param>
/// <returns>stream or null if doesn't exist or error</returns>
public static Stream TryOpenFile(string path)
{
if (path is not null && File.Exists(path))
{
try
{
return File.OpenRead(path);
}
catch (Exception ex) when (ex is UnauthorizedAccessException or NotSupportedException or IOException)
{
Trace.TraceError($"TryOpenFile: {ex.Message}");
}
}
return null;
}
}
}

Просмотреть файл

@ -45,7 +45,6 @@ namespace SOS.Hosting
builder.AddMethod(new GetOperatingSystemDelegate(GetOperatingSystem));
builder.AddMethod(new HostWrapper.GetServiceDelegate(ServiceWrapper.GetService));
builder.AddMethod(new GetTempDirectoryDelegate(GetTempDirectory));
builder.AddMethod(new GetRuntimeDelegate(GetRuntime));
builder.AddMethod(new FlushDelegate(Flush));
@ -84,12 +83,6 @@ namespace SOS.Hosting
return OperatingSystem.Unknown;
}
private string GetTempDirectory(
IntPtr self)
{
return _target.GetTempDirectory();
}
private int GetRuntime(
IntPtr self,
IntPtr* ppRuntime)
@ -133,11 +126,6 @@ namespace SOS.Hosting
private delegate OperatingSystem GetOperatingSystemDelegate(
[In] IntPtr self);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.LPStr)]
private delegate string GetTempDirectoryDelegate(
[In] IntPtr self);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
private delegate int GetRuntimeDelegate(
[In] IntPtr self,

Просмотреть файл

@ -277,38 +277,6 @@ LPCSTR Runtime::GetDacFilePath()
if (access(dacModulePath.c_str(), F_OK) == 0)
#endif
{
#if defined(__linux__)
// We are creating a symlink to the DAC in a temp directory
// where libcoreclrtraceptprovider.so doesn't exist so it
// doesn't get loaded by the DAC causing a LTTng-UST exception.
//
// Issue #https://github.com/dotnet/coreclr/issues/20205
LPCSTR tmpPath = m_target->GetTempDirectory();
if (tmpPath != nullptr)
{
std::string dacSymLink(tmpPath);
dacSymLink.append(NETCORE_DAC_DLL_NAME_A);
// Check if the DAC file already exists in the temp directory because
// of a "loadsymbols" command which downloads everything.
if (access(dacSymLink.c_str(), F_OK) == 0)
{
dacModulePath.assign(dacSymLink);
}
else
{
int error = symlink(dacModulePath.c_str(), dacSymLink.c_str());
if (error == 0)
{
dacModulePath.assign(dacSymLink);
}
else
{
ExtErr("symlink(%s, %s) FAILED %s\n", dacModulePath.c_str(), dacSymLink.c_str(), strerror(errno));
}
}
}
#endif
m_dacFilePath = _strdup(dacModulePath.c_str());
}
}

Просмотреть файл

@ -46,12 +46,6 @@ public:
/// <returns>S_OK or E_NOINTERFACE</returns>
virtual HRESULT STDMETHODCALLTYPE GetService(REFIID serviceId, PVOID* service) = 0;
/// <summary>
/// Returns the unique temporary directory for this instance of SOS
/// </summary>
/// <returns>temporary directory string</returns>
virtual LPCSTR STDMETHODCALLTYPE GetTempDirectory() = 0;
/// <summary>
/// Returns the current runtime instance
/// </summary>

Просмотреть файл

@ -504,6 +504,8 @@ namespace SOS.Hosting
public int AddTarget(ITarget target) => throw new NotImplementedException();
public string GetTempDirectory() => throw new NotImplementedException();
#endregion
#region ICLRDebuggingLibraryProvider* delegates

Просмотреть файл

@ -71,9 +71,6 @@ namespace Microsoft.Diagnostics.DebugServices.UnitTests
// Check that the ITarget properties match the test data
host.TestData.CompareMembers(host.TestData.Target, target);
// Test temp directory
AssertX.DirectoryExists("Target temporary directory", target.GetTempDirectory(), Output);
}
[SkippableTheory, MemberData(nameof(GetConfigurations))]

Просмотреть файл

@ -105,6 +105,8 @@ namespace Microsoft.Diagnostics.DebugServices.UnitTests
public int AddTarget(ITarget target) => throw new NotImplementedException();
public string GetTempDirectory() => throw new NotImplementedException();
#endregion
}