Implement CLRMA interfaces on top of DAC APIs (#4667)
authorMike McLaughlin <mikem@microsoft.com>
Tue, 2 Jul 2024 22:17:54 +0000 (15:17 -0700)
committerGitHub <noreply@github.com>
Tue, 2 Jul 2024 22:17:54 +0000 (15:17 -0700)
Add "clrmaconfig" command to control clrma logging, direct DAC and
managed support.

Move SOSExtensions to separate cpp/h files.

Cleanup Extensions::FlushCheck. Needed to call in the clrma code.

INIT_API_EXT macro cleanup: added ExtInit function that does all command
entry init.

Change the no managed hosting fallback code (platform/*) to use the
IDebuggerServices instance instead of the global dbgeng variables
because it is always available/setup. This allows the CLRMA support to
work with no managed code.

60 files changed:
diagnostics.sln
documentation/clrma.md [new file with mode: 0644]
src/Microsoft.Diagnostics.DebugServices.Implementation/CaptureConsoleService.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.DebugServices.Implementation/CharToLineConverter.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.DebugServices.Implementation/CommandService.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/Utilities.cs
src/Microsoft.Diagnostics.TestHelpers/CharToLineConverter.cs [deleted file]
src/Microsoft.Diagnostics.TestHelpers/LoggingListener.cs
src/Microsoft.Diagnostics.TestHelpers/TestHost/TestDump.cs
src/Microsoft.Diagnostics.TestHelpers/TestHost/TestHost.cs
src/SOS/SOS.Extensions/Clrma/ClrmaServiceWrapper.cs [new file with mode: 0644]
src/SOS/SOS.Extensions/Clrma/ExceptionWrapper.cs
src/SOS/SOS.Extensions/Clrma/ManagedAnalysisWrapper.cs [deleted file]
src/SOS/SOS.Extensions/Clrma/ThreadWrapper.cs
src/SOS/SOS.Extensions/DbgEngOutputHolder.cs [deleted file]
src/SOS/SOS.Extensions/DebuggerServices.cs
src/SOS/SOS.Extensions/HostServices.cs
src/SOS/SOS.Extensions/MemoryRegionServiceFromDebuggerServices.cs
src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs
src/SOS/SOS.Hosting/DataTargetWrapper.cs
src/SOS/SOS.Hosting/DbgEng/Interop/Enums/ImageFileMachine.cs
src/SOS/SOS.Hosting/SOSHost.cs
src/SOS/Strike/CMakeLists.txt
src/SOS/Strike/Strike.vcxproj
src/SOS/Strike/Strike.vcxproj.filters
src/SOS/Strike/clrma.cpp [deleted file]
src/SOS/Strike/clrma/clrma.cpp [new file with mode: 0644]
src/SOS/Strike/clrma/exception.cpp [new file with mode: 0644]
src/SOS/Strike/clrma/exception.h [new file with mode: 0644]
src/SOS/Strike/clrma/managedanalysis.cpp [new file with mode: 0644]
src/SOS/Strike/clrma/managedanalysis.h [new file with mode: 0644]
src/SOS/Strike/clrma/thread.cpp [new file with mode: 0644]
src/SOS/Strike/clrma/thread.h [new file with mode: 0644]
src/SOS/Strike/crosscontext.h [new file with mode: 0644]
src/SOS/Strike/dbgengservices.cpp
src/SOS/Strike/dbgengservices.h
src/SOS/Strike/exts.cpp
src/SOS/Strike/exts.h
src/SOS/Strike/sos.def
src/SOS/Strike/sosextensions.cpp [new file with mode: 0644]
src/SOS/Strike/sosextensions.h [new file with mode: 0644]
src/SOS/Strike/strike.cpp
src/SOS/Strike/util.cpp
src/SOS/Strike/util.h
src/SOS/extensions/extensions.cpp
src/SOS/extensions/extensions.h
src/SOS/extensions/hostcoreclr.cpp
src/SOS/extensions/hostdesktop.cpp
src/SOS/inc/clrmaservice.h [new file with mode: 0644]
src/SOS/inc/debuggerservices.h
src/SOS/inc/lldbservices.h
src/SOS/lldbplugin/services.cpp
src/SOS/lldbplugin/services.h
src/shared/inc/dacprivate.h
src/shared/inc/releaseholder.h
src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/ClrmaTests.cs [new file with mode: 0644]
src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/CommandServiceTests.cs
src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/RunTests.cs
src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/TestDbgEng.cs

index b9a9784095c81e17ec00fd78b0204afb39c934fc..29398788b8b9d6241fa2e4654c4598ba00b6b17d 100644 (file)
@@ -221,10 +221,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "inc", "inc", "{BE45F03E-D70
        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
diff --git a/documentation/clrma.md b/documentation/clrma.md
new file mode 100644 (file)
index 0000000..11fefb5
--- /dev/null
@@ -0,0 +1,75 @@
+## 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. 
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/CaptureConsoleService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/CaptureConsoleService.cs
new file mode 100644 (file)
index 0000000..7fbedeb
--- /dev/null
@@ -0,0 +1,43 @@
+// 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
+    }
+}
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/CharToLineConverter.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/CharToLineConverter.cs
new file mode 100644 (file)
index 0000000..ca6e2b0
--- /dev/null
@@ -0,0 +1,64 @@
+// 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();
+        }
+    }
+}
index 07c7ef52a5d84e4e72ddb14d8aaa60867dcd7ef3..69887c97b7580844e09a935780209be1ce8d9eec 100644 (file)
@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.CommandLine;
 using System.CommandLine.Builder;
 using System.CommandLine.Help;
@@ -36,6 +37,25 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
             _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>
index 351bd02df7d38f8f6ef33b7c92323449c291d40c..d3fffa53e0ed0a77044bcd4f3f62a3c865bce947 100644 (file)
@@ -79,7 +79,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         public string RuntimeModuleDirectory { get; set; }
 
-
         public Version RuntimeVersion
         {
             get
index fd231f2270909905a870f968ecea95d44ff92523..0fb9984ed298a759d23e39e0396723625099ebc2 100644 (file)
@@ -9,8 +9,6 @@ using System.Linq;
 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;
@@ -414,46 +412,4 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
             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
-    }
 }
diff --git a/src/Microsoft.Diagnostics.TestHelpers/CharToLineConverter.cs b/src/Microsoft.Diagnostics.TestHelpers/CharToLineConverter.cs
deleted file mode 100644 (file)
index 9fceacf..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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();
-        }
-    }
-}
index ae48731d0212b7460f2ef8899b9a71a05440e3a3..991325b8629a134e5065095c1b1372e9c43c6c2f 100644 (file)
@@ -3,6 +3,7 @@
 
 using System;
 using System.Diagnostics;
+using Microsoft.Diagnostics.DebugServices.Implementation;
 using Xunit.Abstractions;
 
 namespace Microsoft.Diagnostics.TestHelpers
index 738320ee87976722cb1b9433274017e6ab94bf7f..0829bbc4e6dddd89aaa8fb03a63a306fad4de12b 100644 (file)
@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.IO;
 using System.Runtime.InteropServices;
 using Microsoft.Diagnostics.DebugServices;
@@ -15,6 +16,8 @@ namespace Microsoft.Diagnostics.TestHelpers
     {
         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;
@@ -34,10 +37,13 @@ namespace Microsoft.Diagnostics.TestHelpers
             _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
@@ -56,6 +62,10 @@ namespace Microsoft.Diagnostics.TestHelpers
 
         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);
index 93eea0eec2c1dbe0a2a3d1c509373ca881506cd2..45a50ea7abad93127bc3735d7212a1e63cd837f7 100644 (file)
@@ -2,6 +2,8 @@
 // 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
@@ -28,7 +30,7 @@ namespace Microsoft.Diagnostics.TestHelpers
         {
             get
             {
-                _testData ??= new TestDataReader(TestDataFile);
+                _testData ??= TestDataFile != null ? new TestDataReader(TestDataFile) : null;
                 return _testData;
             }
         }
@@ -42,19 +44,19 @@ namespace Microsoft.Diagnostics.TestHelpers
             }
         }
 
-        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";
     }
 }
diff --git a/src/SOS/SOS.Extensions/Clrma/ClrmaServiceWrapper.cs b/src/SOS/SOS.Extensions/Clrma/ClrmaServiceWrapper.cs
new file mode 100644 (file)
index 0000000..dc47872
--- /dev/null
@@ -0,0 +1,137 @@
+// 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
+    }
+}
index 7380306a69654dc1bc5b5cea728e2f398c62fd8b..c3b4eb4bd79e6150b1200211841aa11d7e18b5a5 100644 (file)
@@ -114,7 +114,7 @@ namespace SOS.Extensions.Clrma
             }
             catch (ArgumentOutOfRangeException)
             {
-                return ManagedAnalysisWrapper.E_BOUNDS;
+                return ClrmaServiceWrapper.E_BOUNDS;
             }
             ip = frame.InstructionPointer;
             sp = frame.StackPointer;
@@ -140,7 +140,7 @@ namespace SOS.Extensions.Clrma
             clrmaClrException = IntPtr.Zero;
             if (index >= InnerExceptions.Length)
             {
-                return ManagedAnalysisWrapper.E_BOUNDS;
+                return ClrmaServiceWrapper.E_BOUNDS;
             }
             ExceptionWrapper exception = InnerExceptions[index];
             exception.AddRef();
diff --git a/src/SOS/SOS.Extensions/Clrma/ManagedAnalysisWrapper.cs b/src/SOS/SOS.Extensions/Clrma/ManagedAnalysisWrapper.cs
deleted file mode 100644 (file)
index 9e4fb30..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-// 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
-    }
-}
index 5c797ea2018604c21bd1f9a235e9e3c329f253c2..cbfd2b1ed8beb087e83b033e8e8175db6e791c2a 100644 (file)
@@ -133,7 +133,7 @@ namespace SOS.Extensions.Clrma
             clrmaClrException = IntPtr.Zero;
             if (index >= NestedExceptions.Length)
             {
-                return ManagedAnalysisWrapper.E_BOUNDS;
+                return ClrmaServiceWrapper.E_BOUNDS;
             }
             ExceptionWrapper exception = NestedExceptions[index];
             exception.AddRef();
diff --git a/src/SOS/SOS.Extensions/DbgEngOutputHolder.cs b/src/SOS/SOS.Extensions/DbgEngOutputHolder.cs
deleted file mode 100644 (file)
index 2963a76..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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;
-        }
-    }
-}
index 9f4fe231f15cb1b1cce9801deacfe247389f7e24..f1f4caf56369553191e5044eef4f9d9d0a8b2853 100644 (file)
@@ -3,12 +3,14 @@
 
 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;
@@ -478,6 +480,40 @@ namespace SOS.Extensions
             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
         {
@@ -489,9 +525,11 @@ namespace SOS.Extensions
             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;
@@ -510,6 +548,8 @@ namespace SOS.Extensions
             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;
         }
     }
 }
index 65b57c1fc08907cb63c5d1caa693f8741e7f5268..ebba6687c2613d8e793e720730ac3c30df2d76f3 100644 (file)
@@ -3,10 +3,12 @@
 
 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;
@@ -145,6 +147,10 @@ namespace SOS.Extensions
             _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();
index 403bcbc8791ca23e53feaf23b8c88e3448130cac..87fdce32aaf8fce99fd59363d9ed0eeb06ced5fa 100644 (file)
@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Linq;
 using System.Text;
 using Microsoft.Diagnostics.DebugServices;
@@ -12,13 +13,12 @@ namespace SOS.Extensions
 {
     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()
@@ -26,13 +26,8 @@ namespace SOS.Extensions
             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)
                 {
@@ -134,7 +129,6 @@ namespace SOS.Extensions
                     if (type == MemoryRegionType.MEM_IMAGE && index < parts.Length)
                     {
                         image = parts[index].Substring(1, parts[index].Length - 2);
-                        index++;
                     }
 
                     if (description.Equals("<unknown>", StringComparison.OrdinalIgnoreCase))
@@ -195,7 +189,7 @@ namespace SOS.Extensions
 
             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.");
             }
         }
 
@@ -211,17 +205,6 @@ namespace SOS.Extensions
             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; }
index fd4a8648c42f374bf65cc0201567ec8dc7356d7f..da99d05a559be6a34d715d53d3a315edabf52788 100644 (file)
@@ -52,7 +52,7 @@ namespace SOS.Extensions
                     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 */,
@@ -101,15 +101,15 @@ namespace SOS.Extensions
             _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)
index f61cbd4310404ab7d5a58db4f669b423514d8699..d3b86016bfb9a02bc1b5ba207924b8d2b94ac670 100644 (file)
@@ -110,7 +110,7 @@ namespace SOS.Hosting
             {
                 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,
index cb9ed62a245b01a52ed5b36ef209996fc3aa9767..da1a4881e2065f33a30ae2b7bb0191ab9e7f592e 100644 (file)
@@ -6,36 +6,37 @@ namespace SOS.Hosting.DbgEng.Interop
     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
     }
 }
