ProjectSection(SolutionItems) = preProject
src\SOS\inc\clrma.h = src\SOS\inc\clrma.h
src\SOS\inc\clrma.idl = src\SOS\inc\clrma.idl
+ src\SOS\inc\clrmaservice.h = src\SOS\inc\clrmaservice.h
src\SOS\inc\debuggerservices.h = src\SOS\inc\debuggerservices.h
src\SOS\inc\host.h = src\SOS\inc\host.h
src\SOS\inc\hostservices.h = src\SOS\inc\hostservices.h
src\SOS\inc\lldbservices.h = src\SOS\inc\lldbservices.h
+ src\SOS\inc\outputservice.h = src\SOS\inc\outputservice.h
src\SOS\inc\remotememoryservice.h = src\SOS\inc\remotememoryservice.h
src\SOS\inc\runtime.h = src\SOS\inc\runtime.h
src\SOS\inc\specialdiaginfo.h = src\SOS\inc\specialdiaginfo.h
--- /dev/null
+## SOS Watson/!analyze support
+
+This document summarizes what functionality is needed by Watson/!analyze to properly bucket crash dumps. The audience is the runtime devs working on the cDAC. Historically !analyze wrapped calls to SOS with an internal interface called CLRMA. This set of interfaces abstracted how !analyze obtained the crash information like managed threads, thread stack traces, managed exception and exception stack traces, etc. from SOS on a crash dump. Now the CLRMA interfaces are a public contract between SOS and !analyze.
+
+CLRMA interface definition: https://github.com/dotnet/diagnostics/blob/main/src/SOS/inc/clrma.idl
+
+### CLRMA interfaces
+
+SOS now exposes new exports (CLRMACreateInstance/CLRMAReleaseInstance) that !analyze looks for to create the top level CLRMA instance (ICLRManagedAnalysis). SOS supports Native AOT runtime crashes via the crash info JSON blob in a runtime global buffer and other .NET runtimes with the direct DAC support under the "SOS/clrma" directory.
+
+#### ICLRManagedAnalysis
+
+This root interface contains some setup functions like ProviderName/AssociateClient and also functions to get the thread (GetThread) and exception (GetException) interface instances. This implementation calls the managed clrma service first to see if there is Native AOT crash info, otherwise, it enables the direct DAC support.
+
+#### ICLRMAClrThread
+
+Provides the details about a specific or the current managed thread like managed stack trace and the current exceptions. If the stack trace isn't implemented (as in the Native AOT case), !analyze uses the native stack trace for the bucketing.
+
+#### ICLRMAClrException
+
+Provides all the details about a specific or current thread's managed exception like the type, message string, stack trace and inner exceptions.
+
+#### ICLRMAObjectInspection
+
+The object inspection interface is a set of optional functions to get an object's type or field's value. It used to get more detailed information about an exception object like the file path field from a FileNotFoundException. They are used to refine the bucketing for exception and other types.
+
+Here are some examples of !analyze object field inspection (this is not exhaustive list):
+
+- FileNotFoundException: _fileName, _fusionLog
+- BadImageFormatException._fileName
+- Exception._remoteStackTraceString
+- IOException._maybeFullPath
+- SocketException.nativeErrorCode
+- TypeInitializationException._typeName
+- TypeLoadException: ClassName, AssemblyName
+- WebException: m_Response.m_Uri.m_String, m_Response.m_StatusDescription, m_Response.m_StatusCode
+
+Not implemented at this time for Native AOT or .NET Core.
+
+### DAC interfaces used by CLRMA
+
+```
+ISOSDacInterface::GetUsefulGlobals()
+ISOSDacInterface::GetThreadStoreData()
+ISOSDacInterface::GetThreadData()
+ISOSDacInterface::GetNestedExceptionData()
+ISOSDacInterface::GetObjectData()
+ISOSDacInterface2::GetObjectExceptionData()
+ISOSDacInterface::GetMethodTableName()
+ISOSDacInterface::GetMethodTableData()
+ISOSDacInterface::GetObjectStringData()
+ISOSDacInterface::GetMethodDescData()
+ISOSDacInterface::GetMethodDescName()
+ISOSDacInterface::GetModuleData()
+ISOSDacInterface::GetPEFileBase()
+ISOSDacInterface::GetPEFileName()
+ISOSDacInterface::GetMethodDescPtrFromIP()
+
+// Module name fallback if debugger and GetPEFileName() can't get the name.
+ISOSDacInterface::GetModule()
+IXCLRDataModule::GetFileName
+
+// Managed stack walking
+IXCLRDataProcess::GetTaskByOSThreadID()
+IXCLRDataTask::CreateStackWalk()
+IXCLRDataStackWalk::Request(DACSTACKPRIV_REQUEST_FRAME_DATA, ...)
+IXCLRDataStackWalk::GetContext()
+IXCLRDataStackWalk::Next()
+```
+
+### References
+
+SOS CLRMA export code: https://github.com/dotnet/diagnostics/blob/main/src/SOS/Strike/clrma/clrma.cpp.
+
+SOS CLRMA wrapper code: https://github.com/dotnet/diagnostics/blob/main/src/SOS/SOS.Extensions/Clrma/ClrmaServiceWrapper.cs.
--- /dev/null
+// 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.Collections.Generic;
+using System.Threading;
+
+namespace Microsoft.Diagnostics.DebugServices.Implementation
+{
+ public sealed class CaptureConsoleService : IConsoleService
+ {
+ private readonly CharToLineConverter _charToLineConverter;
+ private readonly List<string> _builder = new();
+
+ public CaptureConsoleService() => _charToLineConverter = new((line) => _builder.Add(line));
+
+ public void Clear() => _builder.Clear();
+
+ public IReadOnlyList<string> OutputLines => _builder;
+
+ public override string ToString() => string.Concat(_builder);
+
+ #region IConsoleService
+
+ public void Write(string text) => _charToLineConverter.Input(text);
+
+ public void WriteWarning(string text) => _charToLineConverter.Input(text);
+
+ public void WriteError(string text) => _charToLineConverter.Input(text);
+
+ public bool SupportsDml => false;
+
+ public void WriteDml(string text) => throw new NotSupportedException();
+
+ public void WriteDmlExec(string text, string _) => throw new NotSupportedException();
+
+ public CancellationToken CancellationToken { get; set; } = CancellationToken.None;
+
+ int IConsoleService.WindowWidth => int.MaxValue;
+
+ #endregion
+ }
+}
--- /dev/null
+// 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.Text;
+
+namespace Microsoft.Diagnostics.DebugServices.Implementation
+{
+ public sealed class CharToLineConverter
+ {
+ private readonly Action<string> m_callback;
+ private readonly StringBuilder m_text = new();
+
+ public CharToLineConverter(Action<string> callback)
+ {
+ m_callback = callback;
+ }
+
+ public void Input(byte[] buffer, int offset, int count)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ char c = (char)buffer[offset + i];
+ if (c == '\r')
+ {
+ continue;
+ }
+ if (c == '\n')
+ {
+ Flush();
+ }
+ else if (c is '\t' or >= ((char)0x20) and <= ((char)127))
+ {
+ m_text.Append(c);
+ }
+ }
+ }
+
+ public void Input(string text)
+ {
+ foreach (char c in text)
+ {
+ if (c == '\r')
+ {
+ continue;
+ }
+ if (c == '\n')
+ {
+ Flush();
+ }
+ else if (c is '\t' or >= ((char)0x20) and <= ((char)127))
+ {
+ m_text.Append(c);
+ }
+ }
+ }
+
+ public void Flush()
+ {
+ m_callback(m_text.ToString());
+ m_text.Clear();
+ }
+ }
+}
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Help;
_commandGroups.Add(new CommandGroup(_commandPrompt));
}
+ /// <summary>
+ /// Execute the command line and return the captured console output.
+ /// </summary>
+ /// <param name="commandLine">command line text</param>
+ /// <param name="services">services for the command</param>
+ /// <returns>Array of console output lines</returns>
+ /// <exception cref="ArgumentException">empty command line</exception>
+ /// <exception cref="CommandNotFoundException">command not found</exception>
+ /// <exception cref="CommandParsingException ">parsing error</exception>
+ /// <exception cref="DiagnosticsException">other errors</exception>
+ public IReadOnlyList<string> ExecuteAndCapture(string commandLine, IServiceProvider services)
+ {
+ CaptureConsoleService consoleService = new();
+ ServiceContainer serviceContainer = new(services);
+ serviceContainer.AddService(consoleService);
+ Execute(commandLine, services);
+ return consoleService.OutputLines;
+ }
+
/// <summary>
/// Parse and execute the command line.
/// </summary>
public string RuntimeModuleDirectory { get; set; }
-
public Version RuntimeVersion
{
get
using System.Reflection;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading;
using Microsoft.FileFormats;
using Microsoft.FileFormats.ELF;
using Microsoft.FileFormats.MachO;
return arguments;
}
}
-
- public class CaptureConsoleService : IConsoleService
- {
- private readonly StringBuilder _builder = new();
-
- public CaptureConsoleService()
- {
- }
-
- public void Clear() => _builder.Clear();
-
- public override string ToString() => _builder.ToString();
-
- #region IConsoleService
-
- public void Write(string text)
- {
- _builder.Append(text);
- }
-
- public void WriteWarning(string text)
- {
- _builder.Append(text);
- }
-
- public void WriteError(string text)
- {
- _builder.Append(text);
- }
-
- public bool SupportsDml => false;
-
- public void WriteDml(string text) => throw new NotSupportedException();
-
- public void WriteDmlExec(string text, string _) => throw new NotSupportedException();
-
- public CancellationToken CancellationToken { get; set; } = CancellationToken.None;
-
- int IConsoleService.WindowWidth => int.MaxValue;
-
- #endregion
- }
}
+++ /dev/null
-// 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.Text;
-
-namespace Microsoft.Diagnostics.TestHelpers
-{
- public sealed class CharToLineConverter
- {
- private readonly Action<string> m_callback;
- private readonly StringBuilder m_text = new();
-
- public CharToLineConverter(Action<string> callback)
- {
- m_callback = callback;
- }
-
- public void Input(byte[] buffer, int offset, int count)
- {
- for (int i = 0; i < count; i++)
- {
- char c = (char)buffer[offset + i];
- if (c == '\r')
- {
- continue;
- }
- if (c == '\n')
- {
- Flush();
- }
- else if (c is '\t' or >= ((char)0x20) and <= ((char)127))
- {
- m_text.Append(c);
- }
- }
- }
-
- public void Input(string text)
- {
- foreach (char c in text)
- {
- if (c == '\r')
- {
- continue;
- }
- if (c == '\n')
- {
- Flush();
- }
- else if (c is '\t' or >= ((char)0x20) and <= ((char)127))
- {
- m_text.Append(c);
- }
- }
- }
-
- public void Flush()
- {
- m_callback(m_text.ToString());
- m_text.Clear();
- }
- }
-}
using System;
using System.Diagnostics;
+using Microsoft.Diagnostics.DebugServices.Implementation;
using Xunit.Abstractions;
namespace Microsoft.Diagnostics.TestHelpers
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.DebugServices;
{
private readonly ServiceManager _serviceManager;
private readonly ServiceContainer _serviceContainer;
+ private readonly ContextService _contextService;
+ private readonly CommandService _commandService;
private readonly SymbolService _symbolService;
private DataTarget _dataTarget;
private int _targetIdFactory;
_serviceContainer.AddService<IServiceManager>(_serviceManager);
_serviceContainer.AddService<IHost>(this);
- ContextService contextService = new(this);
- _serviceContainer.AddService<IContextService>(contextService);
+ _contextService = new(this);
+ _serviceContainer.AddService<IContextService>(_contextService);
- _symbolService = new SymbolService(this);
+ _commandService = new();
+ _serviceContainer.AddService<ICommandService>(_commandService);
+
+ _symbolService = new(this);
_serviceContainer.AddService<ISymbolService>(_symbolService);
// Automatically enable symbol server support
public ServiceContainer ServiceContainer => _serviceContainer;
+ public CommandService CommandService => _commandService;
+
+ public override IReadOnlyList<string> ExecuteHostCommand(string commandLine) => _commandService.ExecuteAndCapture(commandLine, _contextService.Services);
+
protected override ITarget GetTarget()
{
_dataTarget = DataTarget.LoadDump(DumpFile);
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
using Microsoft.Diagnostics.DebugServices;
namespace Microsoft.Diagnostics.TestHelpers
{
get
{
- _testData ??= new TestDataReader(TestDataFile);
+ _testData ??= TestDataFile != null ? new TestDataReader(TestDataFile) : null;
return _testData;
}
}
}
}
- public bool IsTestDbgEng => Config.AllSettings.TryGetValue("TestDbgEng", out string value) && value == "true";
+ public abstract IReadOnlyList<string> ExecuteHostCommand(string commandLine);
protected abstract ITarget GetTarget();
public string DumpFile => TestConfiguration.MakeCanonicalPath(Config.AllSettings["DumpFile"]);
- public string TestDataFile => TestConfiguration.MakeCanonicalPath(Config.AllSettings["TestDataFile"]);
+ public string TestDataFile => TestConfiguration.MakeCanonicalPath(Config.AllSettings.GetValueOrDefault("TestDataFile"));
public override string ToString() => DumpFile;
}
public static class TestHostExtensions
{
- public static bool IsTestDbgEng(this TestConfiguration config) => config.AllSettings.TryGetValue("TestDbgEng", out string value) && value == "true";
+ public static bool IsTestDbgEng(this TestConfiguration config) => config.AllSettings.GetValueOrDefault("TestDbgEng", string.Empty) == "true";
}
}
--- /dev/null
+// 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;
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.Runtime.Utilities;
+using SOS.Hosting;
+using SOS.Hosting.DbgEng.Interop;
+
+namespace SOS.Extensions.Clrma
+{
+ public sealed class ClrmaServiceWrapper : COMCallableIUnknown
+ {
+ public static readonly Guid IID_ICLRMAService = new("1FCF4C14-60C1-44E6-84ED-20506EF3DC60");
+
+ public const int E_BOUNDS = unchecked((int)0x8000000B);
+ public const uint DEBUG_ANY_ID = uint.MaxValue;
+
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ServiceWrapper _serviceWrapper;
+ private ICrashInfoService _crashInfoService;
+
+ public ClrmaServiceWrapper(ITarget target, IServiceProvider serviceProvider, ServiceWrapper serviceWrapper)
+ {
+ _serviceProvider = serviceProvider;
+ _serviceWrapper = serviceWrapper;
+
+ target.OnFlushEvent.Register(() => _crashInfoService = null);
+
+ VTableBuilder builder = AddInterface(IID_ICLRMAService, validate: false);
+ builder.AddMethod(new AssociateClientDelegate(AssociateClient));
+ builder.AddMethod(new GetThreadDelegate(GetThread));
+ builder.AddMethod(new GetExceptionDelegate(GetException));
+ builder.AddMethod(new GetObjectInspectionDelegate(GetObjectInspection));
+ builder.Complete();
+ // Since this wrapper is only created through a ServiceWrapper factory, no AddRef() is needed.
+ }
+
+ protected override void Destroy()
+ {
+ Trace.TraceInformation("ClrmaServiceWrapper.Destroy");
+ _serviceWrapper.RemoveServiceWrapper(ClrmaServiceWrapper.IID_ICLRMAService);
+ _crashInfoService = null;
+ }
+
+ private int AssociateClient(
+ IntPtr self,
+ IntPtr punk)
+ {
+ // If the crash info service doesn't exist, then tell Watson/CLRMA to go on to the next provider
+ return CrashInfoService is null ? HResult.E_NOINTERFACE : HResult.S_OK;
+ }
+
+ private int GetThread(
+ IntPtr self,
+ uint osThreadId,
+ out IntPtr clrmaClrThread)
+ {
+ clrmaClrThread = IntPtr.Zero;
+ if (CrashInfoService is null)
+ {
+ return HResult.E_FAIL;
+ }
+ IThread thread = ThreadService?.GetThreadFromId(osThreadId);
+ if (thread is null)
+ {
+ return HResult.E_INVALIDARG;
+ }
+ ThreadWrapper threadWrapper = new(CrashInfoService, thread);
+ clrmaClrThread = threadWrapper.ICLRMACClrThread;
+ return HResult.S_OK;
+ }
+
+ private int GetException(
+ IntPtr self,
+ ulong address,
+ out IntPtr clrmaClrException)
+ {
+ clrmaClrException = IntPtr.Zero;
+ IException exception = null;
+ try
+ {
+ exception = CrashInfoService?.GetException(address);
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ }
+ if (exception is null)
+ {
+ return HResult.E_INVALIDARG;
+ }
+ ExceptionWrapper exceptionWrapper = new(exception);
+ clrmaClrException = exceptionWrapper.ICLRMACClrException;
+ return HResult.S_OK;
+ }
+
+ private int GetObjectInspection(
+ IntPtr self,
+ out IntPtr clrmaObjectInspection)
+ {
+ clrmaObjectInspection = IntPtr.Zero;
+ return HResult.E_NOTIMPL;
+ }
+
+ private ICrashInfoService CrashInfoService => _crashInfoService ??= _serviceProvider.GetService<ICrashInfoService>();
+
+ private IThreadService ThreadService => _serviceProvider.GetService<IThreadService>();
+
+ #region ICLRMAService delegates
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate int AssociateClientDelegate(
+ [In] IntPtr self,
+ [In] IntPtr punk);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate int GetThreadDelegate(
+ [In] IntPtr self,
+ [In] uint osThreadId,
+ [Out] out IntPtr clrmaClrThread);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate int GetExceptionDelegate(
+ [In] IntPtr self,
+ [In] ulong address,
+ [Out] out IntPtr clrmaClrException);
+
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate int GetObjectInspectionDelegate(
+ [In] IntPtr self,
+ [Out] out IntPtr objectInspection);
+
+ #endregion
+ }
+}
}
catch (ArgumentOutOfRangeException)
{
- return ManagedAnalysisWrapper.E_BOUNDS;
+ return ClrmaServiceWrapper.E_BOUNDS;
}
ip = frame.InstructionPointer;
sp = frame.StackPointer;
clrmaClrException = IntPtr.Zero;
if (index >= InnerExceptions.Length)
{
- return ManagedAnalysisWrapper.E_BOUNDS;
+ return ClrmaServiceWrapper.E_BOUNDS;
}
ExceptionWrapper exception = InnerExceptions[index];
exception.AddRef();
+++ /dev/null
-// 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;
-using Microsoft.Diagnostics.DebugServices;
-using Microsoft.Diagnostics.Runtime.Utilities;
-using SOS.Hosting;
-using SOS.Hosting.DbgEng.Interop;
-
-namespace SOS.Extensions.Clrma
-{
- public sealed class ManagedAnalysisWrapper : COMCallableIUnknown
- {
- public static readonly Guid IID_ICLRManagedAnalysis = new("8CA73A16-C017-4c8f-AD51-B758727478CA");
-
- public const int E_BOUNDS = unchecked((int)0x8000000B);
- public const uint DEBUG_ANY_ID = uint.MaxValue;
-
- private readonly IServiceProvider _serviceProvider;
- private readonly ServiceWrapper _serviceWrapper;
- private ICrashInfoService _crashInfoService;
- private IDebugClient _debugClient;
- private ThreadWrapper _thread;
- private ExceptionWrapper _exception;
-
- public ManagedAnalysisWrapper(ITarget target, IServiceProvider serviceProvider, ServiceWrapper serviceWrapper)
- {
- _serviceProvider = serviceProvider;
- _serviceWrapper = serviceWrapper;
-
- target.OnFlushEvent.Register(Flush);
-
- VTableBuilder builder = AddInterface(IID_ICLRManagedAnalysis, validate: false);
- builder.AddMethod(new GetProviderNameDelegate(GetProviderName));
- builder.AddMethod(new AssociateClientDelegate(AssociateClient));
- builder.AddMethod(new GetThreadDelegate(GetThread));
- builder.AddMethod(new GetExceptionDelegate(GetException));
- builder.AddMethod(new ObjectInspectionDelegate(ObjectInspection));
- builder.Complete();
- // Since this wrapper is only created through a ServiceWrapper factory, no AddRef() is needed.
- }
-
- protected override void Destroy()
- {
- Trace.TraceInformation("ManagedAnalysisWrapper.Destroy");
- _serviceWrapper.RemoveServiceWrapper(ManagedAnalysisWrapper.IID_ICLRManagedAnalysis);
- Flush();
- }
-
- private void Flush()
- {
- _crashInfoService = null;
- FlushDebugClient();
- FlushThread();
- FlushException();
- }
-
- private void FlushDebugClient()
- {
- if (_debugClient != null)
- {
- int count = Marshal.ReleaseComObject(_debugClient);
- Debug.Assert(count >= 0);
- _debugClient = null;
- }
- }
-
- private void FlushThread()
- {
- _thread?.ReleaseWithCheck();
- _thread = null;
- }
-
- private void FlushException()
- {
- _exception?.ReleaseWithCheck();
- _exception = null;
- }
-
- private int GetProviderName(
- IntPtr self,
- out string provider)
- {
- provider = "SOSCLRMA";
- return HResult.S_OK;
- }
-
- private int AssociateClient(
- IntPtr self,
- IntPtr punk)
- {
- // If the crash info service doesn't exist, then tell Watson/CLRMA to go on to the next provider
- if (CrashInfoService is null)
- {
- return HResult.E_NOINTERFACE;
- }
- FlushDebugClient();
- _debugClient = Marshal.GetObjectForIUnknown(punk) as IDebugClient;
- if (_debugClient == null)
- {
- return HResult.E_NOINTERFACE;
- }
- // We don't currently need the IDebugClient instance passed this this function.
- return HResult.S_OK;
- }
-
- private int GetThread(
- IntPtr self,
- uint osThreadId,
- out IntPtr clrmaClrThread)
- {
- clrmaClrThread = IntPtr.Zero;
- if (CrashInfoService is null)
- {
- return HResult.E_FAIL;
- }
- // osThreadId == 0 is current thread and -1 is "last event thread". The only thread
- // information we have currently is the crashing thread id so always return it.
- if (osThreadId == 0)
- {
- HResult hr = ((IDebugSystemObjects)_debugClient).GetCurrentThreadSystemId(out osThreadId);
- if (!hr.IsOK)
- {
- return hr;
- }
- }
- else if (osThreadId == uint.MaxValue)
- {
- HResult hr = ((IDebugControl)_debugClient).GetLastEventInformation(
- out DEBUG_EVENT _,
- out uint _,
- out uint threadIndex,
- IntPtr.Zero,
- 0,
- out uint _,
- null,
- 0,
- out uint _);
-
- if (!hr.IsOK)
- {
- return hr;
- }
- if (threadIndex == DEBUG_ANY_ID)
- {
- return HResult.E_INVALIDARG;
- }
- uint[] ids = new uint[1];
- uint[] sysIds = new uint[1];
- hr = ((IDebugSystemObjects)_debugClient).GetThreadIdsByIndex(threadIndex, 1, ids, sysIds);
- if (!hr.IsOK)
- {
- return hr;
- }
- osThreadId = sysIds[0];
- }
- if (_thread is null || _thread.ThreadId != osThreadId)
- {
- IThread thread = ThreadService?.GetThreadFromId(osThreadId);
- if (thread is null)
- {
- return HResult.E_INVALIDARG;
- }
- FlushThread();
- _thread = new ThreadWrapper(CrashInfoService, thread);
- }
- _thread.AddRef();
- clrmaClrThread = _thread.ICLRMACClrThread;
- return HResult.S_OK;
- }
-
- private int GetException(
- IntPtr self,
- ulong address,
- out IntPtr clrmaClrException)
- {
- clrmaClrException = IntPtr.Zero;
- if (_exception is null || _exception.Exception.Address != address)
- {
- IException exception = null;
- try
- {
- exception = CrashInfoService?.GetException(address);
- }
- catch (ArgumentOutOfRangeException)
- {
- }
- if (exception is null)
- {
- return HResult.E_INVALIDARG;
- }
- FlushException();
- _exception = new ExceptionWrapper(exception);
- }
- _exception.AddRef();
- clrmaClrException = _exception.ICLRMACClrException;
- return HResult.S_OK;
- }
-
- private int ObjectInspection(
- IntPtr self,
- out IntPtr clrmaObjectInspection)
- {
- clrmaObjectInspection = IntPtr.Zero;
- return HResult.E_NOTIMPL;
- }
-
- private ICrashInfoService CrashInfoService => _crashInfoService ??= _serviceProvider.GetService<ICrashInfoService>();
-
- private IThreadService ThreadService => _serviceProvider.GetService<IThreadService>();
-
- #region ICLRManagedAnalysis delegates
-
- [UnmanagedFunctionPointer(CallingConvention.Winapi)]
- private delegate int GetProviderNameDelegate(
- [In] IntPtr self,
- [Out, MarshalAs(UnmanagedType.BStr)] out string provider);
-
- [UnmanagedFunctionPointer(CallingConvention.Winapi)]
- private delegate int AssociateClientDelegate(
- [In] IntPtr self,
- [In] IntPtr punk);
-
- [UnmanagedFunctionPointer(CallingConvention.Winapi)]
- private delegate int GetThreadDelegate(
- [In] IntPtr self,
- [In] uint osThreadId,
- [Out] out IntPtr clrmaClrThread);
-
- [UnmanagedFunctionPointer(CallingConvention.Winapi)]
- private delegate int GetExceptionDelegate(
- [In] IntPtr self,
- [In] ulong address,
- [Out] out IntPtr clrmaClrException);
-
- [UnmanagedFunctionPointer(CallingConvention.Winapi)]
- private delegate int ObjectInspectionDelegate(
- [In] IntPtr self,
- [Out] out IntPtr objectInspection);
-
- #endregion
- }
-}
clrmaClrException = IntPtr.Zero;
if (index >= NestedExceptions.Length)
{
- return ManagedAnalysisWrapper.E_BOUNDS;
+ return ClrmaServiceWrapper.E_BOUNDS;
}
ExceptionWrapper exception = NestedExceptions[index];
exception.AddRef();
+++ /dev/null
-// 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.Runtime.InteropServices;
-using Microsoft.Diagnostics.Runtime.Utilities;
-using SOS.Hosting.DbgEng.Interop;
-
-namespace SOS.Extensions
-{
- /// <summary>
- /// A helper class to capture output of DbgEng commands that will restore the previous output callbacks
- /// when disposed.
- /// </summary>
- internal sealed class DbgEngOutputHolder : IDebugOutputCallbacksWide, IDisposable
- {
- private readonly IDebugClient5 _client;
- private readonly IDebugOutputCallbacksWide _previous;
-
- public DEBUG_OUTPUT InterestMask { get; set; }
-
- /// <summary>
- /// Event fired when we receive output from the debugger.
- /// </summary>
- public Action<DEBUG_OUTPUT, string> OutputReceived;
-
- public DbgEngOutputHolder(IDebugClient5 client, DEBUG_OUTPUT interestMask = DEBUG_OUTPUT.NORMAL)
- {
- _client = client;
- InterestMask = interestMask;
-
- _client.GetOutputCallbacksWide(out _previous);
- HResult hr = _client.SetOutputCallbacksWide(this);
- if (!hr)
- {
- throw Marshal.GetExceptionForHR(hr);
- }
- }
-
- public void Dispose()
- {
- if (_previous is not null)
- {
- _client.SetOutputCallbacksWide(_previous);
- }
- }
-
- public int Output(DEBUG_OUTPUT Mask, [In, MarshalAs(UnmanagedType.LPStr)] string Text)
- {
- if ((InterestMask & Mask) != 0 && Text is not null)
- {
- OutputReceived?.Invoke(Mask, Text);
- }
-
- return 0;
- }
- }
-}
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.DebugServices.Implementation;
using Microsoft.Diagnostics.Runtime;
using Microsoft.Diagnostics.Runtime.Utilities;
using SOS.Hosting;
return hr;
}
+ public void FlushCheck()
+ {
+ VTable.FlushCheck(Self);
+ }
+
+ public IReadOnlyList<string> ExecuteHostCommand(string commandLine, DEBUG_OUTPUT interestMask = DEBUG_OUTPUT.NORMAL)
+ {
+ CaptureConsoleService console = new();
+ ExecuteHostCommand(commandLine, (DEBUG_OUTPUT mask, string text) =>
+ {
+ if ((mask & interestMask) != 0)
+ {
+ console.Write(text);
+ }
+ });
+ return console.OutputLines;
+ }
+
+ public delegate void ExecuteHostCommandCallback(DEBUG_OUTPUT mask, string text);
+
+ public void ExecuteHostCommand(string commandLine, ExecuteHostCommandCallback outputCallback)
+ {
+ IntPtr callbackPtr = Marshal.GetFunctionPointerForDelegate(outputCallback);
+ byte[] commandLineBytes = Encoding.ASCII.GetBytes(commandLine + "\0");
+ fixed (byte* ptr = commandLineBytes)
+ {
+ HResult hr = VTable.ExecuteHostCommand(Self, ptr, callbackPtr);
+ if (!hr.IsOK)
+ {
+ throw new DiagnosticsException($"{commandLine} FAILED {hr}");
+ }
+ }
+ }
+
[StructLayout(LayoutKind.Sequential)]
private readonly unsafe struct IDebuggerServicesVTable
{
public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, byte*, uint, out int, int> ReadVirtual;
public readonly delegate* unmanaged[Stdcall]<IntPtr, ulong, byte*, uint, out int, int> WriteVirtual;
public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, out uint, int> GetNumberModules;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, out ulong, int> GetModuleByIndex;
public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, ulong, byte*, uint, out uint, byte*, uint, uint*, byte*, uint, uint*, int> GetModuleNames;
public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, out ulong, out ulong, out uint, out uint, int> GetModuleInfo;
public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, ulong, byte*, byte*, uint, uint*, int> GetModuleVersionInformation;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, byte*, uint, out uint, out ulong, int> GetModuleByModuleName;
public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, int> GetNumberThreads;
public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, uint, uint*, uint*, int> GetThreadIdsByIndex;
public readonly delegate* unmanaged[Stdcall]<IntPtr, uint, uint, uint, byte*, int> GetThreadContextBySystemId;
public readonly delegate* unmanaged[Stdcall]<IntPtr, DEBUG_OUTPUT, byte*, void> OutputDmlString;
public readonly delegate* unmanaged[Stdcall]<IntPtr, IntPtr, byte*, int> AddModuleSymbol;
public readonly delegate* unmanaged[Stdcall]<IntPtr, out uint, out uint, out int, void*, int, uint*, byte*, int, uint*, int> GetLastEventInformation;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, void> FlushCheck;
+ public readonly delegate* unmanaged[Stdcall]<IntPtr, byte*, IntPtr, int> ExecuteHostCommand;
}
}
}
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
+using System.Text;
using Microsoft.Diagnostics.DebugServices;
using Microsoft.Diagnostics.DebugServices.Implementation;
using Microsoft.Diagnostics.ExtensionCommands;
_hostWrapper.ReleaseWithCheck();
}
+ public IReadOnlyList<string> ExecuteCommand(string commandLine) => _commandService.ExecuteAndCapture(commandLine, _contextService.Services);
+
+ public IReadOnlyList<string> ExecuteHostCommand(string commandLine) => DebuggerServices.ExecuteHostCommand(commandLine, DEBUG_OUTPUT.NORMAL | DEBUG_OUTPUT.ERROR);
+
#region IHost
public IServiceEvent OnShutdownEvent { get; } = new ServiceEvent();
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Microsoft.Diagnostics.DebugServices;
{
internal sealed class MemoryRegionServiceFromDebuggerServices : IMemoryRegionService
{
- private readonly IDebugClient5 _client;
- private readonly IDebugControl5 _control;
+ private const string AddressCommand = "!address";
+ private readonly DebuggerServices _debuggerServices;
- public MemoryRegionServiceFromDebuggerServices(IDebugClient5 client)
+ public MemoryRegionServiceFromDebuggerServices(DebuggerServices debuggerServices)
{
- _client = client;
- _control = (IDebugControl5)client;
+ _debuggerServices = debuggerServices;
}
public IEnumerable<IMemoryRegion> EnumerateRegions()
bool foundHeader = false;
bool skipped = false;
- (int hr, string text) = RunCommandWithOutput("!address");
- if (hr < 0)
- {
- throw new InvalidOperationException($"!address failed with hresult={hr:x}");
- }
-
- foreach (string line in text.Split('\n'))
+ IReadOnlyList<string> lines = _debuggerServices.ExecuteHostCommand(AddressCommand);
+ foreach (string line in lines)
{
if (line.Length == 0)
{
if (type == MemoryRegionType.MEM_IMAGE && index < parts.Length)
{
image = parts[index].Substring(1, parts[index].Length - 2);
- index++;
}
if (description.Equals("<unknown>", StringComparison.OrdinalIgnoreCase))
if (!foundHeader)
{
- throw new InvalidOperationException($"!address did not produce a standard header.\nThis may mean symbols could not be resolved for ntdll.\nPlease run !address and make sure the output looks correct.");
+ throw new InvalidOperationException($"{AddressCommand} did not produce a standard header.\nThis may mean symbols could not be resolved for ntdll.\nPlease run {AddressCommand} and make sure the output looks correct.");
}
}
return false;
}
- private (int hresult, string output) RunCommandWithOutput(string command)
- {
- using DbgEngOutputHolder dbgengOutput = new(_client);
- StringBuilder sb = new(1024);
- dbgengOutput.OutputReceived += (mask, text) => sb.Append(text);
-
- int hr = _control.ExecuteWide(DEBUG_OUTCTL.THIS_CLIENT, command, DEBUG_EXECUTE.DEFAULT);
-
- return (hr, sb.ToString());
- }
-
private sealed class AddressMemoryRange : IMemoryRegion
{
public ulong Start { get; internal set; }
IMAGE_FILE_MACHINE.I386 => Architecture.X86,
IMAGE_FILE_MACHINE.ARM => Architecture.Arm,
IMAGE_FILE_MACHINE.THUMB => Architecture.Arm,
- IMAGE_FILE_MACHINE.THUMB2 => Architecture.Arm,
+ IMAGE_FILE_MACHINE.ARMNT => Architecture.Arm,
IMAGE_FILE_MACHINE.AMD64 => Architecture.X64,
IMAGE_FILE_MACHINE.ARM64 => Architecture.Arm64,
IMAGE_FILE_MACHINE.RISCV64 => (Architecture)9 /* Architecture.RiscV64 */,
_serviceContainerFactory.AddServiceFactory<ICrashInfoService>((services) => CreateCrashInfoService(services, debuggerServices));
OnFlushEvent.Register(() => FlushService<ICrashInfoService>());
- if (debuggerServices.DebugClient is not null)
+ if (Host.HostType == HostType.DbgEng)
{
- _serviceContainerFactory.AddServiceFactory<IMemoryRegionService>((services) => new MemoryRegionServiceFromDebuggerServices(debuggerServices.DebugClient));
+ _serviceContainerFactory.AddServiceFactory<IMemoryRegionService>((services) => new MemoryRegionServiceFromDebuggerServices(debuggerServices));
}
Finished();
TargetWrapper targetWrapper = Services.GetService<TargetWrapper>();
- targetWrapper?.ServiceWrapper.AddServiceWrapper(ManagedAnalysisWrapper.IID_ICLRManagedAnalysis, () => new ManagedAnalysisWrapper(this, Services, targetWrapper.ServiceWrapper));
+ targetWrapper?.ServiceWrapper.AddServiceWrapper(ClrmaServiceWrapper.IID_ICLRMAService, () => new ClrmaServiceWrapper(this, Services, targetWrapper.ServiceWrapper));
}
private unsafe ICrashInfoService CreateCrashInfoService(IServiceProvider services, DebuggerServices debuggerServices)
{
Architecture.X64 => IMAGE_FILE_MACHINE.AMD64,
Architecture.X86 => IMAGE_FILE_MACHINE.I386,
- Architecture.Arm => IMAGE_FILE_MACHINE.THUMB2,
+ Architecture.Arm => IMAGE_FILE_MACHINE.ARMNT,
Architecture.Arm64 => IMAGE_FILE_MACHINE.ARM64,
(Architecture)9 /* Architecture.RiscV64 */ => IMAGE_FILE_MACHINE.RISCV64,
_ => IMAGE_FILE_MACHINE.UNKNOWN,
public enum IMAGE_FILE_MACHINE : uint
{
UNKNOWN = 0,
- I386 = 0x014c, // Intel 386.
- R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian
- R4000 = 0x0166, // MIPS little-endian
- R10000 = 0x0168, // MIPS little-endian
+ I386 = 0x014c, // Intel 386.
+ R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian
+ R4000 = 0x0166, // MIPS little-endian
+ R10000 = 0x0168, // MIPS little-endian
WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2
- ALPHA = 0x0184, // Alpha_AXP
- SH3 = 0x01a2, // SH3 little-endian
+ ALPHA = 0x0184, // Alpha_AXP
+ SH3 = 0x01a2, // SH3 little-endian
SH3DSP = 0x01a3,
- SH3E = 0x01a4, // SH3E little-endian
- SH4 = 0x01a6, // SH4 little-endian
- SH5 = 0x01a8, // SH5
- ARM = 0x01c0, // ARM Little-Endian
- THUMB = 0x01c2,
- THUMB2 = 0x1c4,
+ SH3E = 0x01a4, // SH3E little-endian
+ SH4 = 0x01a6, // SH4 little-endian
+ SH5 = 0x01a8, // SH5
+ ARM = 0x01c0, // ARM Little-Endian
+ THUMB = 0x01c2, // ARM Thumb/Thumb-2 Little-Endian
+ ARMNT = 0x01c4, // ARM Thumb-2 Little-Endian
AM33 = 0x01d3,
- POWERPC = 0x01F0, // IBM PowerPC Little-Endian
+ POWERPC = 0x01F0, // IBM PowerPC Little-Endian
POWERPCFP = 0x01f1,
- IA64 = 0x0200, // Intel 64
- MIPS16 = 0x0266, // MIPS
- ALPHA64 = 0x0284, // ALPHA64
- MIPSFPU = 0x0366, // MIPS
+ IA64 = 0x0200, // Intel 64
+ MIPS16 = 0x0266, // MIPS
+ ALPHA64 = 0x0284, // ALPHA64
+ MIPSFPU = 0x0366, // MIPS
MIPSFPU16 = 0x0466, // MIPS
AXP64 = 0x0284,
- TRICORE = 0x0520, // Infineon
+ TRICORE = 0x0520, // Infineon
CEF = 0x0CEF,
- EBC = 0x0EBC, // EFI Byte Code
- AMD64 = 0x8664, // AMD64 (K8)
- M32R = 0x9041, // M32R little-endian
- ARM64 = 0xAA64, // ARM64 Little-endian
+ EBC = 0x0EBC, // EFI Byte Code
+ AMD64 = 0x8664, // AMD64 (K8)
+ M32R = 0x9041, // M32R little-endian
+ ARM64 = 0xAA64, // ARM64 Little-endian
CEE = 0xC0EE,
+ LOONGARCH64 = 0x6264,
RISCV64 = 0x5064
}
}
*type = IMAGE_FILE_MACHINE.I386;
break;
case Architecture.Arm:
- *type = IMAGE_FILE_MACHINE.THUMB2;
+ *type = IMAGE_FILE_MACHINE.ARMNT;
break;
case Architecture.Arm64:
*type = IMAGE_FILE_MACHINE.ARM64;
add_definitions(-DUSE_STL)
set(SOS_SOURCES
- clrma.cpp
disasm.cpp
dllsext.cpp
eeheap.cpp
sigparser.cpp
sildasm.cpp
sos.cpp
+ sosextensions.cpp
stressLogDump.cpp
strike.cpp
util.cpp
vm.cpp
WatchCmd.cpp
Native.rc
+ clrma/clrma.cpp
+ clrma/managedanalysis.cpp
+ clrma/exception.cpp
+ clrma/thread.cpp
platform/datatarget.cpp
platform/hostimpl.cpp
platform/targetimpl.cpp
stressLogDump.cpp
strike.cpp
sos.cpp
+ sosextensions.cpp
util.cpp
platform/datatarget.cpp
platform/hostimpl.cpp
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
- <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src\shared;$(RepoRoot)src\shared\inc;$(RepoRoot)src\shared\pal\prebuilt\inc;$(RepoRoot)src\shared\pal\inc;$(RepoRoot)src\shared\pal\inc\rt;$(SolutionDir)src\shared\gcdump;$(SolutionDir)src\SOS\Strike\platform;$(SolutionDir)src\SOS\inc;$(SolutionDir)src\SOS\extensions;C:\Program Files (x86)\Microsoft Visual Studio\Preview\Enterprise\DIA SDK\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories>$(RepoRoot)artifacts\obj;$(RepoRoot)src\shared;$(RepoRoot)src\shared\inc;$(RepoRoot)src\shared\pal\prebuilt\inc;C:\Program Files (x86)\Microsoft Visual Studio\Preview\Enterprise\DIA SDK\include;$(SolutionDir)src\SOS\Strike\;$(SolutionDir)src\SOS\inc;$(SolutionDir)src\SOS\extensions;$(SolutionDir)src\shared\gcdump;$(SolutionDir)src\SOS\Strike\platform;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>%(AdditionalOptions) /d2Zi+ /Zm200 /ZH:SHA_256 /source-charset:utf-8 /homeparams</AdditionalOptions>
<AssemblerListingLocation>Debug/</AssemblerListingLocation>
<BufferSecurityCheck>true</BufferSecurityCheck>
</ProjectReference>
</ItemDefinitionGroup>
<ItemGroup>
- <ClCompile Include="clrma.cpp" />
+ <ClCompile Include="clrma\exception.cpp" />
+ <ClCompile Include="clrma\clrma.cpp" />
+ <ClCompile Include="clrma\thread.cpp" />
+ <ClCompile Include="clrma\managedanalysis.cpp" />
<ClCompile Include="dbgengservices.cpp" />
<ClCompile Include="disasm.cpp" />
<ClCompile Include="dllsext.cpp" />
<ClCompile Include="platform\targetimpl.cpp" />
<ClCompile Include="sildasm.cpp" />
<ClCompile Include="sos.cpp" />
+ <ClCompile Include="sosextensions.cpp" />
<ClCompile Include="stressLogDump.cpp" />
<ClCompile Include="strike.cpp" />
<ClCompile Include="symbols.cpp" />
<ClCompile Include="disasmARM64.cpp" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="clrma\exception.h" />
+ <ClInclude Include="clrma\thread.h" />
+ <ClInclude Include="clrma\managedanalysis.h" />
<ClInclude Include="data.h" />
<ClInclude Include="dbgengservices.h" />
<ClInclude Include="disasm.h" />
<ClInclude Include="platform\runtimeimpl.h" />
<ClInclude Include="platform\targetimpl.h" />
<ClInclude Include="sos.h" />
+ <ClInclude Include="sosextensions.h" />
<ClInclude Include="sos_md.h" />
<ClInclude Include="sos_stacktrace.h" />
<ClInclude Include="strike.h" />
<Filter>platform</Filter>
</ClCompile>
<ClCompile Include="managedcommands.cpp" />
- <ClCompile Include="clrma.cpp" />
+ <ClCompile Include="clrma\clrma.cpp">
+ <Filter>clrma</Filter>
+ </ClCompile>
+ <ClCompile Include="clrma\managedanalysis.cpp">
+ <Filter>clrma</Filter>
+ </ClCompile>
+ <ClCompile Include="clrma\exception.cpp">
+ <Filter>clrma</Filter>
+ </ClCompile>
+ <ClCompile Include="clrma\thread.cpp">
+ <Filter>clrma</Filter>
+ </ClCompile>
+ <ClCompile Include="sosextensions.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="data.h" />
<ClInclude Include="platform\targetimpl.h">
<Filter>platform</Filter>
</ClInclude>
+ <ClInclude Include="clrma\managedanalysis.h">
+ <Filter>clrma</Filter>
+ </ClInclude>
+ <ClInclude Include="clrma\exception.h">
+ <Filter>clrma</Filter>
+ </ClInclude>
+ <ClInclude Include="clrma\thread.h">
+ <Filter>clrma</Filter>
+ </ClInclude>
+ <ClInclude Include="sosextensions.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Native.rc" />
<Filter Include="platform">
<UniqueIdentifier>{8340fe5a-df30-45ca-8a4e-7811ac6ebbd2}</UniqueIdentifier>
</Filter>
+ <Filter Include="clrma">
+ <UniqueIdentifier>{e8809ec7-01fb-4c2b-b1fe-6be4f9c1a91a}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />
+++ /dev/null
-#include <windows.h>
-#include <unknwn.h>
-#include <clrma.h> // IDL
-#include "exts.h"
-
-HRESULT CLRMACreateInstance(ICLRManagedAnalysis** ppCLRMA);
-HRESULT CLRMAReleaseInstance();
-
-ICLRManagedAnalysis* g_managedAnalysis = nullptr;
-
-//
-// Exports
-//
-
-HRESULT CLRMACreateInstance(ICLRManagedAnalysis** ppCLRMA)
-{
- HRESULT hr = E_UNEXPECTED;
-
- if (ppCLRMA == nullptr)
- {
- return E_INVALIDARG;
- }
- *ppCLRMA = nullptr;
-
- if (g_managedAnalysis == nullptr)
- {
- Extensions* extensions = Extensions::GetInstance();
- if (extensions == nullptr || extensions->GetDebuggerServices() == nullptr)
- {
- return E_FAIL;
- }
- ITarget* target = extensions->GetTarget();
- if (target == nullptr)
- {
- return E_FAIL;
- }
- hr = target->GetService(__uuidof(ICLRManagedAnalysis), (void**)&g_managedAnalysis);
- if (FAILED(hr))
- {
- return hr;
- }
- }
- g_managedAnalysis->AddRef();
- *ppCLRMA = g_managedAnalysis;
- return S_OK;
-}
-
-HRESULT CLRMAReleaseInstance()
-{
- if (g_managedAnalysis != nullptr)
- {
- g_managedAnalysis->Release();
- g_managedAnalysis = nullptr;
- }
- return S_OK;
-}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "exts.h"
+#include "managedanalysis.h"
+
+HRESULT CLRMACreateInstance(ICLRManagedAnalysis** ppCLRMA);
+HRESULT CLRMAReleaseInstance();
+
+ICLRManagedAnalysis* g_managedAnalysis = nullptr;
+
+int g_clrmaGlobalFlags = ClrmaGlobalFlags::LoggingEnabled | ClrmaGlobalFlags::DacClrmaEnabled | ClrmaGlobalFlags::ManagedClrmaEnabled;
+
+//
+// Exports
+//
+
+HRESULT
+CLRMACreateInstance(ICLRManagedAnalysis** ppCLRMA)
+{
+ HRESULT hr = E_UNEXPECTED;
+
+ if (ppCLRMA == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppCLRMA = nullptr;
+
+ if (g_managedAnalysis == nullptr)
+ {
+ g_managedAnalysis = new (std::nothrow) ClrmaManagedAnalysis();
+ if (g_managedAnalysis == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+ OnUnloadTask::Register(([]() { CLRMAReleaseInstance(); }));
+ }
+
+ g_managedAnalysis->AddRef();
+ *ppCLRMA = g_managedAnalysis;
+ return S_OK;
+}
+
+HRESULT
+CLRMAReleaseInstance()
+{
+ TraceInformation("CLRMAReleaseInstance\n");
+ if (g_managedAnalysis != nullptr)
+ {
+ g_managedAnalysis->Release();
+ g_managedAnalysis = nullptr;
+ }
+ return S_OK;
+}
+
+DECLARE_API(clrmaconfig)
+{
+ INIT_API_EXT();
+
+ BOOL bEnable = FALSE;
+ BOOL bDisable = FALSE;
+ BOOL bDacClrma = FALSE;
+ BOOL bManagedClrma = FALSE;
+ BOOL bLogging = FALSE;
+
+ CMDOption option[] =
+ { // name, vptr, type, hasValue
+ {"-enable", &bEnable, COBOOL, FALSE},
+ {"-disable", &bDisable, COBOOL, FALSE},
+ {"-dac", &bDacClrma, COBOOL, FALSE},
+ {"-managed", &bManagedClrma, COBOOL, FALSE},
+ {"-logging", &bLogging, COBOOL, FALSE},
+ };
+
+ if (!GetCMDOption(args, option, ARRAY_SIZE(option), NULL, 0, NULL))
+ {
+ return E_INVALIDARG;
+ }
+
+ if (bEnable)
+ {
+ if (bDacClrma)
+ {
+ g_clrmaGlobalFlags |= ClrmaGlobalFlags::DacClrmaEnabled;
+ }
+ if (bManagedClrma)
+ {
+ g_clrmaGlobalFlags |= ClrmaGlobalFlags::ManagedClrmaEnabled;
+ }
+ if (bLogging)
+ {
+ g_clrmaGlobalFlags |= ClrmaGlobalFlags::LoggingEnabled;
+ }
+ }
+ else if (bDisable)
+ {
+ if (bDacClrma)
+ {
+ g_clrmaGlobalFlags &= ~ClrmaGlobalFlags::DacClrmaEnabled;
+ }
+ if (bManagedClrma)
+ {
+ g_clrmaGlobalFlags &= ~ClrmaGlobalFlags::ManagedClrmaEnabled;
+ }
+ if (bLogging)
+ {
+ g_clrmaGlobalFlags &= ~ClrmaGlobalFlags::LoggingEnabled;
+ }
+ }
+
+ ExtOut("CLRMA logging: %s\n", (g_clrmaGlobalFlags & ClrmaGlobalFlags::LoggingEnabled) ? "enabled (disable with '-disable -logging')" : "disabled (enable with '-enable -logging')");
+ ExtOut("CLRMA direct DAC support: %s\n", (g_clrmaGlobalFlags & ClrmaGlobalFlags::DacClrmaEnabled) ? "enabled (disable with '-disable -dac')" : "disabled (enable with '-enable -dac')");
+ ExtOut("CLRMA managed support: %s\n", (g_clrmaGlobalFlags & ClrmaGlobalFlags::ManagedClrmaEnabled) ? "enabled (disable with '-disable -managed')" : "disabled (enable with '-enable -managed')");
+
+ return Status;
+}
+
+extern void InternalOutputVaList(ULONG mask, PCSTR format, va_list args);
+
+void
+TraceInformation(PCSTR format, ...)
+{
+ if (g_clrmaGlobalFlags & ClrmaGlobalFlags::LoggingEnabled)
+ {
+ va_list args;
+ va_start(args, format);
+ InternalOutputVaList(DEBUG_OUTPUT_NORMAL, format, args);
+ va_end(args);
+ }
+}
+
+void
+TraceError(PCSTR format, ...)
+{
+ if (g_clrmaGlobalFlags & ClrmaGlobalFlags::LoggingEnabled)
+ {
+ va_list args;
+ va_start(args, format);
+ InternalOutputVaList(DEBUG_OUTPUT_ERROR, format, args);
+ va_end(args);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "managedanalysis.h"
+
+extern BOOL IsAsyncException(const DacpExceptionObjectData& excData);
+
+_Use_decl_annotations_
+ClrmaException::ClrmaException(ClrmaManagedAnalysis* managedAnalysis, ULONG64 address) :
+ m_lRefs(1),
+ m_managedAnalysis(managedAnalysis),
+ m_address(address),
+ m_typeName(nullptr),
+ m_message(nullptr),
+ m_exceptionDataInitialized(false),
+ m_stackFramesInitialized(false),
+ m_innerExceptionsInitialized(false)
+{
+ _ASSERTE(address != 0);
+ managedAnalysis->AddRef();
+}
+
+ClrmaException::~ClrmaException()
+{
+ TraceInformation("~ClrmaException\n");
+ if (m_typeName != nullptr)
+ {
+ delete [] m_typeName;
+ m_typeName = nullptr;
+ }
+ if (m_message != nullptr)
+ {
+ delete [] m_message;
+ m_message = nullptr;
+ }
+ if (m_managedAnalysis != nullptr)
+ {
+ m_managedAnalysis->Release();
+ m_managedAnalysis = nullptr;
+ }
+}
+
+/// <summary>
+/// This called by each clrma exception methods to initialize and cache the exception data.
+/// </summary>
+HRESULT
+ClrmaException::Initialize()
+{
+ if (m_managedAnalysis == nullptr)
+ {
+ return E_UNEXPECTED;
+ }
+ if (!m_exceptionDataInitialized)
+ {
+ TraceInformation("ClrmaException::Initialize %016llx\n", m_address);
+
+ HRESULT hr;
+ DacpObjectData objData;
+ if (FAILED(hr = objData.Request(m_managedAnalysis->SosDacInterface(), m_address)))
+ {
+ TraceError("ClrmaException::Initialize GetObjectData FAILED %08x\n", hr);
+ return hr;
+ }
+
+ if (m_managedAnalysis->IsExceptionObj(objData.MethodTable) != 0)
+ {
+ if (FAILED(hr = m_exceptionData.Request(m_managedAnalysis->SosDacInterface(), m_address)))
+ {
+ TraceError("ClrmaException::Initialize GetObjectExceptionData FAILED %08x\n", hr);
+ return hr;
+ }
+ UINT cbTypeName = 0;
+ if (SUCCEEDED(hr = m_managedAnalysis->SosDacInterface()->GetMethodTableName(objData.MethodTable, 0, nullptr, &cbTypeName)))
+ {
+ ArrayHolder<WCHAR> typeName = new (std::nothrow)WCHAR[cbTypeName];
+ if (typeName != nullptr)
+ {
+ if (SUCCEEDED(hr = m_managedAnalysis->SosDacInterface()->GetMethodTableName(objData.MethodTable, cbTypeName, typeName, nullptr)))
+ {
+ m_typeName = typeName.Detach();
+ }
+ else
+ {
+ TraceError("ClrmaException::Initialize GetMethodTableName(%016x) 2 FAILED %08x\n", objData.MethodTable, hr);
+ }
+ }
+ }
+ else
+ {
+ TraceError("ClrmaException::Initialize GetMethodTableName(%016x) 1 FAILED %08x\n", objData.MethodTable, hr);
+ }
+ if (m_exceptionData.Message == 0)
+ {
+ // To match the built-in SOS provider that scrapes !pe output.
+ const WCHAR* none = L"<none>";
+ m_message = new (std::nothrow) WCHAR[wcslen(none) + 1];
+ wcscpy(m_message, none);
+ }
+ else
+ {
+ m_message = m_managedAnalysis->GetStringObject(m_exceptionData.Message);
+ }
+ }
+ m_exceptionDataInitialized = true;
+ }
+ return S_OK;
+}
+
+// IUnknown
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaException::QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface
+ )
+{
+ if (Interface == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *Interface = nullptr;
+
+ if (IsEqualIID(InterfaceId, IID_IUnknown))
+ {
+ *Interface = (IUnknown*)this;
+ AddRef();
+ return S_OK;
+ }
+ else if (IsEqualIID(InterfaceId, __uuidof(ICLRMAClrException)))
+ {
+ *Interface = (ICLRMAClrException*)this;
+ AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG)
+ClrmaException::AddRef()
+{
+ return (ULONG)InterlockedIncrement(&m_lRefs);
+}
+
+STDMETHODIMP_(ULONG)
+ClrmaException::Release()
+{
+ LONG lRefs = InterlockedDecrement(&m_lRefs);
+ if (lRefs == 0)
+ {
+ delete this;
+ }
+ return lRefs;
+}
+
+//
+// ICLRMAClrException
+//
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaException::get_DebuggerCommand(
+ BSTR* pValue
+ )
+{
+ if (pValue == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+ *pValue = nullptr;
+ return E_NOTIMPL;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaException::get_Address(
+ ULONG64* pValue
+ )
+{
+ if (pValue == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+ *pValue = m_address;
+ return S_OK;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaException::get_HResult(
+ HRESULT* pValue
+ )
+{
+ if (pValue == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pValue = 0;
+
+ HRESULT hr;
+ if (FAILED(hr = Initialize()))
+ {
+ return hr;
+ }
+
+ *pValue = m_exceptionData.HResult;
+ return S_OK;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaException::get_Type(
+ BSTR* pValue
+ )
+{
+ if (pValue == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pValue = nullptr;
+
+ HRESULT hr;
+ if (FAILED(hr = Initialize()))
+ {
+ return hr;
+ }
+
+ const WCHAR* typeName = m_typeName;
+ if (typeName == nullptr)
+ {
+ // To match the built-in SOS provider that scrapes !pe output
+ typeName = L"<Unknown>";
+ }
+
+ *pValue = SysAllocString(typeName);
+
+ return ((*pValue) != nullptr) ? S_OK : S_FALSE;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaException::get_Message(
+ BSTR* pValue
+ )
+{
+ if (pValue == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pValue = nullptr;
+
+ HRESULT hr;
+ if (FAILED(hr = Initialize()))
+ {
+ return hr;
+ }
+
+ if (m_message != nullptr)
+ {
+ *pValue = SysAllocString(m_message);
+ }
+
+ return ((*pValue) != nullptr) ? S_OK : S_FALSE;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaException::get_FrameCount(
+ UINT* pCount
+ )
+{
+ TraceInformation("ClrmaException::get_FrameCount\n");
+
+ if (pCount == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pCount = 0;
+
+ HRESULT hr;
+ if (FAILED(hr = Initialize()))
+ {
+ return hr;
+ }
+
+ if (!m_stackFramesInitialized)
+ {
+ GetStackFrames();
+ m_stackFramesInitialized = true;
+ }
+
+ *pCount = (USHORT)m_stackFrames.size();
+
+ return ((*pCount) != 0) ? S_OK : S_FALSE;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaException::Frame(
+ UINT nFrame,
+ ULONG64* pAddrIP,
+ ULONG64* pAddrSP,
+ BSTR* bstrModule,
+ BSTR* bstrFunction,
+ ULONG64* pDisplacement
+ )
+{
+ TraceInformation("ClrmaException::Frame %d\n", nFrame);
+
+ if (!pAddrIP || !pAddrSP || !bstrModule || !bstrFunction || !pDisplacement)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pAddrIP = 0;
+ *pAddrSP = 0;
+ *bstrModule = nullptr;
+ *bstrFunction = nullptr;
+ *pDisplacement = 0;
+
+ UINT nCount = 0;
+ if (HRESULT hr = get_FrameCount(&nCount))
+ {
+ return hr;
+ }
+
+ if (nFrame >= nCount)
+ {
+ return E_BOUNDS;
+ }
+
+ BSTR moduleName = SysAllocString(m_stackFrames[nFrame].Module.c_str());
+ if (moduleName == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ BSTR functionName = SysAllocString(m_stackFrames[nFrame].Function.c_str());
+ if (functionName == nullptr)
+ {
+ SysFreeString(moduleName);
+ return E_OUTOFMEMORY;
+ }
+
+ *pAddrIP = m_stackFrames[nFrame].IP;
+ *pAddrSP = m_stackFrames[nFrame].SP;
+ *bstrModule = moduleName;
+ *bstrFunction = functionName;
+ *pDisplacement = m_stackFrames[nFrame].Displacement;
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaException::get_InnerExceptionCount(
+ USHORT* pCount
+ )
+{
+ TraceInformation("ClrmaException::get_InnerExceptionCount\n");
+
+ if (pCount == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pCount = 0;
+
+ HRESULT hr;
+ if (FAILED(hr = Initialize()))
+ {
+ return hr;
+ }
+
+ if (!m_innerExceptionsInitialized)
+ {
+ m_innerExceptions.clear();
+
+ if (m_exceptionData.InnerException != 0)
+ {
+ m_innerExceptions.push_back(m_exceptionData.InnerException);
+ }
+
+ m_innerExceptionsInitialized = true;
+ }
+
+ *pCount = (USHORT)m_innerExceptions.size();
+
+ return ((*pCount) != 0) ? S_OK : S_FALSE;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaException::InnerException(
+ USHORT nIndex,
+ ICLRMAClrException** ppClrException)
+{
+ TraceInformation("ClrmaException::InnerException %d\n", nIndex);
+
+ if (ppClrException == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppClrException = nullptr;
+
+ HRESULT hr;
+ USHORT nCount = 0;
+ if (hr = get_InnerExceptionCount(&nCount))
+ {
+ return hr;
+ }
+
+ if (nIndex >= nCount)
+ {
+ return E_BOUNDS;
+ }
+
+ ReleaseHolder<ClrmaException> exception = new (std::nothrow) ClrmaException(m_managedAnalysis, m_innerExceptions[nIndex]);
+ if (exception == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (FAILED(hr = exception->QueryInterface(__uuidof(ICLRMAClrException), (void**)ppClrException)))
+ {
+ return hr;
+ }
+
+ return S_OK;
+}
+
+HRESULT
+ClrmaException::GetStackFrames()
+{
+ HRESULT hr;
+
+ m_stackFrames.clear();
+
+ if (m_exceptionData.StackTrace == 0)
+ {
+ return S_OK;
+ }
+
+ DacpObjectData arrayObjData;
+ if (FAILED(hr = arrayObjData.Request(m_managedAnalysis->SosDacInterface(), m_exceptionData.StackTrace)))
+ {
+ TraceError("ClrmaException::GetStackFrames GetObjectData(%016llx) FAILED %08x\n", m_exceptionData.StackTrace, hr);
+ return hr;
+ }
+
+ if (arrayObjData.ObjectType != OBJ_ARRAY || arrayObjData.dwNumComponents == 0)
+ {
+ TraceError("ClrmaException::GetStackFrames StackTrace not array or empty\n");
+ return E_FAIL;
+ }
+ CLRDATA_ADDRESS arrayDataPtr = arrayObjData.ArrayDataPtr;
+
+ // If the stack trace is object[] (.NET 9 or greater), the StackTraceElement array is referenced by the first entry
+ if (arrayObjData.ElementTypeHandle == m_managedAnalysis->ObjectMethodTable())
+ {
+ if (FAILED(hr = m_managedAnalysis->ReadPointer(arrayDataPtr, &arrayDataPtr)))
+ {
+ TraceError("ClrmaException::GetStackFrames ReadPointer(%016llx) FAILED %08x\n", arrayDataPtr, hr);
+ return hr;
+ }
+ }
+
+ bool bAsync = IsAsyncException(m_exceptionData);
+
+ if (m_managedAnalysis->PointerSize() == 8)
+ {
+ StackTrace64 stackTrace;
+ if (FAILED(hr = m_managedAnalysis->ReadMemory(arrayDataPtr, &stackTrace, sizeof(StackTrace64))))
+ {
+ TraceError("ClrmaException::GetStackFrames ReadMemory(%016llx) StackTrace64 FAILED %08x\n", arrayDataPtr, hr);
+ return hr;
+ }
+ if (stackTrace.m_size > 0)
+ {
+ CLRDATA_ADDRESS elementPtr = arrayDataPtr + offsetof(StackTrace64, m_elements);
+ for (ULONG i = 0; i < MAX_STACK_FRAMES && i < stackTrace.m_size; i++)
+ {
+ StackTraceElement64 stackTraceElement;
+ if (SUCCEEDED(hr = m_managedAnalysis->ReadMemory(elementPtr, &stackTraceElement, sizeof(StackTraceElement64))))
+ {
+ StackFrame frame;
+ frame.Frame = i;
+ frame.SP = stackTraceElement.sp;
+ frame.IP = stackTraceElement.ip;
+ if ((m_managedAnalysis->ProcessorType() == IMAGE_FILE_MACHINE_AMD64) && bAsync)
+ {
+ frame.IP++;
+ }
+ if (SUCCEEDED(hr = m_managedAnalysis->GetMethodDescInfo(stackTraceElement.pFunc, frame, /* stripFunctionParameters */ true)))
+ {
+ m_stackFrames.push_back(frame);
+ }
+ }
+ else
+ {
+ TraceError("ClrmaException::GetStackFrames ReadMemory(%016llx) StackTraceElement64 FAILED %08x\n", elementPtr, hr);
+ }
+ elementPtr += sizeof(StackTraceElement64);
+ }
+ }
+ }
+ else
+ {
+ StackTrace32 stackTrace;
+ if (FAILED(hr = m_managedAnalysis->ReadMemory(arrayDataPtr, &stackTrace, sizeof(StackTrace32))))
+ {
+ TraceError("ClrmaException::GetStackFrames ReadMemory(%016llx) StackTrace32 FAILED %08x\n", arrayDataPtr, hr);
+ return hr;
+ }
+ if (stackTrace.m_size > 0)
+ {
+ CLRDATA_ADDRESS elementPtr = arrayDataPtr + offsetof(StackTrace32, m_elements);
+ for (ULONG i = 0; i < MAX_STACK_FRAMES && i < stackTrace.m_size; i++)
+ {
+ StackTraceElement32 stackTraceElement;
+ if (SUCCEEDED(hr = m_managedAnalysis->ReadMemory(elementPtr, &stackTraceElement, sizeof(StackTraceElement32))))
+ {
+ StackFrame frame;
+ frame.Frame = i;
+ frame.SP = stackTraceElement.sp;
+ frame.IP = stackTraceElement.ip;
+ if ((m_managedAnalysis->ProcessorType() == IMAGE_FILE_MACHINE_I386) && (!bAsync || i != 0))
+ {
+ frame.IP++;
+ }
+ if (SUCCEEDED(hr = m_managedAnalysis->GetMethodDescInfo(stackTraceElement.pFunc, frame, /* stripFunctionParameters */ true)))
+ {
+ m_stackFrames.push_back(frame);
+ }
+ }
+ else
+ {
+ TraceError("ClrmaException::GetStackFrames ReadMemory(%016llx) StackTraceElement32 FAILED %08x\n", elementPtr, hr);
+ }
+ elementPtr += sizeof(StackTraceElement32);
+ }
+ }
+ }
+
+ return S_OK;
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include "managedanalysis.h"
+
+class ClrmaException : public ICLRMAClrException
+{
+public:
+ ClrmaException(_In_ ClrmaManagedAnalysis* managedAnalysis, _In_ ULONG64 address);
+ virtual ~ClrmaException();
+
+public:
+ // IUnknown
+ STDMETHOD(QueryInterface)(_In_ REFIID InterfaceId, _Out_ PVOID* Interface);
+ STDMETHOD_(ULONG, AddRef)();
+ STDMETHOD_(ULONG, Release)();
+
+ // ICLRMAClrException
+ STDMETHOD(get_DebuggerCommand)(_Out_ BSTR* pValue);
+ STDMETHOD(get_Address)(_Out_ ULONG64* pValue);
+ STDMETHOD(get_HResult)(_Out_ HRESULT* pValue);
+ STDMETHOD(get_Type)(_Out_ BSTR* pValue);
+ STDMETHOD(get_Message)(_Out_ BSTR* pValue);
+
+ STDMETHOD(get_FrameCount)(_Out_ UINT* pCount);
+ STDMETHOD(Frame)(_In_ UINT nFrame, _Out_ ULONG64* pAddrIP, _Out_ ULONG64* pAddrSP, _Out_ BSTR* bstrModule, _Out_ BSTR* bstrFunction, _Out_ ULONG64* pDisplacement);
+
+ STDMETHOD(get_InnerExceptionCount)(_Out_ USHORT* pCount);
+ STDMETHOD(InnerException)(_In_ USHORT nIndex, _COM_Outptr_result_maybenull_ ICLRMAClrException** ppClrException);
+
+private:
+ HRESULT Initialize();
+ HRESULT GetStackFrames();
+
+ LONG m_lRefs;
+ ClrmaManagedAnalysis* m_managedAnalysis;
+ ULONG64 m_address;
+
+ // ClrmaException::Initialize()
+ DacpExceptionObjectData m_exceptionData;
+ WCHAR* m_typeName;
+ WCHAR* m_message;
+ bool m_exceptionDataInitialized;
+
+ // Initialized in ClrmaException::get_FrameCount
+ std::vector<StackFrame> m_stackFrames;
+ bool m_stackFramesInitialized;
+
+ // Initialized in ClrmaException::get_InnerExceptionCount
+ std::vector<CLRDATA_ADDRESS> m_innerExceptions;
+ bool m_innerExceptionsInitialized;
+};
+
+// This struct needs to match the definition in the runtime and the target bitness.
+// See: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/clrex.h
+
+struct StackTraceElement32
+{
+ ULONG32 ip;
+ ULONG32 sp;
+ ULONG32 pFunc; // MethodDesc
+ INT flags; // This is StackTraceElementFlags but it needs to always be "int" sized for backward compatibility.
+};
+
+struct StackTraceElement64
+{
+ ULONG64 ip;
+ ULONG64 sp;
+ ULONG64 pFunc; // MethodDesc
+ INT flags; // This is StackTraceElementFlags but it needs to always be "int" sized for backward compatibility.
+};
+
+// This is the layout of the _stackTrace pointer in an exception object. It is a managed array of bytes or if .NET 9.0 or greater
+// an array of objects where the first entry is the address of stack trace element array. The layout is target bitness dependent.
+
+#pragma warning(disable:4200)
+
+struct StackTrace32
+{
+ ULONG32 m_size; // ArrayHeader
+ ULONG32 m_thread; //
+ StackTraceElement32 m_elements[0];
+};
+
+struct StackTrace64
+{
+ ULONG64 m_size; // ArrayHeader
+ ULONG64 m_thread; //
+ StackTraceElement64 m_elements[0];
+};
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "managedanalysis.h"
+
+extern bool IsWindowsTarget();
+extern "C" IXCLRDataProcess * GetClrDataFromDbgEng();
+
+_Use_decl_annotations_
+ClrmaManagedAnalysis::ClrmaManagedAnalysis() :
+ m_lRefs(1),
+ m_pointerSize(0),
+ m_fileSeparator(0),
+ m_processorType(0),
+ m_debugClient(nullptr),
+ m_debugData(nullptr),
+ m_debugSystem(nullptr),
+ m_debugControl(nullptr),
+ m_debugSymbols(nullptr),
+ m_clrmaService(nullptr),
+ m_clrData(nullptr),
+ m_sosDac(nullptr)
+{
+}
+
+ClrmaManagedAnalysis::~ClrmaManagedAnalysis()
+{
+ TraceInformation("~ClrmaManagedAnalysis\n");
+ ReleaseDebugClient();
+}
+
+HRESULT
+ClrmaManagedAnalysis::QueryDebugClient(IUnknown* pUnknown)
+{
+ HRESULT hr;
+ ReleaseHolder<IDebugClient> debugClient;
+ if (FAILED(hr = pUnknown->QueryInterface(__uuidof(IDebugClient), (void**)&debugClient)))
+ {
+ return hr;
+ }
+ ReleaseHolder<IDebugDataSpaces> debugData;
+ if (FAILED(hr = debugClient->QueryInterface(__uuidof(IDebugDataSpaces), (void**)&debugData)))
+ {
+ return hr;
+ }
+ ReleaseHolder<IDebugSystemObjects> debugSystem;
+ if (FAILED(hr = debugClient->QueryInterface(__uuidof(IDebugSystemObjects), (void**)&debugSystem)))
+ {
+ return hr;
+ }
+ ReleaseHolder<IDebugControl> debugControl;
+ if (FAILED(hr = debugClient->QueryInterface(__uuidof(IDebugControl), (void**)&debugControl)))
+ {
+ return hr;
+ }
+ ReleaseHolder<IDebugSymbols3> debugSymbols;
+ if (FAILED(hr = debugClient->QueryInterface(__uuidof(IDebugSymbols3), (void**)&debugSymbols)))
+ {
+ return hr;
+ }
+ m_debugClient = debugClient.Detach();
+ m_debugData = debugData.Detach();
+ m_debugSystem = debugSystem.Detach();
+ m_debugControl = debugControl.Detach();
+ m_debugSymbols = debugSymbols.Detach();
+
+ if (FAILED(hr = m_debugControl->GetExecutingProcessorType(&m_processorType)))
+ {
+ return hr;
+ }
+ switch (m_processorType)
+ {
+ case IMAGE_FILE_MACHINE_ARM64:
+ case IMAGE_FILE_MACHINE_AMD64:
+ case IMAGE_FILE_MACHINE_LOONGARCH64:
+ case IMAGE_FILE_MACHINE_RISCV64:
+ m_pointerSize = 8;
+ break;
+
+ case IMAGE_FILE_MACHINE_I386:
+ case IMAGE_FILE_MACHINE_ARM:
+ case IMAGE_FILE_MACHINE_THUMB:
+ case IMAGE_FILE_MACHINE_ARMNT:
+ m_pointerSize = 4;
+ break;
+
+ default:
+ return E_INVALIDARG;
+ }
+ if (IsWindowsTarget())
+ {
+ m_fileSeparator = L'\\';
+ }
+ else
+ {
+ m_fileSeparator = L'/';
+ }
+ return S_OK;
+}
+
+void
+ClrmaManagedAnalysis::ReleaseDebugClient()
+{
+ if (m_clrData != nullptr)
+ {
+ m_clrData->Release();
+ m_clrData = nullptr;
+ }
+ if (m_sosDac != nullptr)
+ {
+ m_sosDac->Release();
+ m_sosDac = nullptr;
+ }
+ if (m_clrmaService != nullptr)
+ {
+ m_clrmaService->Release();
+ m_clrmaService = nullptr;
+ }
+ if (m_debugSymbols != nullptr)
+ {
+ m_debugSymbols->Release();
+ m_debugSymbols= nullptr;
+ }
+ if (m_debugControl != nullptr)
+ {
+ m_debugControl->Release();
+ m_debugControl = nullptr;
+ }
+ if (m_debugSystem != nullptr)
+ {
+ m_debugSystem->Release();
+ m_debugSystem = nullptr;
+ }
+ if (m_debugData != nullptr)
+ {
+ m_debugData->Release();
+ m_debugData = nullptr;
+ }
+ if (m_debugClient != nullptr)
+ {
+ m_debugClient->Release();
+ m_debugClient = nullptr;
+ }
+}
+
+//
+// IUnknown
+//
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaManagedAnalysis::QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface
+ )
+{
+ if (Interface == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *Interface = nullptr;
+
+ if (IsEqualIID(InterfaceId, IID_IUnknown))
+ {
+ *Interface = (IUnknown*)this;
+ AddRef();
+ return S_OK;
+ }
+ else if (IsEqualIID(InterfaceId, __uuidof(ICLRManagedAnalysis)))
+ {
+ *Interface = (ICLRManagedAnalysis*)this;
+ AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG)
+ClrmaManagedAnalysis::AddRef()
+{
+ return (ULONG)InterlockedIncrement(&m_lRefs);
+}
+
+STDMETHODIMP_(ULONG)
+ClrmaManagedAnalysis::Release()
+{
+ LONG lRefs = InterlockedDecrement(&m_lRefs);
+ if (lRefs == 0)
+ {
+ delete this;
+ }
+ return lRefs;
+}
+
+//
+// ICLRManagedAnalysis
+//
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaManagedAnalysis::AssociateClient(
+ IUnknown* pUnknown
+ )
+{
+ TraceInformation("ClrmaManagedAnalysis::AssociateClient\n");
+
+ if (pUnknown == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ // Release previous client and DAC interfaces
+ ReleaseDebugClient();
+
+ // Setup the debugger client interfaces
+ HRESULT hr;
+ if (FAILED(hr = QueryDebugClient(pUnknown)))
+ {
+ TraceError("AssociateClient QueryDebugClient FAILED %08x\n", hr);
+ return hr;
+ }
+
+ Extensions* extensions = Extensions::GetInstance();
+ if (extensions != nullptr && extensions->GetDebuggerServices() != nullptr)
+ {
+ extensions->FlushCheck();
+
+ ITarget* target = extensions->GetTarget();
+ if (target != nullptr)
+ {
+ //
+ // First try getting the managed CLRMA service instance
+ //
+ if (g_clrmaGlobalFlags & ClrmaGlobalFlags::ManagedClrmaEnabled)
+ {
+ TraceInformation("AssociateClient trying managed CLRMA\n");
+ ReleaseHolder<ICLRMAService> clrmaService;
+ if (SUCCEEDED(hr = target->GetService(__uuidof(ICLRMAService), (void**)&clrmaService)))
+ {
+ if (SUCCEEDED(hr = clrmaService->AssociateClient(m_debugClient)))
+ {
+ m_clrmaService = clrmaService.Detach();
+ return S_OK;
+ }
+ }
+ }
+ //
+ // If there isn't a managed CLRMA service, use the DAC CLRMA implementation
+ //
+ if (g_clrmaGlobalFlags & ClrmaGlobalFlags::DacClrmaEnabled)
+ {
+ TraceInformation("AssociateClient trying DAC CLRMA\n");
+ IRuntime* runtime = nullptr;
+ if (FAILED(hr = target->GetRuntime(&runtime)))
+ {
+ TraceError("AssociateClient GetRuntime FAILED %08x\n", hr);
+ return hr;
+ }
+ ReleaseHolder<IXCLRDataProcess> clrData;
+ if (FAILED(hr = runtime->GetClrDataProcess((IXCLRDataProcess**)&clrData)))
+ {
+ clrData = GetClrDataFromDbgEng();
+ if (clrData == nullptr)
+ {
+ TraceError("AssociateClient GetClrDataProcess FAILED %08x\n", hr);
+ return hr;
+ }
+ }
+ ReleaseHolder<ISOSDacInterface> sosDac;
+ if (FAILED(hr = clrData->QueryInterface(__uuidof(ISOSDacInterface), (void**)&sosDac)))
+ {
+ TraceError("AssociateClient QueryInterface ISOSDacInterface FAILED %08x\n", hr);
+ return hr;
+ }
+ if (FAILED(hr = sosDac->GetUsefulGlobals(&m_usefulGlobals)))
+ {
+ TraceError("AssociateClient GetUsefulGlobals FAILED %08x\n", hr);
+ return hr;
+ }
+ clrData->AddRef();
+ m_clrData = clrData.Detach();
+ m_sosDac = sosDac.Detach();
+ return S_OK;
+ }
+ }
+ }
+
+ return E_NOINTERFACE;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaManagedAnalysis::get_ProviderName(
+ BSTR* bstrProvider
+ )
+{
+ TraceInformation("ClrmaManagedAnalysis::get_ProviderName\n");
+
+ if (bstrProvider == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *bstrProvider = SysAllocString(L"SOSCLRMA");
+
+ if ((*bstrProvider) == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaManagedAnalysis::GetThread(
+ ULONG osThreadId,
+ ICLRMAClrThread** ppClrThread
+)
+{
+ TraceInformation("ClrmaManagedAnalysis::GetThread %04x\n", osThreadId);
+
+ if (ppClrThread == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppClrThread = nullptr;
+
+ if (m_debugClient == nullptr)
+ {
+ return E_UNEXPECTED;
+ }
+
+ // Current thread?
+ HRESULT hr;
+ if (osThreadId == 0)
+ {
+ ULONG tid;
+ if (FAILED(hr = m_debugSystem->GetCurrentThreadSystemId(&tid)))
+ {
+ TraceError("GetThread GetCurrentThreadSystemId FAILED %08x\n", hr);
+ return hr;
+ }
+ osThreadId = tid;
+ }
+ // Last event thread?
+ else if (osThreadId == (ULONG)-1)
+ {
+ ULONG lastEventType = 0;
+ ULONG lastEventProcessId = 0;
+ ULONG lastEventThreadIdIndex = DEBUG_ANY_ID;
+ if (FAILED(hr = m_debugControl->GetLastEventInformation(&lastEventType, &lastEventProcessId, &lastEventThreadIdIndex, NULL, 0, NULL, NULL, 0, NULL)))
+ {
+ TraceError("GetThread GetLastEventInformation FAILED %08x\n", hr);
+ return hr;
+ }
+ if (lastEventThreadIdIndex == DEBUG_ANY_ID)
+ {
+ TraceError("GetThread lastEventThreadIdIndex == DEBUG_ANY_ID\n");
+ return E_INVALIDARG;
+ }
+ ULONG ids = 0;
+ ULONG sysIds = 0;
+ if (FAILED(hr = m_debugSystem->GetThreadIdsByIndex(lastEventThreadIdIndex, 1, &ids, &sysIds)))
+ {
+ TraceError("GetThread GetThreadIdsByIndex FAILED %08x\n", hr);
+ return hr;
+ }
+ osThreadId = sysIds;
+ }
+
+ if (m_clrmaService != nullptr)
+ {
+ if (FAILED(hr = m_clrmaService->GetThread(osThreadId, ppClrThread)))
+ {
+ TraceError("GetThread ICLRMAService::GetThread FAILED %08x\n", hr);
+ return hr;
+ }
+ }
+ else
+ {
+ ReleaseHolder<ClrmaThread> thread = new (std::nothrow) ClrmaThread(this, osThreadId);
+ if (thread == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+ if (FAILED(hr = thread->Initialize()))
+ {
+ return hr;
+ }
+ if (FAILED(hr = thread->QueryInterface(__uuidof(ICLRMAClrThread), (void**)ppClrThread)))
+ {
+ TraceError("GetThread QueryInterface ICLRMAClrThread 1 FAILED %08x\n", hr);
+ return hr;
+ }
+ }
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaManagedAnalysis::GetException(
+ ULONG64 address,
+ ICLRMAClrException** ppClrException
+ )
+{
+ TraceInformation("ClrmaManagedAnalysis::GetException %016llx\n", address);
+
+ if (ppClrException == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppClrException = nullptr;
+
+ if (m_debugClient == nullptr)
+ {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr;
+ if (m_clrmaService != nullptr)
+ {
+ if (FAILED(hr = m_clrmaService->GetException(address, ppClrException)))
+ {
+ TraceError("GetException ICLRMAService::GetException FAILED %08x\n", hr);
+ return hr;
+ }
+ }
+ else
+ {
+ if (address == 0)
+ {
+ ReleaseHolder<ICLRMAClrThread> thread;
+ if (FAILED(hr = GetThread(0, (ICLRMAClrThread**)&thread)))
+ {
+ TraceError("GetException GetThread FAILED %08x\n", hr);
+ return hr;
+ }
+ if (FAILED(hr = thread->get_CurrentException(ppClrException)))
+ {
+ TraceError("GetException get_CurrentException FAILED %08x\n", hr);
+ return hr;
+ }
+ }
+ else
+ {
+ ReleaseHolder<ClrmaException> exception = new (std::nothrow) ClrmaException(this, address);
+ if (exception == nullptr)
+ {
+ TraceError("GetException new ClrmaException FAILED\n");
+ return E_OUTOFMEMORY;
+ }
+ if (FAILED(hr = exception->QueryInterface(__uuidof(ICLRMAClrException), (void**)ppClrException)))
+ {
+ TraceError("GetException QueryInterface ICLRMAClrException 1 FAILED %08x\n", hr);
+ return hr;
+ }
+ }
+ }
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaManagedAnalysis::get_ObjectInspection(
+ ICLRMAObjectInspection** ppObjectInspection)
+{
+ TraceInformation("ClrmaManagedAnalysis::get_ObjectInspection\n");
+
+ if (ppObjectInspection == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppObjectInspection = nullptr;
+
+ if (m_debugClient == nullptr)
+ {
+ return E_UNEXPECTED;
+ }
+
+ if (m_clrmaService != nullptr)
+ {
+ return m_clrmaService->GetObjectInspection(ppObjectInspection);
+ }
+
+ return E_NOTIMPL;
+}
+
+HRESULT
+ClrmaManagedAnalysis::GetMethodDescInfo(CLRDATA_ADDRESS methodDesc, StackFrame& frame, bool stripFunctionParameters)
+{
+ HRESULT hr;
+ DacpMethodDescData methodDescData;
+ if (SUCCEEDED(hr = methodDescData.Request(SosDacInterface(), methodDesc)))
+ {
+ // Don't compute the method displacement if IP is 0
+ if (frame.IP > 0)
+ {
+ frame.Displacement = (frame.IP - methodDescData.NativeCodeAddr);
+ }
+
+ DacpModuleData moduleData;
+ if (SUCCEEDED(hr = moduleData.Request(SosDacInterface(), methodDescData.ModulePtr)))
+ {
+ CLRDATA_ADDRESS baseAddress = 0;
+ ULONG index = DEBUG_ANY_ID;
+ if (FAILED(hr = SosDacInterface()->GetPEFileBase(moduleData.PEAssembly, &baseAddress)) || baseAddress == 0)
+ {
+ TraceInformation("GetMethodDescInfo(%016llx) GetPEFileBase %016llx FAILED %08x\n", methodDesc, moduleData.PEAssembly, hr);
+ if (FAILED(hr = m_debugSymbols->GetModuleByOffset(frame.IP, 0, &index, &baseAddress)))
+ {
+ TraceError("GetMethodDescInfo GetModuleByOffset FAILED %08x\n", hr);
+ baseAddress = 0;
+ index = DEBUG_ANY_ID;
+ }
+ }
+
+ // Attempt to get the module name from the debugger
+ ArrayHolder<WCHAR> wszModuleName = new WCHAR[MAX_LONGPATH + 1];
+ if (baseAddress != 0 || index != DEBUG_ANY_ID)
+ {
+ if (SUCCEEDED(hr = m_debugSymbols->GetModuleNameStringWide(DEBUG_MODNAME_MODULE, index, baseAddress, wszModuleName, MAX_LONGPATH, nullptr)))
+ {
+ frame.Module = wszModuleName;
+ }
+ else
+ {
+ TraceError("GetMethodDescInfo(%016llx) GetModuleNameStringWide(%d, %016llx) FAILED %08x\n", methodDesc, index, baseAddress, hr);
+ }
+ }
+
+ // Fallback if we can't get it from the debugger
+ if (frame.Module.empty())
+ {
+ wszModuleName[0] = L'\0';
+ if (FAILED(hr = SosDacInterface()->GetPEFileName(moduleData.PEAssembly, MAX_LONGPATH, wszModuleName, nullptr)))
+ {
+ TraceInformation("GetMethodDescInfo(%016llx) GetPEFileName(%016llx) FAILED %08x\n", methodDesc, moduleData.PEAssembly, hr);
+ ReleaseHolder<IXCLRDataModule> pModule;
+ if (SUCCEEDED(hr = SosDacInterface()->GetModule(moduleData.Address, (IXCLRDataModule**)&pModule)))
+ {
+ ULONG32 nameLen = 0;
+ if (FAILED(hr = pModule->GetFileName(MAX_LONGPATH, &nameLen, wszModuleName)))
+ {
+ TraceError("GetMethodDescInfo IXCLRDataModule::GetFileName FAILED %08x\n", hr);
+ }
+ }
+ else
+ {
+ TraceError("GetMethodDescInfo GetModule FAILED %08x\n", hr);
+ }
+ }
+ if (wszModuleName[0] != L'\0')
+ {
+ frame.Module = wszModuleName;
+ _ASSERTE(m_fileSeparator != 0);
+ size_t nameStart = frame.Module.find_last_of(m_fileSeparator);
+ if (nameStart != -1)
+ {
+ frame.Module = frame.Module.substr(nameStart + 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ TraceError("GetMethodDescInfo(%016llx) ISOSDacInterface::GetModuleData FAILED %08x\n", methodDesc, hr);
+ }
+
+ ArrayHolder<WCHAR> wszNameBuffer = new WCHAR[MAX_LONGPATH + 1];
+ if (SUCCEEDED(hr = SosDacInterface()->GetMethodDescName(methodDesc, MAX_LONGPATH, wszNameBuffer, NULL)))
+ {
+ frame.Function = wszNameBuffer;
+
+ // Under certain circumstances DacpMethodDescData::GetMethodDescName() returns a module qualified method name
+ size_t nameStart = frame.Function.find_first_of(L'!');
+ if (nameStart != -1)
+ {
+ // Fallback to using the module name from the function name
+ if (frame.Module.empty())
+ {
+ frame.Module = frame.Function.substr(0, nameStart);
+ }
+ // Now strip the module name from the function name. Need to do this after the module name fallback
+ frame.Function = frame.Function.substr(nameStart + 1);
+ }
+
+ // Strip off the function parameters
+ if (stripFunctionParameters)
+ {
+ size_t parameterStart = frame.Function.find_first_of(L'(');
+ if (parameterStart != -1)
+ {
+ frame.Function = frame.Function.substr(0, parameterStart);
+ }
+ }
+ }
+ else
+ {
+ TraceError("GetMethodDescInfo(%016llx) ISOSDacInterface::GetMethodDescName FAILED %08x\n", methodDesc, hr);
+ }
+ }
+ else
+ {
+ TraceError("GetMethodDescInfo(%016llx) ISOSDacInterface::GetMethodDescData FAILED %08x\n", methodDesc, hr);
+ }
+ if (frame.Module.empty())
+ {
+ frame.Module = L"UNKNOWN";
+ }
+ if (frame.Function.empty())
+ {
+ frame.Function = L"UNKNOWN";
+ }
+ return S_OK;
+}
+
+CLRDATA_ADDRESS
+ClrmaManagedAnalysis::IsExceptionObj(CLRDATA_ADDRESS mtObj)
+{
+ CLRDATA_ADDRESS walkMT = mtObj;
+ DacpMethodTableData dmtd;
+ HRESULT hr;
+
+ // We want to follow back until we get the mt for System.Exception
+ while (walkMT != NULL)
+ {
+ if (FAILED(hr = dmtd.Request(SosDacInterface(), walkMT)))
+ {
+ TraceError("IsExceptionObj ISOSDacInterface::GetMethodDescData FAILED %08x\n", hr);
+ break;
+ }
+ if (walkMT == m_usefulGlobals.ExceptionMethodTable)
+ {
+ return walkMT;
+ }
+ walkMT = dmtd.ParentMethodTable;
+ }
+
+ return 0;
+}
+
+WCHAR*
+ClrmaManagedAnalysis::GetStringObject(CLRDATA_ADDRESS stringObject)
+{
+ if (stringObject == 0)
+ {
+ return nullptr;
+ }
+ HRESULT hr;
+ DacpObjectData objData;
+ if (FAILED(hr = objData.Request(SosDacInterface(), stringObject)))
+ {
+ TraceError("GetStringObject ISOSDacInterface::GetObjectData FAILED %08x\n", hr);
+ return nullptr;
+ }
+ if (objData.Size > 0x200000)
+ {
+ TraceError("GetStringObject object size (%08llx) > 0x200000\n", objData.Size);
+ return nullptr;
+ }
+ // Ignore the HRESULT because this function fails with E_INVALIDARG but still returns cbNeeded.
+ UINT32 cbNeeded = 0;
+ SosDacInterface()->GetObjectStringData(stringObject, 0, nullptr, &cbNeeded);
+ if (cbNeeded <= 0 || cbNeeded > 0x200000)
+ {
+ TraceError("GetStringObject needed (%08x) > 0x200000\n", cbNeeded);
+ return nullptr;
+ }
+ ArrayHolder<WCHAR> stringBuffer = new (std::nothrow) WCHAR[cbNeeded];
+ if (stringBuffer == nullptr)
+ {
+ TraceError("GetStringObject out of memory\n");
+ return nullptr;
+ }
+ if (FAILED(hr = SosDacInterface()->GetObjectStringData(stringObject, cbNeeded, stringBuffer, nullptr)))
+ {
+ TraceError("GetStringObject ISOSDacInterface::GetObjectStringData FAILED %08x\n", hr);
+ return nullptr;
+ }
+ return stringBuffer.Detach();
+}
+
+HRESULT
+ClrmaManagedAnalysis::ReadPointer(CLRDATA_ADDRESS address, CLRDATA_ADDRESS* pointer)
+{
+ _ASSERTE(pointer != nullptr);
+ _ASSERTE(m_pointerSize == 4 || m_pointerSize == 8);
+ *pointer = 0;
+ return m_debugData->ReadVirtual(address, pointer, m_pointerSize, nullptr);
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include <windows.h>
+#include <unknwn.h>
+#include <dbgeng.h>
+#include <clrma.h> // IDL
+#include <clrmaservice.h>
+#include <dbgtargetcontext.h>
+#include <corhdr.h>
+#include <cordebug.h>
+#include <xclrdata.h>
+#include <sospriv.h>
+#include <releaseholder.h>
+#include <arrayholder.h>
+#include <dacprivate.h>
+#include <extensions.h>
+#include <target.h>
+#include <runtime.h>
+#include <vector>
+
+#ifndef IMAGE_FILE_MACHINE_RISCV64
+#define IMAGE_FILE_MACHINE_RISCV64 0x5064 // RISCV64
+#endif
+
+#ifndef IMAGE_FILE_MACHINE_LOONGARCH64
+#define IMAGE_FILE_MACHINE_LOONGARCH64 0x6264 // LOONGARCH64
+#endif
+
+enum ClrmaGlobalFlags
+{
+ LoggingEnabled = 0x01, // CLRMA logging enabled
+ DacClrmaEnabled = 0x02, // Direct DAC CLRMA code enabled
+ ManagedClrmaEnabled = 0x04, // Native AOT managed support enabled
+};
+
+#define MAX_STACK_FRAMES 1000 // Max number of stack frames returned from thread stackwalk
+
+typedef struct StackFrame
+{
+ ULONG Frame = 0;
+ ULONG64 SP = 0;
+ ULONG64 IP = 0;
+ ULONG64 Displacement = 0;
+ std::wstring Module;
+ std::wstring Function;
+} StackFrame;
+
+extern int g_clrmaGlobalFlags;
+
+extern void TraceInformation(PCSTR format, ...);
+extern void TraceError(PCSTR format, ...);
+
+class ClrmaManagedAnalysis : public ICLRManagedAnalysis
+{
+public:
+ ClrmaManagedAnalysis();
+ virtual ~ClrmaManagedAnalysis();
+
+public:
+ // IUnknown
+ STDMETHOD(QueryInterface)(_In_ REFIID InterfaceId, _Out_ PVOID* Interface);
+ STDMETHOD_(ULONG, AddRef)();
+ STDMETHOD_(ULONG, Release)();
+
+ // ICLRManagedAnalysis
+ STDMETHOD(AssociateClient)(_In_ IUnknown* pUnknown);
+
+ STDMETHOD(get_ProviderName)(_Out_ BSTR* bstrProvider);
+
+ STDMETHOD(GetThread)(_In_ ULONG osThreadId, _COM_Outptr_ ICLRMAClrThread** ppClrThread);
+
+ STDMETHOD(GetException)(_In_ ULONG64 address, _COM_Outptr_ ICLRMAClrException** ppClrException);
+
+ STDMETHOD(get_ObjectInspection)(_COM_Outptr_ ICLRMAObjectInspection** ppObjectInspection);
+
+ // Helper functions
+ inline IXCLRDataProcess* ClrData() { return m_clrData; }
+ inline ISOSDacInterface* SosDacInterface() { return m_sosDac; }
+ inline int PointerSize() { return m_pointerSize; }
+ inline ULONG ProcessorType() { return m_processorType; }
+ inline CLRDATA_ADDRESS ObjectMethodTable() { return m_usefulGlobals.ObjectMethodTable; }
+
+ /// <summary>
+ /// Fills in the frame.Module and frame.Function from the MethodDesc.
+ /// </summary>
+ HRESULT GetMethodDescInfo(CLRDATA_ADDRESS methodDesc, StackFrame& frame, bool stripFunctionParameters);
+
+ /// <summary>
+ /// Returns base Exception MT address if exception derived MT
+ /// </summary>
+ CLRDATA_ADDRESS IsExceptionObj(CLRDATA_ADDRESS mtObj);
+
+ /// <summary>
+ /// Return the string object contents
+ /// </summary>
+ WCHAR* GetStringObject(CLRDATA_ADDRESS stringObject);
+
+ /// <summary>
+ /// Reads a target size pointer.
+ /// </summary>
+ HRESULT ReadPointer(CLRDATA_ADDRESS address, CLRDATA_ADDRESS* pointer);
+
+ /// <summary>
+ /// Read memory
+ /// </summary>
+ inline HRESULT ReadMemory(CLRDATA_ADDRESS address, PVOID buffer, ULONG cb) { return m_debugData->ReadVirtual(address, buffer, cb, nullptr); }
+
+private:
+ HRESULT QueryDebugClient(IUnknown* pUnknown);
+ void ReleaseDebugClient();
+
+ LONG m_lRefs;
+ int m_pointerSize;
+ WCHAR m_fileSeparator;
+ ULONG m_processorType;
+ IDebugClient* m_debugClient;
+ IDebugDataSpaces* m_debugData;
+ IDebugSystemObjects* m_debugSystem;
+ IDebugControl* m_debugControl;
+ IDebugSymbols3* m_debugSymbols;
+
+ // CLRMA service from managed code
+ ICLRMAService* m_clrmaService;
+
+ // DAC interface instances
+ IXCLRDataProcess* m_clrData;
+ ISOSDacInterface* m_sosDac;
+
+ DacpUsefulGlobalsData m_usefulGlobals;
+};
+
+#include "thread.h"
+#include "exception.h"
+
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "managedanalysis.h"
+#include <crosscontext.h>
+
+_Use_decl_annotations_
+ClrmaThread::ClrmaThread(ClrmaManagedAnalysis* managedAnalysis, ULONG osThreadId) :
+ m_lRefs(1),
+ m_managedAnalysis(managedAnalysis),
+ m_osThreadId(osThreadId),
+ m_lastThrownObject(0),
+ m_firstNestedException(0),
+ m_stackFramesInitialized(false),
+ m_nestedExceptionsInitialized(false)
+{
+ _ASSERTE(osThreadId != 0 && osThreadId != (ULONG)-1);
+ managedAnalysis->AddRef();
+}
+
+ClrmaThread::~ClrmaThread()
+{
+ TraceInformation("~ClrmaThread\n");
+ if (m_managedAnalysis != nullptr)
+ {
+ m_managedAnalysis->Release();
+ m_managedAnalysis = nullptr;
+ }
+}
+
+/// <summary>
+/// This function returns success if this thread is managed and caches some managed exception info away.
+/// </summary>
+HRESULT
+ClrmaThread::Initialize()
+{
+ TraceInformation("ClrmaThread::Initialize %04x\n", m_osThreadId);
+ HRESULT hr;
+ DacpThreadStoreData threadStore;
+ if (FAILED(hr = threadStore.Request(m_managedAnalysis->SosDacInterface())))
+ {
+ TraceError("ClrmaThread::Initialize GetThreadStoreData FAILED %08x\n", hr);
+ return hr;
+ }
+ DacpThreadData thread;
+ CLRDATA_ADDRESS curThread = threadStore.firstThread;
+ while (curThread != 0)
+ {
+ if ((hr = thread.Request(m_managedAnalysis->SosDacInterface(), curThread)) != S_OK)
+ {
+ TraceError("ClrmaThread::Initialize GetThreadData FAILED %08x\n", hr);
+ return hr;
+ }
+ if (thread.osThreadId == m_osThreadId)
+ {
+ if (thread.lastThrownObjectHandle != 0)
+ {
+ if (FAILED(hr = m_managedAnalysis->ReadPointer(thread.lastThrownObjectHandle, &m_lastThrownObject)))
+ {
+ TraceError("ClrmaThread::Initialize ReadPointer FAILED %08x\n", hr);
+ }
+ }
+ m_firstNestedException = thread.firstNestedException;
+ return S_OK;
+ }
+ curThread = thread.nextThread;
+ }
+ TraceError("ClrmaThread::Initialize FAILED managed thread not found\n");
+ return E_FAIL;
+}
+
+//
+// IUnknown
+//
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaThread::QueryInterface(
+ REFIID InterfaceId,
+ PVOID* Interface
+ )
+{
+ if (Interface == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *Interface = nullptr;
+
+ if (IsEqualIID(InterfaceId, IID_IUnknown))
+ {
+ *Interface = (IUnknown*)this;
+ AddRef();
+ return S_OK;
+ }
+ else if (IsEqualIID(InterfaceId, __uuidof(ICLRMAClrThread)))
+ {
+ *Interface = (ICLRMAClrThread*)this;
+ AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG)
+ClrmaThread::AddRef()
+{
+ return (ULONG)InterlockedIncrement(&m_lRefs);
+}
+
+STDMETHODIMP_(ULONG)
+ClrmaThread::Release()
+{
+ LONG lRefs = InterlockedDecrement(&m_lRefs);
+ if (lRefs == 0)
+ {
+ delete this;
+ }
+ return lRefs;
+}
+
+//
+// ICLRMAClrThread
+//
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaThread::get_DebuggerCommand(
+ BSTR* pValue
+ )
+{
+ if (pValue == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+ *pValue = nullptr;
+ return E_NOTIMPL;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaThread::get_OSThreadId(
+ ULONG* pOSThreadId
+ )
+{
+ if (pOSThreadId == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+ *pOSThreadId = m_osThreadId;
+ return S_OK;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaThread::get_FrameCount(
+ UINT* pCount
+ )
+{
+ TraceInformation("ClrmaThread::get_FrameCount\n");
+
+ if (pCount == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pCount = 0;
+
+ if (m_managedAnalysis == nullptr)
+ {
+ return E_UNEXPECTED;
+ }
+
+ if (!m_stackFramesInitialized)
+ {
+ m_stackFrames.clear();
+
+ ReleaseHolder<IXCLRDataTask> pTask;
+ HRESULT hr;
+ if (SUCCEEDED(hr = m_managedAnalysis->ClrData()->GetTaskByOSThreadID(
+ m_osThreadId,
+ (IXCLRDataTask**)&pTask)))
+ {
+ ReleaseHolder<IXCLRDataStackWalk> pStackWalk;
+ if (SUCCEEDED(hr = pTask->CreateStackWalk(
+ CLRDATA_SIMPFRAME_UNRECOGNIZED |
+ CLRDATA_SIMPFRAME_MANAGED_METHOD |
+ CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
+ CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
+ (IXCLRDataStackWalk**)&pStackWalk)))
+ {
+ // For each managed stack frame
+ int index = 0;
+ int count = 0;
+ do
+ {
+ StackFrame frame;
+ frame.Frame = index;
+ if (FAILED(hr = GetFrameLocation(pStackWalk, &frame.IP, &frame.SP)))
+ {
+ TraceError("Unwind: GetFrameLocation() FAILED %08x\n", hr);
+ break;
+ }
+ // Only include normal frames, skipping any special frames
+ DacpFrameData frameData;
+ if (SUCCEEDED(hr = frameData.Request(pStackWalk)) && frameData.frameAddr != 0)
+ {
+ TraceInformation("Unwind: skipping special frame SP %016llx IP %016llx\n", frame.SP, frame.IP);
+ continue;
+ }
+ CLRDATA_ADDRESS methodDesc = 0;
+ if (FAILED(hr = m_managedAnalysis->SosDacInterface()->GetMethodDescPtrFromIP(frame.IP, &methodDesc)))
+ {
+ TraceInformation("Unwind: skipping frame GetMethodDescPtrFromIP(%016llx) FAILED %08x\n", frame.IP, hr);
+ continue;
+ }
+ // Get normal module and method names like MethodNameFromIP() does for !clrstack
+ if (FAILED(hr = m_managedAnalysis->GetMethodDescInfo(methodDesc, frame, /* stripFunctionParameters */ false)))
+ {
+ TraceInformation("Unwind: skipping frame GetMethodDescInfo(%016llx) FAILED %08x\n", methodDesc, hr);
+ continue;
+ }
+ m_stackFrames.push_back(frame);
+ index++;
+
+ } while (count++ < MAX_STACK_FRAMES && pStackWalk->Next() == S_OK);
+ }
+ else
+ {
+ TraceError("Unwind: CreateStackWalk FAILED %08x\n", hr);
+ }
+ }
+ else
+ {
+ TraceError("Unwind: GetTaskByOSThreadID FAILED %08x\n", hr);
+ }
+
+ m_stackFramesInitialized = true;
+ }
+
+ *pCount = (USHORT)m_stackFrames.size();
+
+ return ((*pCount) != 0) ? S_OK : S_FALSE;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaThread::Frame(
+ UINT nFrame,
+ ULONG64* pAddrIP,
+ ULONG64* pAddrSP,
+ BSTR* bstrModule,
+ BSTR* bstrFunction,
+ ULONG64* pDisplacement
+ )
+{
+ TraceInformation("ClrmaThread::Frame %d\n", nFrame);
+
+ if (!pAddrIP || !pAddrSP || !bstrModule || !bstrFunction || !pDisplacement)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pAddrIP = 0;
+ *pAddrSP = 0;
+ *bstrModule = nullptr;
+ *bstrFunction = nullptr;
+ *pDisplacement = 0;
+
+ UINT nCount = 0;
+ if (HRESULT hr = get_FrameCount(&nCount))
+ {
+ return hr;
+ }
+
+ if (nFrame >= nCount)
+ {
+ return E_BOUNDS;
+ }
+
+ BSTR moduleName = SysAllocString(m_stackFrames[nFrame].Module.c_str());
+ if (moduleName == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ BSTR functionName = SysAllocString(m_stackFrames[nFrame].Function.c_str());
+ if (functionName == nullptr)
+ {
+ SysFreeString(moduleName);
+ return E_OUTOFMEMORY;
+ }
+
+ *pAddrIP = m_stackFrames[nFrame].IP;
+ *pAddrSP = m_stackFrames[nFrame].SP;
+ *bstrModule = moduleName;
+ *bstrFunction = functionName;
+ *pDisplacement = m_stackFrames[nFrame].Displacement;
+
+ return S_OK;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaThread::get_CurrentException(
+ ICLRMAClrException** ppClrException
+ )
+{
+ TraceInformation("ClrmaThread::get_CurrentException\n");
+
+ if (ppClrException == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppClrException = nullptr;
+
+ if (m_managedAnalysis == nullptr)
+ {
+ return E_UNEXPECTED;
+ }
+
+ if (m_lastThrownObject != 0)
+ {
+ ReleaseHolder<ClrmaException> exception = new (std::nothrow) ClrmaException(m_managedAnalysis, m_lastThrownObject);
+ if (exception == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+ HRESULT hr;
+ if (FAILED(hr = exception->QueryInterface(__uuidof(ICLRMAClrException), (void**)ppClrException)))
+ {
+ return hr;
+ }
+ }
+
+ return ((*ppClrException) != nullptr) ? S_OK : S_FALSE;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaThread::get_NestedExceptionCount(
+ USHORT* pCount
+ )
+{
+ TraceInformation("ClrmaThread::get_NestedExceptionCount\n");
+
+ if (pCount == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *pCount = 0;
+
+ if (m_managedAnalysis == nullptr)
+ {
+ return E_UNEXPECTED;
+ }
+
+ if (!m_nestedExceptionsInitialized)
+ {
+ m_nestedExceptions.clear();
+
+ HRESULT hr;
+ CLRDATA_ADDRESS currentNested = m_firstNestedException;
+ while (currentNested != 0)
+ {
+ CLRDATA_ADDRESS obj = 0, next = 0;
+ if (FAILED(hr = m_managedAnalysis->SosDacInterface()->GetNestedExceptionData(currentNested, &obj, &next)))
+ {
+ TraceError("get_NestedExceptionCount GetNestedExceptionData FAILED %08x\n", hr);
+ return hr;
+ }
+ m_nestedExceptions.push_back(obj);
+ currentNested = next;
+ }
+
+ m_nestedExceptionsInitialized = true;
+ }
+
+ *pCount = (USHORT)m_nestedExceptions.size();
+
+ return ((*pCount) != 0) ? S_OK : S_FALSE;
+}
+
+_Use_decl_annotations_
+STDMETHODIMP
+ClrmaThread::NestedException(
+ USHORT nIndex,
+ ICLRMAClrException** ppClrException
+ )
+{
+ TraceInformation("ClrmaThread::NestedException %d\n", nIndex);
+
+ if (ppClrException == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ *ppClrException = nullptr;
+
+ HRESULT hr;
+ USHORT nCount = 0;
+ if (hr = get_NestedExceptionCount(&nCount))
+ {
+ return hr;
+ }
+
+ if (nIndex >= nCount)
+ {
+ return E_BOUNDS;
+ }
+
+ ReleaseHolder<ClrmaException> exception = new (std::nothrow) ClrmaException(m_managedAnalysis, m_nestedExceptions[nIndex]);
+ if (exception == nullptr)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (FAILED(hr = exception->QueryInterface(__uuidof(ICLRMAClrException), (void**)ppClrException)))
+ {
+ return hr;
+ }
+
+ return ((*ppClrException) != nullptr) ? S_OK : S_FALSE;
+}
+
+HRESULT
+ClrmaThread::GetFrameLocation(
+ IXCLRDataStackWalk* pStackWalk,
+ CLRDATA_ADDRESS* ip,
+ CLRDATA_ADDRESS* sp)
+{
+ ULONG32 contextSize = 0;
+ ULONG32 contextFlags = CONTEXT_ARM64_CONTROL;
+ ULONG processorType = m_managedAnalysis->ProcessorType();
+ switch (processorType)
+ {
+ case IMAGE_FILE_MACHINE_AMD64:
+ contextSize = sizeof(AMD64_CONTEXT);
+ contextFlags = 0x00100001;
+ break;
+
+ case IMAGE_FILE_MACHINE_ARM64:
+ contextSize = sizeof(ARM64_CONTEXT);
+ contextFlags = 0x00400001;
+ break;
+
+ case IMAGE_FILE_MACHINE_I386:
+ contextSize = sizeof(X86_CONTEXT);
+ contextFlags = 0x00010001;
+ break;
+
+ case IMAGE_FILE_MACHINE_ARM:
+ case IMAGE_FILE_MACHINE_THUMB:
+ case IMAGE_FILE_MACHINE_ARMNT:
+ contextSize = sizeof(ARM_CONTEXT);
+ contextFlags = 0x00200001;
+ break;
+
+ case IMAGE_FILE_MACHINE_RISCV64:
+ contextSize = sizeof(RISCV64_CONTEXT);
+ contextFlags = 0x01000001;
+ break;
+
+ default:
+ TraceError("GetFrameLocation: Invalid processor type %04x\n", processorType);
+ return E_FAIL;
+ }
+ CROSS_PLATFORM_CONTEXT context;
+ HRESULT hr = pStackWalk->GetContext(contextFlags, contextSize, nullptr, (BYTE *)&context);
+ if (FAILED(hr))
+ {
+ TraceError("GetFrameLocation GetContext failed: %08x\n", hr);
+ return hr;
+ }
+ if (hr == S_FALSE)
+ {
+ // GetContext returns S_FALSE if the frame iterator is invalid. That's basically an error for us.
+ TraceError("GetFrameLocation GetContext returned S_FALSE\n");
+ return E_FAIL;
+ }
+ switch (processorType)
+ {
+ case IMAGE_FILE_MACHINE_AMD64:
+ *ip = context.Amd64Context.Rip;
+ *sp = context.Amd64Context.Rsp;
+ break;
+
+ case IMAGE_FILE_MACHINE_ARM64:
+ *ip = context.Arm64Context.Pc;
+ *sp = context.Arm64Context.Sp;
+ break;
+
+ case IMAGE_FILE_MACHINE_I386:
+ *ip = context.X86Context.Eip;
+ *sp = context.X86Context.Esp;
+ break;
+
+ case IMAGE_FILE_MACHINE_ARM:
+ case IMAGE_FILE_MACHINE_THUMB:
+ case IMAGE_FILE_MACHINE_ARMNT:
+ *ip = context.ArmContext.Pc & ~THUMB_CODE;
+ *sp = context.ArmContext.Sp;
+ break;
+
+ case IMAGE_FILE_MACHINE_RISCV64:
+ *ip = context.RiscV64Context.Pc;
+ *sp = context.RiscV64Context.Sp;
+ break;
+ }
+ return S_OK;
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include "managedanalysis.h"
+
+class ClrmaThread : public ICLRMAClrThread
+{
+public:
+ ClrmaThread(_In_ ClrmaManagedAnalysis* managedAnalysis, _In_ ULONG osThreadId);
+ virtual ~ClrmaThread();
+
+public:
+ // IUnknown
+ STDMETHOD(QueryInterface)(_In_ REFIID InterfaceId, _Out_ PVOID* Interface);
+ STDMETHOD_(ULONG, AddRef)();
+ STDMETHOD_(ULONG, Release)();
+
+ // ICLRMAClrThread
+ STDMETHOD(get_DebuggerCommand)(_Out_ BSTR* pValue);
+ STDMETHOD(get_OSThreadId)(_Out_ ULONG* pValue);
+
+ STDMETHOD(get_FrameCount)(_Out_ UINT* pCount);
+ STDMETHOD(Frame)(_In_ UINT nFrame, _Out_ ULONG64* pAddrIP, _Out_ ULONG64* pAddrSP, _Out_ BSTR* bstrModule, _Out_ BSTR* bstrFunction, _Out_ ULONG64* pDisplacement);
+
+ STDMETHOD(get_CurrentException)(_COM_Outptr_result_maybenull_ ICLRMAClrException** ppClrException);
+
+ STDMETHOD(get_NestedExceptionCount)(_Out_ USHORT* pCount);
+ STDMETHOD(NestedException)(_In_ USHORT nIndex, _COM_Outptr_ ICLRMAClrException** ppClrException);
+
+ HRESULT Initialize();
+
+private:
+ LONG m_lRefs;
+ ClrmaManagedAnalysis* m_managedAnalysis;
+ ULONG m_osThreadId;
+
+ // ClrmaThread::Initialize()
+ CLRDATA_ADDRESS m_lastThrownObject;
+ CLRDATA_ADDRESS m_firstNestedException;
+
+ // Initialized in ClrmaThread::get_FrameCount
+ std::vector<StackFrame> m_stackFrames;
+ bool m_stackFramesInitialized;
+
+ // Initialized in ClrmaThread::get_NestedExceptionCount
+ std::vector<CLRDATA_ADDRESS> m_nestedExceptions;
+ bool m_nestedExceptionsInitialized;
+
+ HRESULT GetFrameLocation(IXCLRDataStackWalk* pStackWalk, CLRDATA_ADDRESS* ip, CLRDATA_ADDRESS* sp);
+};
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+/// X86 Context
+#define X86_SIZE_OF_80387_REGISTERS 80
+#define X86_MAXIMUM_SUPPORTED_EXTENSION 512
+
+typedef struct {
+ DWORD ControlWord;
+ DWORD StatusWord;
+ DWORD TagWord;
+ DWORD ErrorOffset;
+ DWORD ErrorSelector;
+ DWORD DataOffset;
+ DWORD DataSelector;
+ BYTE RegisterArea[X86_SIZE_OF_80387_REGISTERS];
+ DWORD Cr0NpxState;
+} X86_FLOATING_SAVE_AREA;
+
+typedef struct {
+
+ DWORD ContextFlags;
+ DWORD Dr0;
+ DWORD Dr1;
+ DWORD Dr2;
+ DWORD Dr3;
+ DWORD Dr6;
+ DWORD Dr7;
+
+ X86_FLOATING_SAVE_AREA FloatSave;
+
+ DWORD SegGs;
+ DWORD SegFs;
+ DWORD SegEs;
+ DWORD SegDs;
+
+ DWORD Edi;
+ DWORD Esi;
+ DWORD Ebx;
+ DWORD Edx;
+ DWORD Ecx;
+ DWORD Eax;
+
+ DWORD Ebp;
+ DWORD Eip;
+ DWORD SegCs;
+ DWORD EFlags;
+ DWORD Esp;
+ DWORD SegSs;
+
+ BYTE ExtendedRegisters[X86_MAXIMUM_SUPPORTED_EXTENSION];
+
+} X86_CONTEXT;
+
+typedef struct {
+ ULONGLONG Low;
+ LONGLONG High;
+} M128A_XPLAT;
+
+
+/// AMD64 Context
+typedef struct {
+ WORD ControlWord;
+ WORD StatusWord;
+ BYTE TagWord;
+ BYTE Reserved1;
+ WORD ErrorOpcode;
+ DWORD ErrorOffset;
+ WORD ErrorSelector;
+ WORD Reserved2;
+ DWORD DataOffset;
+ WORD DataSelector;
+ WORD Reserved3;
+ DWORD MxCsr;
+ DWORD MxCsr_Mask;
+ M128A_XPLAT FloatRegisters[8];
+
+#if defined(_WIN64)
+ M128A_XPLAT XmmRegisters[16];
+ BYTE Reserved4[96];
+#else
+ M128A_XPLAT XmmRegisters[8];
+ BYTE Reserved4[220];
+
+ DWORD Cr0NpxState;
+#endif
+
+} AMD64_XMM_SAVE_AREA32;
+
+typedef struct {
+
+ DWORD64 P1Home;
+ DWORD64 P2Home;
+ DWORD64 P3Home;
+ DWORD64 P4Home;
+ DWORD64 P5Home;
+ DWORD64 P6Home;
+
+ DWORD ContextFlags;
+ DWORD MxCsr;
+
+ WORD SegCs;
+ WORD SegDs;
+ WORD SegEs;
+ WORD SegFs;
+ WORD SegGs;
+ WORD SegSs;
+ DWORD EFlags;
+
+ DWORD64 Dr0;
+ DWORD64 Dr1;
+ DWORD64 Dr2;
+ DWORD64 Dr3;
+ DWORD64 Dr6;
+ DWORD64 Dr7;
+
+ DWORD64 Rax;
+ DWORD64 Rcx;
+ DWORD64 Rdx;
+ DWORD64 Rbx;
+ DWORD64 Rsp;
+ DWORD64 Rbp;
+ DWORD64 Rsi;
+ DWORD64 Rdi;
+ DWORD64 R8;
+ DWORD64 R9;
+ DWORD64 R10;
+ DWORD64 R11;
+ DWORD64 R12;
+ DWORD64 R13;
+ DWORD64 R14;
+ DWORD64 R15;
+
+ DWORD64 Rip;
+
+ union {
+ AMD64_XMM_SAVE_AREA32 FltSave;
+ struct {
+ M128A_XPLAT Header[2];
+ M128A_XPLAT Legacy[8];
+ M128A_XPLAT Xmm0;
+ M128A_XPLAT Xmm1;
+ M128A_XPLAT Xmm2;
+ M128A_XPLAT Xmm3;
+ M128A_XPLAT Xmm4;
+ M128A_XPLAT Xmm5;
+ M128A_XPLAT Xmm6;
+ M128A_XPLAT Xmm7;
+ M128A_XPLAT Xmm8;
+ M128A_XPLAT Xmm9;
+ M128A_XPLAT Xmm10;
+ M128A_XPLAT Xmm11;
+ M128A_XPLAT Xmm12;
+ M128A_XPLAT Xmm13;
+ M128A_XPLAT Xmm14;
+ M128A_XPLAT Xmm15;
+ } DUMMYSTRUCTNAME;
+ } DUMMYUNIONNAME;
+
+ M128A_XPLAT VectorRegister[26];
+ DWORD64 VectorControl;
+
+ DWORD64 DebugControl;
+ DWORD64 LastBranchToRip;
+ DWORD64 LastBranchFromRip;
+ DWORD64 LastExceptionToRip;
+ DWORD64 LastExceptionFromRip;
+
+} AMD64_CONTEXT;
+
+typedef struct{
+ __int64 LowPart;
+ __int64 HighPart;
+} FLOAT128_XPLAT;
+
+
+/// ARM Context
+#define ARM_MAX_BREAKPOINTS_CONST 8
+#define ARM_MAX_WATCHPOINTS_CONST 1
+typedef DECLSPEC_ALIGN(8) struct {
+
+ DWORD ContextFlags;
+
+ DWORD R0;
+ DWORD R1;
+ DWORD R2;
+ DWORD R3;
+ DWORD R4;
+ DWORD R5;
+ DWORD R6;
+ DWORD R7;
+ DWORD R8;
+ DWORD R9;
+ DWORD R10;
+ DWORD R11;
+ DWORD R12;
+
+ DWORD Sp;
+ DWORD Lr;
+ DWORD Pc;
+ DWORD Cpsr;
+
+ DWORD Fpscr;
+ DWORD Padding;
+ union {
+ M128A_XPLAT Q[16];
+ ULONGLONG D[32];
+ DWORD S[32];
+ } DUMMYUNIONNAME;
+
+ DWORD Bvr[ARM_MAX_BREAKPOINTS_CONST];
+ DWORD Bcr[ARM_MAX_BREAKPOINTS_CONST];
+ DWORD Wvr[ARM_MAX_WATCHPOINTS_CONST];
+ DWORD Wcr[ARM_MAX_WATCHPOINTS_CONST];
+
+ DWORD Padding2[2];
+
+} ARM_CONTEXT;
+
+// On ARM this mask is or'ed with the address of code to get an instruction pointer
+#ifndef THUMB_CODE
+#define THUMB_CODE 1
+#endif
+
+///ARM64 Context
+#define ARM64_MAX_BREAKPOINTS 8
+#define ARM64_MAX_WATCHPOINTS 2
+typedef struct {
+
+ DWORD ContextFlags;
+ DWORD Cpsr; // NZVF + DAIF + CurrentEL + SPSel
+ union {
+ struct {
+ DWORD64 X0;
+ DWORD64 X1;
+ DWORD64 X2;
+ DWORD64 X3;
+ DWORD64 X4;
+ DWORD64 X5;
+ DWORD64 X6;
+ DWORD64 X7;
+ DWORD64 X8;
+ DWORD64 X9;
+ DWORD64 X10;
+ DWORD64 X11;
+ DWORD64 X12;
+ DWORD64 X13;
+ DWORD64 X14;
+ DWORD64 X15;
+ DWORD64 X16;
+ DWORD64 X17;
+ DWORD64 X18;
+ DWORD64 X19;
+ DWORD64 X20;
+ DWORD64 X21;
+ DWORD64 X22;
+ DWORD64 X23;
+ DWORD64 X24;
+ DWORD64 X25;
+ DWORD64 X26;
+ DWORD64 X27;
+ DWORD64 X28;
+ };
+
+ DWORD64 X[29];
+ };
+
+ DWORD64 Fp;
+ DWORD64 Lr;
+ DWORD64 Sp;
+ DWORD64 Pc;
+
+
+ M128A_XPLAT V[32];
+ DWORD Fpcr;
+ DWORD Fpsr;
+
+ DWORD Bcr[ARM64_MAX_BREAKPOINTS];
+ DWORD64 Bvr[ARM64_MAX_BREAKPOINTS];
+ DWORD Wcr[ARM64_MAX_WATCHPOINTS];
+ DWORD64 Wvr[ARM64_MAX_WATCHPOINTS];
+
+} ARM64_CONTEXT;
+
+///RISCV64 Context
+#define RISCV64_MAX_BREAKPOINTS 8
+#define RISCV64_MAX_WATCHPOINTS 2
+typedef struct {
+
+ DWORD ContextFlags;
+
+ DWORD64 R0;
+ DWORD64 Ra;
+ DWORD64 Sp;
+ DWORD64 Gp;
+ DWORD64 Tp;
+ DWORD64 T0;
+ DWORD64 T1;
+ DWORD64 T2;
+ DWORD64 Fp;
+ DWORD64 S1;
+ DWORD64 A0;
+ DWORD64 A1;
+ DWORD64 A2;
+ DWORD64 A3;
+ DWORD64 A4;
+ DWORD64 A5;
+ DWORD64 A6;
+ DWORD64 A7;
+ DWORD64 S2;
+ DWORD64 S3;
+ DWORD64 S4;
+ DWORD64 S5;
+ DWORD64 S6;
+ DWORD64 S7;
+ DWORD64 S8;
+ DWORD64 S9;
+ DWORD64 S10;
+ DWORD64 S11;
+ DWORD64 T3;
+ DWORD64 T4;
+ DWORD64 T5;
+ DWORD64 T6;
+ DWORD64 Pc;
+
+ ULONGLONG F[32];
+ DWORD Fcsr;
+
+ DWORD Padding[3];
+
+} RISCV64_CONTEXT;
+
+typedef struct _CROSS_PLATFORM_CONTEXT {
+
+ _CROSS_PLATFORM_CONTEXT() {}
+
+ union {
+ X86_CONTEXT X86Context;
+ AMD64_CONTEXT Amd64Context;
+ ARM_CONTEXT ArmContext;
+ ARM64_CONTEXT Arm64Context;
+ RISCV64_CONTEXT RiscV64Context;
+ };
+
+} CROSS_PLATFORM_CONTEXT, *PCROSS_PLATFORM_CONTEXT;
+
return m_symbols->GetNumberModules(loaded, unloaded);
}
+HRESULT
+DbgEngServices::GetModuleByIndex(
+ ULONG index,
+ PULONG64 base)
+{
+ return m_symbols->GetModuleByIndex(index, base);
+}
+
HRESULT
DbgEngServices::GetModuleNames(
ULONG index,
PULONG timestamp,
PULONG checksum)
{
- HRESULT hr = m_symbols->GetModuleByIndex(index, moduleBase);
+ ULONG64 base;
+ HRESULT hr = m_symbols->GetModuleByIndex(index, &base);
if (FAILED(hr)) {
return hr;
}
DEBUG_MODULE_PARAMETERS params;
- hr = m_symbols->GetModuleParameters(1, moduleBase, 0, ¶ms);
+ hr = m_symbols->GetModuleParameters(1, &base, 0, ¶ms);
if (FAILED(hr)) {
return hr;
}
+ if (moduleBase) {
+ *moduleBase = base;
+ }
if (moduleSize) {
*moduleSize = params.Size;
}
return m_symbols->GetModuleVersionInformation(index, base, item, buffer, bufferSize, versionInfoSize);
}
+HRESULT
+DbgEngServices::GetModuleByModuleName(
+ PCSTR name,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base)
+{
+ return m_symbols->GetModuleByModuleName(name, startIndex, index, base);
+}
+
HRESULT
DbgEngServices::GetNumberThreads(
PULONG number)
descriptionUsed);
}
+void
+DbgEngServices::FlushCheck()
+{
+ // Flush the target when the debugger target breaks
+ if (m_flushNeeded)
+ {
+ m_flushNeeded = false;
+ Extensions::GetInstance()->FlushTarget();
+ }
+}
+
+HRESULT
+DbgEngServices::ExecuteHostCommand(
+ PCSTR commandLine,
+ PEXECUTE_COMMAND_OUTPUT_CALLBACK callback)
+{
+ OutputCaptureHolder holder(m_client, callback);
+ return m_control->Execute(DEBUG_OUTCTL_THIS_CLIENT, commandLine, DEBUG_EXECUTE_NO_REPEAT);
+}
+
//----------------------------------------------------------------------------
// IRemoteMemoryService
//----------------------------------------------------------------------------
// Helper Functions
//----------------------------------------------------------------------------
-void
-DbgEngServices::FlushCheck(Extensions* extensions)
-{
- // Flush the target when the debugger target breaks
- if (m_flushNeeded)
- {
- m_flushNeeded = false;
- extensions->FlushTarget();
- }
-}
-
IMachine*
DbgEngServices::GetMachine()
{
// Helper functions
//----------------------------------------------------------------------------
- void FlushCheck(Extensions* extensions);
-
IMachine* GetMachine();
HRESULT SetCurrentThreadIdFromSystemId(
PULONG loaded,
PULONG unloaded);
+ HRESULT STDMETHODCALLTYPE GetModuleByIndex(
+ ULONG index,
+ PULONG64 base);
+
HRESULT STDMETHODCALLTYPE GetModuleNames(
ULONG index,
ULONG64 base,
ULONG bufferSize,
PULONG versionInfoSize);
+ HRESULT STDMETHODCALLTYPE GetModuleByModuleName(
+ PCSTR name,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base);
+
HRESULT STDMETHODCALLTYPE GetNumberThreads(
PULONG number);
ULONG descriptionSize,
PULONG descriptionUsed);
+ void STDMETHODCALLTYPE FlushCheck();
+
+ HRESULT STDMETHODCALLTYPE ExecuteHostCommand(
+ PCSTR commandLine,
+ PEXECUTE_COMMAND_OUTPUT_CALLBACK callback);
+
//----------------------------------------------------------------------------
// IRemoteMemoryService
//----------------------------------------------------------------------------
ULONG64 BaseOffset);
};
+class OutputCaptureHolder : IDebugOutputCallbacks
+{
+private:
+ ULONG m_ref;
+ IDebugClient* m_client;
+ IDebugOutputCallbacks* m_previous;
+ PEXECUTE_COMMAND_OUTPUT_CALLBACK m_callback;
+
+public:
+ //----------------------------------------------------------------------------
+ // IUnknown
+ //----------------------------------------------------------------------------
+
+ HRESULT STDMETHODCALLTYPE
+ QueryInterface(REFIID InterfaceId, PVOID* Interface)
+ {
+ if (InterfaceId == __uuidof(IUnknown) ||
+ InterfaceId == __uuidof(IDebugOutputCallbacks))
+ {
+ *Interface = static_cast<IDebugOutputCallbacks*>(this);
+ AddRef();
+ return S_OK;
+ }
+ *Interface = nullptr;
+ return E_NOINTERFACE;
+ }
+
+ ULONG STDMETHODCALLTYPE
+ AddRef()
+ {
+ LONG ref = InterlockedIncrement(&m_ref);
+ return ref;
+ }
+
+ ULONG STDMETHODCALLTYPE
+ Release()
+ {
+ LONG ref = InterlockedDecrement(&m_ref);
+ return ref;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ Output(ULONG mask, PCSTR text)
+ {
+ m_callback(mask, text);
+ return S_OK;
+ }
+
+public:
+ OutputCaptureHolder(IDebugClient* client, PEXECUTE_COMMAND_OUTPUT_CALLBACK callback) :
+ m_ref(0),
+ m_client(client),
+ m_previous(nullptr),
+ m_callback(callback)
+ {
+ _ASSERTE(SUCCEEDED(client->GetOutputCallbacks(&m_previous)));
+ _ASSERTE(SUCCEEDED(client->SetOutputCallbacks(this)));
+ }
+
+ ~OutputCaptureHolder()
+ {
+ _ASSERTE(SUCCEEDED(m_client->SetOutputCallbacks(m_previous)));
+ _ASSERTE(m_ref == 0);
+ }
+};
+
#ifdef __cplusplus
};
#endif
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-// ==++==
-//
-
-//
-// ==--==
#include "exts.h"
#include "disasm.h"
+
#ifndef FEATURE_PAL
#define VER_PRODUCTVERSION_W (0x0100)
return Status;
}
+HRESULT
+ExtInit(PDEBUG_CLIENT client)
+{
+ HRESULT hr;
+ if ((hr = ExtQuery(client)) == S_OK)
+ {
+ // Reset some global variables on entry
+ ControlC = FALSE;
+ g_bDacBroken = TRUE;
+ g_clrData = NULL;
+ g_sos = NULL;
+
+ // Flush here only on Windows under dbgeng. The lldb sos plugin handles it for Linux/MacOS.
+#ifndef FEATURE_PAL
+ Extensions* extensions = Extensions::GetInstance();
+ if (extensions != nullptr)
+ {
+ extensions->FlushCheck();
+ }
+#endif // !FEATURE_PAL
+ }
+ return hr;
+}
+
IMachine*
GetTargetMachine(ULONG processorType)
{
ExtOut("For more information see https://go.microsoft.com/fwlink/?linkid=2135652\n");
}
+IXCLRDataProcess*
+GetClrDataFromDbgEng()
+{
+#ifdef FEATURE_PAL
+ return nullptr;
+#else
+ IXCLRDataProcess* clrData = nullptr;
+
+ // Fail if ExtensionApis wasn't initialized because we are hosted under dotnet-dump
+ if (Ioctl != nullptr)
+ {
+ // Try getting the DAC interface from dbgeng if the above fails on Windows
+ WDBGEXTS_CLR_DATA_INTERFACE Query;
+
+ Query.Iid = &__uuidof(IXCLRDataProcess);
+ if (Ioctl(IG_GET_CLR_DATA_INTERFACE, &Query, sizeof(Query)))
+ {
+ // No AddRef needed. IG_GET_CLR_DATA_INTERFACE either creates or QI's the IXCLRDataProcess instance.
+ clrData = (IXCLRDataProcess*)Query.Iface;
+ clrData->Flush();
+ }
+ }
+
+ return clrData;
+#endif
+}
+
#ifndef FEATURE_PAL
BOOL IsMiniDumpFileNODAC();
}
#endif // FEATURE_PAL
-
-/// <summary>
-/// Returns the host instance
-///
-/// * dotnet-dump - m_pHost has already been set by SOSInitializeByHost by SOS.Hosting
-/// * lldb - m_pHost has already been set by SOSInitializeByHost by libsosplugin which gets it via the InitializeHostServices callback
-/// * dbgeng - SOS.Extensions provides the instance via the InitializeHostServices callback
-/// </summary>
-IHost* SOSExtensions::GetHost()
-{
- if (m_pHost == nullptr)
- {
-#ifndef FEATURE_PAL
- // Initialize the hosting runtime which will call InitializeHostServices and set m_pHost to the host instance
- InitializeHosting();
-#endif
- // Otherwise, use the local host instance (hostimpl.*) that creates a local target instance (targetimpl.*)
- if (m_pHost == nullptr)
- {
- m_pHost = Host::GetInstance();
- }
- }
- return m_pHost;
-}
-
-/// <summary>
-/// Returns the runtime or fails if no target or current runtime
-/// </summary>
-/// <param name="ppRuntime">runtime instance</param>
-/// <returns>error code</returns>
-HRESULT GetRuntime(IRuntime** ppRuntime)
-{
- SOSExtensions* extensions = (SOSExtensions*)Extensions::GetInstance();
- ITarget* target = extensions->GetTarget();
- if (target == nullptr)
- {
- return E_FAIL;
- }
-#ifndef FEATURE_PAL
- extensions->FlushCheck();
-#endif
- return target->GetRuntime(ppRuntime);
-}
-
-void FlushCheck()
-{
-#ifndef FEATURE_PAL
- SOSExtensions* extensions = (SOSExtensions*)Extensions::GetInstance();
- if (extensions != nullptr)
- {
- extensions->FlushCheck();
- }
-#endif // !FEATURE_PAL
-}
TADDR end;
} TADDR_SEGINFO;
+#include "sosextensions.h"
#include "util.h"
-#ifndef FEATURE_PAL
-#include "dbgengservices.h"
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
static OnUnloadTask *s_pUnloadTaskList;
};
-//-----------------------------------------------------------------------------------------
-// Extension helper class
-//-----------------------------------------------------------------------------------------
-class SOSExtensions : public Extensions
-{
- SOSExtensions(IDebuggerServices* debuggerServices, IHost* host) :
- Extensions(debuggerServices)
- {
- m_pHost = host;
- OnUnloadTask::Register(SOSExtensions::Uninitialize);
- }
-
-#ifndef FEATURE_PAL
- ~SOSExtensions()
- {
- if (m_pDebuggerServices != nullptr)
- {
- ((DbgEngServices*)m_pDebuggerServices)->Uninitialize();
- m_pDebuggerServices->Release();
- m_pDebuggerServices = nullptr;
- }
- }
-#endif
-
-public:
-
-#ifndef FEATURE_PAL
- static HRESULT Initialize(IDebugClient* client)
- {
- if (s_extensions == nullptr)
- {
- DbgEngServices* debuggerServices = new DbgEngServices(client);
- HRESULT hr = debuggerServices->Initialize();
- if (FAILED(hr)) {
- return hr;
- }
- s_extensions = new SOSExtensions(debuggerServices, nullptr);
- }
- return S_OK;
- }
-#endif
-
- static HRESULT Initialize(IHost* host, IDebuggerServices* debuggerServices)
- {
- if (s_extensions == nullptr)
- {
- s_extensions = new SOSExtensions(debuggerServices, host);
- }
- return S_OK;
- }
-
- static void Uninitialize()
- {
- if (s_extensions != nullptr)
- {
- delete s_extensions;
- s_extensions = nullptr;
- }
- }
-
-#ifndef FEATURE_PAL
- void FlushCheck()
- {
- if (m_pDebuggerServices != nullptr)
- {
- ((DbgEngServices*)m_pDebuggerServices)->FlushCheck(this);
- }
- }
-#endif
-
- IHost* GetHost();
-};
-
-extern HRESULT GetRuntime(IRuntime** ppRuntime);
-extern void FlushCheck();
-
-#ifndef MINIDUMP
-
-#define EXIT_API ExtRelease
-
// Safe release and NULL.
#define EXT_RELEASE(Unk) \
((Unk) != NULL ? ((Unk)->Release(), (Unk) = NULL) : NULL)
HRESULT
ExtQuery(PDEBUG_CLIENT client);
+HRESULT
+ExtInit(PDEBUG_CLIENT client);
+
HRESULT
ArchQuery(void);
void
DACMessage(HRESULT Status);
+IXCLRDataProcess*
+GetClrDataFromDbgEng();
+
extern BOOL ControlC;
inline BOOL IsInterrupt()
#define INIT_API_EXT() \
HRESULT Status; \
__ExtensionCleanUp __extensionCleanUp; \
- if ((Status = ExtQuery(client)) != S_OK) return Status; \
- ControlC = FALSE; \
- g_bDacBroken = TRUE; \
- g_clrData = NULL; \
- g_sos = NULL; \
- FlushCheck();
+ if ((Status = ExtInit(client)) != S_OK) return Status;
// Also initializes the target machine
#define INIT_API_NOEE() \
#define CPPMOD
#endif
-#endif
-
#ifdef __cplusplus
}
#endif
ClrStack
clrstack=ClrStack
CLRStack=ClrStack
+ clrmaconfig
crashinfo
DumpALC
dumpalc=DumpALC
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "exts.h"
+#ifndef FEATURE_PAL
+#include "dbgengservices.h"
+#endif
+
+SOSExtensions::SOSExtensions(IDebuggerServices* debuggerServices, IHost* host) :
+ Extensions(debuggerServices)
+{
+ m_pHost = host;
+ OnUnloadTask::Register(SOSExtensions::Uninitialize);
+}
+
+#ifndef FEATURE_PAL
+
+SOSExtensions::~SOSExtensions()
+{
+ if (m_pDebuggerServices != nullptr)
+ {
+ ((DbgEngServices*)m_pDebuggerServices)->Uninitialize();
+ m_pDebuggerServices->Release();
+ m_pDebuggerServices = nullptr;
+ }
+}
+
+HRESULT
+SOSExtensions::Initialize(IDebugClient* client)
+{
+ if (s_extensions == nullptr)
+ {
+ DbgEngServices* debuggerServices = new DbgEngServices(client);
+ HRESULT hr = debuggerServices->Initialize();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ s_extensions = new SOSExtensions(debuggerServices, nullptr);
+ }
+ return S_OK;
+}
+
+#endif
+
+HRESULT
+SOSExtensions::Initialize(IHost* host, IDebuggerServices* debuggerServices)
+{
+ if (s_extensions == nullptr)
+ {
+ s_extensions = new SOSExtensions(debuggerServices, host);
+ }
+ return S_OK;
+}
+
+void
+SOSExtensions::Uninitialize()
+{
+ if (s_extensions != nullptr)
+ {
+ delete s_extensions;
+ s_extensions = nullptr;
+ }
+}
+
+/// <summary>
+/// Returns the host instance
+///
+/// * dotnet-dump - m_pHost has already been set by SOSInitializeByHost by SOS.Hosting
+/// * lldb - m_pHost has already been set by SOSInitializeByHost by libsosplugin which gets it via the InitializeHostServices callback
+/// * dbgeng - SOS.Extensions provides the instance via the InitializeHostServices callback
+/// </summary>
+IHost*
+SOSExtensions::GetHost()
+{
+ if (m_pHost == nullptr)
+ {
+#ifndef FEATURE_PAL
+ // Initialize the hosting runtime which will call InitializeHostServices and set m_pHost to the host instance
+ InitializeHosting();
+#endif
+ // Otherwise, use the local host instance (hostimpl.*) that creates a local target instance (targetimpl.*)
+ if (m_pHost == nullptr)
+ {
+ m_pHost = Host::GetInstance();
+ }
+ }
+ return m_pHost;
+}
+
+/// <summary>
+/// Returns the runtime or fails if no target or current runtime
+/// </summary>
+/// <param name="ppRuntime">runtime instance</param>
+/// <returns>error code</returns>
+HRESULT
+GetRuntime(IRuntime** ppRuntime)
+{
+ Extensions* extensions = Extensions::GetInstance();
+ ITarget* target = extensions->GetTarget();
+ if (target == nullptr)
+ {
+ return E_FAIL;
+ }
+ // Flush here only on Windows under dbgeng. The lldb sos plugin handles it for Linux/MacOS.
+#ifndef FEATURE_PAL
+ extensions->FlushCheck();
+#endif
+ return target->GetRuntime(ppRuntime);
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include <extensions.h>
+
+//-----------------------------------------------------------------------------------------
+// Extension helper class
+//-----------------------------------------------------------------------------------------
+class SOSExtensions : public Extensions
+{
+ SOSExtensions(IDebuggerServices* debuggerServices, IHost* host);
+#ifndef FEATURE_PAL
+ ~SOSExtensions();
+#endif
+
+public:
+#ifndef FEATURE_PAL
+ static HRESULT Initialize(IDebugClient* client);
+#endif
+ static HRESULT Initialize(IHost* host, IDebuggerServices* debuggerServices);
+ static void Uninitialize();
+ IHost* GetHost();
+};
+
+extern HRESULT GetRuntime(IRuntime** ppRuntime);
#include "ExpressionNode.h"
#include "WatchCmd.h"
#include "tls.h"
+#include "clrma/managedanalysis.h"
typedef struct _VM_COUNTERS {
SIZE_T PeakVirtualSize;
sprintf_s(buffer, ARRAY_SIZE(buffer), "breakpoint set --address 0x%p", SOS_PTR(addr));
#endif
ExtOut("Setting breakpoint: %s [%S]\n", buffer, wszNameBuffer);
- g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, buffer, 0);
if (curLimit < MaxBPsCached)
{
#else
sprintf_s(buffer, ARRAY_SIZE(buffer), "breakpoint set --one-shot --address 0x%p", SOS_PTR(startAddr+catcherNativeOffset));
#endif
- g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, buffer, 0);
}
g_stopOnNextCatch = FALSE;
}
ExtOut("Expecting first chance CLRN exception\n");
return E_FAIL;
#else
- g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
+ g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, "process continue", 0);
return S_OK;
#endif
}
case DEBUG_STATUS_GO_HANDLED:
case DEBUG_STATUS_GO_NOT_HANDLED:
#ifndef FEATURE_PAL
- g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "g", 0);
+ g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, "g", 0);
#else
- g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
+ g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, "process continue", 0);
#endif
break;
default:
{
INIT_API_EFN();
EnableModuleLoadUnloadCallbacks();
- return g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!SOSHandleCLRN\" clrn", 0);
+ return g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, "sxe -c \"!SOSHandleCLRN\" clrn", 0);
}
#else // FEATURE_PAL
SOS_PTR(MethodDescData.AddressOfNativeCodeSlot),
SOS_PTR(MethodDescData.AddressOfNativeCodeSlot));
- Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ Status = g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, buffer, 0);
if (FAILED(Status))
{
ExtOut("Unable to set breakpoint with IDebugControl::Execute: %x\n",Status);
{
ExtOut("Adding pending breakpoints...\n");
#ifndef FEATURE_PAL
- Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!SOSHandleCLRN\" clrn", 0);
+ Status = g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, "sxe -c \"!SOSHandleCLRN\" clrn", 0);
#else
Status = g_ExtServices->SetExceptionCallback(HandleExceptionNotification);
#endif // FEATURE_PAL
idp2->SetGcNotification(gea);
// ... and register the notification handler
#ifndef FEATURE_PAL
- g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!SOSHandleCLRN\" clrn", 0);
+ g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, "sxe -c \"!SOSHandleCLRN\" clrn", 0);
#else
g_ExtServices->SetExceptionCallback(HandleExceptionNotification);
#endif // FEATURE_PAL
}
else
{
- Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "thr; .echo wait" ,0);
+ Status = g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, "thr; .echo wait" ,0);
if (FAILED(Status))
{
ExtOut("Error tracing instruction\n");
sprintf_s(buffer, ARRAY_SIZE(buffer),
"r$t%d=0",
preg);
- Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
+ Status = g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, buffer ,0);
if (FAILED(Status))
{
ExtOut("Error initialized register $t%d to zero\n", preg);
sprintf_s(buffer, ARRAY_SIZE(buffer),
"r$t%d=%x",
preg, codeType);
- Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ Status = g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, buffer, 0);
if (FAILED(Status))
{
ExtOut("Error setting register $t%d\n", preg);
sprintf_s(buffer, ARRAY_SIZE(buffer),
"r$t%d=0",
preg);
- Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ Status = g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, buffer, 0);
if (FAILED(Status))
{
ExtOut("Error initialized register $t%d to zero\n", preg);
EXCEPTION_COMPLUS
);
- Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ Status = g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, buffer, 0);
if (FAILED(Status))
{
ExtOut("Error setting breakpoint: %s\n", buffer);
sprintf_s(buffer, ARRAY_SIZE(buffer),
"r$t%d=1",
preg);
- Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
+ Status = g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, buffer, 0);
if (FAILED(Status))
{
ExtOut("Failed to execute the following command: %s\n", buffer);
DECLARE_API(VerifyStackTrace)
{
INIT_API();
- ONLY_SUPPORTED_ON_WINDOWS_TARGET();
BOOL bVerifyManagedExcepStack = FALSE;
CMDOption option[] =
else
{
g_fAllowJitOptimization = FALSE;
- g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!SOSHandleCLRN\" clrn", 0);
+ g_ExtControl->Execute(DEBUG_OUTCTL_NOT_LOGGED, "sxe -c \"!SOSHandleCLRN\" clrn", 0);
ExtOut("JIT optimization will be suppressed\n");
}
}
//
DECLARE_API(runtimes)
{
- INIT_API_NOEE_PROBE_MANAGED("runtimes");
+ INIT_API_NODAC_PROBE_MANAGED("runtimes");
BOOL bNetFx = FALSE;
BOOL bNetCore = FALSE;
HRESULT hr = g_pRuntime->GetClrDataProcess(&g_clrData);
if (FAILED(hr))
{
-#ifdef FEATURE_PAL
- return hr;
-#else
- // Fail if ExtensionApis wasn't initialized because we are hosted under dotnet-dump
- if (Ioctl == nullptr) {
- return hr;
- }
- // Try getting the DAC interface from dbgeng if the above fails on Windows
- WDBGEXTS_CLR_DATA_INTERFACE Query;
-
- Query.Iid = &__uuidof(IXCLRDataProcess);
- if (!Ioctl(IG_GET_CLR_DATA_INTERFACE, &Query, sizeof(Query))) {
+ g_clrData = GetClrDataFromDbgEng();
+ if (g_clrData == nullptr)
+ {
return hr;
}
- g_clrData = (IXCLRDataProcess*)Query.Iface;
- g_clrData->Flush();
-#endif
}
else
{
#include "data.h"
#endif //STRIKE
-#include "cordebug.h"
-#include "static_assert.h"
+#include <cordebug.h>
+#include <static_assert.h>
#include <string>
-#include "extensions.h"
-#include "releaseholder.h"
+#include <extensions.h>
+#include <releaseholder.h>
#include "hostimpl.h"
#include "targetimpl.h"
#include "runtimeimpl.h"
#include "symbols.h"
+#include "crosscontext.h"
typedef LPCSTR LPCUTF8;
typedef LPSTR LPUTF8;
#endif // !FEATURE_PAL
-/// X86 Context
-#define X86_SIZE_OF_80387_REGISTERS 80
-#define X86_MAXIMUM_SUPPORTED_EXTENSION 512
-
-typedef struct {
- DWORD ControlWord;
- DWORD StatusWord;
- DWORD TagWord;
- DWORD ErrorOffset;
- DWORD ErrorSelector;
- DWORD DataOffset;
- DWORD DataSelector;
- BYTE RegisterArea[X86_SIZE_OF_80387_REGISTERS];
- DWORD Cr0NpxState;
-} X86_FLOATING_SAVE_AREA;
-
-typedef struct {
-
- DWORD ContextFlags;
- DWORD Dr0;
- DWORD Dr1;
- DWORD Dr2;
- DWORD Dr3;
- DWORD Dr6;
- DWORD Dr7;
-
- X86_FLOATING_SAVE_AREA FloatSave;
-
- DWORD SegGs;
- DWORD SegFs;
- DWORD SegEs;
- DWORD SegDs;
-
- DWORD Edi;
- DWORD Esi;
- DWORD Ebx;
- DWORD Edx;
- DWORD Ecx;
- DWORD Eax;
-
- DWORD Ebp;
- DWORD Eip;
- DWORD SegCs;
- DWORD EFlags;
- DWORD Esp;
- DWORD SegSs;
-
- BYTE ExtendedRegisters[X86_MAXIMUM_SUPPORTED_EXTENSION];
-
-} X86_CONTEXT;
-
-typedef struct {
- ULONGLONG Low;
- LONGLONG High;
-} M128A_XPLAT;
-
-
-/// AMD64 Context
-typedef struct {
- WORD ControlWord;
- WORD StatusWord;
- BYTE TagWord;
- BYTE Reserved1;
- WORD ErrorOpcode;
- DWORD ErrorOffset;
- WORD ErrorSelector;
- WORD Reserved2;
- DWORD DataOffset;
- WORD DataSelector;
- WORD Reserved3;
- DWORD MxCsr;
- DWORD MxCsr_Mask;
- M128A_XPLAT FloatRegisters[8];
-
-#if defined(_WIN64)
- M128A_XPLAT XmmRegisters[16];
- BYTE Reserved4[96];
-#else
- M128A_XPLAT XmmRegisters[8];
- BYTE Reserved4[220];
-
- DWORD Cr0NpxState;
-#endif
-
-} AMD64_XMM_SAVE_AREA32;
-
-typedef struct {
-
- DWORD64 P1Home;
- DWORD64 P2Home;
- DWORD64 P3Home;
- DWORD64 P4Home;
- DWORD64 P5Home;
- DWORD64 P6Home;
-
- DWORD ContextFlags;
- DWORD MxCsr;
-
- WORD SegCs;
- WORD SegDs;
- WORD SegEs;
- WORD SegFs;
- WORD SegGs;
- WORD SegSs;
- DWORD EFlags;
-
- DWORD64 Dr0;
- DWORD64 Dr1;
- DWORD64 Dr2;
- DWORD64 Dr3;
- DWORD64 Dr6;
- DWORD64 Dr7;
-
- DWORD64 Rax;
- DWORD64 Rcx;
- DWORD64 Rdx;
- DWORD64 Rbx;
- DWORD64 Rsp;
- DWORD64 Rbp;
- DWORD64 Rsi;
- DWORD64 Rdi;
- DWORD64 R8;
- DWORD64 R9;
- DWORD64 R10;
- DWORD64 R11;
- DWORD64 R12;
- DWORD64 R13;
- DWORD64 R14;
- DWORD64 R15;
-
- DWORD64 Rip;
-
- union {
- AMD64_XMM_SAVE_AREA32 FltSave;
- struct {
- M128A_XPLAT Header[2];
- M128A_XPLAT Legacy[8];
- M128A_XPLAT Xmm0;
- M128A_XPLAT Xmm1;
- M128A_XPLAT Xmm2;
- M128A_XPLAT Xmm3;
- M128A_XPLAT Xmm4;
- M128A_XPLAT Xmm5;
- M128A_XPLAT Xmm6;
- M128A_XPLAT Xmm7;
- M128A_XPLAT Xmm8;
- M128A_XPLAT Xmm9;
- M128A_XPLAT Xmm10;
- M128A_XPLAT Xmm11;
- M128A_XPLAT Xmm12;
- M128A_XPLAT Xmm13;
- M128A_XPLAT Xmm14;
- M128A_XPLAT Xmm15;
- } DUMMYSTRUCTNAME;
- } DUMMYUNIONNAME;
-
- M128A_XPLAT VectorRegister[26];
- DWORD64 VectorControl;
-
- DWORD64 DebugControl;
- DWORD64 LastBranchToRip;
- DWORD64 LastBranchFromRip;
- DWORD64 LastExceptionToRip;
- DWORD64 LastExceptionFromRip;
-
-} AMD64_CONTEXT;
-
-typedef struct{
- __int64 LowPart;
- __int64 HighPart;
-} FLOAT128_XPLAT;
-
-
-/// ARM Context
-#define ARM_MAX_BREAKPOINTS_CONST 8
-#define ARM_MAX_WATCHPOINTS_CONST 1
-typedef DECLSPEC_ALIGN(8) struct {
-
- DWORD ContextFlags;
-
- DWORD R0;
- DWORD R1;
- DWORD R2;
- DWORD R3;
- DWORD R4;
- DWORD R5;
- DWORD R6;
- DWORD R7;
- DWORD R8;
- DWORD R9;
- DWORD R10;
- DWORD R11;
- DWORD R12;
-
- DWORD Sp;
- DWORD Lr;
- DWORD Pc;
- DWORD Cpsr;
-
- DWORD Fpscr;
- DWORD Padding;
- union {
- M128A_XPLAT Q[16];
- ULONGLONG D[32];
- DWORD S[32];
- } DUMMYUNIONNAME;
-
- DWORD Bvr[ARM_MAX_BREAKPOINTS_CONST];
- DWORD Bcr[ARM_MAX_BREAKPOINTS_CONST];
- DWORD Wvr[ARM_MAX_WATCHPOINTS_CONST];
- DWORD Wcr[ARM_MAX_WATCHPOINTS_CONST];
-
- DWORD Padding2[2];
-
-} ARM_CONTEXT;
-
-// On ARM this mask is or'ed with the address of code to get an instruction pointer
-#ifndef THUMB_CODE
-#define THUMB_CODE 1
-#endif
-
-///ARM64 Context
-#define ARM64_MAX_BREAKPOINTS 8
-#define ARM64_MAX_WATCHPOINTS 2
-typedef struct {
-
- DWORD ContextFlags;
- DWORD Cpsr; // NZVF + DAIF + CurrentEL + SPSel
- union {
- struct {
- DWORD64 X0;
- DWORD64 X1;
- DWORD64 X2;
- DWORD64 X3;
- DWORD64 X4;
- DWORD64 X5;
- DWORD64 X6;
- DWORD64 X7;
- DWORD64 X8;
- DWORD64 X9;
- DWORD64 X10;
- DWORD64 X11;
- DWORD64 X12;
- DWORD64 X13;
- DWORD64 X14;
- DWORD64 X15;
- DWORD64 X16;
- DWORD64 X17;
- DWORD64 X18;
- DWORD64 X19;
- DWORD64 X20;
- DWORD64 X21;
- DWORD64 X22;
- DWORD64 X23;
- DWORD64 X24;
- DWORD64 X25;
- DWORD64 X26;
- DWORD64 X27;
- DWORD64 X28;
- };
-
- DWORD64 X[29];
- };
-
- DWORD64 Fp;
- DWORD64 Lr;
- DWORD64 Sp;
- DWORD64 Pc;
-
-
- M128A_XPLAT V[32];
- DWORD Fpcr;
- DWORD Fpsr;
-
- DWORD Bcr[ARM64_MAX_BREAKPOINTS];
- DWORD64 Bvr[ARM64_MAX_BREAKPOINTS];
- DWORD Wcr[ARM64_MAX_WATCHPOINTS];
- DWORD64 Wvr[ARM64_MAX_WATCHPOINTS];
-
-} ARM64_CONTEXT;
-
-///RISCV64 Context
-#define RISCV64_MAX_BREAKPOINTS 8
-#define RISCV64_MAX_WATCHPOINTS 2
-typedef struct {
-
- DWORD ContextFlags;
-
- DWORD64 R0;
- DWORD64 Ra;
- DWORD64 Sp;
- DWORD64 Gp;
- DWORD64 Tp;
- DWORD64 T0;
- DWORD64 T1;
- DWORD64 T2;
- DWORD64 Fp;
- DWORD64 S1;
- DWORD64 A0;
- DWORD64 A1;
- DWORD64 A2;
- DWORD64 A3;
- DWORD64 A4;
- DWORD64 A5;
- DWORD64 A6;
- DWORD64 A7;
- DWORD64 S2;
- DWORD64 S3;
- DWORD64 S4;
- DWORD64 S5;
- DWORD64 S6;
- DWORD64 S7;
- DWORD64 S8;
- DWORD64 S9;
- DWORD64 S10;
- DWORD64 S11;
- DWORD64 T3;
- DWORD64 T4;
- DWORD64 T5;
- DWORD64 T6;
- DWORD64 Pc;
-
- ULONGLONG F[32];
- DWORD Fcsr;
-
- DWORD Padding[3];
-
-} RISCV64_CONTEXT;
-
-typedef struct _CROSS_PLATFORM_CONTEXT {
-
- _CROSS_PLATFORM_CONTEXT() {}
-
- union {
- X86_CONTEXT X86Context;
- AMD64_CONTEXT Amd64Context;
- ARM_CONTEXT ArmContext;
- ARM64_CONTEXT Arm64Context;
- RISCV64_CONTEXT RiscV64Context;
- };
-
-} CROSS_PLATFORM_CONTEXT, *PCROSS_PLATFORM_CONTEXT;
-
-
-
WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj = true);
WString MethodNameFromIP(CLRDATA_ADDRESS methodDesc, BOOL bSuppressLines = FALSE, BOOL bAssemblyName = FALSE, BOOL bDisplacement = FALSE, BOOL bAdjustIPForLineNumber = FALSE);
HRESULT GetGCRefs(ULONG osID, SOSStackRefData **ppRefs, unsigned int *pRefCnt, SOSStackRefError **ppErrors, unsigned int *pErrCount);
return m_pHostServices;
}
+/// <summary>
+/// Check if a target flush is needed
+/// </summary>
+void Extensions::FlushCheck()
+{
+ if (m_pDebuggerServices != nullptr)
+ {
+ m_pDebuggerServices->FlushCheck();
+ }
+}
+
/// <summary>
/// Returns the symbol service instance
/// </summary>
size_t length = vsnprintf(str, sizeof(str), format, args);
if (length < sizeof(str))
{
- Extensions::GetInstance()->GetDebuggerServices()->OutputString(mask, str);
+ GetDebuggerServices()->OutputString(mask, str);
}
else
{
if (str_ptr != nullptr)
{
vsnprintf(str_ptr, length + 1, format, argsCopy);
- Extensions::GetInstance()->GetDebuggerServices()->OutputString(mask, str_ptr);
+ GetDebuggerServices()->OutputString(mask, str_ptr);
::free(str_ptr);
}
}
/// <summary>
/// Internal trace output for extensions library
/// </summary>
-void TraceError(PCSTR format, ...)
+void TraceHostingError(PCSTR format, ...)
{
va_list args;
va_start(args, format);
+ GetDebuggerServices()->OutputString(DEBUG_OUTPUT_ERROR, "SOS_HOSTING: ");
InternalOutputVaList(DEBUG_OUTPUT_ERROR, format, args);
va_end(args);
}
/// </summary>
ISymbolService* GetSymbolService();
+ /// <summary>
+ /// Check if a target flush is needed
+ /// </summary>
+ void FlushCheck();
+
/// <summary>
/// Create a new target with the extension services for
/// </summary>
extern HMODULE g_hInstance;
#endif
-extern void TraceError(PCSTR format, ...);
+extern void TraceHostingError(PCSTR format, ...);
bool g_hostingInitialized = false;
static HostRuntimeFlavor g_hostRuntimeFlavor = HostRuntimeFlavor::NetCore;
if (getline(&line, &lineLen, locationFile) == -1)
{
- TraceError("SOS_HOSTING: Unable to read .NET installation marker at %s\n", markerName);
+ TraceHostingError("Unable to read .NET installation marker at %s\n", markerName);
free(line);
return E_FAIL;
}
if (g_hostRuntimeDirectory == nullptr)
{
#if defined(HOST_FREEBSD)
- TraceError("SOS_HOSTING: FreeBSD not supported\n");
+ TraceHostingError("FreeBSD not supported\n");
return E_FAIL;
#else
ArrayHolder<CHAR> programFiles = new CHAR[MAX_LONGPATH];
if (GetEnvironmentVariableA("PROGRAMFILES", programFiles, MAX_LONGPATH) == 0)
{
- TraceError("SOS_HOSTING: PROGRAMFILES environment variable not found\n");
+ TraceHostingError("PROGRAMFILES environment variable not found\n");
return E_FAIL;
}
std::string windowsInstallPath(programFiles);
if (Status != S_OK)
{
- TraceError("SOS_HOSTING: Failed to find runtime directory\n");
+ TraceHostingError("Failed to find runtime directory\n");
return E_FAIL;
}
if (hostRuntimeVersion.Major == 0)
{
- TraceError("SOS_HOSTING: Failed to find a supported runtime within %s\n", hostRuntimeDirectory.c_str());
+ TraceHostingError("Failed to find a supported runtime within %s\n", hostRuntimeDirectory.c_str());
return E_FAIL;
}
Dl_info info;
if (dladdr((PVOID)&InitializeNetCoreHost, &info) == 0)
{
- TraceError("SOS_HOSTING: Failed to get SOS module directory with dladdr()\n");
+ TraceHostingError("Failed to get SOS module directory with dladdr()\n");
return E_FAIL;
}
sosModulePath = info.dli_fname;
ArrayHolder<char> szSOSModulePath = new char[MAX_LONGPATH + 1];
if (GetModuleFileNameA(g_hInstance, szSOSModulePath, MAX_LONGPATH) == 0)
{
- TraceError("SOS_HOSTING: Failed to get SOS module directory\n");
+ TraceHostingError("Failed to get SOS module directory\n");
return HRESULT_FROM_WIN32(GetLastError());
}
sosModulePath = szSOSModulePath;
void* coreclrLib = dlopen(coreClrPath.c_str(), RTLD_NOW | RTLD_LOCAL);
if (coreclrLib == nullptr)
{
- TraceError("SOS_HOSTING: Failed to load runtime module %s\n", coreClrPath.c_str());
+ TraceHostingError("Failed to load runtime module %s\n", coreClrPath.c_str());
return E_FAIL;
}
initializeCoreCLR = (coreclr_initialize_ptr)dlsym(coreclrLib, "coreclr_initialize");
HMODULE coreclrLib = LoadLibraryA(coreClrPath.c_str());
if (coreclrLib == nullptr)
{
- TraceError("SOS_HOSTING: Failed to load runtime module %s\n", coreClrPath.c_str());
+ TraceHostingError("Failed to load runtime module %s\n", coreClrPath.c_str());
return E_FAIL;
}
initializeCoreCLR = (coreclr_initialize_ptr)GetProcAddress(coreclrLib, "coreclr_initialize");
if (initializeCoreCLR == nullptr || createDelegate == nullptr)
{
- TraceError("SOS_HOSTING: coreclr_initialize or coreclr_create_delegate not found in %s\n", coreClrPath.c_str());
+ TraceHostingError("coreclr_initialize or coreclr_create_delegate not found in %s\n", coreClrPath.c_str());
return E_FAIL;
}
size_t lastSlash = sosModuleDirectory.rfind(DIRECTORY_SEPARATOR_CHAR_A);
if (lastSlash == std::string::npos)
{
- TraceError("SOS_HOSTING: Failed to parse SOS module name\n");
+ TraceHostingError("Failed to parse SOS module name\n");
return E_FAIL;
}
sosModuleDirectory.erase(lastSlash);
char* exePath = minipal_getexepath();
if (!exePath)
{
- TraceError("SOS_HOSTING: Could not get full path to current executable\n");
+ TraceHostingError("Could not get full path to current executable\n");
return E_FAIL;
}
free(exePath);
if (FAILED(hr))
{
- TraceError("SOS_HOSTING: Fail to initialize hosting runtime '%s' %08x\n", coreClrPath.c_str(), hr);
+ TraceHostingError("Fail to initialize hosting runtime '%s' %08x\n", coreClrPath.c_str(), hr);
return hr;
}
hr = createDelegate(hostHandle, domainId, ExtensionsDllName, ExtensionsClassName, ExtensionsInitializeFunctionName, (void**)&g_extensionsInitializeFunc);
if (FAILED(hr))
{
- TraceError("SOS_HOSTING: Fail to create hosting delegate %08x\n", hr);
+ TraceHostingError("Fail to create hosting delegate %08x\n", hr);
return hr;
}
}
}
if (FAILED(hr))
{
- TraceError("SOS_HOSTING: Extension host initialization FAILED %08x\n", hr);
+ TraceHostingError("Extension host initialization FAILED %08x\n", hr);
return hr;
}
return hr;
EXTERN_GUID(CLSID_CLRRuntimeHost, 0x90F1A06E, 0x7712, 0x4762, 0x86, 0xB5, 0x7A, 0x5E, 0xBA, 0x6B, 0xDB, 0x02);
extern HMODULE g_hInstance;
-extern void TraceError(PCSTR format, ...);
+extern void TraceHostingError(PCSTR format, ...);
ICLRRuntimeHost* g_clrHost = nullptr;
ArrayHolder<WCHAR> wszSOSModulePath = new WCHAR[MAX_LONGPATH + 1];
if (GetModuleFileNameW(g_hInstance, wszSOSModulePath, MAX_LONGPATH) == 0)
{
- TraceError("SOS_HOSTING: Failed to get SOS module directory\n");
+ TraceHostingError("Failed to get SOS module directory\n");
return HRESULT_FROM_WIN32(GetLastError());
}
ArrayHolder<WCHAR> wszManagedModulePath = new WCHAR[MAX_LONGPATH + 1];
if (wcscpy_s(wszManagedModulePath.GetPtr(), MAX_LONGPATH, wszSOSModulePath.GetPtr()) != 0)
{
- TraceError("SOS_HOSTING: Failed to copy module name\n");
+ TraceHostingError("Failed to copy module name\n");
return E_FAIL;
}
WCHAR* lastSlash = wcsrchr(wszManagedModulePath.GetPtr(), DIRECTORY_SEPARATOR_CHAR_W);
}
if (wcscat_s(wszManagedModulePath.GetPtr(), MAX_LONGPATH, ExtensionsDllNameW) != 0)
{
- TraceError("SOS_HOSTING: Failed to append SOS module name\n");
+ TraceHostingError("Failed to append SOS module name\n");
return E_FAIL;
}
if (g_clrHost == nullptr)
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr) && hr != RPC_E_CHANGED_MODE)
{
- TraceError("SOS_HOSTING: CoInitializeEx failed. %08x\n", hr);
+ TraceHostingError("CoInitializeEx failed. %08x\n", hr);
return hr;
}
// Loads the CLR and then initializes the managed debugger extensions.
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (PVOID*)&metaHost);
if (FAILED(hr) || metaHost == nullptr)
{
- TraceError("SOS_HOSTING: CLRCreateInstance failed %08x\n", hr);
+ TraceHostingError("CLRCreateInstance failed %08x\n", hr);
return hr;
}
ReleaseHolder<ICLRRuntimeInfo> runtimeInfo;
hr = metaHost->GetRuntime(CLR_VERSION, IID_ICLRRuntimeInfo, (PVOID*)&runtimeInfo);
if (FAILED(hr) || runtimeInfo == nullptr)
{
- TraceError("SOS_HOSTING: ICLRMetaHost::GetRuntime failed %08x\n", hr);
+ TraceHostingError("ICLRMetaHost::GetRuntime failed %08x\n", hr);
return hr;
}
hr = runtimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (PVOID*)&g_clrHost);
if (FAILED(hr) || g_clrHost == nullptr)
{
- TraceError("SOS_HOSTING: ICLRRuntimeInfo::GetInterface failed %08x\n", hr);
+ TraceHostingError("ICLRRuntimeInfo::GetInterface failed %08x\n", hr);
return hr;
}
hr = g_clrHost->Start();
if (FAILED(hr))
{
- TraceError("SOS_HOSTING: ICLRRuntimeHost::Start failed %08x\n", hr);
+ TraceHostingError("ICLRRuntimeHost::Start failed %08x\n", hr);
g_clrHost->Release();
g_clrHost = nullptr;
return hr;
hr = g_clrHost->ExecuteInDefaultAppDomain(wszManagedModulePath.GetPtr(), ExtensionsClassNameW, ExtensionsInitializeFunctionNameW, wszSOSModulePath.GetPtr(), (DWORD *)&ret);
if (FAILED(hr))
{
- TraceError("SOS_HOSTING: ICLRRuntimeHost::ExecuteInDefaultAppDomain failed %08x\n", hr);
+ TraceHostingError("ICLRRuntimeHost::ExecuteInDefaultAppDomain failed %08x\n", hr);
g_clrHost->Release();
g_clrHost = nullptr;
return hr;
}
if (ret != 0)
{
- TraceError("SOS_HOSTING: Extension initialization failed %08x\n", ret);
+ TraceHostingError("Extension initialization failed %08x\n", ret);
g_clrHost->Release();
g_clrHost = nullptr;
return ret;
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#pragma once
+
+#include <stdarg.h>
+#include <unknwn.h>
+#include <clrma.h> // IDL
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// <summary>
+/// ICLRMAService
+/// </summary>
+MIDL_INTERFACE("1FCF4C14-60C1-44E6-84ED-20506EF3DC60")
+ICLRMAService : public IUnknown
+{
+public:
+ virtual HRESULT STDMETHODCALLTYPE AssociateClient(
+ IUnknown *pUnknown) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetThread(
+ ULONG osThreadId,
+ ICLRMAClrThread **ppClrThread) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetException(
+ ULONG64 address,
+ ICLRMAClrException **ppClrException) = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE GetObjectInspection(
+ ICLRMAObjectInspection **ppObjectInspection) = 0;
+};
+
+#ifdef __cplusplus
+};
+#endif
extern "C" {
#endif
+typedef bool (*PEXECUTE_COMMAND_OUTPUT_CALLBACK)(ULONG mask, const char *text);
+
/// <summary>
/// IDebuggerServices
///
PULONG loaded,
PULONG unloaded) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetModuleByIndex(
+ ULONG index,
+ PULONG64 base) = 0;
+
virtual HRESULT STDMETHODCALLTYPE GetModuleNames(
ULONG index,
ULONG64 base,
ULONG bufferSize,
PULONG versionInfoSize) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetModuleByModuleName(
+ PCSTR name,
+ ULONG startIndex,
+ PULONG index,
+ PULONG64 base) = 0;
+
virtual HRESULT STDMETHODCALLTYPE GetNumberThreads(
PULONG number) = 0;
PSTR description,
ULONG descriptionSize,
PULONG descriptionUsed) = 0;
+
+ virtual void STDMETHODCALLTYPE FlushCheck() = 0;
+
+ virtual HRESULT STDMETHODCALLTYPE ExecuteHostCommand(
+ PCSTR commandLine,
+ PEXECUTE_COMMAND_OUTPUT_CALLBACK callback) = 0;
};
#ifdef __cplusplus
// Symbol messages, such as for !sym noisy.
#define DEBUG_OUTPUT_SYMBOLS 0x00000200
+// Output control flags.
+// Output generated by methods called by this
+// client will be sent only to this clients
+// output callbacks.
+#define DEBUG_OUTCTL_THIS_CLIENT 0x00000000
+// Output will be sent to all clients.
+#define DEBUG_OUTCTL_ALL_CLIENTS 0x00000001
+// Output will be sent to all clients except
+// the client generating the output.
+#define DEBUG_OUTCTL_ALL_OTHER_CLIENTS 0x00000002
+// Output will be discarded immediately and will not
+// be logged or sent to callbacks.
+#define DEBUG_OUTCTL_IGNORE 0x00000003
+// Output will be logged but not sent to callbacks.
+#define DEBUG_OUTCTL_LOG_ONLY 0x00000004
+// All send control bits.
+#define DEBUG_OUTCTL_SEND_MASK 0x00000007
+// Do not place output from this client in
+// the global log file.
+#define DEBUG_OUTCTL_NOT_LOGGED 0x00000008
+// Send output to clients regardless of whether the
+// mask allows it or not.
+#define DEBUG_OUTCTL_OVERRIDE_MASK 0x00000010
+// Text is markup instead of plain text.
#define DEBUG_OUTCTL_DML 0x00000020
+
+// Special values which mean leave the output settings
+// unchanged.
#define DEBUG_OUTCTL_AMBIENT_DML 0xfffffffe
+#define DEBUG_OUTCTL_AMBIENT_TEXT 0xffffffff
// Execute and ExecuteCommandFile flags.
// These flags only apply to the command
interface ILLDBServices;
typedef HRESULT (*PFN_EXCEPTION_CALLBACK)(ILLDBServices *services);
typedef HRESULT (*PFN_RUNTIME_LOADED_CALLBACK)(ILLDBServices *services);
+typedef void (*PFN_MODULE_LOAD_CALLBACK)(void* param, const char* moduleFilePath, ULONG64 moduleAddress, int moduleSize);
//----------------------------------------------------------------------------
// ILLDBServices
OutputString(mask, str);
}
+void
+LLDBServices::FlushCheck()
+{
+ // The infrastructure expects a target to only be created if there is a valid process.
+ lldb::SBProcess process = GetCurrentProcess();
+ if (process.IsValid())
+ {
+ InitializeThreadInfo(process);
+
+ // Has the process changed since the last commmand?
+ Extensions::GetInstance()->UpdateTarget(GetProcessId(process));
+
+ // Has the target "moved" (been continued) since the last command? Flush the target.
+ uint32_t stopId = process.GetStopID();
+ if (stopId != m_currentStopId)
+ {
+ m_currentStopId = stopId;
+ Extensions::GetInstance()->FlushTarget();
+ }
+ }
+ else
+ {
+ Extensions::GetInstance()->DestroyTarget();
+ m_threadInfoInitialized = false;
+ m_processId = 0;
+ }
+}
+
+HRESULT
+LLDBServices::ExecuteHostCommand(
+ PCSTR commandLine,
+ PEXECUTE_COMMAND_OUTPUT_CALLBACK callback)
+{
+ return Execute(DEBUG_OUTCTL_THIS_CLIENT, commandLine, DEBUG_EXECUTE_NO_REPEAT);
+}
+
//----------------------------------------------------------------------------
// Helper functions
//----------------------------------------------------------------------------
return true;
}
-void
-LLDBServices::FlushCheck()
-{
- // The infrastructure expects a target to only be created if there is a valid process.
- lldb::SBProcess process = GetCurrentProcess();
- if (process.IsValid())
- {
- InitializeThreadInfo(process);
-
- // Has the process changed since the last commmand?
- Extensions::GetInstance()->UpdateTarget(GetProcessId(process));
-
- // Has the target "moved" (been continued) since the last command? Flush the target.
- uint32_t stopId = process.GetStopID();
- if (stopId != m_currentStopId)
- {
- m_currentStopId = stopId;
- Extensions::GetInstance()->FlushTarget();
- }
- }
- else
- {
- Extensions::GetInstance()->DestroyTarget();
- m_threadInfoInitialized = false;
- m_processId = 0;
- }
-}
-
lldb::SBCommand
LLDBServices::AddCommand(
const char* name,
ULONG mask,
PCSTR str);
+ void STDMETHODCALLTYPE FlushCheck();
+
+ HRESULT STDMETHODCALLTYPE ExecuteHostCommand(
+ PCSTR commandLine,
+ PEXECUTE_COMMAND_OUTPUT_CALLBACK callback);
+
//----------------------------------------------------------------------------
// LLDBServices (internal)
//----------------------------------------------------------------------------
PCSTR GetPluginModuleDirectory();
- void FlushCheck();
-
lldb::SBCommand AddCommand(const char *name, lldb::SBCommandPluginInterface *impl, const char *help);
void AddManagedCommand(const char* name, const char* help);
HRESULT Request(ISOSDacInterface *sos, CLRDATA_ADDRESS addr)
{
- return Request(sos, addr, (TADDR)0);
+ return Request(sos, addr, 0);
}
};
{
return sos->GetMethodDescData(
addr,
- (TADDR)0, // IP address
+ 0, // IP address
this,
0, // cRejitData
NULL, // rejitData[]
{
public:
ReleaseHolder()
- : m_ptr(NULL)
+ : m_ptr(nullptr)
{}
ReleaseHolder(T* ptr)
T* Detach()
{
T* pT = m_ptr;
- m_ptr = NULL;
+ m_ptr = nullptr;
return pT;
}
void Release()
{
- if (m_ptr != NULL)
+ if (m_ptr != nullptr)
{
m_ptr->Release();
- m_ptr = NULL;
+ m_ptr = nullptr;
}
}
--- /dev/null
+// 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.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using Microsoft.Diagnostics.TestHelpers;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Extensions;
+
+// Newer SDKs flag MemberData(nameof(Configurations)) with this error
+// Avoid unnecessary zero-length array allocations. Use Array.Empty<object>() instead.
+#pragma warning disable CA1825
+
+namespace Microsoft.Diagnostics.DebugServices.UnitTests
+{
+ public class ClrmaTests : IDisposable
+ {
+ private const string ListenerName = "ClrmaTests";
+
+ private static IEnumerable<object[]> _configurations;
+
+ public static IEnumerable<object[]> GetConfigurations()
+ {
+ return _configurations ??= TestRunConfiguration.Instance.Configurations
+ .Where((config) => config.IsTestDbgEng() && config.AllSettings.ContainsKey("DumpFile"))
+ .Select((config) => new TestDbgEng(config))
+ .Select((host) => new[] { host })
+ .ToImmutableArray();
+ }
+
+ private ITestOutputHelper Output { get; set; }
+
+ public ClrmaTests(ITestOutputHelper output)
+ {
+ Output = output;
+ }
+
+ void IDisposable.Dispose()
+ {
+ }
+
+ private static readonly (string, int)[] s_clrmaFilterList = [
+ ( "Command: ", 1 ),
+ //( "OSThreadId: ", 1 ),
+ ( "Integrated managed debugging does not support enumeration of symbols.", 1 ),
+ ( "See https://aka.ms/ManagedDebuggingWithSos for more details.", 1 ),
+ ( "Unable to load image ", 1 ),
+ ];
+
+ [SkippableTheory, MemberData(nameof(GetConfigurations))]
+ public void BangClrmaTests(TestHost host)
+ {
+ ITarget target = host.Target;
+ Assert.NotNull(target);
+
+ if (target.Host.HostType != HostType.DbgEng)
+ {
+ throw new SkipTestException("Test only supported on dbgeng");
+ }
+ IDiagnosticLoggingService logging = target.Services.GetService<IDiagnosticLoggingService>();
+ bool enabled = logging.IsEnabled;
+ string filePath = logging.FilePath;
+ logging.Disable();
+ try
+ {
+ host.ExecuteHostCommand("!sos clrmaconfig -disable -logging -dac");
+ IEnumerable<string> builtIn = Filter(host.ExecuteHostCommand("!clrma"), s_clrmaFilterList);
+
+ // Now try the direct DAC CLRMA provider in SOS
+ host.ExecuteHostCommand("!sos clrmaconfig -enable -dac");
+ IEnumerable<string> directDac = Filter(host.ExecuteHostCommand("!clrma"), s_clrmaFilterList);
+
+ IEnumerable<string> diff1 = builtIn.Except(directDac);
+ IEnumerable<string> diff2 = directDac.Except(builtIn);
+
+ if (diff1.Any() || diff2.Any())
+ {
+ string builtInFile = Path.GetTempFileName();
+ string directDacFile = Path.GetTempFileName();
+ File.WriteAllLines(builtInFile, builtIn);
+ File.WriteAllLines(directDacFile, directDac);
+
+ Output.WriteLine(string.Empty);
+ Output.WriteLine("------------------------------------");
+ Output.WriteLine($"Built-In Provider: {builtInFile}");
+ Output.WriteLine("------------------------------------");
+ foreach (string line in diff1)
+ {
+ Output.WriteLine(line);
+ }
+ Output.WriteLine(string.Empty);
+ Output.WriteLine("------------------------------------");
+ Output.WriteLine($"Direct Dac: {directDacFile}");
+ Output.WriteLine("------------------------------------");
+ foreach (string line in diff2)
+ {
+ Output.WriteLine(line);
+ }
+ Assert.Fail();
+ }
+ }
+ finally
+ {
+ if (enabled)
+ {
+ logging.Enable(filePath);
+ }
+ }
+ }
+
+ // The !analyze lines containing this text and the number of lines to skip
+ private static readonly (string, int)[] s_analyzeFilterList = [
+ ( "Key : Analysis.CPU.mSec", 2 ),
+ ( "Key : Analysis.Elapsed.mSec", 2 ),
+ ( "Key : Analysis.IO.Other.Mb", 2 ),
+ ( "Key : Analysis.IO.Read.Mb", 2 ),
+ ( "Key : Analysis.IO.Write.Mb", 2 ),
+ ( "Key : Analysis.Init.CPU.mSec", 2 ),
+ ( "Key : Analysis.Init.Elapsed.mSec", 2 ),
+ ( "Key : Analysis.Memory.CommitPeak.Mb", 2 ),
+ ( "Timeline: !analyze.Start", 4 ),
+ ( "ANALYSIS_SESSION_TIME:", 1 ),
+ ( "MANAGED_ANALYSIS_PROVIDER:", 1 ),
+ ( "MANAGED_THREAD_ID:", 1 ),
+ ( "Stack_Frames_Extraction_Time_(ms):", 1 ),
+ ( "Stack_Attribute_Extraction_Time_(ms):", 1 ),
+ ( "ANALYSIS_SESSION_ELAPSED_TIME:", 1 ),
+ ( "STACK_COMMAND:", 1 ),
+ ( "ManagedExceptionCommand:", 1 ),
+ ( "DebuggerCommand:", 1 ),
+ ( "Integrated managed debugging does not support enumeration of symbols.", 1 ),
+ ( "See https://aka.ms/ManagedDebuggingWithSos for more details.", 1 ),
+ ( "Unable to load image ", 1 ),
+ ];
+
+ [SkippableTheory, MemberData(nameof(GetConfigurations))]
+ public void BangAnalyzeTests(TestHost host)
+ {
+ ITarget target = host.Target;
+ Assert.NotNull(target);
+
+ if (target.Host.HostType != HostType.DbgEng)
+ {
+ throw new SkipTestException("Test only supported on dbgeng");
+ }
+ IDiagnosticLoggingService logging = target.Services.GetService<IDiagnosticLoggingService>();
+ bool enabled = logging.IsEnabled;
+ string filePath = logging.FilePath;
+ logging.Disable();
+ try
+ {
+ // Flush/reload to workaround k command issue
+ host.ExecuteHostCommand(".reload");
+
+ host.ExecuteHostCommand("!sos clrmaconfig -disable -logging -dac");
+ IEnumerable<string> builtIn = Filter(host.ExecuteHostCommand("!analyze -v6"), s_analyzeFilterList);
+
+ // Flush/reload to workaround k command issue
+ host.ExecuteHostCommand(".reload");
+
+ // Now try the direct DAC CLRMA provider in SOS
+ host.ExecuteHostCommand("!sos clrmaconfig -enable -dac");
+ IEnumerable<string> directDac = Filter(host.ExecuteHostCommand("!analyze -v6"), s_analyzeFilterList);
+
+ IEnumerable<string> diff1 = builtIn.Except(directDac);
+ IEnumerable<string> diff2 = directDac.Except(builtIn);
+
+ if (diff1.Any() || diff2.Any())
+ {
+ string builtInFile = Path.GetTempFileName();
+ string directDacFile = Path.GetTempFileName();
+ File.WriteAllLines(builtInFile, builtIn);
+ File.WriteAllLines(directDacFile, directDac);
+
+ Output.WriteLine(string.Empty);
+ Output.WriteLine("------------------------------------");
+ Output.WriteLine($"Built-In Provider: {builtInFile}");
+ Output.WriteLine("------------------------------------");
+ foreach (string line in diff1)
+ {
+ Output.WriteLine(line);
+ }
+ Output.WriteLine(string.Empty);
+ Output.WriteLine("------------------------------------");
+ Output.WriteLine($"Direct Dac: {directDacFile}");
+ Output.WriteLine("------------------------------------");
+ foreach (string line in diff2)
+ {
+ Output.WriteLine(line);
+ }
+ Assert.Fail();
+ }
+ }
+ finally
+ {
+ if (enabled)
+ {
+ logging.Enable(filePath);
+ }
+ }
+ }
+
+ private static IEnumerable<string> Filter(IReadOnlyList<string> lines, (string, int)[] filterList)
+ {
+ for (int i = 0; i < lines.Count;)
+ {
+ string line = lines[i];
+ if (!string.IsNullOrWhiteSpace(line))
+ {
+ bool skip = false;
+ foreach ((string key, int skip) skipItem in filterList)
+ {
+ if (line.Contains(skipItem.key))
+ {
+ i += skipItem.skip;
+ skip = true;
+ break;
+ }
+ }
+ if (skip)
+ {
+ continue;
+ }
+ yield return line;
+ }
+ i++;
+ }
+ }
+ }
+}
CaptureConsoleService consoleService = new();
testDump.ServiceContainer.AddService<IConsoleService>(consoleService);
- CommandService commandService = new();
- testDump.ServiceContainer.AddService<ICommandService>(commandService);
-
// Add all the test commands
- commandService.AddCommands(typeof(TestCommand1).Assembly);
+ testDump.CommandService.AddCommands(typeof(TestCommand1).Assembly);
// See if the test commands exists
- Assert.Contains(commandService.Commands, ((string name, string help, IEnumerable<string> aliases) cmd) => cmd.name == "testcommand");
+ Assert.Contains(testDump.CommandService.Commands, ((string name, string help, IEnumerable<string> aliases) cmd) => cmd.name == "testcommand");
// Invoke only TestCommand1
TestCommand1.FilterValue = true;
TestCommand2.Invoked = false;
TestCommand3.FilterValue = false;
TestCommand3.Invoked = false;
- commandService.Execute("testcommand", testDump.Target.Services);
+ testDump.CommandService.Execute("testcommand", testDump.Target.Services);
Assert.True(TestCommand1.Invoked);
Assert.False(TestCommand2.Invoked);
Assert.False(TestCommand3.Invoked);
// Check for TestCommand1 help
- string help1 = commandService.GetDetailedHelp("testcommand", testDump.Target.Services, consoleWidth: int.MaxValue);
+ string help1 = testDump.CommandService.GetDetailedHelp("testcommand", testDump.Target.Services, consoleWidth: int.MaxValue);
Assert.NotNull(help1);
Output.WriteLine(help1);
Assert.Contains("Test command #1", help1);
TestCommand2.Invoked = false;
TestCommand3.FilterValue = false;
TestCommand3.Invoked = false;
- commandService.Execute("testcommand", testDump.Target.Services);
+ testDump.CommandService.Execute("testcommand", testDump.Target.Services);
Assert.False(TestCommand1.Invoked);
Assert.True(TestCommand2.Invoked);
Assert.False(TestCommand3.Invoked);
TestCommand2.Invoked = false;
TestCommand3.FilterValue = true;
TestCommand3.Invoked = false;
- commandService.Execute("testcommand", "--foo 23", testDump.Target.Services);
+ testDump.CommandService.Execute("testcommand", "--foo 23", testDump.Target.Services);
Assert.False(TestCommand1.Invoked);
Assert.False(TestCommand2.Invoked);
Assert.True(TestCommand3.Invoked);
// Check for TestCommand3 help
- string help3 = commandService.GetDetailedHelp("testcommand", testDump.Target.Services, consoleWidth: int.MaxValue);
+ string help3 = testDump.CommandService.GetDetailedHelp("testcommand", testDump.Target.Services, consoleWidth: int.MaxValue);
Assert.NotNull(help3);
Output.WriteLine(help3);
Assert.Contains("Test command #3", help3);
TestCommand3.Invoked = false;
try
{
- commandService.Execute("testcommand", testDump.Target.Services);
+ testDump.CommandService.Execute("testcommand", testDump.Target.Services);
}
catch (DiagnosticsException ex)
{
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Runtime.InteropServices;
+using Microsoft.Diagnostics.DebugServices.Implementation;
using Microsoft.Diagnostics.TestHelpers;
+using SOS.Extensions;
using Xunit.Abstractions;
+using Xunit.Extensions;
namespace Microsoft.Diagnostics.DebugServices.UnitTests
{
[Command(Name = "runtests", Help = "Runs the debug services xunit tests.")]
public class RunTestsCommand : CommandBase, ITestOutputHelper
{
- [ServiceImport]
- public ITarget Target { get; set; }
+ [Argument(Help = "Test name: debugservices, clrma or analyze.")]
+ public string[] TestNames { get; set; } = Array.Empty<string>();
- [Argument(Help = "Test data xml file path.")]
- public string TestDataPath { get; set; }
+ [Option(Name = "--testdata", Help = "Test data xml file path.")]
+ public string TestDataFile { get; set; }
+
+ [Option(Name = "--dumpfile", Help = "Dump file path.")]
+ public string DumpFile { get; set; }
public override void Invoke()
{
- IEnumerable<TestHost> configurations;
- if (TestDataPath != null)
+ ITarget target = Services.GetService<ITarget>();
+ string os;
+ if (target.OperatingSystem == OSPlatform.Linux)
{
- Dictionary<string, string> initialConfig = new()
- {
- ["OS"] = OS.Kind.ToString().ToLowerInvariant(),
- ["TargetArchitecture"] = OS.TargetArchitecture.ToString().ToLowerInvariant(),
- ["TestDataFile"] = TestDataPath,
- };
- configurations = new[] { new TestDebugger(new TestConfiguration(initialConfig), Target) };
+ os = "linux";
+ }
+ else if (target.OperatingSystem == OSPlatform.OSX)
+ {
+ os = "osx";
+ }
+ else if (target.OperatingSystem == OSPlatform.Windows)
+ {
+ os = "windows";
}
else
{
- TestConfiguration.BaseDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
- configurations = TestRunConfiguration.Instance.Configurations.Select((config) => new TestDebugger(config, Target));
+ os = "unknown";
}
- using DebugServicesTests debugServicesTests = new(this);
+ Dictionary<string, string> initialConfig = new()
+ {
+ ["OS"] = os,
+ ["TargetArchitecture"] = target.Architecture.ToString().ToLowerInvariant(),
+ ["TestDataFile"] = TestDataFile,
+ ["DumpFile"] = DumpFile
+ };
+ IEnumerable<TestHost> configurations = new[] { new TestDebugger(new TestConfiguration(initialConfig), target, Services) };
+ bool passed = true;
foreach (TestHost host in configurations)
{
- if (!host.Config.IsTestDbgEng())
+ foreach (string testName in TestNames)
{
try
{
- debugServicesTests.TargetTests(host);
- debugServicesTests.ModuleTests(host);
- debugServicesTests.ThreadTests(host);
- debugServicesTests.RuntimeTests(host);
+ switch (testName.ToLower())
+ {
+ case "debugservices":
+ {
+ if (host.TestDataFile == null)
+ {
+ throw new DiagnosticsException("TestDataFile option (--testdata) required");
+ }
+ if (host.DumpFile == null)
+ {
+ throw new DiagnosticsException("DumpFile option (--dumpfile) required");
+ }
+ using DebugServicesTests debugServicesTests = new(this);
+ debugServicesTests.TargetTests(host);
+ debugServicesTests.ModuleTests(host);
+ debugServicesTests.ThreadTests(host);
+ debugServicesTests.RuntimeTests(host);
+ break;
+ }
+
+ case "clrma":
+ {
+ using ClrmaTests clrmaTests = new(this);
+ clrmaTests.BangClrmaTests(host);
+ break;
+ }
+
+ case "analyze":
+ {
+ using ClrmaTests clrmaTests = new(this);
+ clrmaTests.BangAnalyzeTests(host);
+ break;
+ }
+
+ default:
+ throw new DiagnosticsException($"Invalid test name");
+ }
}
catch (Exception ex)
{
- Trace.TraceError("Tests FAILED:");
- Trace.TraceError(ex.ToString());
- return;
+ if (ex is SkipTestException)
+ {
+ WriteLineWarning($"Test {testName} SKIPPED - {ex.Message}");
+ }
+ else
+ {
+ WriteLineError($"Test {testName} FAILED - {ex.Message}");
+ passed = false;
+ }
+ continue;
}
+ WriteLine($"Test {testName} PASSED");
}
}
- WriteLine("Tests PASSED");
+ if (passed)
+ {
+ WriteLine($"All Tests PASSED");
+ }
}
#region ITestOutputHelper
internal class TestDebugger : TestHost
{
private readonly ITarget _target;
+ private readonly IServiceProvider _services;
+ private readonly CommandService _commandService;
- internal TestDebugger(TestConfiguration config, ITarget target)
+ internal TestDebugger(TestConfiguration config, ITarget target, IServiceProvider services)
: base(config)
{
_target = target;
+ _services = services;
+ // dotnet-dump adds the CommandService implementation class as a service
+ _commandService = services.GetService<CommandService>();
+ }
+
+ public override IReadOnlyList<string> ExecuteHostCommand(string commandLine)
+ {
+ if (HostServices.Instance != null)
+ {
+ return HostServices.Instance.ExecuteHostCommand(commandLine);
+ }
+ else
+ {
+ throw new NotSupportedException("ExecuteHostCommand");
+ }
}
protected override ITarget GetTarget() => _target;
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
+using Microsoft.Diagnostics.DebugServices.Implementation;
using Microsoft.Diagnostics.Runtime;
using Microsoft.Diagnostics.Runtime.Utilities;
using Microsoft.Diagnostics.TestHelpers;
{
}
+ public override IReadOnlyList<string> ExecuteHostCommand(string commandLine) => HostServices.Instance.ExecuteHostCommand(commandLine);
+
protected override ITarget GetTarget()
{
// Create/initialize dbgeng controller