Add IContextService and implementation and other misc changes/fixed. (#2247)
authorMike McLaughlin <mikem@microsoft.com>
Fri, 21 May 2021 23:10:48 +0000 (16:10 -0700)
committerGitHub <noreply@github.com>
Fri, 21 May 2021 23:10:48 +0000 (16:10 -0700)
Add IContextService and implementation

Moved all the "current context" properties and methods out of the individual services into IContextService. The context service is used by the "UI" (i.e. dotnet-dump command line) and the SOSHost service.  The rest of the services shouldn't have current ambient state.

Added Target property to IRuntime, IThread and IModule. Added Equals/GetHashCode too.

Add Target.OnDestroyEvent, make DisposeOnClose an extension method

Remove native ITarget.Close()

Fixed some SOSHost life time issues by separating and making global the SOS module loading and host wrapper in the new SOSLibrary class. Once the SOS module is loaded, it stays around until the host/dotnet-dump exits.

Fix soshelp on managed commands like parallelstacks or timerinfo before runtimes are loaded or the app is started.

Properly flush the ClrRuntime instance with FlushCachedData()

Add clrmodules export

Add DOTNET_ENABLED_SOS_LOGGING env var.

Fix initial target being created twice on MacOS under lldb. Initialize/read thread info table in GetCurrentProcessSystemId, etc.

More COM wrapper cleanup.

Release/cleanup the host, host service and symbol wrappers.

Fix problem with a second target being created under dbgeng when the
app terminates. The UpdateTarget in the ChangeEngineState handler isn't
needed because the CreateProcess/ExitProcess events managed the target
lifetime.

Add cleanup for SOSHost on context change

When a context change happens (thread, runtime or target), the SOSHost instance was discarded but not disposed/cleanup. Now SOSHost instance is disposed on context change.

Added a one shot event register on IServiceEvent used by SOSHost context change handler.

Setting a new target context would fire the context change event 3 times

Changed the lifetime of the SOSHost (again) from discard/disposed on every context change to per-target.  I made things to complicated.

Add temporary workaround to single-file/export enumerate perf problem on dbgeng. Enabled Windows single-file support with env var: DOTNET_ENABLE_SOS_SINGLEFILE=1

Fix cross-bitness DAC support on Windows; go back to using PlatformSpecificFileName.

Move RuntimeModuleDirectory to IRuntime

Better hash code combining

68 files changed:
THIRD-PARTY-NOTICES.TXT
src/Microsoft.Diagnostics.DebugServices.Implementation/ContextService.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.DebugServices.Implementation/ImageMappingMemoryService.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/MetadataMappingMemoryService.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/Module.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleService.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/Runtime.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/RuntimeService.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceEvent.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/ServiceProvider.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/Target.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/TargetFromDataReader.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/Thread.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/ThreadService.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/ThreadServiceFromDataReader.cs
src/Microsoft.Diagnostics.DebugServices.Implementation/UnregisterCallback.cs [deleted file]
src/Microsoft.Diagnostics.DebugServices.Implementation/Utilities.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.DebugServices/ContextServiceExtensions.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.DebugServices/IContextService.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.DebugServices/IHost.cs
src/Microsoft.Diagnostics.DebugServices/IModule.cs
src/Microsoft.Diagnostics.DebugServices/IRuntime.cs
src/Microsoft.Diagnostics.DebugServices/IRuntimeService.cs
src/Microsoft.Diagnostics.DebugServices/IServiceEvent.cs
src/Microsoft.Diagnostics.DebugServices/ITarget.cs
src/Microsoft.Diagnostics.DebugServices/IThread.cs
src/Microsoft.Diagnostics.DebugServices/IThreadService.cs
src/Microsoft.Diagnostics.DebugServices/TargetExtensions.cs
src/Microsoft.Diagnostics.ExtensionCommands/ClrMDHelper.cs
src/Microsoft.Diagnostics.ExtensionCommands/DumpConcurrentDictionaryCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/DumpConcurrentQueueCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/DumpGenCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/ExtensionCommandBase.cs
src/Microsoft.Diagnostics.ExtensionCommands/Host/LoggingCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/Host/RuntimesCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/Host/SetClrPathCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/Host/ThreadsCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/ParallelStacksCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/TaskStateCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/ThreadPoolQueueCommand.cs
src/Microsoft.Diagnostics.ExtensionCommands/TimersCommand.cs
src/SOS/SOS.Extensions/ContextServiceFromDebuggerServices.cs [new file with mode: 0644]
src/SOS/SOS.Extensions/HostServices.cs
src/SOS/SOS.Extensions/TargetFromFromDebuggerServices.cs
src/SOS/SOS.Extensions/ThreadServiceFromDebuggerServices.cs
src/SOS/SOS.Hosting/CorDebugDataTargetWrapper.cs
src/SOS/SOS.Hosting/DataTargetWrapper.cs
src/SOS/SOS.Hosting/HostWrapper.cs
src/SOS/SOS.Hosting/LLDBServices.cs
src/SOS/SOS.Hosting/RuntimeWrapper.cs
src/SOS/SOS.Hosting/SOSHost.cs
src/SOS/SOS.Hosting/SOSLibrary.cs [new file with mode: 0644]
src/SOS/SOS.Hosting/SymbolServiceExtensions.cs [new file with mode: 0644]
src/SOS/SOS.Hosting/SymbolServiceWrapper.cs
src/SOS/SOS.Hosting/TargetWrapper.cs
src/SOS/Strike/dbgengservices.cpp
src/SOS/Strike/platform/runtimeimpl.cpp
src/SOS/Strike/platform/runtimeimpl.h
src/SOS/Strike/platform/targetimpl.cpp
src/SOS/Strike/platform/targetimpl.h
src/SOS/Strike/sos.def
src/SOS/Strike/sos_unixexports.src
src/SOS/Strike/strike.cpp
src/SOS/inc/runtime.h
src/SOS/inc/target.h
src/SOS/lldbplugin/services.cpp
src/SOS/lldbplugin/soscommand.cpp
src/Tools/dotnet-dump/Analyzer.cs

index 29cb3d06d3f41aa1dfed6a238840c6c0109386d2..51b3247c68082a7cc22c89ce7eb31b7e36ceb990 100644 (file)
@@ -298,3 +298,27 @@ This set of code is covered by the following license:
     See the License for the specific language governing permissions and
     limitations under the License.
 
+-------------------------------------------------
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/ContextService.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/ContextService.cs
new file mode 100644 (file)
index 0000000..a2836da
--- /dev/null
@@ -0,0 +1,227 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Diagnostics.DebugServices.Implementation
+{
+    /// <summary>
+    /// Manages the current target, thread and runtime contexts
+    /// </summary>
+    public class ContextService : IContextService
+    {
+        protected readonly IHost Host;
+        private ITarget _currentTarget;
+        private IThread _currentThread;
+        private IRuntime _currentRuntime;
+
+        public readonly ServiceProvider ServiceProvider;
+
+        public ContextService(IHost host)
+        {
+            Host = host;
+
+            ServiceProvider = new ServiceProvider(new Func<IServiceProvider>[] {
+                // First check the current runtime for the service
+                () => GetCurrentRuntime()?.Services,
+                // If there is no target, then provide just the global services
+                () => GetCurrentTarget()?.Services ?? host.Services
+            });
+
+            // These services depend on no caching
+            ServiceProvider.AddServiceFactoryWithNoCaching<ITarget>(GetCurrentTarget);
+            ServiceProvider.AddServiceFactoryWithNoCaching<IThread>(GetCurrentThread);
+            ServiceProvider.AddServiceFactoryWithNoCaching<IRuntime>(GetCurrentRuntime);
+        }
+
+        #region IContextService
+
+        /// <summary>
+        /// Current context service provider. Contains the current ITarget, IThread 
+        /// and IRuntime instances along with all per target and global services.
+        /// </summary>
+        public IServiceProvider Services => ServiceProvider;
+
+        /// <summary>
+        /// Fires anytime the current context changes.
+        /// </summary>
+        public IServiceEvent OnContextChange { get; } = new ServiceEvent();
+
+        /// <summary>
+        /// Sets the current target.
+        /// </summary>
+        /// <param name="targetId">target id</param>
+        /// <exception cref="DiagnosticsException">invalid target id</exception>
+        public void SetCurrentTarget(int targetId)
+        {
+            ITarget target = Host.EnumerateTargets().FirstOrDefault((target) => target.Id == targetId);
+            if (target is null) {
+                throw new DiagnosticsException($"Invalid target id {targetId}");
+            }
+            SetCurrentTarget(target);
+        }
+
+        /// <summary>
+        /// Clears (nulls) the current target
+        /// </summary>
+        public void ClearCurrentTarget() => SetCurrentTarget(null);
+
+        /// <summary>
+        /// Set the current thread.
+        /// </summary>
+        /// <param name="threadId">thread id</param>
+        /// <exception cref="DiagnosticsException">invalid thread id</exception>
+        public void SetCurrentThread(uint threadId) => SetCurrentThread(ThreadService?.GetThreadFromId(threadId));
+
+        /// <summary>
+        /// Clears (nulls) the current thread
+        /// </summary>
+        public void ClearCurrentThread() => SetCurrentThread(null);
+
+        /// <summary>
+        /// Set the current runtime 
+        /// </summary>
+        /// <param name="runtimeId">runtime id</param>
+        /// <exception cref="DiagnosticsException">invalid runtime id</exception>
+        public void SetCurrentRuntime(int runtimeId)
+        {
+            IRuntime runtime = RuntimeService?.EnumerateRuntimes().FirstOrDefault((runtime) => runtime.Id == runtimeId);
+            if (runtime is null) {
+                throw new DiagnosticsException($"Invalid runtime id {runtimeId}");
+            }
+            SetCurrentRuntime(runtime);
+        }
+
+        /// <summary>
+        /// Clears (nulls) the current runtime
+        /// </summary>
+        public void ClearCurrentRuntime() => SetCurrentRuntime(null);
+
+        #endregion
+
+        /// <summary>
+        /// Returns the current target.
+        /// </summary>
+        public ITarget GetCurrentTarget() => _currentTarget ??= Host.EnumerateTargets().FirstOrDefault();
+
+        /// <summary>
+        /// Allows hosts to set the initial current target
+        /// </summary>
+        /// <param name="target"></param>
+        public void SetCurrentTarget(ITarget target)
+        {
+            if (!IsTargetEqual(target, _currentTarget))
+            {
+                _currentTarget = target;
+                _currentThread = null;
+                _currentRuntime = null;
+                ServiceProvider.FlushServices();
+                OnContextChange.Fire();
+            }
+        }
+
+        /// <summary>
+        /// Returns the current thread.
+        /// </summary>
+        public virtual IThread GetCurrentThread() => _currentThread ??= ThreadService?.EnumerateThreads().FirstOrDefault();
+
+        /// <summary>
+        /// Allows hosts to set the initial current thread
+        /// </summary>
+        /// <param name="thread"></param>
+        public virtual void SetCurrentThread(IThread thread)
+        {
+            if (!IsThreadEqual(thread, _currentThread))
+            {
+                _currentThread = thread;
+                ServiceProvider.FlushServices();
+                OnContextChange.Fire();
+            }
+        }
+
+        /// <summary>
+        /// Find the current runtime.
+        /// </summary>
+        public IRuntime GetCurrentRuntime()
+        {
+            if (_currentRuntime is null)
+            {
+                IEnumerable<IRuntime> runtimes = RuntimeService?.EnumerateRuntimes();
+                if (runtimes is not null)
+                {
+                    // First check if there is a .NET Core runtime loaded
+                    foreach (IRuntime runtime in runtimes)
+                    {
+                        if (runtime.RuntimeType == RuntimeType.NetCore || runtime.RuntimeType == RuntimeType.SingleFile)
+                        {
+                            _currentRuntime = runtime;
+                            break;
+                        }
+                    }
+                    // If no .NET Core runtime, then check for desktop runtime
+                    if (_currentRuntime is null)
+                    {
+                        foreach (IRuntime runtime in runtimes)
+                        {
+                            if (runtime.RuntimeType == RuntimeType.Desktop)
+                            {
+                                _currentRuntime = runtime;
+                                break;
+                            }
+                        }
+                    }
+                    // If no core or desktop runtime, get the first one if any
+                    if (_currentRuntime is null)
+                    {
+                        _currentRuntime = runtimes.FirstOrDefault();
+                    }
+                }
+            }
+            return _currentRuntime;
+        }
+
+        /// <summary>
+        /// Allows hosts to set the initial current runtime 
+        /// </summary>
+        public void SetCurrentRuntime(IRuntime runtime)
+        {
+            if (!IsRuntimeEqual(runtime, _currentRuntime))
+            {
+                _currentRuntime = runtime;
+                ServiceProvider.FlushServices();
+                OnContextChange.Fire();
+            }
+        }
+
+        protected bool IsTargetEqual(ITarget left, ITarget right)
+        {
+            if (left is null || right is null) {
+                return left == right;
+            }
+            return left == right;
+        }
+
+        protected bool IsThreadEqual(IThread left, IThread right)
+        {
+            if (left is null || right is null) {
+                return left == right;
+            }
+            return left == right;
+        }
+
+        protected bool IsRuntimeEqual(IRuntime left, IRuntime right)
+        {
+            if (left is null || right is null) {
+                return left == right;
+            }
+            return left == right;
+        }
+
+        protected IThreadService ThreadService => GetCurrentTarget()?.Services.GetService<IThreadService>();
+
+        protected IRuntimeService RuntimeService => GetCurrentTarget()?.Services.GetService<IRuntimeService>();
+    }
+}
index 5a65d80edeabbf51bffa314ab1bbefa04b062cb0..44ceb9551274d7a99ee668ce8120abbf9cca8bb3 100644 (file)
@@ -39,7 +39,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
             _memoryCache = new MemoryCache(ReadMemoryFromModule);
             _recursionProtection = new HashSet<ulong>();
             target.OnFlushEvent.Register(_memoryCache.FlushCache);
-            target.DisposeOnClose(target.Services.GetService<ISymbolService>()?.OnChangeEvent.Register(_memoryCache.FlushCache));
+            target.DisposeOnDestroy(target.Services.GetService<ISymbolService>()?.OnChangeEvent.Register(_memoryCache.FlushCache));
         }
 
         #region IMemoryService
index 47526c65a5bbe73d2877917c3cee75b0198c6388..5b061c76ed0d7d9e50b04b545718ecfa3fb0c3f9 100644 (file)
@@ -39,7 +39,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
             _target = target;
             _memoryService = memoryService;
             target.OnFlushEvent.Register(Flush);
-            target.DisposeOnClose(SymbolService?.OnChangeEvent.Register(Flush));
+            target.DisposeOnDestroy(SymbolService?.OnChangeEvent.Register(Flush));
         }
 
         /// <summary>
index aef141ce5f69be8aa5cd721f3a1af11ae024da79..88d418c0e39cf586013ec030f57710136be49830 100644 (file)
@@ -67,6 +67,8 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         #region IModule
 
+        public ITarget Target => ModuleService.Target;
+
         public IServiceProvider Services => ServiceProvider;
 
         public abstract int ModuleIndex { get; }
@@ -207,6 +209,17 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         protected abstract ModuleService ModuleService { get; }
 