index 5d23b98c3f6e5b61f1bff09e74758adca50c4010..345aa86a1581164544a714fd2f8f294bbc78bcba 100644 (file)
@@ -174,7 +174,7 @@ namespace SOS.Hosting
                     *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;
index 19551ab0a8d3a5db3f04c5835df4bb8f7a1c2bad..bb68c99ba9dc809a2e97e8742f484122c29b29e9 100644 (file)
@@ -82,7 +82,6 @@ if (CLR_CMAKE_HOST_WIN32)
   add_definitions(-DUSE_STL)
 
   set(SOS_SOURCES
-    clrma.cpp
     disasm.cpp
     dllsext.cpp
     eeheap.cpp
@@ -97,12 +96,17 @@ if (CLR_CMAKE_HOST_WIN32)
     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
@@ -162,6 +166,7 @@ else(CLR_CMAKE_HOST_WIN32)
     stressLogDump.cpp
     strike.cpp
     sos.cpp
+    sosextensions.cpp
     util.cpp
     platform/datatarget.cpp
     platform/hostimpl.cpp
index 609d90ec029c4d17a557ad547e47e7fd50b70b79..cd1f2ea38284229d28cab3e0718b6c613959e4db 100644 (file)
@@ -84,7 +84,7 @@
   </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" />
index 53a91f82dda68622498502658e3adeaa58acf2b6..b8c2df5854dd5c97e03745dcbd1ccca5fc5fbad7 100644 (file)
       <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" />
diff --git a/src/SOS/Strike/clrma.cpp b/src/SOS/Strike/clrma.cpp
deleted file mode 100644 (file)
index 1077d77..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#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;
-}
diff --git a/src/SOS/Strike/clrma/clrma.cpp b/src/SOS/Strike/clrma/clrma.cpp
new file mode 100644 (file)
index 0000000..c62aae0
--- /dev/null
@@ -0,0 +1,143 @@
+// 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);
+    }
+}
diff --git a/src/SOS/Strike/clrma/exception.cpp b/src/SOS/Strike/clrma/exception.cpp
new file mode 100644 (file)
index 0000000..1579dbf
--- /dev/null
@@ -0,0 +1,552 @@
+// 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;
+}
diff --git a/src/SOS/Strike/clrma/exception.h b/src/SOS/Strike/clrma/exception.h
new file mode 100644 (file)
index 0000000..04e2f7e
--- /dev/null
@@ -0,0 +1,92 @@
+// 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];
+};
diff --git a/src/SOS/Strike/clrma/managedanalysis.cpp b/src/SOS/Strike/clrma/managedanalysis.cpp
new file mode 100644 (file)
index 0000000..04df2f2
--- /dev/null
@@ -0,0 +1,698 @@
+// 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);
+}
diff --git a/src/SOS/Strike/clrma/managedanalysis.h b/src/SOS/Strike/clrma/managedanalysis.h
new file mode 100644 (file)
index 0000000..a78852e
--- /dev/null
@@ -0,0 +1,137 @@
+// 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"
+
diff --git a/src/SOS/Strike/clrma/thread.cpp b/src/SOS/Strike/clrma/thread.cpp
new file mode 100644 (file)
index 0000000..c2d1d37
--- /dev/null
@@ -0,0 +1,514 @@
+// 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;
+}
diff --git a/src/SOS/Strike/clrma/thread.h b/src/SOS/Strike/clrma/thread.h
new file mode 100644 (file)
index 0000000..96d2f25
--- /dev/null
@@ -0,0 +1,52 @@
+// 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);
+};
diff --git a/src/SOS/Strike/crosscontext.h b/src/SOS/Strike/crosscontext.h
new file mode 100644 (file)
index 0000000..8f2ba92
--- /dev/null
@@ -0,0 +1,348 @@
+// 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;
+
index 58187515b9c78b95e545bff62d1cfb21b60e6003..381e660325805aa196ef49794bf78ce32d7223fd 100644 (file)
@@ -242,6 +242,14 @@ DbgEngServices::GetNumberModules(
     return m_symbols->GetNumberModules(loaded, unloaded);
 }
 
+HRESULT
+DbgEngServices::GetModuleByIndex(
+    ULONG index,
+    PULONG64 base)
+{
+    return m_symbols->GetModuleByIndex(index, base);
+}
+
 HRESULT 
 DbgEngServices::GetModuleNames(
     ULONG index,
@@ -268,15 +276,19 @@ DbgEngServices::GetModuleInfo(
     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, &params);
+    hr = m_symbols->GetModuleParameters(1, &base, 0, &params);
     if (FAILED(hr)) {
         return hr;
     }
+    if (moduleBase) {
+        *moduleBase = base;
+    }
     if (moduleSize) {
         *moduleSize = params.Size;
     }
@@ -301,6 +313,16 @@ DbgEngServices::GetModuleVersionInformation(
     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)
@@ -536,6 +558,26 @@ DbgEngServices::GetLastEventInformation(
         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
 //----------------------------------------------------------------------------
@@ -708,17 +750,6 @@ HRESULT DbgEngServices::UnloadModule(
 // 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()
 {
index f7ba96cac24d68931c4c6d301cfd1ecbef87659a..73eff8044dcf71f447c2efa05b28ed644961496e 100644 (file)
@@ -41,8 +41,6 @@ public:
     // Helper functions
     //----------------------------------------------------------------------------
 
-    void FlushCheck(Extensions* extensions);
-
     IMachine* GetMachine();
 
     HRESULT SetCurrentThreadIdFromSystemId(
@@ -103,6 +101,10 @@ public:
         PULONG loaded,
         PULONG unloaded);
 
+    HRESULT STDMETHODCALLTYPE GetModuleByIndex(
+        ULONG index,
+        PULONG64 base);
+
     HRESULT STDMETHODCALLTYPE GetModuleNames(
         ULONG index,
         ULONG64 base,
@@ -131,6 +133,12 @@ public:
         ULONG bufferSize,
         PULONG versionInfoSize);
     
+    HRESULT STDMETHODCALLTYPE GetModuleByModuleName(
+        PCSTR name,
+        ULONG startIndex,
+        PULONG index,
+        PULONG64 base);
+
     HRESULT STDMETHODCALLTYPE GetNumberThreads(
         PULONG number);
 
@@ -218,6 +226,12 @@ public:
         ULONG descriptionSize,
         PULONG descriptionUsed);
 
+    void STDMETHODCALLTYPE FlushCheck();
+
+    HRESULT STDMETHODCALLTYPE ExecuteHostCommand(
+        PCSTR commandLine,
+        PEXECUTE_COMMAND_OUTPUT_CALLBACK callback);
+
     //----------------------------------------------------------------------------
     // IRemoteMemoryService
     //----------------------------------------------------------------------------
@@ -305,6 +319,72 @@ public:
         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
index eba9fa9bbc0bb4ed8fbf0ba5e30821b92b1f1d8e..31629e6647c83826e9f63a1581324d3f3cdad8bd 100644 (file)
@@ -1,13 +1,9 @@
 // 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)
@@ -100,6 +96,30 @@ ExtQuery(ILLDBServices* services)
     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)
 {
@@ -275,6 +295,33 @@ DACMessage(HRESULT Status)
     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();
@@ -440,57 +487,3 @@ DebugClient::Release()
 }
 
 #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
-}
index 4b1212b34cea2d9a0cf8b5923f4b36a3d376d615..633b925e760c8df1e180d282a614cfb45d675523 100644 (file)
@@ -73,12 +73,9 @@ typedef struct _TADDR_SEGINFO
     TADDR end;
 } TADDR_SEGINFO;
 