+        public override bool Equals(object obj)
+        {
+            IModule module = (IModule)obj;
+            return Target == module.Target && ImageBase == module.ImageBase;
+        }
+
+        public override int GetHashCode()
+        {
+            return Utilities.CombineHashCodes(Target.GetHashCode(), ImageBase.GetHashCode());
+        }
+
         public override string ToString()
         {
             return $"#{ModuleIndex} {ImageBase:X16} {_flags} {FileName ?? ""}";
index 017dde7171d75619195e96acce5ead876ef42f8a..8dc1c0be00597f71baf054e0abbb0663863f3ce3 100644 (file)
@@ -39,7 +39,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         // MachO writable segment attribute
         const uint VmProtWrite = 0x02;
 
-        protected readonly ITarget Target;
+        internal protected readonly ITarget Target;
         private IMemoryService _memoryService;
         private ISymbolService _symbolService;
         private ReadVirtualCache _versionCache;
@@ -60,8 +60,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
                 {
                     foreach (IModule module in _modules.Values)
                     {
-                        if (module is IDisposable disposable)
-                        {
+                        if (module is IDisposable disposable) {
                             disposable.Dispose();
                         }
                     }
index 9858a75b0d6ee8b1ca53878061a407ce0bb9eed1..6eb88cb6a9ce5147d3d6c8adbd51535301272a20 100644 (file)
@@ -20,8 +20,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
     /// </summary>
     public class Runtime : IRuntime
     {
-        private readonly ITarget _target;
-        private readonly IRuntimeService _runtimeService;
         private readonly ClrInfo _clrInfo;
         private ISymbolService _symbolService;
         private ClrRuntime _clrRuntime;
@@ -30,13 +28,11 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         public readonly ServiceProvider ServiceProvider;
 
-        public Runtime(ITarget target, IRuntimeService runtimeService, ClrInfo clrInfo, int id)
+        public Runtime(ITarget target, int id, ClrInfo clrInfo)
         {
-            Trace.TraceInformation($"Creating runtime #{id} {clrInfo.Flavor} {clrInfo}");
-            _target = target;
-            _runtimeService = runtimeService;
-            _clrInfo = clrInfo;
+            Target = target ?? throw new ArgumentNullException(nameof(target));
             Id = id;
+            _clrInfo = clrInfo ?? throw new ArgumentNullException(nameof(clrInfo));
 
             RuntimeType = RuntimeType.Unknown;
             if (clrInfo.Flavor == ClrFlavor.Core) {
@@ -51,21 +47,25 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
             ServiceProvider.AddService<ClrInfo>(clrInfo);
             ServiceProvider.AddServiceFactoryWithNoCaching<ClrRuntime>(() => CreateRuntime());
 
-            target.OnFlushEvent.Register(() => {
-                _clrRuntime?.DacLibrary.DacPrivateInterface.Flush();
-            });
+            target.OnFlushEvent.Register(() => _clrRuntime?.FlushCachedData());
+
+            Trace.TraceInformation($"Created runtime #{id} {clrInfo.Flavor} {clrInfo}");
         }
 
         #region IRuntime
 
-        public IServiceProvider Services => ServiceProvider;
-
         public int Id { get; }
 
+        public ITarget Target { get; }
+
+        public IServiceProvider Services => ServiceProvider;
+
         public RuntimeType RuntimeType { get; }
 
         public IModule RuntimeModule { get; }
 
+        public string RuntimeModuleDirectory { get; set; }
+
         public string GetDacFilePath()
         {
             if (_dacFilePath is null)
@@ -133,15 +133,20 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         private string GetDacFileName()
         {
-            return ClrInfoProvider.GetDacFileName(_clrInfo.Flavor, _target.OperatingSystem);
+            if (_clrInfo.SingleFileRuntimeInfo.HasValue)
+            {
+                return ClrInfoProvider.GetDacFileName(_clrInfo.Flavor, Target.OperatingSystem);
+            }
+            Debug.Assert(!string.IsNullOrEmpty(_clrInfo.DacInfo.PlatformSpecificFileName));
+            return _clrInfo.DacInfo.PlatformSpecificFileName;
         }
 
         private string GetLocalDacPath(string dacFileName)
         {
             string dacFilePath;
-            if (!string.IsNullOrEmpty(_runtimeService.RuntimeModuleDirectory))
+            if (!string.IsNullOrEmpty(RuntimeModuleDirectory))
             {
-                dacFilePath = Path.Combine(_runtimeService.RuntimeModuleDirectory, dacFileName);
+                dacFilePath = Path.Combine(RuntimeModuleDirectory, dacFileName);
             }
             else
             {
@@ -162,10 +167,10 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         private string GetDbiFileName()
         {
-            string name = _target.GetPlatformModuleName("mscordbi");
+            string name = Target.GetPlatformModuleName("mscordbi");
 
             // If this is the Linux runtime module name, but we are running on Windows return the cross-OS DBI name.
-            if (_target.OperatingSystem == OSPlatform.Linux && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            if (Target.OperatingSystem == OSPlatform.Linux && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
             {
                 name = "mscordbi.dll";
             }
@@ -175,9 +180,9 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         private string GetLocalPath(string fileName)
         {
             string localFilePath;
-            if (!string.IsNullOrEmpty(_runtimeService.RuntimeModuleDirectory))
+            if (!string.IsNullOrEmpty(RuntimeModuleDirectory))
             {
-                localFilePath = Path.Combine(_runtimeService.RuntimeModuleDirectory, fileName);
+                localFilePath = Path.Combine(RuntimeModuleDirectory, fileName);
             }
             else
             {
@@ -192,7 +197,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         private string DownloadFile(string fileName)
         {
-            OSPlatform platform = _target.OperatingSystem;
+            OSPlatform platform = Target.OperatingSystem;
             string filePath = null;
 
             if (SymbolService.IsSymbolStoreEnabled)
@@ -253,16 +258,17 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
             return filePath;
         }
 
-        private ISymbolService SymbolService => _symbolService ??= _target.Services.GetService<ISymbolService>(); 
+        private ISymbolService SymbolService => _symbolService ??= Target.Services.GetService<ISymbolService>(); 
 
         public override bool Equals(object obj)
         {
-            return Id == ((Runtime)obj).Id;
+            IRuntime runtime = (IRuntime)obj;
+            return Target == runtime.Target && Id == runtime.Id;
         }
 
         public override int GetHashCode()
         {
-            return Id.GetHashCode();
+            return Utilities.CombineHashCodes(Target.GetHashCode(), Id.GetHashCode());
         }
 
         private static readonly string[] s_runtimeTypeNames = {
@@ -283,6 +289,9 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
             else {
                 sb.AppendLine($"    Runtime module path: {RuntimeModule.FileName}");
             }
+            if (RuntimeModuleDirectory is not null) {
+                sb.AppendLine($"    Runtime module directory: {RuntimeModuleDirectory}");
+            }
             if (_dacFilePath is not null) {
                 sb.AppendLine($"    DAC: {_dacFilePath}");
             }
index 6949fff5c19ee30d445d4936947e630ad1b07af9..c54516c913f4f7427d6e0130884f21daae91d202 100644 (file)
@@ -21,11 +21,11 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
     public class RuntimeService : IRuntimeService, IDataReader, IExportReader
     {
         private readonly ITarget _target;
+        private readonly bool _exportReaderEnabled;
         private readonly IDisposable _onFlushEvent;
         private DataTarget _dataTarget;
-        private string _runtimeModuleDirectory;
         private List<Runtime> _runtimes;
-        private Runtime _currentRuntime;
+        private IContextService _contextService;
         private IModuleService _moduleService;
         private IThreadService _threadService;
         private IMemoryService _memoryService;
@@ -33,6 +33,8 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         public RuntimeService(ITarget target)
         {
             _target = target;
+            // TODO - mikem 4/30/21 - remove when dbgeng services doesn't take so long looking up exports (attempts to load PDBs).
+            _exportReaderEnabled = target.Host.HostType != HostType.DbgEng || Environment.GetEnvironmentVariable("DOTNET_ENABLE_SOS_SINGLEFILE") == "1";
             _onFlushEvent = target.OnFlushEvent.Register(() => {
                 if (_runtimes is not null && _runtimes.Count == 0)
                 {
@@ -45,58 +47,38 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
             // Can't make RuntimeService IDisposable directly because _dataTarget.Dispose() disposes the IDataReader 
             // passed which is this RuntimeService instance which would call _dataTarget.Dispose again and causing a 
             // stack overflow.
-            target.DisposeOnClose(new UnregisterCallback(() => {
+            target.OnDestroyEvent.Register(() => {
                 _dataTarget?.Dispose();
                 _dataTarget = null;
                 _onFlushEvent.Dispose();
-            }));
+            });
         }
 
         #region IRuntimeService
 
-        /// <summary>
-        /// Directory of the runtime module (coreclr.dll, libcoreclr.so, etc.)
-        /// </summary>
-        public string RuntimeModuleDirectory
-        {
-            get { return _runtimeModuleDirectory; }
-            set
-            {
-                _runtimeModuleDirectory = value;
-                _runtimes = null;
-                _currentRuntime = null;
-            }
-        }
-
         /// <summary>
         /// Returns the list of runtimes in the target
         /// </summary>
-        public IEnumerable<IRuntime> EnumerateRuntimes() => BuildRuntimes();
-
-        /// <summary>
-        /// Returns the current runtime
-        /// </summary>
-        public IRuntime CurrentRuntime
+        public IEnumerable<IRuntime> EnumerateRuntimes()
         {
-            get
+            if (_runtimes is null)
             {
-                if (_currentRuntime is null) {
-                    _currentRuntime = FindRuntime();
+                _runtimes = new List<Runtime>();
+                if (_dataTarget is null)
+                {
+                    _dataTarget = new DataTarget(new CustomDataTarget(this)) {
+                        BinaryLocator = null
+                    };
+                }
+                if (_dataTarget is not null)
+                {
+                    for (int i = 0; i < _dataTarget.ClrVersions.Length; i++)
+                    {
+                        _runtimes.Add(new Runtime(_target, i, _dataTarget.ClrVersions[i]));
+                    }
                 }
-                return _currentRuntime;
-            }
-        }
-
-        /// <summary>
-        /// Set the current runtime 
-        /// </summary>
-        /// <param name="runtimeId">runtime id</param>
-        public void SetCurrentRuntime(int runtimeId)
-        {
-            if (_runtimes is null || runtimeId >= _runtimes.Count) {
-                throw new DiagnosticsException($"Invalid runtime id {runtimeId}");
             }
-            _currentRuntime = _runtimes[runtimeId];
+            return _runtimes;
         }
 
         #endregion
@@ -126,15 +108,15 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         int IDataReader.ProcessId => unchecked((int)_target.ProcessId.GetValueOrDefault());
 
-        IEnumerable<ModuleInfo> IDataReader.EnumerateModules() => 
+        IEnumerable<ModuleInfo> IDataReader.EnumerateModules() =>
             ModuleService.EnumerateModules().Select((module) => CreateModuleInfo(module)).ToList();
 
         private ModuleInfo CreateModuleInfo(IModule module) =>
             new ModuleInfo(
                 this,
-                module.ImageBase, 
+                module.ImageBase,
                 module.FileName,
-                isVirtual:true,
+                isVirtual: true,
                 unchecked((int)module.IndexFileSize.GetValueOrDefault(0)),
                 unchecked((int)module.IndexTimeStamp.GetValueOrDefault(0)),
                 new ImmutableArray<byte>());
@@ -186,7 +168,9 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
             return false;
         }
 
-        void IDataReader.FlushCachedData() => _target.Flush();
+        void IDataReader.FlushCachedData()
+        {
+        }
 
         #endregion
 
@@ -235,16 +219,19 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         bool IExportReader.TryGetSymbolAddress(ulong baseAddress, string name, out ulong offset)
         {
-            try
+            if (_exportReaderEnabled)
             {
-                IExportSymbols exportSymbols = ModuleService.GetModuleFromBaseAddress(baseAddress).Services.GetService<IExportSymbols>();
-                if (exportSymbols is not null)
+                try
+                {
+                    IExportSymbols exportSymbols = ModuleService.GetModuleFromBaseAddress(baseAddress).Services.GetService<IExportSymbols>();
+                    if (exportSymbols is not null)
+                    {
+                        return exportSymbols.TryGetSymbolAddress(name, out offset);
+                    }
+                }
+                catch (DiagnosticsException)
                 {
-                    return exportSymbols.TryGetSymbolAddress(name, out offset);
                 }
-            }
-            catch (DiagnosticsException)
-            {
             }
             offset = 0;
             return false;
@@ -252,61 +239,9 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         #endregion
 
-        /// <summary>
-        /// Find the runtime
-        /// </summary>
-        private Runtime FindRuntime()
-        {
-            IEnumerable<Runtime> runtimes = BuildRuntimes();
-            Runtime runtime = null;
+        private IRuntime CurrentRuntime => ContextService.Services.GetService<IRuntime>();
 
-            // First check if there is a .NET Core runtime loaded
-            foreach (Runtime r in runtimes)
-            {
-                if (r.RuntimeType == RuntimeType.NetCore || r.RuntimeType == RuntimeType.SingleFile)
-                {
-                    runtime = r;
-                    break;
-                }
-            }
-            // If no .NET Core runtime, then check for desktop runtime
-            if (runtime is null)
-            {
-                foreach (Runtime r in runtimes)
-                {
-                    if (r.RuntimeType == RuntimeType.Desktop)
-                    {
-                        runtime = r;
-                        break;
-                    }
-                }
-            }
-            return runtime;
-        }
-
-        private IEnumerable<Runtime> BuildRuntimes()
-        {
-            if (_runtimes is null)
-            {
-                _runtimes = new List<Runtime>();
-                if (_dataTarget is null)
-                {
-                    // Don't use the default binary locator or provide one. clrmd uses it to download the DAC, download assemblies
-                    // to get metadata in its data target or for invalid memory region mapping and we already do all of that.
-                    _dataTarget = new DataTarget(new CustomDataTarget(this)) {
-                        BinaryLocator = null
-                    };
-                }
-                if (_dataTarget is not null)
-                {
-                    for (int i = 0; i < _dataTarget.ClrVersions.Length; i++)
-                    {
-                        _runtimes.Add(new Runtime(_target, this, _dataTarget.ClrVersions[i], i));
-                    }
-                }
-            }
-            return _runtimes;
-        }
+        private IContextService ContextService => _contextService ??= _target.Services.GetService<IContextService>();
 
         private IModuleService ModuleService => _moduleService ??= _target.Services.GetService<IModuleService>();
 
@@ -317,14 +252,11 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         public override string ToString()
         {
             var sb = new StringBuilder();
-            if (_runtimeModuleDirectory is not null) {
-                sb.AppendLine($"Runtime module path: {_runtimeModuleDirectory}");
-            }
             if (_runtimes is not null)
             {
                 foreach (IRuntime runtime in _runtimes)
                 {
-                    string current = _runtimes.Count > 1 ? runtime == _currentRuntime ? "*" : " " : "";
+                    string current = _runtimes.Count > 1 ? runtime == CurrentRuntime ? "*" : " " : "";
                     sb.Append(current);
                     sb.AppendLine(runtime.ToString());
                 }
index a0f073d5f047d0d57c90be99c18bcff6586fa8a4..79e9e88f23ecd43258870c0f0dafbfc5489e4aff 100644 (file)
@@ -15,9 +15,19 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         {
             private readonly Action _callback;
 
-            internal EventNode(Action callback)
+            internal EventNode(bool oneshot, Action callback)
             {
-                _callback = callback;
+                if (oneshot)
+                {
+                    _callback = () => {
+                        callback();
+                        Remove();
+                    };
+                }
+                else
+                {
+                    _callback = callback;
+                }
             }
 
             internal void Fire()
@@ -37,10 +47,14 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         {
         }
 
-        public IDisposable Register(Action callback)
+        public IDisposable Register(Action callback) => Register(oneshot: false, callback);
+
+        public IDisposable RegisterOneShot(Action callback) => Register(oneshot: true, callback);
+
+        private IDisposable Register(bool oneshot, Action callback)
         {
             // Insert at the end of the list
-            var node = new EventNode(callback);
+            var node = new EventNode(oneshot, callback);
             _events.InsertBefore(node);
             return node;
         }
index c779e7d1a1a2297b72c4e9ea6590917e62e870d8..24b98961a7546992e6cc9922b599d0e97df15dff 100644 (file)
@@ -9,15 +9,15 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 {
     public class ServiceProvider : IServiceProvider
     {
-        readonly IServiceProvider _parent;
-        readonly Dictionary<Type, Func<object>> _factories;
-        readonly Dictionary<Type, object> _services;
+        private readonly Func<IServiceProvider>[] _parents;
+        private readonly Dictionary<Type, Func<object>> _factories;
+        private readonly Dictionary<Type, object> _services;
 
         /// <summary>
         /// Create a service provider instance
         /// </summary>
         public ServiceProvider()
-            : this(null, null)
+            : this(Array.Empty<Func<IServiceProvider>>())
         {
         }
 
@@ -26,19 +26,18 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         /// </summary>
         /// <param name="parent">search this provider if service isn't found in this instance</param>
         public ServiceProvider(IServiceProvider parent)
-            : this(parent, null)
+            : this(new Func<IServiceProvider>[] { () => parent })
         {
         }
 
         /// <summary>
         /// Create a service provider with parent provider and service factories
         /// </summary>
-        /// <param name="parent">search this provider if service isn't found in this instance or null</param>
-        /// <param name="factories">a dictionary of the factories to create the services</param>
-        public ServiceProvider(IServiceProvider parent, Dictionary<Type, Func<object>> factories)
+        /// <param name="parents">an array of functions to return the next provider to search if service isn't found in this instance</param>
+        public ServiceProvider(Func<IServiceProvider>[] parents) 
         {
-            _parent = parent;
-            _factories = factories ?? new Dictionary<Type, Func<object>>();
+            _parents = parents;
+            _factories = new Dictionary<Type, Func<object>>();
             _services = new Dictionary<Type, object>();
         }
 
@@ -78,11 +77,16 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         public void AddService(Type type, object service) => _services.Add(type, service);
 
         /// <summary>
-        /// Remove the service instance for the type.
+        /// Flushes the cached service instance for the specified type. Does not remove the service factory registered for the type.
         /// </summary>
         /// <param name="type">service type</param>
         public void RemoveService(Type type) => _services.Remove(type);
 
+        /// <summary>
+        /// Flushes all the cached instances of the services. Does not remove any of the service factories registered.
+        /// </summary>
+        public void FlushServices() => _services.Clear();
+
         /// <summary>
         /// Returns the instance of the service or returns null if service doesn't exist
         /// </summary>
@@ -97,9 +101,16 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
                     service = factory();
                 }
             }
-            if (service == null && _parent != null)
+            if (service == null)
             {
-                service = _parent.GetService(type);
+                foreach (Func<IServiceProvider> parent in _parents)
+                {
+                    service = parent()?.GetService(type);
+                    if (service != null)
+                    {
+                        break;
+                    }
+                }
             }
             return service;
         }
index a88bbdc7c7108118cd3181ceb253738fc7062c70..61337d2aed942b713a3c0bcf56452e32a15c6a9d 100644 (file)
@@ -15,23 +15,22 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
     /// <summary>
     /// ITarget base implementation
     /// </summary>
-    public abstract class Target : ITarget
+    public abstract class Target : ITarget, IDisposable
     {
-        private static int _targetIdFactory;
         private readonly string _dumpPath;
-        private readonly List<IDisposable> _disposables;
         private string _tempDirectory;
 
         public readonly ServiceProvider ServiceProvider;
 
-        public Target(IHost host, string dumpPath)
+        public Target(IHost host, int id, string dumpPath)
         {
             Trace.TraceInformation($"Creating target #{Id}");
             Host = host;
+            Id = id;
             _dumpPath = dumpPath;
-            _disposables = new List<IDisposable>();
 
             OnFlushEvent = new ServiceEvent();
+            OnDestroyEvent = new ServiceEvent();
 
             // Initialize the per-target services
             ServiceProvider = new ServiceProvider(host.Services);
@@ -48,15 +47,10 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         /// </summary>
         public IHost Host { get; }
 
-        /// <summary>
-        /// Invoked when this target is flushed (via the Flush() call).
-        /// </summary>
-        public IServiceEvent OnFlushEvent { get; }
-
         /// <summary>
         /// The target id
         /// </summary>
-        public int Id { get; } = _targetIdFactory++;
+        public int Id { get; }
 
         /// <summary>
         /// Returns the target OS (which may be different from the OS this is running on)
@@ -100,6 +94,11 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         /// </summary>
         public IServiceProvider Services => ServiceProvider;
 
+        /// <summary>
+        /// Invoked when this target is flushed (via the Flush() call).
+        /// </summary>
+        public IServiceEvent OnFlushEvent { get; }
+
         /// <summary>
         /// Flushes any cached state in the target.
         /// </summary>
@@ -110,36 +109,22 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         }
 
         /// <summary>
-        /// Registers an object to be disposed when ITarget.Close() is called.
+        /// Invoked when the target is closed.
         /// </summary>
-        /// <param name="disposable">object to be disposed on Close() or null</param>
-        public void DisposeOnClose(IDisposable disposable)
-        {
-            if (disposable != null)
-            {
-                _disposables.Add(disposable);
-            }
-        }
+        public IServiceEvent OnDestroyEvent { get; }
+
+        #endregion
 
         /// <summary>
         /// Releases the target and the target's resources.
         /// </summary>
-        public void Close()
+        public void Dispose()
         {
-            Trace.TraceInformation($"Closing target #{Id}");
-            Flush();
-
-            foreach (var disposable in _disposables)
-            {
-                disposable.Dispose();
-            }
-            _disposables.Clear();
-
+            Trace.TraceInformation($"Disposing target #{Id}");
+            OnDestroyEvent.Fire();
             CleanupTempDirectory();
         }
 
-        #endregion
-
         private void CleanupTempDirectory()
         {
             if (_tempDirectory != null)
@@ -161,7 +146,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         public override bool Equals(object obj)
         {
-            return Id == ((Target)obj).Id;
+            return Id == ((ITarget)obj).Id;
         }
 
         public override int GetHashCode()
index 83b5cb317cd3aa28157d36359db065fd473e4c78..493415975e380b11cab466e14fb3e4841c8ae2ce 100644 (file)
@@ -22,9 +22,10 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         /// <param name="dataReader">IDataReader</param>
         /// <param name="targetOS">target operating system</param>
         /// <param name="host">the host instance</param>
+        /// <param name="id">target id</param>
         /// <param name="dumpPath">path of dump for this target</param>
-        public TargetFromDataReader(IDataReader dataReader, OSPlatform targetOS, IHost host, string dumpPath)
-            : base(host, dumpPath)
+        public TargetFromDataReader(IDataReader dataReader, OSPlatform targetOS, IHost host, int id, string dumpPath)
+            : base(host, id, dumpPath)
         {
             _dataReader = dataReader;
 
index 321dca7dd4325449fdc940783b7b49a8f07b35fc..577e1905ac77bf6ca0b2b485db3cc4aec9bf8874 100644 (file)
@@ -26,12 +26,14 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         #region IThread
 
-        public IServiceProvider Services => ServiceProvider;
-
         public int ThreadIndex { get; }
 
         public uint ThreadId { get; }
 
+        public ITarget Target => _threadService.Target;
+
+        public IServiceProvider Services => ServiceProvider;
+
         public bool TryGetRegisterValue(int index, out ulong value)
         {
             value = 0;
@@ -88,6 +90,17 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
 
         #endregion
 
+        public override bool Equals(object obj)
+        {
+            IThread thread = (IThread)obj;
+            return Target == thread.Target && ThreadId == thread.ThreadId;
+        }
+
+        public override int GetHashCode()
+        {
+            return Utilities.CombineHashCodes(Target.GetHashCode(), ThreadId.GetHashCode());
+        }
+
         public override string ToString()
         {
             return $"#{ThreadIndex} {ThreadId:X8}";
index d1bcb161310c0286fdd7bc424d5653a5259c2a5e..e201665a533edb227c7d974d746f91858382cd1d 100644 (file)
@@ -17,7 +17,7 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
     /// </summary>
     public abstract class ThreadService : IThreadService
     {
-        protected readonly ITarget Target;
+        internal protected readonly ITarget Target;
         private readonly int _contextSize;
         private readonly uint _contextFlags;
         private readonly Dictionary<string, RegisterInfo> _lookupByName;
@@ -154,11 +154,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
             return _lookupByIndex.TryGetValue(index, out info);
         }
 
-        /// <summary>
-        /// Current OS thread Id
-        /// </summary>
-        public virtual uint? CurrentThreadId { get; set; }
-
         /// <summary>
         /// Enumerate all the native threads
         /// </summary>
index 29db285b3081c644939316d71c780dff04ff8d7d..68e57af5b9647e896c88a766f9c007f6f43b4931 100644 (file)
@@ -25,19 +25,6 @@ namespace Microsoft.Diagnostics.DebugServices.Implementation
         {
             _dataReader = dataReader;
             _threadReader = (IThreadReader)dataReader;
-
-            if (dataReader is IThreadReader threadReader)
-            {
-                // Initialize the current thread
-                IEnumerable<uint> threads = threadReader.EnumerateOSThreadIds();
-                if (threads.Any()) {
-                    CurrentThreadId = threads.First();
-                }
-            }
-            else
-            {
-                throw new InvalidOperationException("IThreadReader not implemented");
-            }
         }
 
         protected override bool GetThreadContext(uint threadId, uint contextFlags, uint contextSize, byte[] context)
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/UnregisterCallback.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/UnregisterCallback.cs
deleted file mode 100644 (file)
index 396946d..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-
-namespace Microsoft.Diagnostics.DebugServices.Implementation
-{
-    /// <summary>
-    /// Helper class that implements unregister callback
-    /// </summary>
-    public class UnregisterCallback : IDisposable
-    {
-        Action m_action;
-
-        public UnregisterCallback(Action action)
-        {
-            m_action = action;
-        }
-
-        public void Dispose()
-        {
-            var action = m_action;
-            if (action != null) {
-                m_action = null;
-                action();
-            }
-        }
-    }
-}
diff --git a/src/Microsoft.Diagnostics.DebugServices.Implementation/Utilities.cs b/src/Microsoft.Diagnostics.DebugServices.Implementation/Utilities.cs
new file mode 100644 (file)
index 0000000..c1cc261
--- /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.
+// See the LICENSE file in the project root for more information.
+
+namespace Microsoft.Diagnostics.DebugServices.Implementation
+{
+    public class Utilities
+    {
+        /// <summary>
+        /// Combines two hash codes into a single hash code, in an order-dependent manner.
+        /// </summary>
+        /// <remarks>
+        /// This function is neither commutative nor associative; the hash codes must be combined in
+        /// a deterministic order.  Do not use this when hashing collections whose contents are
+        /// nondeterministically ordered!
+        /// </remarks>
+        public static int CombineHashCodes(int hashCode0, int hashCode1)
+        {
+            unchecked {
+                // This specific hash function is based on the Boost C++ library's CombineHash function:
+                // http://stackoverflow.com/questions/4948780/magic-numbers-in-boosthash-combine
+                // http://www.boost.org/doc/libs/1_46_1/doc/html/hash/combine.html 
+                return hashCode0 ^ (hashCode1 + (int) 0x9e3779b9 + (hashCode0 << 6) + (hashCode0 >> 2));
+            }
+        }
+    }
+}
diff --git a/src/Microsoft.Diagnostics.DebugServices/ContextServiceExtensions.cs b/src/Microsoft.Diagnostics.DebugServices/ContextServiceExtensions.cs
new file mode 100644 (file)
index 0000000..c8e1736
--- /dev/null
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+    public static class ContextServiceExtensions
+    {
+        /// <summary>
+        /// Returns the current target
+        /// </summary>
+        public static ITarget GetCurrentTarget(this IContextService contextService)
+        {
+            return contextService.Services.GetService<ITarget>();
+        }
+
+        /// <summary>
+        /// Returns the current thread
+        /// </summary>
+        public static IThread GetCurrentThread(this IContextService contextService)
+        {
+            return contextService.Services.GetService<IThread>();
+        }
+
+        /// <summary>
+        /// Returns the current runtime
+        /// </summary>
+        public static IRuntime GetCurrentRuntime(this IContextService contextService)
+        {
+            return contextService.Services.GetService<IRuntime>();
+        }
+    }
+}
diff --git a/src/Microsoft.Diagnostics.DebugServices/IContextService.cs b/src/Microsoft.Diagnostics.DebugServices/IContextService.cs
new file mode 100644 (file)
index 0000000..1ecf284
--- /dev/null
@@ -0,0 +1,59 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Threading;
+
+namespace Microsoft.Diagnostics.DebugServices
+{
+    /// <summary>
+    /// Console output service
+    /// </summary>
+    public interface IContextService
+    {
+        /// <summary>
+        /// Current context service provider. Contains the current ITarget, IThread 
+        /// and IRuntime instances along with all per target and global services.
+        /// </summary>
+        IServiceProvider Services { get; }
+
+        /// <summary>
+        /// Fires anytime the current context changes.
+        /// </summary>
+        IServiceEvent OnContextChange { get; }
+
+        /// <summary>
+        /// Sets the current target.
+        /// </summary>
+        /// <param name="targetId">target id</param>
+        void SetCurrentTarget(int targetId);
+
+        /// <summary>
+        /// Clears (nulls) the current target
+        /// </summary>
+        void ClearCurrentTarget();
+
+        /// <summary>
+        /// Set the current thread.
+        /// </summary>
+        /// <param name="threadId">thread id</param>
+        void SetCurrentThread(uint threadId);
+
+        /// <summary>
+        /// Clears (nulls) the current thread.
+        /// </summary>
+        void ClearCurrentThread();
+
+        /// <summary>
+        /// Set the current runtime 
+        /// </summary>
+        /// <param name="runtimeId">runtime id</param>
+        void SetCurrentRuntime(int runtimeId);
+
+        /// <summary>
+        /// Clears (nulls) the current runtime
+        /// </summary>
+        void ClearCurrentRuntime();
+    }
+}
index 4031a2d141091774ceb8db9099a3a783f5136a82..4c60e6d15ded3684b95439a7ee6a9eb57f796baf 100644 (file)
@@ -45,14 +45,9 @@ namespace Microsoft.Diagnostics.DebugServices
         IEnumerable<ITarget> EnumerateTargets();
 
         /// <summary>
-        /// Current target instances or null
+        /// Destroys/closes the specified target instance
         /// </summary>
-        ITarget CurrentTarget { get; }
-
-        /// <summary>
-        /// Sets the current target.
-        /// </summary>
-        /// <param name="targetid">target id</param>
-        void SetCurrentTarget(int targetid);
+        /// <param name="target">target instance</param>
+        void DestroyTarget(ITarget target);
     }
 }
index 234a50db9114cda1be14c2cd41902eb7d858d5d6..3f346e4d2b715221bfd34bb9a2275459920ff4d9 100644 (file)
@@ -13,6 +13,11 @@ namespace Microsoft.Diagnostics.DebugServices
     /// </summary>
     public interface IModule
     {
+        /// <summary>
+        /// Target for this module.
+        /// </summary>
+        ITarget Target { get; }
+
         /// <summary>
         /// The per module services like an optional clrmd's PEImage or PEReader instances if a PE module.
         /// </summary>
index 1ac6e6ee94331efe7be895ac5655c2595ceae013..56bd7723ea8ed7279fc1b2f40da6e2f899654e4e 100644 (file)
@@ -23,14 +23,19 @@ namespace Microsoft.Diagnostics.DebugServices
     public interface IRuntime
     {
         /// <summary>
-        /// The per target services like clrmd's ClrInfo and ClrRuntime.
+        /// Runtime id
         /// </summary>
-        IServiceProvider Services { get; }
+        int Id { get; }
 
         /// <summary>
-        /// Runtime id
+        /// The target for this runtime.
         /// </summary>
-        int Id { get; }
+        ITarget Target { get; }
+
+        /// <summary>
+        /// The per target services like clrmd's ClrInfo and ClrRuntime.
+        /// </summary>
+        IServiceProvider Services { get; }
 
         /// <summary>
         /// Returns the runtime OS and type
@@ -42,6 +47,11 @@ namespace Microsoft.Diagnostics.DebugServices
         /// </summary>
         IModule RuntimeModule { get; }
 
+        /// <summary>
+        /// Directory of the runtime module (coreclr.dll, libcoreclr.so, etc.)
+        /// </summary>
+        string RuntimeModuleDirectory { get; set; }
+
         /// <summary>
         /// Returns the DAC file path
         /// </summary>
index 7d8c7b8504990c175499ba7d3c8f5c2437568fdd..240ff67c6d33fa4f24141e42883be1ef209276ae 100644 (file)
@@ -12,25 +12,9 @@ namespace Microsoft.Diagnostics.DebugServices
     /// </summary>
     public interface IRuntimeService
     {
-        /// <summary>
-        /// Directory of the runtime module (coreclr.dll, libcoreclr.so, etc.)
-        /// </summary>
-        string RuntimeModuleDirectory { get; set; }
-
         /// <summary>
         /// Returns the list of runtimes in the target
         /// </summary>
         IEnumerable<IRuntime> EnumerateRuntimes();
-
-        /// <summary>
-        /// Returns the current runtime or null if no runtime was found
-        /// </summary>
-        IRuntime CurrentRuntime { get; }
-
-        /// <summary>
-        /// Set the current runtime 
-        /// </summary>
-        /// <param name="runtimeId">runtime id</param>
-        void SetCurrentRuntime(int runtimeId);
     }
 }
index 6bb2497bc834d830b47aec446a2134fe93dbc5f3..bcbaed50adfcfec8a35d3041cadf6aedd3c97f74 100644 (file)
@@ -18,6 +18,14 @@ namespace Microsoft.Diagnostics.DebugServices
         /// <returns>An opaque IDisposable that will unregister the callback when disposed</returns>
         IDisposable Register(Action callback);
 
+        /// <summary>
+        /// Register for the event callback. Puts the new callback at the end of the list. Automatically 
+        /// removed from the event list when fired.
+        /// </summary>
+        /// <param name="callback">callback delegate</param>
+        /// <returns>An opaque IDisposable that will unregister the callback when disposed</returns>
+        IDisposable RegisterOneShot(Action callback);
+
         /// <summary>
         /// Fires the event
         /// </summary>
index 1a674a7df158a5255b3d342ae78a740444771683..4043cb99ccb7592a28aba8b689894da1e7e0012b 100644 (file)
@@ -13,11 +13,6 @@ namespace Microsoft.Diagnostics.DebugServices
     /// </summary>
     public interface ITarget
     {
-        /// <summary>
-        /// Invoked when this target is flushed (via the Flush() call).
-        /// </summary>
-        IServiceEvent OnFlushEvent { get; }
-
         /// <summary>
         /// Returns the host interface instance
         /// </summary>
@@ -60,19 +55,18 @@ namespace Microsoft.Diagnostics.DebugServices
         IServiceProvider Services { get; }
 
         /// <summary>
-        /// Flushes any cached state in the target.
+        /// Invoked when this target is flushed (via the Flush() call).
         /// </summary>
-        void Flush();
+        IServiceEvent OnFlushEvent { get; }
 
         /// <summary>
-        /// Registers an object to be disposed when ITarget.Close() is called.
+        /// Flushes any cached state in the target.
         /// </summary>
-        /// <param name="disposable">object to be disposed on Close() or null</param>
-        void DisposeOnClose(IDisposable disposable);
+        void Flush();
 
         /// <summary>
-        /// Releases the target and the target's resources.
+        /// Invoked when the target is destroyed.
         /// </summary>
-        void Close();
+        IServiceEvent OnDestroyEvent { get; }
     }
 }
index c91b1b9091cb6568325cc6cd9a171417b2f807c4..9d12c7da79a9b9c753bef097a9acf4fb026917fd 100644 (file)
@@ -11,11 +11,6 @@ namespace Microsoft.Diagnostics.DebugServices
     /// </summary>
     public interface IThread
     {
-        /// <summary>
-        /// The per thread services.
-        /// </summary>
-        IServiceProvider Services { get; }
-
         /// <summary>
         /// Debugger specific thread index.
         /// </summary>
@@ -26,6 +21,16 @@ namespace Microsoft.Diagnostics.DebugServices
         /// </summary>
         uint ThreadId { get; }
 
+        /// <summary>
+        /// The target for this target.
+        /// </summary>
+        ITarget Target { get; }
+
+        /// <summary>
+        /// The per thread services.
+        /// </summary>
+        IServiceProvider Services { get; }
+
         /// <summary>
         /// Returns the register value for the thread and register index. This function
         /// can only return register values that are 64 bits or less and currently the
index 35eadfc58bdbce44f24e07d277e78245adc38c23..2d9cb395beb7bfe43d8bcc9f8cd9ae512eba36c7 100644 (file)
@@ -48,11 +48,6 @@ namespace Microsoft.Diagnostics.DebugServices
         /// <returns>true if index found</returns>
         bool TryGetRegisterInfo(int registerIndex, out RegisterInfo info);
 
-        /// <summary>
-        /// Current OS thread Id
-        /// </summary>
-        uint? CurrentThreadId { get; set; }
-
         /// <summary>
         /// Enumerate all the native threads
         /// </summary>
@@ -60,7 +55,7 @@ namespace Microsoft.Diagnostics.DebugServices
         IEnumerable<IThread> EnumerateThreads();
 
         /// <summary>
-        /// Get the thread info from the thread index
+        /// Get the thread from the thread index
         /// </summary>
         /// <param name="threadIndex">index</param>
         /// <returns>thread info</returns>
@@ -68,7 +63,7 @@ namespace Microsoft.Diagnostics.DebugServices
         IThread GetThreadFromIndex(int threadIndex);
 
         /// <summary>
-        /// Get the thread info from the OS thread id
+        /// Get the thread from the OS thread id
         /// </summary>
         /// <param name="threadId">os id</param>
         /// <returns>thread info</returns>
index 5e12a425182e2d1a8dec93d48db87a4652cf6543..a8beb0692b9e5a104d5d1b6e42b8a065abce8b26 100644 (file)
@@ -18,7 +18,7 @@ namespace Microsoft.Diagnostics.DebugServices
         /// <returns>platform module name</returns>
         public static string GetPlatformModuleName(this ITarget target, string moduleName)
         {
-            if (target.OperatingSystem == OSPlatform.Windows) 
+            if (target.OperatingSystem == OSPlatform.Windows)
             {
                 return moduleName + ".dll";
             }
@@ -30,7 +30,18 @@ namespace Microsoft.Diagnostics.DebugServices
             {
                 return "lib" + moduleName + ".dylib";
             }
-            else throw new PlatformNotSupportedException(target.OperatingSystem.ToString());
+            throw new PlatformNotSupportedException(target.OperatingSystem.ToString());
+        }
+
+        /// <summary>
+        /// Registers an object to be disposed when target is destroyed.
+        /// </summary>
+        /// <param name="target">target instance</param>
+        /// <param name="disposable">object to be disposed or null</param>
+        /// <returns>IDisposable to unregister this event or null</returns>
+        public static IDisposable DisposeOnDestroy(this ITarget target, IDisposable disposable)
+        {
+            return disposable != null ? target.OnDestroyEvent.Register(() => disposable.Dispose()) : null;
         }
     }
 }
index 9bb4917650f8748120630ac30cee3dd28105c6ee..4f6a631cbb178d0a39946c11f6c7c605ac5ebb9d 100644 (file)
@@ -6,6 +6,7 @@ using Microsoft.Diagnostics.DebugServices;
 using Microsoft.Diagnostics.Runtime;
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Threading.Tasks;
 
 namespace Microsoft.Diagnostics.ExtensionCommands
@@ -17,7 +18,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands
 
         public ClrMDHelper(ClrRuntime clr)
         {
-            _clr = clr ?? throw new DiagnosticsException("No CLR runtime set");
+            Debug.Assert(clr != null);
+            _clr = clr;
             _heap = _clr.Heap;
         }
 
index 0e1195ef52e947b35fe3d4b23419a57245fda11a..f5b1d5e19587bc04007532067d701917a81351e7 100644 (file)
@@ -16,7 +16,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
 
         public ClrRuntime Runtime { get; set; }
 
-        public override void Invoke()
+        public override void ExtensionInvoke()
         {
             if (string.IsNullOrEmpty(Address))
             {
index 857f6eb889db1c818fce6a91257019302fbbea55..f886b5b923ac14750a4ebfe3981c4f9a99453f72 100644 (file)
@@ -16,7 +16,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
 
         public ClrRuntime Runtime { get; set; }
 
-        public override void Invoke()
+        public override void ExtensionInvoke()
         {
             if (string.IsNullOrEmpty(Address))
             {
index da86dcbd44fa714ee16e121903c9c41357b1cc96..5c50bb0d0627770fd32d2a2e9aad6271f1ce9672 100644 (file)
@@ -25,7 +25,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
         [Option(Name = "-mt", Help = "The address pointing on a Method table.")]
         public string MethodTableAddress { get; set; }
 
-        public override void Invoke()
+        public override void ExtensionInvoke()
         {
             var generation = ParseGenerationArgument(Generation);
             if (generation != GCGeneration.NotSet)
index f123345e7b9dfc81503e77eb0d4667452333f0f2..c2f4c099de72aea355647202813c762b0b09560c 100644 (file)
@@ -9,11 +9,21 @@ namespace Microsoft.Diagnostics.ExtensionCommands
     public abstract class ExtensionCommandBase : CommandBase
     {
         /// <summary>
-        /// Helper bound to the current ClrRuntime that provides
-        /// high level services on top of ClrMD.
+        /// Helper bound to the current ClrRuntime that provides high level services on top of ClrMD.
         /// </summary>
         public ClrMDHelper Helper { get; set; }
 
+        public override void Invoke()
+        {
+            if (Helper == null)
+            {
+                throw new DiagnosticsException("No CLR runtime set");
+            }
+            ExtensionInvoke();
+        }
+
+        public abstract void ExtensionInvoke();
+
         [HelpInvoke]
         public void InvokeHelp()
         {
index 97c846863a268d7d5132719190e81f5f8e7737f6..10ec2d68d1e49b2e9c1ed78e5e0f5ff1befdee76 100644 (file)
@@ -22,17 +22,36 @@ namespace Microsoft.Diagnostics.ExtensionCommands
         public override void Invoke()
         {
             if (Enable) {
-                if (Trace.Listeners[ListenerName] == null) {
-                    Trace.Listeners.Add(new LoggingListener());
-                    Trace.AutoFlush = true;
-                }
+                EnableLogging();
             }
             else if (Disable) {
-                Trace.Listeners.Remove(ListenerName);
+                DisableLogging();
             }
             WriteLine("Logging is {0}", Trace.Listeners[ListenerName] != null ? "enabled" : "disabled");
         }
 
+        public static void Initialize()
+        {
+            if (Environment.GetEnvironmentVariable("DOTNET_ENABLED_SOS_LOGGING") == "1")
+            {
+                EnableLogging();
+            }
+        }
+
+        public static void EnableLogging()
+        {
+            if (Trace.Listeners[ListenerName] == null)
+            {
+                Trace.Listeners.Add(new LoggingListener());
+                Trace.AutoFlush = true;
+            }
+        }
+
+        public static void DisableLogging()
+        {
+            Trace.Listeners.Remove(ListenerName);
+        }
+
         class LoggingListener : TraceListener
         {
             internal LoggingListener()
index a00b555372454042390ed6ce8a7d37bac0fcfde1..3b73de48c7a0b17523ec861a911f655c190533a2 100644 (file)
@@ -12,6 +12,8 @@ namespace Microsoft.Diagnostics.ExtensionCommands
     {
         public IRuntimeService RuntimeService { get; set; }
 
+        public IContextService ContextService { get; set; }
+
         [Option(Name = "-netfx", Help = "Switches to the desktop .NET Framework if exists.")]
         public bool NetFx { get; set; }
 
@@ -32,7 +34,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
                     if (NetFx && runtime.RuntimeType == RuntimeType.Desktop ||
                         NetCore && runtime.RuntimeType  == RuntimeType.NetCore)
                     {
-                        RuntimeService.SetCurrentRuntime(runtime.Id);
+                        ContextService.SetCurrentRuntime(runtime.Id);
                         WriteLine("Switched to {0} runtime successfully", name);
                         return;
                     }
@@ -46,7 +48,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
 
                 foreach (IRuntime runtime in RuntimeService.EnumerateRuntimes())
                 {
-                    string current = displayStar ? (runtime == RuntimeService.CurrentRuntime ? "*" : " ") : "";
+                    string current = displayStar ? (runtime == ContextService.GetCurrentRuntime() ? "*" : " ") : "";
                     Write(current);
                     Write(runtime.ToString());
                 }
index dfeb151ea177908efc512bb06bb72e74a379d18d..e7979568c4724ffaca2064d829d476498006e517 100644 (file)
@@ -10,7 +10,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
     [Command(Name = "setclrpath", Help = "Set the path to load coreclr DAC/DBI files.")]
     public class SetClrPath: CommandBase
     {
-        public IRuntimeService RuntimeService { get; set; }
+        public IRuntime Runtime { get; set; }
 
         [Argument(Name = "path", Help = "Runtime directory path.")]
         public string Argument { get; set; }
@@ -20,22 +20,22 @@ namespace Microsoft.Diagnostics.ExtensionCommands
 
         public override void Invoke()
         {
-            if (RuntimeService == null)
+            if (Runtime == null)
             {
-                throw new DiagnosticsException("Runtime service required");
+                throw new DiagnosticsException("Runtime required");
             }
             if (Clear)
             {
-                RuntimeService.RuntimeModuleDirectory = null;
+                Runtime.RuntimeModuleDirectory = null;
             }
             else if (Argument == null)
             {
-                WriteLine("Load path for DAC/DBI: '{0}'", RuntimeService.RuntimeModuleDirectory ?? "<none>");
+                WriteLine("Load path for DAC/DBI: '{0}'", Runtime.RuntimeModuleDirectory ?? "<none>");
             }
             else
             {
-                RuntimeService.RuntimeModuleDirectory = Path.GetFullPath(Argument);
-                WriteLine("Set load path for DAC/DBI to '{0}'", RuntimeService.RuntimeModuleDirectory);
+                Runtime.RuntimeModuleDirectory = Path.GetFullPath(Argument);
+                WriteLine("Set load path for DAC/DBI to '{0}'", Runtime.RuntimeModuleDirectory);
             }
         }
     }
index 4c7c2d2ac6bd7c9febdf4616a7c685cd44b4e69d..9f700c2928e15ab941d3600662ea33cc0d31268f 100644 (file)
@@ -18,8 +18,12 @@ namespace Microsoft.Diagnostics.ExtensionCommands
         [Option(Name = "--verbose", Aliases = new string[] { "-v" }, Help = "Displays more details.")]
         public bool Verbose { get; set; }
 
+        public IThread CurrentThread { get; set; }
+
         public IThreadService ThreadService { get; set; }
 
+        public IContextService ContextService { get; set; }
+
         public override void Invoke()
         {
             if (Thread.HasValue)
@@ -33,11 +37,11 @@ namespace Microsoft.Diagnostics.ExtensionCommands
                 {
                     thread = ThreadService.GetThreadFromIndex(unchecked((int)Thread.Value));
                 }
-                ThreadService.CurrentThreadId = thread.ThreadId;
+                ContextService.SetCurrentThread(thread.ThreadId);
             }
             else
             {
-                uint currentThreadId = ThreadService.CurrentThreadId.GetValueOrDefault(uint.MaxValue);
+                uint currentThreadId = CurrentThread != null ? CurrentThread.ThreadId : uint.MaxValue;
                 foreach (IThread thread in ThreadService.EnumerateThreads())
                 {
                     WriteLine("{0}{1} 0x{2:X4} ({2})", thread.ThreadId == currentThreadId ? "*" : " ", thread.ThreadIndex, thread.ThreadId);
index bb36d5fd40304f726d7e87d23cae270c15a4e9e2..ba07cac25ca6ac99c5b6e63e24c12e537b93a7e0 100644 (file)
@@ -17,7 +17,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
         [Option(Name = "--allthreads", Aliases = new string[] { "-a" }, Help = "Displays all threads per group instead of at most 4 by default.")]
         public bool AllThreads { get; set; }
 
-        public override void Invoke()
+        public override void ExtensionInvoke()
         {
             var ps = ParallelStacks.Runtime.ParallelStack.Build(Runtime);
             if (ps == null)
@@ -39,7 +39,6 @@ namespace Microsoft.Diagnostics.ExtensionCommands
             }
 
             WriteLine($"==> {ps.ThreadIds.Count} threads with {ps.Stacks.Count} roots{Environment.NewLine}");
-
         }
 
         protected override string GetDetailedHelp()
index 21b5030c5d6ccc99e840cabd46f5e7343232932b..0a41b18982aa23d9d3e1d9ac2fcdbeffd4f3186d 100644 (file)
@@ -16,7 +16,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
         [Option(Name = "--value", Aliases = new string[] { "-v" }, Help = "<value> is the value of a Task m_stateFlags field.")]
         public ulong? Value { get; set; }
 
-        public override void Invoke()
+        public override void ExtensionInvoke()
         {
             if (string.IsNullOrEmpty(Address) && !Value.HasValue)
             {
index 6c263bf22a02956b9bc710b16c09d5bb63ae7b5c..73b26e343c332f8e0b1ce39c83cba5f594999c90 100644 (file)
@@ -12,7 +12,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
     [Command(Name = "threadpoolqueue", Aliases = new string[] { "tpq" }, Help = "Display queued ThreadPool work items.")]
     public class ThreadPoolQueueCommand : ExtensionCommandBase
     {
-        public override void Invoke()
+        public override void ExtensionInvoke()
         {
             var workItems = new Dictionary<string, WorkInfo>();
             int workItemCount = 0;
index fe61d53b8884b386e403c9e748ea227cc92c2763..cf415baa14b7a2de664bab8e810d2389fd245205 100644 (file)
@@ -12,7 +12,7 @@ namespace Microsoft.Diagnostics.ExtensionCommands
     [Command(Name = "timerinfo", Aliases = new string[] { "ti" }, Help = "Display running timers details.")]
     public class TimersCommand : ExtensionCommandBase
     {
-        public override void Invoke()
+        public override void ExtensionInvoke()
         {
             try
             {
@@ -73,7 +73,6 @@ namespace Microsoft.Diagnostics.ExtensionCommands
             {
                 WriteLine(x.Message);
             }
-
         }
 
         static string GetTimerString(TimerInfo timer)
diff --git a/src/SOS/SOS.Extensions/ContextServiceFromDebuggerServices.cs b/src/SOS/SOS.Extensions/ContextServiceFromDebuggerServices.cs
new file mode 100644 (file)
index 0000000..849d472
--- /dev/null
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.DebugServices.Implementation;
+using Microsoft.Diagnostics.Runtime.Utilities;
+using System.Diagnostics;
+
+namespace SOS.Extensions
+{
+    /// <summary>
+    /// Provides the context services on native debuggers
+    /// </summary>
+    internal class ContextServiceFromDebuggerServices : ContextService
+    {
+        private readonly DebuggerServices _debuggerServices;
+
+        internal ContextServiceFromDebuggerServices(IHost host, DebuggerServices debuggerServices)
+            : base(host)
+        {
+            Debug.Assert(debuggerServices != null);
+            _debuggerServices = debuggerServices;
+        }
+
+        public override IThread GetCurrentThread()
+        {
+            HResult hr = _debuggerServices.GetCurrentThreadId(out uint threadId);
+            if (hr != HResult.S_OK)
+            {
+                Trace.TraceError("GetCurrentThreadId() FAILED {0:X8}", hr);
+                return null;
+            }
+            return ThreadService?.GetThreadFromId(threadId);
+        }
+
+        public override void SetCurrentThread(IThread thread)
+        {
+            if (thread != null)
+            {
+                HResult hr = _debuggerServices.SetCurrentThreadId(thread.ThreadId);
+                if (hr != HResult.S_OK)
+                {
+                    Trace.TraceError("SetCurrentThreadId() FAILED {0:X8}", hr);
+                    return;
+                }
+            }
+            base.SetCurrentThread(thread);
+        }
+    }
+}
index 30128a98c3f6e216ea2bda0f2f30e648212b238e..7424a7ae7d9e9321f086dc63fd3e006f028f1592 100644 (file)
@@ -42,7 +42,11 @@ namespace SOS.Extensions
         private readonly CommandProcessor _commandProcessor;
         private readonly SymbolService _symbolService;
         private readonly HostWrapper _hostWrapper;
+        private ContextServiceFromDebuggerServices _contextService;
+        private int _targetIdFactory;
         private ITarget _target;
+        private TargetWrapper _targetWrapper;
+        private IMemoryService _memoryService;
 
         /// <summary>
         /// Enable the assembly resolver to get the right versions in the same directory as this assembly.
@@ -52,6 +56,7 @@ namespace SOS.Extensions
             if (RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework")) {
                 AssemblyResolver.Enable();
             }
+            LoggingCommand.Initialize();
         }
 
         /// <summary>
@@ -101,9 +106,9 @@ namespace SOS.Extensions
             _serviceProvider.AddService<ICommandService>(_commandProcessor);
             _serviceProvider.AddService<ISymbolService>(_symbolService);
 
-            _hostWrapper = new HostWrapper(this);
+            _hostWrapper = new HostWrapper(this, () => _targetWrapper);
             _hostWrapper.AddServiceWrapper(IID_IHostServices, this);
-            _hostWrapper.AddServiceWrapper(SymbolServiceWrapper.IID_ISymbolService, () => new SymbolServiceWrapper(this));
+            _hostWrapper.AddServiceWrapper(SymbolServiceWrapper.IID_ISymbolService, () => new SymbolServiceWrapper(this, () => _memoryService));
 
             VTableBuilder builder = AddInterface(IID_IHostServices, validate: false);
             builder.AddMethod(new GetHostDelegate(GetHost));
@@ -123,6 +128,8 @@ namespace SOS.Extensions
         protected override void Destroy()
         {
             Trace.TraceInformation("HostServices.Destroy");
+            _hostWrapper.RemoveServiceWrapper(IID_IHostServices);
+            _hostWrapper.Release();
         }
 
         #region IHost
@@ -135,9 +142,27 @@ namespace SOS.Extensions
 
         IEnumerable<ITarget> IHost.EnumerateTargets() => _target != null ? new ITarget[] { _target } : Array.Empty<ITarget>();
 
-        ITarget IHost.CurrentTarget => _target;
-
-        void IHost.SetCurrentTarget(int targetid) => throw new NotImplementedException();
+        public void DestroyTarget(ITarget target)
+        {
+            if (target == null) {
+                throw new ArgumentNullException(nameof(target));
+            }
+            Trace.TraceInformation("IHost.DestroyTarget #{0}", target.Id);
+            if (target == _target)
+            {
+                _target = null;
+                _memoryService = null;
+                if (_targetWrapper != null)
+                {
+                    _targetWrapper.Release();
+                    _targetWrapper = null;
+                }
+                _contextService.ClearCurrentTarget();
+                if (target is IDisposable disposable) {
+                    disposable.Dispose();
+                }
+            }
+        }
 
         #endregion
 
@@ -156,6 +181,7 @@ namespace SOS.Extensions
             IntPtr self,
             IntPtr iunk)
         {
+            Trace.TraceInformation("HostServices.RegisterDebuggerServices");
             if (iunk == IntPtr.Zero || DebuggerServices != null) {
                 return HResult.E_FAIL;
             }
@@ -182,8 +208,15 @@ namespace SOS.Extensions
             {
                 var consoleService = new ConsoleServiceFromDebuggerServices(DebuggerServices);
                 _serviceProvider.AddService<IConsoleService>(consoleService);
+
+                _contextService = new ContextServiceFromDebuggerServices(this, DebuggerServices);
+                _serviceProvider.AddService<IContextService>(_contextService);
                 _serviceProvider.AddServiceFactory<IThreadUnwindService>(() => new ThreadUnwindServiceFromDebuggerServices(DebuggerServices));
-                Trace.TraceInformation("HostServices.RegisterDebuggerServices");
+
+                _contextService.ServiceProvider.AddServiceFactory<ClrMDHelper>(() => {
+                    ClrRuntime clrRuntime = _contextService.Services.GetService<ClrRuntime>();
+                    return clrRuntime != null ? new ClrMDHelper(clrRuntime) : null;
+                });
 
                 // Add each extension command to the native debugger
                 foreach ((string name, string help, IEnumerable<string> aliases) in _commandProcessor.Commands)
@@ -219,13 +252,15 @@ namespace SOS.Extensions
             IntPtr self)
         {
             Trace.TraceInformation("HostServices.CreateTarget");
-            if (_target != null) {
+            if (_target != null || DebuggerServices == null) {
                 return HResult.E_FAIL;
             }
             try
             {
-                _target = new TargetFromDebuggerServices(DebuggerServices, this);
-                _serviceProvider.AddService<ITarget>(_target);
+                _target = new TargetFromDebuggerServices(DebuggerServices, this, _targetIdFactory++);
+                _contextService.SetCurrentTarget(_target);
+                _targetWrapper = new TargetWrapper(_contextService.Services);
+                _memoryService = _contextService.Services.GetService<IMemoryService>();
             }
             catch (Exception ex)
             {
@@ -239,7 +274,7 @@ namespace SOS.Extensions
             IntPtr self,
             uint processId)
         {
-            Trace.TraceInformation($"HostServices.UpdateTarget {processId}");
+            Trace.TraceInformation("HostServices.UpdateTarget {0} #{1}", processId, _target != null ? _target.Id : "<none>");
             if (_target == null)
             {
                 return CreateTarget(self);
@@ -265,13 +300,10 @@ namespace SOS.Extensions
         private void DestroyTarget(
             IntPtr self)
         {
-            Trace.TraceInformation("HostServices.DestroyTarget {0}", _target != null ? _target.Id : "<none>");
-            _hostWrapper.DestroyTarget();
+            Trace.TraceInformation("HostServices.DestroyTarget #{0}", _target != null ? _target.Id : "<none>");
             if (_target != null)
             {
-                _serviceProvider.RemoveService(typeof(ITarget));
-                _target.Close();
-                _target = null;
+                DestroyTarget(_target);
             }
         }
 
@@ -285,7 +317,7 @@ namespace SOS.Extensions
             }
             try
             {
-                return _commandProcessor.Execute(commandLine, GetServices());
+                return _commandProcessor.Execute(commandLine, _contextService.Services);
             }
             catch (Exception ex)
             {
@@ -300,7 +332,7 @@ namespace SOS.Extensions
         {
             try
             {
-                if (!_commandProcessor.DisplayHelp(command, GetServices()))
+                if (!_commandProcessor.DisplayHelp(command, _contextService.Services))
                 {
                     return HResult.E_INVALIDARG;
                 }
@@ -317,51 +349,22 @@ namespace SOS.Extensions
             IntPtr self)
         {
             Trace.TraceInformation("HostServices.Uninitialize");
-            _hostWrapper.DestroyTarget();
-            if (_target != null)
+            DestroyTarget(self);
+
+            if (DebuggerServices != null)
             {
-                _target.Close();
-                _target = null;
+                DebuggerServices.Release();
+                DebuggerServices = null;
             }
-            DebuggerServices.Release();
-            DebuggerServices = null;
 
             // Send shutdown event on exit
             OnShutdownEvent.Fire();
+
+            // Release the host services wrapper
+            Release();
         }
 
         #endregion
-
-        private IServiceProvider GetServices()
-        {
-            // If there is no target, then provide just the global services
-            ServiceProvider services = _serviceProvider;
-            if (_target != null)
-            {
-                // Create a per command invocation service provider. These services may change between each command invocation.
-                services = new ServiceProvider(_target.Services);
-
-                // Add the current thread if any
-                services.AddServiceFactory<IThread>(() =>
-                {
-                    IThreadService threadService = _target.Services.GetService<IThreadService>();
-                    if (threadService != null && threadService.CurrentThreadId.HasValue) {
-                        return threadService.GetThreadFromId(threadService.CurrentThreadId.Value);
-                    }
-                    return null;
-                });
-
-                // Add the current runtime and related services
-                var runtimeService = _target.Services.GetService<IRuntimeService>();
-                if (runtimeService != null)
-                {
-                    services.AddServiceFactory<IRuntime>(() => runtimeService.CurrentRuntime);
-                    services.AddServiceFactory<ClrRuntime>(() => services.GetService<IRuntime>()?.Services.GetService<ClrRuntime>());
-                    services.AddServiceFactory<ClrMDHelper>(() => new ClrMDHelper(services.GetService<ClrRuntime>()));
-                }
-            }
-            return services;
-        }
         
         #region IHostServices delegates
 
index f10075daf50501e6a3ade31633cccfe14f11e515..3a44ba2f805735ae98ba62ea34e676da95eb06e6 100644 (file)
@@ -21,8 +21,8 @@ namespace SOS.Extensions
         /// <summary>
         /// Create a target instance from IDataReader
         /// </summary>
-        internal TargetFromDebuggerServices(DebuggerServices debuggerServices, IHost host)
-            : base(host, dumpPath: null)
+        internal TargetFromDebuggerServices(DebuggerServices debuggerServices, IHost host, int id)
+            : base(host, id, dumpPath: null)
         {
             Debug.Assert(debuggerServices != null);
 
index dfb28e7be206dd6626f19398fd3775686b6411f0..63dc87364efc92b8f6798890228dffceef8164ab 100644 (file)
@@ -24,32 +24,6 @@ namespace SOS.Extensions
             _debuggerServices = debuggerServices;
         }
 
-        #region IThreadService
-
-        public override uint? CurrentThreadId 
-        { 
-            get
-            {
-                HResult hr = _debuggerServices.GetCurrentThreadId(out uint threadId);
-                if (hr != HResult.S_OK)
-                {
-                    Trace.TraceError("GetCurrentThreadId() FAILED {0:X8}", hr);
-                    return null;
-                }
-                return threadId;
-            }
-            set
-            {
-                HResult hr = _debuggerServices.SetCurrentThreadId(value.Value);
-                if (hr != HResult.S_OK)
-                {
-                    Trace.TraceError("SetCurrentThreadId() FAILED {0:X8}", hr);
-                }
-            }
-        }
-
-        #endregion
-
         protected override bool GetThreadContext(uint threadId, uint contextFlags, uint contextSize, byte[] context)
         {
             return _debuggerServices.GetThreadContext(threadId, contextFlags, contextSize, context) == HResult.S_OK;
index 2b6023fc31dfa4c3a89670da24e091149e8eae20..5a4c3507dd3e77e55d4d3b08a90ff3c792b5bbfe 100644 (file)
@@ -18,23 +18,22 @@ namespace SOS.Hosting
         private static readonly Guid IID_ICorDebugMetaDataLocator = new Guid("7cef8ba9-2ef7-42bf-973f-4171474f87d9");
 
         private readonly ITarget _target;
+        private readonly ISymbolService _symbolService;
         private readonly IMemoryService _memoryService;
         private readonly IThreadService _threadService;
         private readonly IThreadUnwindService _threadUnwindService;
-        private readonly SymbolServiceWrapper _symbolServiceWrapper;
         private readonly ulong _ignoreAddressBitsMask;
 
         public IntPtr ICorDebugDataTarget { get; }
 
-        internal CorDebugDataTargetWrapper(ITarget target, IRuntime runtime)
+        internal CorDebugDataTargetWrapper(IServiceProvider services)
         {
-            Debug.Assert(target != null);
-            Debug.Assert(runtime != null);
-            _target = target;
-            _memoryService = target.Services.GetService<IMemoryService>();
-            _threadService = target.Services.GetService<IThreadService>();
-            _threadUnwindService = target.Services.GetService<IThreadUnwindService>();
-            _symbolServiceWrapper = new SymbolServiceWrapper(target.Host);
+            Debug.Assert(services != null);
+            _target = services.GetService<ITarget>();
+            _symbolService = services.GetService<ISymbolService>();
+            _memoryService = services.GetService<IMemoryService>();
+            _threadService = services.GetService<IThreadService>();
+            _threadUnwindService = services.GetService<IThreadUnwindService>();
             _ignoreAddressBitsMask = _memoryService.SignExtensionMask();
 
             VTableBuilder builder = AddInterface(IID_ICorDebugDataTarget, validate: false);
@@ -215,8 +214,7 @@ namespace SOS.Hosting
             IntPtr pPathBufferSize,
             IntPtr pPathBuffer)
         {
-            return _symbolServiceWrapper.GetICorDebugMetadataLocator(
-                IntPtr.Zero, imagePath, imageTimestamp, imageSize, pathBufferSize, pPathBufferSize, pPathBuffer);
+            return _symbolService.GetICorDebugMetadataLocator(imagePath, imageTimestamp, imageSize, pathBufferSize, pPathBufferSize, pPathBuffer);
         }
 
         #endregion
index 61a77b861df882504ec36a97427fba371e8e1910..57ec91d4fdef7a9538aa2fea7875c60dc690104f 100644 (file)
@@ -23,30 +23,30 @@ namespace SOS.Hosting
         // For ClrMD's magic hand shake
         private const ulong MagicCallbackConstant = 0x43;
 
-        private readonly ITarget _target;
+        private readonly IServiceProvider _services;
         private readonly ulong _runtimeBaseAddress;
+        private readonly ISymbolService _symbolService;
         private readonly IMemoryService _memoryService;
         private readonly IThreadService _threadService;
         private readonly IModuleService _moduleService;
         private readonly IThreadUnwindService _threadUnwindService;
         private readonly IRemoteMemoryService _remoteMemoryService;
-        private readonly SymbolServiceWrapper _symbolServiceWrapper;
         private readonly ulong _ignoreAddressBitsMask;
 
         public IntPtr IDataTarget { get; }
 
-        public DataTargetWrapper(ITarget target, IRuntime runtime)
+        public DataTargetWrapper(IServiceProvider services, IRuntime runtime)
         {
-            Debug.Assert(target != null);
+            Debug.Assert(services != null);
             Debug.Assert(runtime != null);
-            _target = target;
+            _services = services;
             _runtimeBaseAddress = runtime.RuntimeModule.ImageBase;
-            _memoryService = target.Services.GetService<IMemoryService>();
-            _threadService = target.Services.GetService<IThreadService>();
-            _threadUnwindService = target.Services.GetService<IThreadUnwindService>();
-            _moduleService = target.Services.GetService<IModuleService>();
-            _remoteMemoryService = target.Services.GetService<IRemoteMemoryService>();
-            _symbolServiceWrapper = new SymbolServiceWrapper(target.Host);
+            _symbolService = services.GetService<ISymbolService>();
+            _memoryService = services.GetService<IMemoryService>();
+            _threadService = services.GetService<IThreadService>();
+            _threadUnwindService = services.GetService<IThreadUnwindService>();
+            _moduleService = services.GetService<IModuleService>();
+            _remoteMemoryService = services.GetService<IRemoteMemoryService>();
             _ignoreAddressBitsMask = _memoryService.SignExtensionMask();
 
             VTableBuilder builder = AddInterface(IID_ICLRDataTarget, false);
@@ -103,7 +103,12 @@ namespace SOS.Hosting
             IntPtr self,
             out IMAGE_FILE_MACHINE machineType)
         {
-            machineType = _target.Architecture switch
+            ITarget target = _services.GetService<ITarget>();
+            if (target == null) {
+                machineType = IMAGE_FILE_MACHINE.UNKNOWN;
+                return HResult.E_FAIL;
+            }
+            machineType = target.Architecture switch
             {
                 Architecture.X64 => IMAGE_FILE_MACHINE.AMD64,
                 Architecture.X86 => IMAGE_FILE_MACHINE.I386,
@@ -195,7 +200,7 @@ namespace SOS.Hosting
             IntPtr self,
             out uint threadId)
         {
-            uint? id = _threadService.CurrentThreadId;
+            uint? id = _services.GetService<IThread>()?.ThreadId;
             if (id.HasValue)
             {
                 threadId = id.Value;
@@ -330,8 +335,7 @@ namespace SOS.Hosting
             IntPtr buffer,
             IntPtr dataSize)
         {
-            return _symbolServiceWrapper.GetMetadataLocator(
-                IntPtr.Zero, fileName, imageTimestamp, imageSize, mvid, mdRva, flags, bufferSize, buffer, dataSize);
+            return _symbolService.GetMetadataLocator(fileName, imageTimestamp, imageSize, mvid, mdRva, flags, bufferSize, buffer, dataSize);
         }
 
         #endregion
@@ -484,4 +488,4 @@ namespace SOS.Hosting
 
         #endregion
     }
-}
\ No newline at end of file
+}
index 86c7074770a0056af0ee61453f55db66b9c38d72..042bb40ca518471af36b8ec0d35c1b1b053db706 100644 (file)
@@ -16,16 +16,16 @@ namespace SOS.Hosting
         private static readonly Guid IID_IHost = new Guid("E0CD8534-A88B-40D7-91BA-1B4C925761E9");
 
         private readonly IHost _host;
+        private readonly Func<TargetWrapper> _getTarget;
         private readonly Dictionary<Guid, Func<COMCallableIUnknown>> _factories = new Dictionary<Guid, Func<COMCallableIUnknown>>();
         private readonly Dictionary<Guid, COMCallableIUnknown> _wrappers = new Dictionary<Guid, COMCallableIUnknown>();
 
-        private TargetWrapper _targetWrapper;
-
         public IntPtr IHost { get; }
 
-        public HostWrapper(IHost host)
+        public HostWrapper(IHost host, Func<TargetWrapper> getTarget)
         {
             _host = host;
+            _getTarget = getTarget;
 
             VTableBuilder builder = AddInterface(IID_IHost, validate: false);
             builder.AddMethod(new GetHostTypeDelegate(GetHostType));
@@ -39,6 +39,11 @@ namespace SOS.Hosting
         protected override void Destroy()
         {
             Trace.TraceInformation("HostWrapper.Destroy");
+            foreach (var wrapper in _wrappers.Values)
+            {
+                wrapper.Release();
+            }
+            _wrappers.Clear();
         }
 
         /// <summary>
@@ -61,6 +66,16 @@ namespace SOS.Hosting
             _wrappers.Add(serviceId, service);
         }
 
+        /// <summary>
+        /// Remove the service instance
+        /// </summary>
+        /// <param name="serviceId">guid</param>
+        public void RemoveServiceWrapper(in Guid serviceId)
+        {
+            _factories.Remove(serviceId);
+            _wrappers.Remove(serviceId);
+        }
+
         /// <summary>
         /// Returns the wrapper instance for the guid
         /// </summary>
@@ -82,15 +97,6 @@ namespace SOS.Hosting
             return service;
         }
 
-        public void DestroyTarget()
-        {
-            if (_targetWrapper != null)
-            {
-                _targetWrapper.Release();
-                _targetWrapper = null;
-            }
-        }
-
         #region IHost
 
         /// <summary>
@@ -111,8 +117,7 @@ namespace SOS.Hosting
             ptr = IntPtr.Zero;
 
             COMCallableIUnknown wrapper = GetServiceWrapper(guid);
-            if (wrapper == null)
-            {
+            if (wrapper == null) {
                 return HResult.E_NOINTERFACE;
             }
             wrapper.AddRef();
@@ -122,22 +127,18 @@ namespace SOS.Hosting
         /// <summary>
         /// Returns the current target wrapper or null
         /// </summary>
-        /// <param name="target">target wrapper address returned</param>
+        /// <param name="targetWrapper">target wrapper address returned</param>
         /// <returns>S_OK</returns>
-        private HResult GetCurrentTarget(IntPtr self, out IntPtr target)
+        private HResult GetCurrentTarget(IntPtr self, out IntPtr targetWrapper)
         {
-            target = IntPtr.Zero;
-
-            if (_host.CurrentTarget == null) {
+            TargetWrapper wrapper = _getTarget();
+            if (wrapper == null)
+            {
+                targetWrapper = IntPtr.Zero;
                 return HResult.E_NOINTERFACE;
             }
-            // TODO: this only supports one target instance. Need to add a dictionary lookup for multi-target support.
-            if (_targetWrapper == null) {
-                _targetWrapper = new TargetWrapper(_host.CurrentTarget);
-            }
-            _targetWrapper.AddRef();
-            target = _targetWrapper.ITarget;
-
+            wrapper.AddRef();
+            targetWrapper = wrapper.ITarget;
             return HResult.S_OK;
         }
 
index ad130b310f5a25201ae2d54d16bfee87da3f02bf..f954c39919574537ac882c8b07ce4d807413573c 100644 (file)
@@ -99,9 +99,8 @@ namespace SOS.Hosting
         string GetCoreClrDirectory(
             IntPtr self)
         {
-            IRuntime currentRuntime = _soshost.Target.Services.GetService<IRuntimeService>()?.CurrentRuntime;
-            if (currentRuntime != null)
-            {
+            IRuntime currentRuntime = _soshost.Services.GetService<IRuntime>();
+            if (currentRuntime is not null) {
                 return Path.GetDirectoryName(currentRuntime.RuntimeModule.FileName);
             }
             return null;
index bcfd1fe6ac30e963afa647b587d3832ece73a8f6..87f9b31d2e70303fb57068e95097f57c2ab70a8b 100644 (file)
@@ -21,11 +21,11 @@ namespace SOS.Hosting
         /// </summary>
         enum RuntimeConfiguration
         {
-            WindowsDesktop      = 0,
-            WindowsCore         = 1,
-            UnixCore            = 2,
-            OSXCore             = 3,
-            Unknown             = 4
+            WindowsDesktop = 0,
+            WindowsCore = 1,
+            UnixCore = 2,
+            OSXCore = 3,
+            Unknown = 4
         }
 
         private static readonly Guid IID_IRuntime = new Guid("A5F152B9-BA78-4512-9228-5091A4CB7E35");
@@ -81,7 +81,7 @@ namespace SOS.Hosting
 
         #endregion
 
-        private readonly ITarget _target;
+        private readonly IServiceProvider _services;
         private readonly IRuntime _runtime;
         private readonly IDisposable _onFlushEvent;
         private IntPtr _clrDataProcess = IntPtr.Zero;
@@ -91,25 +91,20 @@ namespace SOS.Hosting
 
         public IntPtr IRuntime { get; }
 
-        internal RuntimeWrapper(ITarget target, IRuntime runtime)
+        internal RuntimeWrapper(IServiceProvider services, IRuntime runtime)
         {
-            Debug.Assert(target != null);
+            Debug.Assert(services != null);
             Debug.Assert(runtime != null);
-            _target = target;
+            _services = services;
             _runtime = runtime;
-
-            _onFlushEvent = target.OnFlushEvent.Register(() => {
-                // TODO: there is a better way to flush _corDebugProcess with ICorDebugProcess4::ProcessStateChanged(FLUSH_ALL)
-                _corDebugProcess = IntPtr.Zero;
-                // TODO: there is a better way to flush _clrDataProcess with ICLRDataProcess::Flush()
-                _clrDataProcess = IntPtr.Zero;
-            });
+            _onFlushEvent = runtime.Target.OnFlushEvent.Register(Flush);
 
             VTableBuilder builder = AddInterface(IID_IRuntime, validate: false);
 
             builder.AddMethod(new GetRuntimeConfigurationDelegate(GetRuntimeConfiguration));
             builder.AddMethod(new GetModuleAddressDelegate(GetModuleAddress));
             builder.AddMethod(new GetModuleSizeDelegate(GetModuleSize));
+            builder.AddMethod(new SetRuntimeDirectoryDelegate(SetRuntimeDirectory));
             builder.AddMethod(new GetRuntimeDirectoryDelegate(GetRuntimeDirectory));
             builder.AddMethod(new GetClrDataProcessDelegate(GetClrDataProcess));
             builder.AddMethod(new GetCorDebugInterfaceDelegate(GetCorDebugInterface));
@@ -124,6 +119,7 @@ namespace SOS.Hosting
         {
             Trace.TraceInformation("RuntimeWrapper.Destroy");
             _onFlushEvent.Dispose();
+            Flush();
             if (_dacHandle != IntPtr.Zero)
             {
                 DataTarget.PlatformFunctions.FreeLibrary(_dacHandle);
@@ -136,6 +132,22 @@ namespace SOS.Hosting
             }
         }
 
+        private void Flush()
+        {
+            // TODO: there is a better way to flush _corDebugProcess with ICorDebugProcess4::ProcessStateChanged(FLUSH_ALL)
+            if (_corDebugProcess == IntPtr.Zero)
+            {
+                COMHelper.Release(_corDebugProcess);
+                _corDebugProcess = IntPtr.Zero;
+            }
+            // TODO: there is a better way to flush _clrDataProcess with ICLRDataProcess::Flush()
+            if (_clrDataProcess == IntPtr.Zero)
+            {
+                COMHelper.Release(_clrDataProcess);
+                _clrDataProcess = IntPtr.Zero;
+            }
+        }
+
         #region IRuntime (native)
 
         private RuntimeConfiguration GetRuntimeConfiguration(
@@ -148,11 +160,11 @@ namespace SOS.Hosting
 
                 case RuntimeType.NetCore:
                 case RuntimeType.SingleFile:
-                    if (_target.OperatingSystem == OSPlatform.Windows)
+                    if (_runtime.Target.OperatingSystem == OSPlatform.Windows)
                     {
                         return RuntimeConfiguration.WindowsCore;
                     }
-                    else if (_target.OperatingSystem == OSPlatform.Linux || _target.OperatingSystem == OSPlatform.OSX)
+                    else if (_runtime.Target.OperatingSystem == OSPlatform.Linux || _runtime.Target.OperatingSystem == OSPlatform.OSX)
                     {
                         return RuntimeConfiguration.UnixCore;
                     }
@@ -173,9 +185,20 @@ namespace SOS.Hosting
             return _runtime.RuntimeModule.ImageSize;
         }
 
+        private void SetRuntimeDirectory(
+            IntPtr self,
+            string runtimeModuleDirectory)
+        { 
+            _runtime.RuntimeModuleDirectory = runtimeModuleDirectory;
+        }
+
         private string GetRuntimeDirectory(
             IntPtr self)
         {
+            if (_runtime.RuntimeModuleDirectory is not null)
+            {
+                return _runtime.RuntimeModuleDirectory;
+            }
             return Path.GetDirectoryName(_runtime.RuntimeModule.FileName);
         }
 
@@ -219,7 +242,7 @@ namespace SOS.Hosting
             byte* fileVersionBuffer,
             int fileVersionBufferSizeInBytes)
         {
-            IModuleService moduleService = _target.Services.GetService<IModuleService>();
+            IModuleService moduleService = _services.GetService<IModuleService>();
             IModule module;
             try
             {
@@ -282,7 +305,7 @@ namespace SOS.Hosting
                 Trace.TraceError("Failed to obtain DAC CLRDataCreateInstance");
                 return IntPtr.Zero;
             }
-            var dataTarget = new DataTargetWrapper(_target, _runtime);
+            var dataTarget = new DataTargetWrapper(_services, _runtime);
             int hr = createInstance(IID_IXCLRDataProcess, dataTarget.IDataTarget, out IntPtr unk);
             if (hr != 0)
             {
@@ -317,7 +340,7 @@ namespace SOS.Hosting
                 Build = 0,
                 Revision = 0,
             };
-            var dataTarget = new CorDebugDataTargetWrapper(_target,  _runtime);
+            var dataTarget = new CorDebugDataTargetWrapper(_services);
             ulong clrInstanceId = _runtime.RuntimeModule.ImageBase;
             int hresult = 0;
 
@@ -451,6 +474,11 @@ namespace SOS.Hosting
         private delegate ulong GetModuleSizeDelegate(
             [In] IntPtr self);
 
+        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+        private delegate void SetRuntimeDirectoryDelegate(
+            [In] IntPtr self,
+            [In, MarshalAs(UnmanagedType.LPStr)] string runtimeModuleDirectory);
+
         [UnmanagedFunctionPointer(CallingConvention.Winapi)]
         [return: MarshalAs(UnmanagedType.LPStr)]
         private delegate string GetRuntimeDirectoryDelegate(
index de9d7df03fc427d67330115efeb15f53e8f2a6db..5189d5858999d01f242df9856a35ec237d6adf67 100644 (file)
@@ -10,7 +10,6 @@ using System;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
-using System.Reflection;
 using System.Runtime.InteropServices;
 using System.Text;
 using Architecture = System.Runtime.InteropServices.Architecture;
@@ -18,60 +17,43 @@ using Architecture = System.Runtime.InteropServices.Architecture;
 namespace SOS.Hosting
 {
     /// <summary>
-    /// Helper code to hosting SOS under ClrMD
+    /// Helper code to hosting the native SOS code
     /// </summary>
     public sealed class SOSHost : IDisposable
     {
-        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
-        private delegate int SOSCommandDelegate(
-            IntPtr ILLDBServices,
-            [In, MarshalAs(UnmanagedType.LPStr)] string args);
-
-        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
-        private delegate int SOSInitializeDelegate(
-            IntPtr IHost);
-
-        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
-        private delegate void SOSUninitializeDelegate();
-
-        private const string SOSInitialize = "SOSInitializeByHost";
-        private const string SOSUninitialize = "SOSUninitializeByHost";
-
         // This is what dbgeng/IDebuggerServices returns for non-PE modules that don't have a timestamp
         internal const uint InvalidTimeStamp = 0xFFFFFFFE;
         internal const uint InvalidChecksum = 0xFFFFFFFF;
 
+        internal readonly IServiceProvider Services;
         internal readonly ITarget Target;
+        internal readonly TargetWrapper TargetWrapper;
         internal readonly IConsoleService ConsoleService;
         internal readonly IModuleService ModuleService;
         internal readonly IThreadService ThreadService;
         internal readonly IMemoryService MemoryService;
+        private readonly SOSLibrary _sosLibrary;
         private readonly IntPtr _interface;
-        private readonly HostWrapper _hostWrapper;
         private readonly ulong _ignoreAddressBitsMask;
-        private IntPtr _sosLibrary = IntPtr.Zero;
         private bool _disposed;
 
         /// <summary>
-        /// The native SOS binaries path. Default is OS/architecture (RID) named directory in the same directory as this assembly.
+        /// Create an instance of the hosting class. Has the lifetime of the target. Depends on the
+        /// context service for the current thread and runtime.
         /// </summary>
-        public string SOSPath { get; set; }
-
-        /// <summary>
-        /// Create an instance of the hosting class
-        /// </summary>
-        /// <param name="target">target instance</param>
-        public SOSHost(ITarget target)
-        {
-            Target = target;
-            ConsoleService = target.Services.GetService<IConsoleService>();
-            ModuleService = target.Services.GetService<IModuleService>();
-            ThreadService = target.Services.GetService<IThreadService>();
-            MemoryService = target.Services.GetService<IMemoryService>();
+        /// <param name="services">service provider</param>
+        public SOSHost(IServiceProvider services)
+        {
+            Services = services;
+            Target = services.GetService<ITarget>() ?? throw new DiagnosticsException("No target");
+            TargetWrapper = new TargetWrapper(services);
+            Target.DisposeOnDestroy(this);
+            ConsoleService = services.GetService<IConsoleService>();
+            ModuleService = services.GetService<IModuleService>();
+            ThreadService = services.GetService<IThreadService>();
+            MemoryService = services.GetService<IMemoryService>();
             _ignoreAddressBitsMask = MemoryService.SignExtensionMask();
-
-            string rid = InstallHelper.GetRid();
-            SOSPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), rid);
+            _sosLibrary = services.GetService<SOSLibrary>();
 
             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
             {
@@ -83,97 +65,19 @@ namespace SOS.Hosting
                 var lldbServices = new LLDBServices(this);
                 _interface = lldbServices.ILLDBServices;
             }
-            _hostWrapper = new HostWrapper(target.Host);
-            _hostWrapper.AddServiceWrapper(SymbolServiceWrapper.IID_ISymbolService, () => new SymbolServiceWrapper(target.Host));
-        }
-
-        /// <summary>
-        /// Loads and initializes the SOS module.
-        /// </summary>
-        public void InitializeSOSHost()
-        {
-            if (_sosLibrary == IntPtr.Zero)
-            {
-                string sos;
-                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
-                    sos = "sos.dll";
-                }
-                else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
-                    sos = "libsos.so";
-                }
-                else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
-                    sos = "libsos.dylib";
-                }
-                else {
-                    throw new PlatformNotSupportedException($"Unsupported operating system: {RuntimeInformation.OSDescription}");
-                }
-                string sosPath = Path.Combine(SOSPath, sos);
-                try
-                {
-                    _sosLibrary = Microsoft.Diagnostics.Runtime.DataTarget.PlatformFunctions.LoadLibrary(sosPath);
-                }
-                catch (DllNotFoundException ex)
-                {
-                    // This is a workaround for the Microsoft SDK docker images. Can fail when LoadLibrary uses libdl.so to load the SOS module.
-                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
-                    {
-                        throw new DllNotFoundException("Problem loading SOS module. Try installing libc6-dev (apt-get install libc6-dev) to work around this problem.", ex);
-                    }
-                    else
-                    {
-                        throw;
-                    }
-                }
-                if (_sosLibrary == IntPtr.Zero)
-                {
-                    throw new FileNotFoundException($"SOS module {sosPath} not found");
-                }
-                var initializeFunc = GetDelegateFunction<SOSInitializeDelegate>(_sosLibrary, SOSInitialize);
-                if (initializeFunc == null)
-                {
-                    throw new EntryPointNotFoundException($"Can not find SOS module initialization function: {SOSInitialize}");
-                }
-                Target.DisposeOnClose(Target.Host.OnShutdownEvent.Register(OnShutdownEvent));
-                int result = initializeFunc(_hostWrapper.IHost);
-                if (result != 0)
-                {
-                    throw new InvalidOperationException($"SOS initialization FAILED 0x{result:X8}");
-                }
-                Trace.TraceInformation("SOS initialized: sosPath '{0}'", sosPath);
-            }
         }
 
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        ~SOSHost()
-        {
-            Dispose(false);
-        }
-
-        private void Dispose(bool _)
+        void IDisposable.Dispose()
         {
+            Trace.TraceInformation($"SOSHost.Dispose {_disposed}");
             if (!_disposed)
             {
-                Trace.TraceInformation("SOSHost.Dispose");
                 _disposed = true;
+                TargetWrapper.Release();
                 COMHelper.Release(_interface);
             }
         }
 
-        /// <summary>
-        /// Shutdown/clean up the native SOS.
-        /// </summary>
-        private void OnShutdownEvent()
-        {
-            Trace.TraceInformation("SOSHost: OnShutdownEvent");
-            var uninitializeFunc = GetDelegateFunction<SOSUninitializeDelegate>(_sosLibrary, SOSUninitialize);
-            uninitializeFunc?.Invoke();
-        }
-
         /// <summary>
         /// Execute a SOS command.
         /// </summary>
@@ -198,19 +102,11 @@ namespace SOS.Hosting
         /// <param name="command">just the command name</param>
         /// <param name="arguments">the command arguments and options</param>
         public void ExecuteCommand(string command, string arguments)
-        {
-            Debug.Assert(_sosLibrary != null);
-
-            var commandFunc = GetDelegateFunction<SOSCommandDelegate>(_sosLibrary, command);
-            if (commandFunc == null)
-            {
-                throw new EntryPointNotFoundException($"Can not find SOS command: {command}");
-            }
-            int result = commandFunc(_interface, arguments ?? "");
-            if (result != HResult.S_OK)
-            {
-                Trace.TraceError($"SOS command FAILED 0x{result:X8}");
+        { 
+            if (_disposed) {
+                throw new ObjectDisposedException("SOSHost instance disposed");
             }
+            _sosLibrary.ExecuteCommand(_interface, command, arguments);
         }
 
         #region Reverse PInvoke Implementations
@@ -681,11 +577,12 @@ namespace SOS.Hosting
             IntPtr context,
             uint contextSize)
         {
-            if (!ThreadService.CurrentThreadId.HasValue)
+            IThread thread = Services.GetService<IThread>();
+            if (thread is not null)
             {
-                return HResult.E_FAIL;
+                return GetThreadContextBySystemId(self, thread.ThreadId, 0, contextSize, context);
             }
-            return GetThreadContextBySystemId(self, ThreadService.CurrentThreadId.Value, 0, contextSize, context);
+            return HResult.E_FAIL;
         }
 
         internal int GetThreadContextBySystemId(
@@ -757,11 +654,12 @@ namespace SOS.Hosting
             IntPtr self,
             out uint id)
         {
-            if (!ThreadService.CurrentThreadId.HasValue) {
-                id = 0;
-                return HResult.E_FAIL;
+            IThread thread = Services.GetService<IThread>();
+            if (thread is not null) { 
+                return GetThreadIdBySystemId(self, thread.ThreadId, out id);
             }
-            return GetThreadIdBySystemId(self, ThreadService.CurrentThreadId.Value, out id);
+            id = 0;
+            return HResult.E_FAIL;
         }
 
         internal int SetCurrentThreadId(
@@ -770,7 +668,11 @@ namespace SOS.Hosting
         {
             try
             {
-                ThreadService.CurrentThreadId = ThreadService.GetThreadFromIndex(unchecked((int)id)).ThreadId;
+                var contextService = Services.GetService<IContextService>();
+                if (contextService is null) {
+                    return HResult.E_FAIL;
+                }
+                contextService.SetCurrentThread(ThreadService.GetThreadFromIndex(unchecked((int)id)).ThreadId);
             }
             catch (DiagnosticsException)
             {
@@ -783,10 +685,10 @@ namespace SOS.Hosting
             IntPtr self,
             out uint sysId)
         {
-            uint? id = ThreadService.CurrentThreadId;
-            if (id.HasValue)
-            {
-                sysId = id.Value;
+            IThread thread = Services.GetService<IThread>();
+            if (thread is not null)
+            { 
+                sysId = thread.ThreadId;
                 return HResult.S_OK;
             }
             sysId = 0;
@@ -847,12 +749,12 @@ namespace SOS.Hosting
             IntPtr self,
             ulong* offset)
         {
-            if (ThreadService.CurrentThreadId.HasValue)
-            {
-                uint threadId = ThreadService.CurrentThreadId.Value;
+            IThread thread = Services.GetService<IThread>();
+            if (thread is not null)
+            { 
                 try
                 {
-                    ulong teb = ThreadService.GetThreadFromId(threadId).GetThreadTeb();
+                    ulong teb = thread.GetThreadTeb();
                     Write(offset, teb);
                     return HResult.S_OK;
                 }
@@ -946,15 +848,12 @@ namespace SOS.Hosting
             int index, 
             out ulong value)
         {
-            if (ThreadService.CurrentThreadId.HasValue)
-            {
-                IThread thread = ThreadService.GetThreadFromId(ThreadService.CurrentThreadId.Value);
-                if (thread != null)
+            IThread thread = Services.GetService<IThread>();
+            if (thread is not null)
+            { 
+                if (thread.TryGetRegisterValue(index, out value))
                 {
-                    if (thread.TryGetRegisterValue(index, out value))
-                    {
-                        return HResult.S_OK;
-                    }
+                    return HResult.S_OK;
                 }
             }
             value = 0;
diff --git a/src/SOS/SOS.Hosting/SOSLibrary.cs b/src/SOS/SOS.Hosting/SOSLibrary.cs
new file mode 100644 (file)
index 0000000..6d0ce84
--- /dev/null
@@ -0,0 +1,176 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.Runtime.Utilities;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace SOS.Hosting
+{
+    /// <summary>
+    /// Helper code to load and initialize SOS
+    /// </summary>
+    public sealed class SOSLibrary
+    {
+        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+        private delegate int SOSCommandDelegate(
+            IntPtr ILLDBServices,
+            [In, MarshalAs(UnmanagedType.LPStr)] string args);
+
+        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+        private delegate int SOSInitializeDelegate(
+            IntPtr IHost);
+
+        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+        private delegate void SOSUninitializeDelegate();
+
+        private const string SOSInitialize = "SOSInitializeByHost";
+        private const string SOSUninitialize = "SOSUninitializeByHost";
+
+        private readonly IContextService _contextService;
+        private readonly HostWrapper _hostWrapper;
+        private IntPtr _sosLibrary = IntPtr.Zero;
+
+        /// <summary>
+        /// The native SOS binaries path. Default is OS/architecture (RID) named directory in the same directory as this assembly.
+        /// </summary>
+        public string SOSPath { get; set; }
+
+        public static SOSLibrary Create(IHost host)
+        {
+            SOSLibrary sosLibrary = null;
+            try
+            {
+                sosLibrary = new SOSLibrary(host);
+                sosLibrary.Initialize();
+            }
+            catch
+            {
+                sosLibrary.Uninitialize();
+                sosLibrary = null;
+                throw;
+            }
+            host.OnShutdownEvent.Register(() => {
+                sosLibrary.Uninitialize();
+                sosLibrary = null;
+            });
+            return sosLibrary;
+        }
+
+        /// <summary>
+        /// Create an instance of the hosting class
+        /// </summary>
+        /// <param name="target">target instance</param>
+        private SOSLibrary(IHost host)
+        {
+            _contextService = host.Services.GetService<IContextService>();
+
+            string rid = InstallHelper.GetRid();
+            SOSPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), rid);
+
+            _hostWrapper = new HostWrapper(host, () => GetSOSHost()?.TargetWrapper);
+            _hostWrapper.AddServiceWrapper(SymbolServiceWrapper.IID_ISymbolService, () => new SymbolServiceWrapper(host, () => GetSOSHost()?.MemoryService));
+        }
+
+        /// <summary>
+        /// Loads and initializes the SOS module.
+        /// </summary>
+        private void Initialize()
+        {
+            if (_sosLibrary == IntPtr.Zero)
+            {
+                string sos;
+                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
+                    sos = "sos.dll";
+                }
+                else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
+                    sos = "libsos.so";
+                }
+                else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
+                    sos = "libsos.dylib";
+                }
+                else {
+                    throw new PlatformNotSupportedException($"Unsupported operating system: {RuntimeInformation.OSDescription}");
+                }
+                string sosPath = Path.Combine(SOSPath, sos);
+                try
+                {
+                    _sosLibrary = Microsoft.Diagnostics.Runtime.DataTarget.PlatformFunctions.LoadLibrary(sosPath);
+                }
+                catch (DllNotFoundException ex)
+                {
+                    // This is a workaround for the Microsoft SDK docker images. Can fail when LoadLibrary uses libdl.so to load the SOS module.
+                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+                    {
+                        throw new DllNotFoundException("Problem loading SOS module. Try installing libc6-dev (apt-get install libc6-dev) to work around this problem.", ex);
+                    }
+                    else
+                    {
+                        throw;
+                    }
+                }
+                if (_sosLibrary == IntPtr.Zero)
+                {
+                    throw new FileNotFoundException($"SOS module {sosPath} not found");
+                }
+                var initializeFunc = SOSHost.GetDelegateFunction<SOSInitializeDelegate>(_sosLibrary, SOSInitialize);
+                if (initializeFunc == null)
+                {
+                    throw new EntryPointNotFoundException($"Can not find SOS module initialization function: {SOSInitialize}");
+                }
+                int result = initializeFunc(_hostWrapper.IHost);
+                if (result != 0)
+                {
+                    throw new InvalidOperationException($"SOS initialization FAILED 0x{result:X8}");
+                }
+                Trace.TraceInformation("SOS initialized: sosPath '{0}'", sosPath);
+            }
+        }
+
+        /// <summary>
+        /// Shutdown/clean up the native SOS module.
+        /// </summary>
+        private void Uninitialize()
+        {
+            Trace.TraceInformation("SOSHost: Uninitialize");
+            if (_sosLibrary != IntPtr.Zero)
+            {
+                var uninitializeFunc = SOSHost.GetDelegateFunction<SOSUninitializeDelegate>(_sosLibrary, SOSUninitialize);
+                uninitializeFunc?.Invoke();
+
+                Microsoft.Diagnostics.Runtime.DataTarget.PlatformFunctions.FreeLibrary(_sosLibrary);
+                _sosLibrary = IntPtr.Zero;
+            }
+            _hostWrapper.Release();
+        }
+
+        /// <summary>
+        /// Execute a SOS command.
+        /// </summary>
+        /// <param name="client">client interface</param>
+        /// <param name="command">just the command name</param>
+        /// <param name="arguments">the command arguments and options</param>
+        public void ExecuteCommand(IntPtr client, string command, string arguments)
+        {
+            Debug.Assert(_sosLibrary != IntPtr.Zero);
+
+            var commandFunc = SOSHost.GetDelegateFunction<SOSCommandDelegate>(_sosLibrary, command);
+            if (commandFunc == null)
+            {
+                throw new EntryPointNotFoundException($"Can not find SOS command: {command}");
+            }
+            int result = commandFunc(client, arguments ?? "");
+            if (result != HResult.S_OK)
+            {
+                Trace.TraceError($"SOS command FAILED 0x{result:X8}");
+            }
+        }
+
+        private SOSHost GetSOSHost() => _contextService.Services.GetService<SOSHost>();
+    }
+}
diff --git a/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs b/src/SOS/SOS.Hosting/SymbolServiceExtensions.cs
new file mode 100644 (file)
index 0000000..21b4a62
--- /dev/null
@@ -0,0 +1,141 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Diagnostics.DebugServices;
+using Microsoft.Diagnostics.Runtime.Utilities;
+using Microsoft.FileFormats;
+using Microsoft.SymbolStore;
+using Microsoft.SymbolStore.KeyGenerators;
+using System;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace SOS.Hosting
+{
+    public static class SymbolServiceExtensions
+    {
+        // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) 
+        const int E_INSUFFICIENT_BUFFER = unchecked((int)0x8007007a);
+
+        /// <summary>
+        /// Metadata locator helper for the DAC.
+        /// </summary>
+        /// <param name="imagePath">file name and path to module</param>
+        /// <param name="imageTimestamp">module timestamp</param>
+        /// <param name="imageSize">module image</param>
+        /// <param name="mvid">not used</param>
+        /// <param name="mdRva">not used</param>
+        /// <param name="flags">not used</param>
+        /// <param name="bufferSize">size of incoming buffer (pMetadata)</param>
+        /// <param name="pMetadata">pointer to buffer</param>
+        /// <param name="pMetadataSize">size of outgoing metadata</param>
+        /// <returns>HRESULT</returns>
+        public static int GetMetadataLocator(
+            this ISymbolService symbolService,
+            string imagePath,
+            uint imageTimestamp,
+            uint imageSize,
+            byte[] mvid,
+            uint mdRva,
+            uint flags,
+            uint bufferSize,
+            IntPtr pMetadata,
+            IntPtr pMetadataSize)
+        {
+            Debug.Assert(imageTimestamp != 0);
+            Debug.Assert(imageSize != 0);
+
+            if (pMetadata == IntPtr.Zero) {
+                return HResult.E_INVALIDARG;
+            }
+            int hr = HResult.S_OK;
+            int dataSize = 0;
+
+            ImmutableArray<byte> metadata = symbolService.GetMetadata(imagePath, imageTimestamp, imageSize);
+            if (!metadata.IsEmpty)
+            {
+                dataSize = metadata.Length;
+                int size = Math.Min((int)bufferSize, dataSize);
+                Marshal.Copy(metadata.ToArray(), 0, pMetadata, size);
+            }
+            else
+            {
+                hr = HResult.E_FAIL;
+            }
+
+            if (pMetadataSize != IntPtr.Zero) {
+                Marshal.WriteInt32(pMetadataSize, dataSize);
+            }
+            return hr;
+        }
+
+        /// <summary>
+        /// Metadata locator helper for the DAC.
+        /// </summary>
+        /// <param name="imagePath">file name and path to module</param>
+        /// <param name="imageTimestamp">module timestamp</param>
+        /// <param name="imageSize">module image</param>
+        /// <param name="pathBufferSize">output buffer size</param>
+        /// <param name="pPathBufferSize">native pointer to put actual path size</param>
+        /// <param name="pwszPathBuffer">native pointer to WCHAR path buffer</param>
+        /// <returns>HRESULT</returns>
+        public static int GetICorDebugMetadataLocator(
+            this ISymbolService symbolService,
+            string imagePath,
+            uint imageTimestamp,
+            uint imageSize,
+            uint pathBufferSize,
+            IntPtr pPathBufferSize,
+            IntPtr pwszPathBuffer)
+        {
+            int hr = HResult.S_OK;
+            int actualSize = 0;
+
+            Debug.Assert(pwszPathBuffer != IntPtr.Zero);
+            try
+            {
+                if (symbolService.IsSymbolStoreEnabled)
+                {
+                    SymbolStoreKey key = PEFileKeyGenerator.GetKey(imagePath, imageTimestamp, imageSize);
+                    string localFilePath = symbolService.DownloadFile(key);
+                    localFilePath += "\0";              // null terminate the string
+                    actualSize = localFilePath.Length;
+
+                    if (pathBufferSize > actualSize)
+                    {
+                        Trace.TraceInformation($"GetICorDebugMetadataLocator: SUCCEEDED {localFilePath}");
+                        Marshal.Copy(localFilePath.ToCharArray(), 0, pwszPathBuffer, actualSize);
+                    }
+                    else
+                    {
+                        Trace.TraceError("GetICorDebugMetadataLocator: E_INSUFFICIENT_BUFFER");
+                        hr = E_INSUFFICIENT_BUFFER;
+                    }
+                }
+                else
+                {
+                    Trace.TraceError($"GetICorDebugMetadataLocator: {imagePath} {imageTimestamp:X8} {imageSize:X8} symbol store not enabled");
+                    hr = HResult.E_FAIL;
+                }
+            }
+            catch (Exception ex) when
+                (ex is UnauthorizedAccessException ||
+                 ex is BadImageFormatException ||
+                 ex is InvalidVirtualAddressException ||
+                 ex is IOException)
+            {
+                Trace.TraceError($"GetICorDebugMetadataLocator: {imagePath} {imageTimestamp:X8} {imageSize:X8} ERROR {ex.Message}");
+                hr = HResult.E_FAIL;
+            }
+            if (pPathBufferSize != IntPtr.Zero)
+            {
+                Marshal.WriteInt32(pPathBufferSize, actualSize);
+            }
+            return hr;
+        }
+    }
+}
index 74973a5364e5015d354d4d156bd6ed8788648f7b..a5cee29318ffdd04410f08db198860de44267e5d 100644 (file)
@@ -73,18 +73,16 @@ namespace SOS.Hosting
 
         public static readonly Guid IID_ISymbolService = new Guid("7EE88D46-F8B3-4645-AD3E-01FE7D4F70F1");
 
-        // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) 
-        const int E_INSUFFICIENT_BUFFER = unchecked((int)0x8007007a);
-
-        private readonly IHost _host;
+        private readonly Func<IMemoryService> _getMemoryService;
         private readonly ISymbolService _symbolService;
-        private IMemoryService _memoryService;
 
-        public SymbolServiceWrapper(IHost host)
+        public SymbolServiceWrapper(IHost host, Func<IMemoryService> getMemoryService)
         {
             Debug.Assert(host != null);
-            _host = host;
+            Debug.Assert(getMemoryService != null);
+            _getMemoryService = getMemoryService;
             _symbolService = host.Services.GetService<ISymbolService>();
+            Debug.Assert(_symbolService != null);
 
             VTableBuilder builder = AddInterface(IID_ISymbolService, validate: false);
             builder.AddMethod(new IsSymbolStoreEnabledDelegate((IntPtr self) => _symbolService.IsSymbolStoreEnabled));
@@ -103,6 +101,7 @@ namespace SOS.Hosting
             builder.AddMethod(new GetMetadataLocatorDelegate(GetMetadataLocator));
             builder.AddMethod(new GetICorDebugMetadataLocatorDelegate(GetICorDebugMetadataLocator));
             builder.Complete();
+
             AddRef();
         }
 
@@ -695,50 +694,7 @@ namespace SOS.Hosting
             IntPtr pPathBufferSize,
             IntPtr pwszPathBuffer)
         {
-            int hr = HResult.S_OK;
-            int actualSize = 0;
-
-            Debug.Assert(pwszPathBuffer != IntPtr.Zero);
-            try
-            {
-                if (_symbolService.IsSymbolStoreEnabled)
-                {
-                    SymbolStoreKey key = PEFileKeyGenerator.GetKey(imagePath, imageTimestamp, imageSize);
-                    string localFilePath = _symbolService.DownloadFile(key);
-                    localFilePath += "\0";              // null terminate the string
-                    actualSize = localFilePath.Length;
-
-                    if (pathBufferSize > actualSize)
-                    {
-                        Trace.TraceInformation($"GetICorDebugMetadataLocator: SUCCEEDED {localFilePath}");
-                        Marshal.Copy(localFilePath.ToCharArray(), 0, pwszPathBuffer, actualSize);
-                    }
-                    else
-                    {
-                        Trace.TraceError("GetICorDebugMetadataLocator: E_INSUFFICIENT_BUFFER");
-                        hr = E_INSUFFICIENT_BUFFER;
-                    }
-                }
-                else
-                {
-                    Trace.TraceError($"GetICorDebugMetadataLocator: {imagePath} {imageTimestamp:X8} {imageSize:X8} symbol store not enabled");
-                    hr = HResult.E_FAIL;
-                }
-            }
-            catch (Exception ex) when
-                (ex is UnauthorizedAccessException ||
-                 ex is BadImageFormatException ||
-                 ex is InvalidVirtualAddressException ||
-                 ex is IOException)
-            {
-                Trace.TraceError($"GetICorDebugMetadataLocator: {imagePath} {imageTimestamp:X8} {imageSize:X8} ERROR {ex.Message}");
-                hr = HResult.E_FAIL;
-            }
-            if (pPathBufferSize != IntPtr.Zero)
-            {
-                Marshal.WriteInt32(pPathBufferSize, actualSize);
-            }
-            return hr;
+            return _symbolService.GetICorDebugMetadataLocator(imagePath, imageTimestamp, imageSize, pathBufferSize, pPathBufferSize, pwszPathBuffer);
         }
 
         /// <summary>
@@ -996,21 +952,7 @@ namespace SOS.Hosting
             return pathName.Substring(pos + 1);
         }
 
-        private IMemoryService MemoryService
-        {
-            get
-            {
-                if (_memoryService == null)
-                {
-                    ITarget target = _host.CurrentTarget;
-                    if (target == null) {
-                        throw new DiagnosticsException("SymbolService: no current target");
-                    }
-                    _memoryService = target.Services.GetService<IMemoryService>();
-                }
-                return _memoryService;
-            }
-        }
+        private IMemoryService MemoryService => _getMemoryService() ?? throw new DiagnosticsException("SymbolServiceWrapper: no current target");
 
         #region Symbol service delegates
 
index 229dca52758abba30ff88b2d3bf10b2a5c578364..1ff3de96aa1db4781a402abf8130363f0a302d6a 100644 (file)
@@ -26,22 +26,21 @@ namespace SOS.Hosting
 
         public static readonly Guid IID_ITarget = new Guid("B4640016-6CA0-468E-BA2C-1FFF28DE7B72");
 
+        private readonly IServiceProvider _services;
         private readonly ITarget _target;
         private readonly Dictionary<IRuntime, RuntimeWrapper> _wrappers = new Dictionary<IRuntime, RuntimeWrapper>();
 
-        public TargetWrapper(ITarget target)
+        public TargetWrapper(IServiceProvider services)
         {
-            Debug.Assert(target != null);
-            _target = target;
+            _services = services;
+            _target = services.GetService<ITarget>() ?? throw new DiagnosticsException("No target");
 
             VTableBuilder builder = AddInterface(IID_ITarget, validate: false);
 
             builder.AddMethod(new GetOperatingSystemDelegate(GetOperatingSystem));
             builder.AddMethod(new GetTempDirectoryDelegate(GetTempDirectory));
-            builder.AddMethod(new GetRuntimeDirectoryDelegate(GetRuntimeDirectory));
             builder.AddMethod(new GetRuntimeDelegate(GetRuntime));
             builder.AddMethod(new FlushDelegate(Flush));
-            builder.AddMethod(new CloseDelegate(Close));
 
             ITarget = builder.Complete();
 
@@ -79,31 +78,20 @@ namespace SOS.Hosting
             return _target.GetTempDirectory();
         }
 
-        private string GetRuntimeDirectory(
-            IntPtr self)
-        {
-            var runtimeService = _target.Services.GetService<IRuntimeService>();
-            if (runtimeService == null)
-            {
-                return null;
-            }
-            return runtimeService.RuntimeModuleDirectory;
-        }
-
-        private int GetRuntime(
+        private HResult GetRuntime(
             IntPtr self,
             IntPtr* ppRuntime)
         {
             if (ppRuntime == null) {
                 return HResult.E_INVALIDARG;
             }
-            IRuntime runtime = _target.Services.GetService<IRuntimeService>()?.CurrentRuntime;
+            IRuntime runtime = _services.GetService<IRuntime>();
             if (runtime == null) {
                 return HResult.E_NOINTERFACE;
             }
             if (!_wrappers.TryGetValue(runtime, out RuntimeWrapper wrapper))
             {
-                wrapper = new RuntimeWrapper(_target, runtime);
+                wrapper = new RuntimeWrapper(_services, runtime);
                 _wrappers.Add(runtime, wrapper);
             }
             *ppRuntime = wrapper.IRuntime;
@@ -116,12 +104,6 @@ namespace SOS.Hosting
             _target.Flush();
         }
 
-        private void Close(
-            IntPtr self)
-        {
-            _target.Close();
-        }
-
         #region ITarget delegates
 
         [UnmanagedFunctionPointer(CallingConvention.Winapi)]
@@ -134,12 +116,7 @@ namespace SOS.Hosting
             [In] IntPtr self);
 
         [UnmanagedFunctionPointer(CallingConvention.Winapi)]
-        [return: MarshalAs(UnmanagedType.LPStr)]
-        private delegate string GetRuntimeDirectoryDelegate(
-            [In] IntPtr self);
-
-        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
-        private delegate int GetRuntimeDelegate(
+        private delegate HResult GetRuntimeDelegate(
             [In] IntPtr self,
             [Out] IntPtr* ppRuntime);
 
@@ -147,10 +124,6 @@ namespace SOS.Hosting
         private delegate void FlushDelegate(
             [In] IntPtr self);
 
-        [UnmanagedFunctionPointer(CallingConvention.Winapi)]
-        private delegate void CloseDelegate(
-            [In] IntPtr self);
-
         #endregion
     }
 }
index bd11981aeebfabd206d9ae2a4c58c16e2e01a3ab..3b1c47b1057e7d7ef016db6b39f21b4b9b00267d 100644 (file)
@@ -502,22 +502,10 @@ HRESULT DbgEngServices::ChangeEngineState(
     {
         if (((Argument & DEBUG_STATUS_MASK) == DEBUG_STATUS_BREAK) && ((Argument & DEBUG_STATUS_INSIDE_WAIT) == 0))
         {
-            ULONG processId = 0;
-            if (FAILED(m_system->GetCurrentProcessSystemId(&processId)))
-            {
-                m_control->Output(DEBUG_OUTPUT_NORMAL, "ChangeEngineState: DestroyTarget\n");
-                Extensions::GetInstance()->DestroyTarget();
-            }
-            else 
-            {
-                m_control->Output(DEBUG_OUTPUT_NORMAL, "ChangeEngineState: processId %d\n", processId);
+            m_control->Output(DEBUG_OUTPUT_NORMAL, "ChangeEngineState\n");
 
-                // Has the process changed since the last commmand?
-                Extensions::GetInstance()->UpdateTarget(processId);
-
-                // Flush the target when the debugger target breaks
-                Extensions::GetInstance()->FlushTarget();
-            }
+            // Flush the target when the debugger target breaks
+            Extensions::GetInstance()->FlushTarget();
         }
     }
     return DEBUG_STATUS_NO_CHANGE;
index 7d368657d1ede214243ecf81453f9edbeea34e15..dedac177acd058d21cd3284ab2d7b0c8abba2d71 100644 (file)
@@ -364,33 +364,41 @@ ULONG Runtime::Release()
 //----------------------------------------------------------------------------
 
 /**********************************************************************\
- * Returns the runtime directory of the target
+ * Set the runtime module directory to search for DAC/DBI
+\**********************************************************************/
+void Runtime::SetRuntimeDirectory(LPCSTR runtimeModuleDirectory)
+{
+    if (m_runtimeDirectory != nullptr)
+    {
+        free((void*)m_runtimeDirectory);
+        m_runtimeDirectory = nullptr;
+    }
+    if (runtimeModuleDirectory != nullptr)
+    {
+        m_runtimeDirectory = _strdup(runtimeModuleDirectory);
+    }
+}
+
+/**********************************************************************\
+ * Returns the runtime directory
 \**********************************************************************/
 LPCSTR Runtime::GetRuntimeDirectory()
 {
     if (m_runtimeDirectory == nullptr)
     {
-        LPCSTR runtimeDirectory = m_target->GetRuntimeDirectory();
-        if (runtimeDirectory != nullptr)
+        if (GetFileAttributesA(m_name) == INVALID_FILE_ATTRIBUTES)
         {
-            m_runtimeDirectory = _strdup(runtimeDirectory);
+            ExtDbgOut("Error: Runtime module %s doesn't exist %08x\n", m_name, HRESULT_FROM_WIN32(GetLastError()));
+            return nullptr;
         }
-        else 
+        // Parse off the file name
+        char* runtimeDirectory = _strdup(m_name);
+        char* lastSlash = strrchr(runtimeDirectory, GetTargetDirectorySeparatorW());
+        if (lastSlash != nullptr)
         {
-            if (GetFileAttributesA(m_name) == INVALID_FILE_ATTRIBUTES)
-            {
-                ExtDbgOut("Error: Runtime module %s doesn't exist %08x\n", m_name, HRESULT_FROM_WIN32(GetLastError()));
-                return nullptr;
-            }
-            // Parse off the file name
-            char* runtimeDirectory = _strdup(m_name);
-            char* lastSlash = strrchr(runtimeDirectory, GetTargetDirectorySeparatorW());
-            if (lastSlash != nullptr)
-            {
-                *lastSlash = '\0';
-            }
-            m_runtimeDirectory = runtimeDirectory;
+            *lastSlash = '\0';
         }
+        m_runtimeDirectory = runtimeDirectory;
     }
     return m_runtimeDirectory;
 }
@@ -581,7 +589,7 @@ void Runtime::DisplayStatus()
         ExtOut("    Runtime module path: %s\n", m_name);
     }
     if (m_runtimeDirectory != nullptr) {
-        ExtOut("    Runtime directory: %s\n", m_runtimeDirectory);
+        ExtOut("    Runtime module directory: %s\n", m_runtimeDirectory);
     }
     if (m_dacFilePath != nullptr) {
         ExtOut("    DAC file path: %s\n", m_dacFilePath);
index b6cdc83ea7402cd27706c84f1017c68e665df339..01c85f55660bf7fd200defffe018d8aa01974361 100644 (file)
@@ -186,6 +186,8 @@ public:
 
     ULONG64 STDMETHODCALLTYPE GetModuleSize() const { return m_size; }
 
+    void STDMETHODCALLTYPE SetRuntimeDirectory(LPCSTR runtimeModuleDirectory);
+
     LPCSTR STDMETHODCALLTYPE GetRuntimeDirectory();
 
     HRESULT STDMETHODCALLTYPE GetClrDataProcess(IXCLRDataProcess** ppClrDataProcess);
index 3be7d461b6806af46dd9c783e8f34e060ead82d7..d752da7e559e7bb8a02007d2a68d69c6e676127d 100644 (file)
@@ -17,7 +17,6 @@ Target* Target::s_target = nullptr;
 Target::Target() :
     m_ref(1),
     m_tmpPath(nullptr),
-    m_runtimeModulePath(nullptr),
 #ifndef FEATURE_PAL
     m_desktop(nullptr),
 #endif
@@ -27,11 +26,34 @@ Target::Target() :
 
 Target::~Target()
 {
-    Close();
-    if (m_runtimeModulePath != nullptr)
+    // Clean up the temporary directory files and DAC symlink.
+    LPCSTR tmpPath = (LPCSTR)InterlockedExchangePointer((PVOID *)&m_tmpPath, nullptr);
+    if (tmpPath != nullptr)
     {
-        free((void*)m_runtimeModulePath);
-        m_runtimeModulePath = nullptr;
+        std::string directory(tmpPath);
+        directory.append("*");
+
+        WIN32_FIND_DATAA data;
+        HANDLE findHandle = FindFirstFileA(directory.c_str(), &data);
+
+        if (findHandle != INVALID_HANDLE_VALUE) 
+        {
+            do
+            {
+                if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+                {
+                    std::string file(tmpPath);
+                    file.append(data.cFileName);
+                    DeleteFileA(file.c_str());
+                }
+            } 
+            while (0 != FindNextFileA(findHandle, &data));
+
+            FindClose(findHandle);
+        }
+
+        RemoveDirectoryA(tmpPath);
+        free((void*)tmpPath);
     }
     if (m_netcore != nullptr)
     {
@@ -124,17 +146,6 @@ bool Target::SwitchRuntimeInstance(bool desktop)
 }
 #endif
 
-/**********************************************************************\
- * Set the runtime directory path
-\**********************************************************************/
-void Target::SetRuntimeDirectoryInstance(LPCSTR runtimeModulePath)
-{
-    if (m_runtimeModulePath != nullptr) {
-        free((void*)m_runtimeModulePath);
-    }
-    m_runtimeModulePath = _strdup(runtimeModulePath);
-}
-
 /**********************************************************************\
  * Display the internal target and runtime status
 \**********************************************************************/
@@ -152,9 +163,6 @@ void Target::DisplayStatusInstance()
     if (m_tmpPath != nullptr) {
         ExtOut("Temp path: %s\n", m_tmpPath);
     }
-    if (m_runtimeModulePath != nullptr) {
-        ExtOut("Runtime module path: %s\n", m_runtimeModulePath);
-    }
     if (m_netcore != nullptr) {
         m_netcore->DisplayStatus();
     }
@@ -261,11 +269,6 @@ LPCSTR Target::GetTempDirectory()
     return m_tmpPath;
 }
 
-LPCSTR Target::GetRuntimeDirectory()
-{
-    return m_runtimeModulePath;
-}
-
 HRESULT Target::GetRuntime(IRuntime** ppRuntime)
 {
     return CreateInstance(ppRuntime);
@@ -283,39 +286,6 @@ void Target::Flush()
 #endif
 }
 
-void Target::Close()
-{
-    // Clean up the temporary directory files and DAC symlink.
-    LPCSTR tmpPath = (LPCSTR)InterlockedExchangePointer((PVOID *)&m_tmpPath, nullptr);
-    if (tmpPath != nullptr)
-    {
-        std::string directory(tmpPath);
-        directory.append("*");
-
-        WIN32_FIND_DATAA data;
-        HANDLE findHandle = FindFirstFileA(directory.c_str(), &data);
-
-        if (findHandle != INVALID_HANDLE_VALUE) 
-        {
-            do
-            {
-                if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
-                {
-                    std::string file(tmpPath);
-                    file.append(data.cFileName);
-                    DeleteFileA(file.c_str());
-                }
-            } 
-            while (0 != FindNextFileA(findHandle, &data));
-
-            FindClose(findHandle);
-        }
-
-        RemoveDirectoryA(tmpPath);
-        free((void*)tmpPath);
-    }
-}
-
 bool IsWindowsTarget()
 {
     ITarget* target = GetTarget();
index f071cb90506874f4abc215ff4790db577765f8ce..2300810f8b64132f23f43b252e8d8ae9a180ee19 100644 (file)
@@ -17,7 +17,6 @@ class Target : public ITarget
 private:
     LONG m_ref;
     LPCSTR m_tmpPath;
-    LPCSTR m_runtimeModulePath;
 #ifndef FEATURE_PAL
     Runtime* m_desktop;
 #endif
@@ -28,7 +27,6 @@ private:
 #ifndef FEATURE_PAL
     bool SwitchRuntimeInstance(bool desktop);
 #endif
-    void SetRuntimeDirectoryInstance(LPCSTR runtimeModulePath);
     void DisplayStatusInstance();
 
     Target();
@@ -39,19 +37,6 @@ public:
 
     HRESULT CreateInstance(IRuntime** ppRuntime);
 
-    static bool SetRuntimeDirectory(LPCSTR runtimeModulePath)
-    {
-        std::string fullPath;
-        if (!GetAbsolutePath(runtimeModulePath, fullPath))
-        {
-            return false;
-        }
-        GetInstance();
-        _ASSERTE(s_target != nullptr);
-        s_target->SetRuntimeDirectoryInstance(fullPath.c_str());
-        return true;
-    }
-
 #ifndef FEATURE_PAL
     static bool SwitchRuntime(bool desktop)
     {
@@ -97,12 +82,8 @@ public:
 
     LPCSTR STDMETHODCALLTYPE GetTempDirectory();
 
-    LPCSTR STDMETHODCALLTYPE GetRuntimeDirectory();
-    
     HRESULT STDMETHODCALLTYPE GetRuntime(IRuntime** pRuntime);
 
     void STDMETHODCALLTYPE Flush();
-
-    void STDMETHODCALLTYPE Close();
 };
 
index 21c522969fc3589ec78d7dcdd3b3306afa18ecea..81dff1bef83db047d0db5c779e85d8cca004eb91 100644 (file)
@@ -7,6 +7,7 @@ EXPORTS
     AnalyzeOOM
     analyzeoom=AnalyzeOOM
     ao=AnalyzeOOM
+    clrmodules
     ClrStack
     clrstack=ClrStack
     CLRStack=ClrStack
@@ -73,6 +74,7 @@ EXPORTS
     EHInfo
     ehinfo=EHInfo
     Ehinfo=EHInfo
+    ext
     FinalizeQueue
     finalizequeue=FinalizeQueue
     fq=FinalizeQueue
@@ -243,7 +245,6 @@ EXPORTS
     tracetocode=TraceToCode
 #endif
 
-    ext
     SOSInitializeByHost
     SOSUninitializeByHost
     InitializeHostServices
index ce570f7ff231ffe7db27a0e2e4e0d55d4f3ac157..6e5eebe9e72b5684ca2415055c933d87a0ed50ce 100644 (file)
@@ -3,6 +3,7 @@
 ; See the LICENSE file in the project root for more information.
 
 bpmd
+clrmodules
 ClrStack
 dbgout
 DumpALC
@@ -31,6 +32,7 @@ EEVersion
 GCWhere
 EEStack
 EHInfo
+ext
 FinalizeQueue
 FindAppDomain
 GCInfo
@@ -62,6 +64,5 @@ Token2EE
 u
 VerifyHeap
 
-ext
 SOSInitializeByHost
 SOSUninitializeByHost
index 6821c25f353fea36632ac34e3b7880e4a5b21079..203b32dc5ee877ad4f78d24c484590c518384b36 100644 (file)
@@ -16719,10 +16719,12 @@ DECLARE_API(SetClrPath)
     {
         std::string command("setclrpath ");
         command.append(args);
-        Status = hostServices->DispatchCommand(command.c_str());
+        return hostServices->DispatchCommand(command.c_str());
     }
     else
     {
+        INIT_API_EE();
+
         StringHolder runtimeModulePath;
         CMDValue arg[] =
         {
@@ -16735,22 +16737,20 @@ DECLARE_API(SetClrPath)
         }
         if (narg > 0)
         {
-            if (!Target::SetRuntimeDirectory(runtimeModulePath.data))
+            std::string fullPath;
+            if (!GetAbsolutePath(runtimeModulePath.data, fullPath))
             {
-                ExtErr("Invalid runtime path %s\n", runtimeModulePath.data);
+                ExtErr("Invalid runtime directory %s\n", fullPath.c_str());
                 return E_FAIL;
             }
+            g_pRuntime->SetRuntimeDirectory(fullPath.c_str());
         }
-        ITarget* target = GetTarget();
-        if (target != nullptr)
-        {
-            const char* runtimeDirectory = target->GetRuntimeDirectory();
-            if (runtimeDirectory != nullptr) {
-                ExtOut("Runtime module path: %s\n", runtimeDirectory);
-            }
+        const char* runtimeDirectory = g_pRuntime->GetRuntimeDirectory();
+        if (runtimeDirectory != nullptr) {
+            ExtOut("Runtime module directory: %s\n", runtimeDirectory);
         }
     }
-    return Status;
+    return S_OK;
 }
 
 //
@@ -16809,25 +16809,48 @@ DECLARE_API(runtimes)
 }
 
 //
-// Enables and disables managed extension logging
+// Executes managed extension commands
 //
-DECLARE_API(logging)
+HRESULT ExecuteCommand(PCSTR command, PCSTR args)
 {
-    INIT_API_NOEE();
-
     IHostServices* hostServices = GetHostServices();
     if (hostServices != nullptr)
     {
-        std::string command("logging ");
-        command.append(args);
-        Status = hostServices->DispatchCommand(command.c_str());
+        std::string commandLine(command);
+        if (args != nullptr && strlen(args) > 0)
+        {
+            commandLine.append(" ");
+            commandLine.append(args);
+        }
+        if (!commandLine.empty())
+        {
+            return hostServices->DispatchCommand(commandLine.c_str());
+        }
     }
-    else 
+    else
     {
         ExtErr("Command not loaded\n");
+        return E_FAIL;
     }
+    return S_OK;
+}
 
-    return Status;
+//
+// Dumps the managed assemblies 
+//
+DECLARE_API(clrmodules)
+{
+    INIT_API_EXT();
+    return ExecuteCommand("clrmodules", args);
+}
+
+//
+// Enables and disables managed extension logging
+//
+DECLARE_API(logging)
+{
+    INIT_API_EXT();
+    return ExecuteCommand("logging", args);
 }
 
 //
@@ -16835,25 +16858,8 @@ DECLARE_API(logging)
 //
 DECLARE_API(ext)
 {
-    INIT_API_NOEE();
-
-    IHostServices* hostServices = GetHostServices();
-    if (hostServices != nullptr)
-    {
-        // Just load the managed infrastructure if no command. This is useful in lldb
-        // where the managed extension commands are not added until a command does 
-        // GetHostServices() like soshelp, logging, sosstatus, setsymbolserver and ext.
-        if (args != nullptr && strlen(args) > 0)
-        {
-            Status = hostServices->DispatchCommand(args);
-        }
-    }
-    else 
-    {
-        ExtErr("Command not loaded\n");
-    }
-
-    return Status;
+    INIT_API_EXT();
+    return ExecuteCommand("", args);
 }
 
 void PrintHelp (__in_z LPCSTR pszCmdName)
index 009b71237766819603d084503d16fe3494cc4a16..c5b914e5979a76d7cb59b1f07ff56782740d3f24 100644 (file)
@@ -55,7 +55,12 @@ public:
     virtual ULONG64 STDMETHODCALLTYPE GetModuleSize() const = 0;
 
     /// <summary>
-    /// Returns the directory of the runtime file
+    /// Set the runtime module directory to search for DAC/DBI
+    /// </summary>
+    virtual void STDMETHODCALLTYPE SetRuntimeDirectory(LPCSTR runtimeModuleDirectory) = 0;
+
+    /// <summary>
+    /// Returns the directory of the runtime module
     /// </summary>
     virtual LPCSTR STDMETHODCALLTYPE GetRuntimeDirectory() = 0;
 
index 1cf51e5a79e0b2c8f8580dd58cbab4244625f664..771a7b27626e8e83f151cb71f82bb3011bcbfc99 100644 (file)
@@ -43,12 +43,6 @@ public:
     /// <returns>temporary directory string</returns>
     virtual LPCSTR STDMETHODCALLTYPE GetTempDirectory() = 0;
 
-    /// <summary>
-    /// Returns the directory of the runtime file
-    /// </summary>
-    /// <returns>runtime directory or null if none set</returns>
-    virtual LPCSTR STDMETHODCALLTYPE GetRuntimeDirectory() = 0;
-
     /// <summary>
     /// Returns the current runtime instance
     /// </summary>
@@ -60,11 +54,6 @@ public:
     /// Flushes any internal caching or state 
     /// </summary>
     virtual void STDMETHODCALLTYPE Flush() = 0;
-
-    /// <summary>
-    /// Cleans up any internal resources 
-    /// </summary>
-    virtual void STDMETHODCALLTYPE Close() = 0;
 };
 
 #ifdef __cplusplus
index 28e6ee1fd5c7b4d0f5bb817d6f5e7620e983d526..f812c076708284668ff1af56e6e6bf72b9285961 100644 (file)
@@ -504,6 +504,7 @@ LLDBServices::GetLastEventInformation(
     {
         return E_FAIL;
     }
+    InitializeThreadInfo(process);
 
     *processId = GetProcessId(process);
     *threadId = GetThreadId(thread);
@@ -1381,6 +1382,8 @@ LLDBServices::GetCurrentProcessSystemId(
         return E_FAIL;
     }
 
+    InitializeThreadInfo(process);
+
     *sysId = GetProcessId(process);
     return S_OK;
 }
index 59d22fa940992f60975e068df843a7e9e06430ee..27f80ae2222539e625fd8a4ff3194900943cd723 100644 (file)
@@ -151,6 +151,7 @@ sosCommandInitialize(lldb::SBDebugger debugger)
     g_services->AddCommand("sos", new sosCommand(nullptr), "Various .NET Core debugging commands. See 'soshelp' for more details. sos <command-name> <args>");
     g_services->AddCommand("ext", new sosCommand("ext"), "Execute extension command. See 'soshelp' for more details. ext <command-name> <args>");
     g_services->AddCommand("bpmd", new sosCommand("bpmd"), "Creates a breakpoint at the specified managed method in the specified module.");
+    g_services->AddCommand("clrmodules", new sosCommand("clrmodules"), "Lists the managed modules in the process.");
     g_services->AddCommand("clrstack", new sosCommand("ClrStack"), "Provides a stack trace of managed code only.");
     g_services->AddCommand("clrthreads", new sosCommand("Threads"), "List the managed threads running.");
     g_services->AddCommand("clru", new sosCommand("u"), "Displays an annotated disassembly of a managed method.");
index c2f11aff92e7849fda27c87564d8eb95bf9307ad..d6b7be75f645ca009e0097f63f5dcc82884a0743 100644 (file)
@@ -26,19 +26,31 @@ namespace Microsoft.Diagnostics.Tools.Dump
         private readonly ConsoleProvider _consoleProvider;
         private readonly CommandProcessor _commandProcessor;
         private readonly SymbolService _symbolService;
+        private readonly ContextService _contextService;
+        private int _targetIdFactory;
         private Target _target;
 
         public Analyzer()
         {
+            LoggingCommand.Initialize();
+
             _serviceProvider = new ServiceProvider();
             _consoleProvider = new ConsoleProvider();
             _commandProcessor = new CommandProcessor();
             _symbolService = new SymbolService(this);
+            _contextService = new ContextService(this);
 
             _serviceProvider.AddService<IHost>(this);
             _serviceProvider.AddService<IConsoleService>(_consoleProvider);
             _serviceProvider.AddService<ICommandService>(_commandProcessor);
             _serviceProvider.AddService<ISymbolService>(_symbolService);
+            _serviceProvider.AddService<IContextService>(_contextService);
+            _serviceProvider.AddServiceFactory<SOSLibrary>(() => SOSLibrary.Create(this));
+
+            _contextService.ServiceProvider.AddServiceFactory<ClrMDHelper>(() => {
+                ClrRuntime clrRuntime = _contextService.Services.GetService<ClrRuntime>();
+                return clrRuntime != null ? new ClrMDHelper(clrRuntime) : null;
+            });
 
             _commandProcessor.AddCommands(new Assembly[] { typeof(Analyzer).Assembly });
             _commandProcessor.AddCommands(new Assembly[] { typeof(ClrMDHelper).Assembly });
@@ -81,13 +93,10 @@ namespace Microsoft.Diagnostics.Tools.Dump
                 if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || dataTarget.DataReader.EnumerateModules().Any((module) => Path.GetExtension(module.FileName) == ".dylib")) {
                     targetPlatform = OSPlatform.OSX;
                 }
-                _target = new TargetFromDataReader(dataTarget.DataReader, targetPlatform, this, dump_path.FullName);
+                _target = new TargetFromDataReader(dataTarget.DataReader, targetPlatform, this, _targetIdFactory++, dump_path.FullName);
+                _contextService.SetCurrentTarget(_target);
 
-                _target.ServiceProvider.AddServiceFactory<SOSHost>(() => {
-                    var sosHost = new SOSHost(_target);
-                    sosHost.InitializeSOSHost();
-                    return sosHost;
-                });
+                _target.ServiceProvider.AddServiceFactory<SOSHost>(() => new SOSHost(_contextService.Services));
 
                 // Automatically enable symbol server support
                 _symbolService.AddSymbolServer(msdl: true, symweb: false, symbolServerPath: null, authToken: null, timeoutInMinutes: 0);
@@ -98,8 +107,7 @@ namespace Microsoft.Diagnostics.Tools.Dump
                 {
                     foreach (string cmd in command)
                     {
-                        Parse(cmd);
-
+                        _commandProcessor.Execute(cmd, _contextService.Services);
                         if (_consoleProvider.Shutdown) {
                             break;
                         }
@@ -112,7 +120,7 @@ namespace Microsoft.Diagnostics.Tools.Dump
                     _consoleProvider.WriteLine("Type 'quit' or 'exit' to exit the session.");
 
                     _consoleProvider.Start((string commandLine, CancellationToken cancellation) => {
-                        Parse(commandLine);
+                        _commandProcessor.Execute(commandLine, _contextService.Services);
                     });
                 }
             }
@@ -133,8 +141,7 @@ namespace Microsoft.Diagnostics.Tools.Dump
             {
                 if (_target != null)
                 {
-                    _target.Close();
-                    _target = null;
+                    DestroyTarget(_target);
                 }
                 // Persist the current command history
                 try
@@ -154,36 +161,6 @@ namespace Microsoft.Diagnostics.Tools.Dump
             return Task.FromResult(0);
         }
 
-        private void Parse(string commandLine)
-        {
-            // If there is no target, then provide just the global services
-            ServiceProvider services = _serviceProvider;
-            if (_target != null)
-            {
-                // Create a per command invocation service provider. These services may change between each command invocation.
-                services = new ServiceProvider(_target.Services);
-
-                // Add the current thread if any
-                services.AddServiceFactory<IThread>(() => {
-                    IThreadService threadService = _target.Services.GetService<IThreadService>();
-                    if (threadService != null && threadService.CurrentThreadId.HasValue) {
-                        return threadService.GetThreadFromId(threadService.CurrentThreadId.Value);
-                    }
-                    return null;
-                });
-
-                // Add the current runtime and related services
-                var runtimeService = _target.Services.GetService<IRuntimeService>();
-                if (runtimeService != null)
-                {
-                    services.AddServiceFactory<IRuntime>(() => runtimeService.CurrentRuntime);
-                    services.AddServiceFactory<ClrRuntime>(() => services.GetService<IRuntime>()?.Services.GetService<ClrRuntime>());
-                    services.AddServiceFactory<ClrMDHelper>(() => new ClrMDHelper(services.GetService<ClrRuntime>()));
-                }
-            }
-            _commandProcessor.Execute(commandLine, services);
-        }
-
         #region IHost
 
         public IServiceEvent OnShutdownEvent { get; } = new ServiceEvent();
@@ -194,9 +171,20 @@ namespace Microsoft.Diagnostics.Tools.Dump
 
         IEnumerable<ITarget> IHost.EnumerateTargets() => _target != null ? new ITarget[] { _target } : Array.Empty<ITarget>();
 
-        ITarget IHost.CurrentTarget => _target;
-
-        void IHost.SetCurrentTarget(int targetid) => throw new NotImplementedException();
+        public void DestroyTarget(ITarget target)
+        {
+            if (target == null) {
+                throw new ArgumentNullException(nameof(target));
+            }
+            if (target == _target)
+            {
+                _target = null;
+                _contextService.ClearCurrentTarget();
+                if (target is IDisposable disposable) {
+                    disposable.Dispose();
+                }
+            }
+        }
 
         #endregion
     }