+#include "sosextensions.h"
 #include "util.h"
 
-#ifndef FEATURE_PAL
-#include "dbgengservices.h"
-#endif
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -122,86 +119,6 @@ private:
     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)
@@ -236,6 +153,9 @@ extern BOOL InitializePAL();
 HRESULT
 ExtQuery(PDEBUG_CLIENT client);
 
+HRESULT
+ExtInit(PDEBUG_CLIENT client);
+
 HRESULT 
 ArchQuery(void);
 
@@ -251,6 +171,9 @@ EENotLoadedMessage(HRESULT Status);
 void 
 DACMessage(HRESULT Status);
 
+IXCLRDataProcess*
+GetClrDataFromDbgEng();
+
 extern BOOL ControlC;
 
 inline BOOL IsInterrupt()
@@ -282,12 +205,7 @@ public:
 #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()                                         \
@@ -597,8 +515,6 @@ extern ReadVirtualCache *rvCache;
 #define CPPMOD
 #endif
 
-#endif
-
 #ifdef __cplusplus
 }
 #endif
index b22c8b14c882b3c6f41e8eef0aa301f9dc00ce0a..437ae42a7155e994dbcbdadbf305881e0572184e 100644 (file)
@@ -12,6 +12,7 @@ EXPORTS
     ClrStack
     clrstack=ClrStack
     CLRStack=ClrStack
+    clrmaconfig
     crashinfo
     DumpALC
     dumpalc=DumpALC
diff --git a/src/SOS/Strike/sosextensions.cpp b/src/SOS/Strike/sosextensions.cpp
new file mode 100644 (file)
index 0000000..bbb1725
--- /dev/null
@@ -0,0 +1,109 @@
+// 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);
+}
diff --git a/src/SOS/Strike/sosextensions.h b/src/SOS/Strike/sosextensions.h
new file mode 100644 (file)
index 0000000..892c7e3
--- /dev/null
@@ -0,0 +1,27 @@
+// 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);
index 55e13a15491ec832ac1c0e81448c9fa8fcde13a9..8c3c5eba87f746da861da2b15f6c5f7e7f4e7ee4 100644 (file)
 #include "ExpressionNode.h"
 #include "WatchCmd.h"
 #include "tls.h"
+#include "clrma/managedanalysis.h"
 
 typedef struct _VM_COUNTERS {
     SIZE_T PeakVirtualSize;
@@ -4949,7 +4950,7 @@ void IssueDebuggerBPCommand ( CLRDATA_ADDRESS addr )
         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)
         {
@@ -5710,7 +5711,7 @@ public:
 #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;
         }
@@ -5809,7 +5810,7 @@ HRESULT HandleCLRNotificationEvent()
         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
     }
@@ -5830,9 +5831,9 @@ HRESULT HandleCLRNotificationEvent()
             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:
@@ -5866,7 +5867,7 @@ HRESULT HandleRuntimeLoadedNotification(IDebugClient* client)
 {
     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
@@ -6221,7 +6222,7 @@ DECLARE_API(bpmd)
                 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);
@@ -6253,7 +6254,7 @@ DECLARE_API(bpmd)
     {
         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
@@ -8621,7 +8622,7 @@ DECLARE_API(FindRoots)
         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
@@ -9140,7 +9141,7 @@ DECLARE_API(TraceToCode)
         }
         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");
@@ -9194,7 +9195,7 @@ DECLARE_API(GetCodeTypeFlags)
     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);
@@ -9245,7 +9246,7 @@ DECLARE_API(GetCodeTypeFlags)
     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);
@@ -9314,7 +9315,7 @@ DECLARE_API(StopOnException)
     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);
@@ -9334,7 +9335,7 @@ DECLARE_API(StopOnException)
             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);
@@ -9380,7 +9381,7 @@ DECLARE_API(StopOnException)
                 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);
@@ -12652,7 +12653,6 @@ HRESULT ImplementEFNGetManagedExcepStack(
 DECLARE_API(VerifyStackTrace)
 {
     INIT_API();
-    ONLY_SUPPORTED_ON_WINDOWS_TARGET();
 
     BOOL bVerifyManagedExcepStack = FALSE;
     CMDOption option[] =
@@ -12922,7 +12922,7 @@ DECLARE_API(SuppressJitOptimization)
         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");
         }
     }
@@ -13556,7 +13556,7 @@ DECLARE_API(SetClrPath)
 //
 DECLARE_API(runtimes)
 {
-    INIT_API_NOEE_PROBE_MANAGED("runtimes");
+    INIT_API_NODAC_PROBE_MANAGED("runtimes");
 
     BOOL bNetFx = FALSE;
     BOOL bNetCore = FALSE;
index 63001e1999d7c54a63f2b7336f61a4eaa2cfff81..606451115a7d0a28b2d0d9b5bb6307079fd36638 100644 (file)
@@ -3943,23 +3943,11 @@ HRESULT LoadClrDebugDll(void)
     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
     {
index f3f68aa2b760c8d21522b3bd1d2c5246671e4143..dad6bf9c4a20fb2314037a3b3b0b705fa1e6e8fe 100644 (file)
@@ -30,15 +30,16 @@ inline void RestoreSOToleranceState() {}
 #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;
@@ -2135,351 +2136,6 @@ private:
 
 #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);
index 3c4b70fdff1549bbd163e462ee1e11f978f8df2a..a87aaadcaf0a38eefb1c895a02d9ac6cb28dd144 100644 (file)
@@ -124,6 +124,17 @@ IHostServices* Extensions::GetHostServices()
     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>
@@ -268,7 +279,7 @@ void InternalOutputVaList(
     size_t length = vsnprintf(str, sizeof(str), format, args);
     if (length < sizeof(str))
     {
-        Extensions::GetInstance()->GetDebuggerServices()->OutputString(mask, str);
+        GetDebuggerServices()->OutputString(mask, str);
     }
     else
     {
@@ -277,7 +288,7 @@ void InternalOutputVaList(
         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);
         }
     }
@@ -286,10 +297,11 @@ void InternalOutputVaList(
 /// <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);
 }
index b24862655ac790ec78bb6999ed3683fd61596464..03f3854eb8655ef9526b1521bdca674b306b7de3 100644 (file)
@@ -84,6 +84,11 @@ public:
     /// </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>
index 8d9619e63710499f9030c3d27460e0ee5c9337e8..7a9e63552b517b0a1fcbf624cce508521612be31 100644 (file)
@@ -58,7 +58,7 @@ extern HRESULT InitializeDesktopClrHost();
 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;
@@ -403,7 +403,7 @@ static HRESULT ProbeInstallationMarkerFile(const char* const markerName, std::st
 
     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;
     }
@@ -473,7 +473,7 @@ static HRESULT GetHostRuntime(std::string& coreClrPath, std::string& hostRuntime
     if (g_hostRuntimeDirectory == nullptr)
     {
 #if defined(HOST_FREEBSD)
-        TraceError("SOS_HOSTING: FreeBSD not supported\n");
+        TraceHostingError("FreeBSD not supported\n");
         return E_FAIL;
 #else
 
@@ -496,7 +496,7 @@ static HRESULT GetHostRuntime(std::string& coreClrPath, std::string& hostRuntime
         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);
@@ -510,7 +510,7 @@ static HRESULT GetHostRuntime(std::string& coreClrPath, std::string& hostRuntime
 
         if (Status != S_OK)
         {
-            TraceError("SOS_HOSTING: Failed to find runtime directory\n");
+            TraceHostingError("Failed to find runtime directory\n");
             return E_FAIL;
         }
 
@@ -527,7 +527,7 @@ static HRESULT GetHostRuntime(std::string& coreClrPath, std::string& hostRuntime
 
         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;
         }
 
@@ -557,7 +557,7 @@ static HRESULT InitializeNetCoreHost()
     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;
@@ -565,7 +565,7 @@ static HRESULT InitializeNetCoreHost()
     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;
@@ -589,7 +589,7 @@ static HRESULT InitializeNetCoreHost()
         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");
@@ -598,7 +598,7 @@ static HRESULT InitializeNetCoreHost()
         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");
@@ -607,7 +607,7 @@ static HRESULT InitializeNetCoreHost()
 
         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;
         }
 
@@ -616,7 +616,7 @@ static HRESULT InitializeNetCoreHost()
         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);
@@ -651,7 +651,7 @@ static HRESULT InitializeNetCoreHost()
         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;
         }
 
@@ -661,14 +661,14 @@ static HRESULT InitializeNetCoreHost()
         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;
         }
     }
@@ -682,7 +682,7 @@ static HRESULT InitializeNetCoreHost()
     }
     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;
index 40281155865ad241ec1eb68f325b8f652ef7f0d4..5d17d0e6bde20f8a23a75597bf745ac56d705309 100644 (file)
@@ -19,7 +19,7 @@
 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;
 
@@ -35,13 +35,13 @@ HRESULT InitializeDesktopClrHost()
     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);
@@ -51,7 +51,7 @@ HRESULT InitializeDesktopClrHost()
     }
     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)
@@ -59,7 +59,7 @@ HRESULT InitializeDesktopClrHost()
         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.
@@ -67,26 +67,26 @@ HRESULT InitializeDesktopClrHost()
         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;
@@ -96,14 +96,14 @@ HRESULT InitializeDesktopClrHost()
     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;
diff --git a/src/SOS/inc/clrmaservice.h b/src/SOS/inc/clrmaservice.h
new file mode 100644 (file)
index 0000000..4887d03
--- /dev/null
@@ -0,0 +1,38 @@
+// 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
index 1ec815f4a0662eeb2395fd6989c89bedcb832f20..e333177bc3e0e2606b066642dd7d184f9740baea 100644 (file)
@@ -10,6 +10,8 @@
 extern "C" {
 #endif
 
+typedef bool (*PEXECUTE_COMMAND_OUTPUT_CALLBACK)(ULONG mask, const char *text);
+
 /// <summary>
 /// IDebuggerServices 
 /// 
@@ -64,6 +66,10 @@ public:
         PULONG loaded,
         PULONG unloaded) = 0;
 
+    virtual HRESULT STDMETHODCALLTYPE GetModuleByIndex(
+        ULONG index,
+        PULONG64 base) = 0;
+
     virtual HRESULT STDMETHODCALLTYPE GetModuleNames(
         ULONG index,
         ULONG64 base,
@@ -92,6 +98,12 @@ public:
         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;
 
@@ -177,6 +189,12 @@ public:
         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
index fb07983b6a758978ea1b9c4857d9f1d9d5c2f801..b31f2512236356d7d7164e6cb86241d739a98025 100644 (file)
@@ -38,8 +38,36 @@ extern "C" {
 // 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
@@ -229,6 +257,7 @@ typedef struct _DEBUG_STACK_FRAME_EX
 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
index 54c430d20d3cfcc8de1bd6a3ce3920a97b2abbcc..f056814beba78fd05ba4c0e865bad9c073bc5a54 100644 (file)
@@ -2532,6 +2532,42 @@ LLDBServices::OutputDmlString(
     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
 //----------------------------------------------------------------------------
@@ -2910,34 +2946,6 @@ LLDBServices::ReadVirtualCache(ULONG64 address, PVOID buffer, ULONG bufferSize,
     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,
index 085a74b1ad7bef33f39248cf8d5885d9efa74cf6..a69bb236a278531c8f211699dc7ae2a5c56364ea 100644 (file)
@@ -415,14 +415,18 @@ public:
         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);
index f568efe429309bc67e0535ef5b23bb0e01e481b0..749e6c187428e458be80251a219a943ffa854b25 100644 (file)
@@ -467,7 +467,7 @@ struct MSLAYOUT DacpAssemblyData
 
     HRESULT Request(ISOSDacInterface *sos, CLRDATA_ADDRESS addr)
     {
-        return Request(sos, addr, (TADDR)0);
+        return Request(sos, addr, 0);
     }
 };
 
@@ -577,7 +577,7 @@ struct MSLAYOUT DacpMethodDescData
     {
         return sos->GetMethodDescData(
             addr,
-            (TADDR)0,   // IP address
+            0,      // IP address
             this,
             0,      // cRejitData
             NULL,   // rejitData[]
index f9da8d83906acef9814c115843d7c5e2bda2389d..620964d9feb785878a418e048a326804b74d57ba 100644 (file)
@@ -17,7 +17,7 @@ class ReleaseHolder
 {
 public:
     ReleaseHolder()
-        : m_ptr(NULL)
+        : m_ptr(nullptr)
     {}
 
     ReleaseHolder(T* ptr)
@@ -59,16 +59,16 @@ public:
     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;
         }
     }
 
diff --git a/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/ClrmaTests.cs b/src/tests/Microsoft.Diagnostics.DebugServices.UnitTests/ClrmaTests.cs
new file mode 100644 (file)
index 0000000..3be29ff
--- /dev/null
@@ -0,0 +1,234 @@
+// 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++;
+            }
+        }
+    }
+}
index ce504124fb5f52dd281c4dfe8cb040e09edfec9b..23f9312893da295c60e86e47d23d20167746d6ff 100644 (file)
@@ -51,14 +51,11 @@ namespace Microsoft.Diagnostics.DebugServices.UnitTests
             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;
@@ -67,13 +64,13 @@ namespace Microsoft.Diagnostics.DebugServices.UnitTests
             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);
@@ -85,7 +82,7 @@ namespace Microsoft.Diagnostics.DebugServices.UnitTests
             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);
@@ -98,13 +95,13 @@ namespace Microsoft.Diagnostics.DebugServices.UnitTests
             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);
@@ -118,7 +115,7 @@ namespace Microsoft.Diagnostics.DebugServices.UnitTests
             TestCommand3.Invoked = false;
             try
             {
-                commandService.Execute("testcommand", testDump.Target.Services);
+                testDump.CommandService.Execute("testcommand", testDump.Target.Services);
             }
             catch (DiagnosticsException ex)
             {
index 99fe878914dc9cab6200b6c130c7b81dd8b79b15..7ae2286083f59dc9882ec9f19301192cb33ae451 100644 (file)
 
 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
@@ -74,11 +136,28 @@ namespace Microsoft.Diagnostics.DebugServices.UnitTests
     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;
index 5b1a6dd577a144b4ba58b4fafdc4d5b65d5e948d..2deea6b1f8258be85ff69d317b7ca13569d2035d 100644 (file)
@@ -2,9 +2,11 @@
 // 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;
@@ -23,6 +25,8 @@ namespace Microsoft.Diagnostics.DebugServices.UnitTests
         {
         }
 
+        public override IReadOnlyList<string> ExecuteHostCommand(string commandLine) => HostServices.Instance.ExecuteHostCommand(commandLine);
+
         protected override ITarget GetTarget()
         {
             // Create/initialize dbgeng controller