diff --git a/src/Microsoft.Diagnostics.DebugServices/AnalyzeContext.cs b/src/Microsoft.Diagnostics.DebugServices/AnalyzeContext.cs
index e1f4e27b80..80f7b15bf2 100644
--- a/src/Microsoft.Diagnostics.DebugServices/AnalyzeContext.cs
+++ b/src/Microsoft.Diagnostics.DebugServices/AnalyzeContext.cs
@@ -24,5 +24,10 @@ public AnalyzeContext()
/// Cancellation token for current command
///
public CancellationToken CancellationToken { get; set; }
+
+ ///
+ /// Directory of the runtime module (coreclr.dll, libcoreclr.so, etc.)
+ ///
+ public string RuntimeModuleDirectory { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.Diagnostics.Repl/Command/Attributes.cs b/src/Microsoft.Diagnostics.Repl/Command/Attributes.cs
index 2264e6241d..5690cb2e6c 100644
--- a/src/Microsoft.Diagnostics.Repl/Command/Attributes.cs
+++ b/src/Microsoft.Diagnostics.Repl/Command/Attributes.cs
@@ -7,7 +7,19 @@
namespace Microsoft.Diagnostics.Repl
{
///
- /// Base command option attribute.
+ /// OS Platforms to add command
+ ///
+ [Flags]
+ public enum CommandPlatform : byte
+ {
+ All = 0x00,
+ Windows = 0x01,
+ Linux = 0x02,
+ OSX = 0x04,
+ }
+
+ ///
+ /// Base command, option and argument class.
///
public class BaseAttribute : Attribute
{
@@ -22,11 +34,22 @@ public class BaseAttribute : Attribute
public string Help;
}
+ ///
+ /// Base command and command alias class.
+ ///
+ public class CommandBaseAttribute : BaseAttribute
+ {
+ ///
+ /// Optional OS platform for the command
+ ///
+ public CommandPlatform Platform = CommandPlatform.All;
+ }
+
///
/// Marks the class as a Command.
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
- public class CommandAttribute : BaseAttribute
+ public class CommandAttribute : CommandBaseAttribute
{
///
/// Sets the value of the CommandBase.AliasExpansion when the command is executed.
@@ -38,7 +61,7 @@ public class CommandAttribute : BaseAttribute
/// Adds an alias to the previous command attribute
///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
- public class CommandAliasAttribute : BaseAttribute
+ public class CommandAliasAttribute : CommandBaseAttribute
{
}
diff --git a/src/Microsoft.Diagnostics.Repl/Command/CommandProcessor.cs b/src/Microsoft.Diagnostics.Repl/Command/CommandProcessor.cs
index 4a2791e17d..992d4e0e1e 100644
--- a/src/Microsoft.Diagnostics.Repl/Command/CommandProcessor.cs
+++ b/src/Microsoft.Diagnostics.Repl/Command/CommandProcessor.cs
@@ -10,6 +10,7 @@
using System.Diagnostics;
using System.Linq;
using System.Reflection;
+using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace Microsoft.Diagnostics.Repl
@@ -116,63 +117,70 @@ private void BuildCommands(CommandLineBuilder rootBuilder, Type type)
{
if (baseAttribute is CommandAttribute commandAttribute)
{
- command = new Command(commandAttribute.Name, commandAttribute.Help);
- var properties = new List<(PropertyInfo, Option)>();
- var arguments = new List<(PropertyInfo, Argument)>();
-
- foreach (PropertyInfo property in type.GetProperties().Where(p => p.CanWrite))
+ if (IsValidPlatform(commandAttribute))
{
- var argumentAttribute = (ArgumentAttribute)property.GetCustomAttributes(typeof(ArgumentAttribute), inherit: false).SingleOrDefault();
- if (argumentAttribute != null)
- {
- IArgumentArity arity = property.PropertyType.IsArray ? ArgumentArity.ZeroOrMore : ArgumentArity.ZeroOrOne;
-
- var argument = new Argument {
- Name = argumentAttribute.Name ?? property.Name.ToLowerInvariant(),
- Description = argumentAttribute.Help,
- ArgumentType = property.PropertyType,
- Arity = arity
- };
- command.AddArgument(argument);
- arguments.Add((property, argument));
- }
- else
+ command = new Command(commandAttribute.Name, commandAttribute.Help);
+ var properties = new List<(PropertyInfo, Option)>();
+ var arguments = new List<(PropertyInfo, Argument)>();
+
+ foreach (PropertyInfo property in type.GetProperties().Where(p => p.CanWrite))
{
- var optionAttribute = (OptionAttribute)property.GetCustomAttributes(typeof(OptionAttribute), inherit: false).SingleOrDefault();
- if (optionAttribute != null)
+ var argumentAttribute = (ArgumentAttribute)property.GetCustomAttributes(typeof(ArgumentAttribute), inherit: false).SingleOrDefault();
+ if (argumentAttribute != null)
{
- var option = new Option(optionAttribute.Name ?? BuildAlias(property.Name), optionAttribute.Help) {
- Argument = new Argument { ArgumentType = property.PropertyType }
- };
- command.AddOption(option);
- properties.Add((property, option));
+ IArgumentArity arity = property.PropertyType.IsArray ? ArgumentArity.ZeroOrMore : ArgumentArity.ZeroOrOne;
- foreach (var optionAliasAttribute in (OptionAliasAttribute[])property.GetCustomAttributes(typeof(OptionAliasAttribute), inherit: false))
- {
- option.AddAlias(optionAliasAttribute.Name);
- }
+ var argument = new Argument {
+ Name = argumentAttribute.Name ?? property.Name.ToLowerInvariant(),
+ Description = argumentAttribute.Help,
+ ArgumentType = property.PropertyType,
+ Arity = arity
+ };
+ command.AddArgument(argument);
+ arguments.Add((property, argument));
}
else
{
- // If not an option, add as just a settable properties
- properties.Add((property, null));
+ var optionAttribute = (OptionAttribute)property.GetCustomAttributes(typeof(OptionAttribute), inherit: false).SingleOrDefault();
+ if (optionAttribute != null)
+ {
+ var option = new Option(optionAttribute.Name ?? BuildAlias(property.Name), optionAttribute.Help) {
+ Argument = new Argument { ArgumentType = property.PropertyType }
+ };
+ command.AddOption(option);
+ properties.Add((property, option));
+
+ foreach (var optionAliasAttribute in (OptionAliasAttribute[])property.GetCustomAttributes(typeof(OptionAliasAttribute), inherit: false))
+ {
+ option.AddAlias(optionAliasAttribute.Name);
+ }
+ }
+ else
+ {
+ // If not an option, add as just a settable properties
+ properties.Add((property, null));
+ }
}
}
- }
- var handler = new Handler(this, commandAttribute.AliasExpansion, arguments, properties, type);
- _commandHandlers.Add(command.Name, handler);
- command.Handler = handler;
+ var handler = new Handler(this, commandAttribute.AliasExpansion, arguments, properties, type);
+ _commandHandlers.Add(command.Name, handler);
+ command.Handler = handler;
- rootBuilder.AddCommand(command);
+ rootBuilder.AddCommand(command);
+ }
}
if (baseAttribute is CommandAliasAttribute commandAliasAttribute)
{
- if (command == null) {
- throw new ArgumentException($"No previous CommandAttribute for this CommandAliasAttribute: {type.Name}");
+ if (IsValidPlatform(commandAliasAttribute))
+ {
+ if (command == null)
+ {
+ throw new ArgumentException($"No previous CommandAttribute for this CommandAliasAttribute: {type.Name}");
+ }
+ command.AddAlias(commandAliasAttribute.Name);
}
- command.AddAlias(commandAliasAttribute.Name);
}
}
}
@@ -189,6 +197,29 @@ private T GetService()
return service;
}
+ ///
+ /// Returns true if the command should be added.
+ ///
+ private static bool IsValidPlatform(CommandBaseAttribute attribute)
+ {
+ if (attribute.Platform != CommandPlatform.All)
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return (attribute.Platform & CommandPlatform.Windows) != 0;
+ }
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return (attribute.Platform & CommandPlatform.Linux) != 0;
+ }
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return (attribute.Platform & CommandPlatform.OSX) != 0;
+ }
+ }
+ return true;
+ }
+
private static string BuildAlias(string parameterName)
{
if (string.IsNullOrWhiteSpace(parameterName)) {
diff --git a/src/SOS/SOS.Hosting/LLDBServices.cs b/src/SOS/SOS.Hosting/LLDBServices.cs
index 554b43b3fb..49a5e491b5 100644
--- a/src/SOS/SOS.Hosting/LLDBServices.cs
+++ b/src/SOS/SOS.Hosting/LLDBServices.cs
@@ -92,13 +92,17 @@ public LLDBServices(SOSHost soshost)
string GetCoreClrDirectory(
IntPtr self)
{
- foreach (ModuleInfo module in _soshost.DataReader.EnumerateModules())
+ if (_soshost.AnalyzeContext.RuntimeModuleDirectory == null)
{
- if (SOSHost.IsRuntimeModule(module)) {
- return Path.GetDirectoryName(module.FileName) + Path.DirectorySeparatorChar;
+ foreach (ModuleInfo module in _soshost.DataReader.EnumerateModules())
+ {
+ if (SOSHost.IsRuntimeModule(module))
+ {
+ _soshost.AnalyzeContext.RuntimeModuleDirectory = Path.GetDirectoryName(module.FileName) + Path.DirectorySeparatorChar;
+ }
}
}
- return null;
+ return _soshost.AnalyzeContext.RuntimeModuleDirectory;
}
int VirtualUnwind(
diff --git a/src/SOS/SOS.Hosting/SOSHost.cs b/src/SOS/SOS.Hosting/SOSHost.cs
index b6792a4ea8..909f2b1397 100644
--- a/src/SOS/SOS.Hosting/SOSHost.cs
+++ b/src/SOS/SOS.Hosting/SOSHost.cs
@@ -155,13 +155,13 @@ struct SOSNetCoreCallbacks
GetExpressionDelegate = SOSHost.GetExpression,
};
- internal readonly IDataReader DataReader;
-
const string DesktopRuntimeModuleName = "clr";
private static readonly string s_coreclrModuleName;
- private readonly AnalyzeContext _analyzeContext;
+ internal readonly IDataReader DataReader;
+ internal readonly AnalyzeContext AnalyzeContext;
+
private readonly RegisterService _registerService;
private readonly MemoryService _memoryService;
private readonly IConsoleService _console;
@@ -203,7 +203,7 @@ public SOSHost(IServiceProvider serviceProvider)
DataTarget dataTarget = serviceProvider.GetService();
DataReader = dataTarget.DataReader;
_console = serviceProvider.GetService();
- _analyzeContext = serviceProvider.GetService();
+ AnalyzeContext = serviceProvider.GetService();
_memoryService = serviceProvider.GetService();
_registerService = serviceProvider.GetService();
_versionCache = new ReadVirtualCache(_memoryService);
@@ -343,7 +343,7 @@ internal static UIntPtr GetExpression(
internal int GetInterrupt(
IntPtr self)
{
- return _analyzeContext.CancellationToken.IsCancellationRequested ? S_OK : E_FAIL;
+ return AnalyzeContext.CancellationToken.IsCancellationRequested ? S_OK : E_FAIL;
}
internal int OutputVaList(
@@ -877,7 +877,7 @@ internal int GetThreadContext(
IntPtr context,
uint contextSize)
{
- uint threadId = (uint)_analyzeContext.CurrentThreadId;
+ uint threadId = (uint)AnalyzeContext.CurrentThreadId;
byte[] registerContext = _registerService.GetThreadContext(threadId);
if (registerContext == null) {
return E_FAIL;
@@ -934,7 +934,7 @@ internal int GetCurrentThreadId(
IntPtr self,
out uint id)
{
- return GetThreadIdBySystemId(self, (uint)_analyzeContext.CurrentThreadId, out id);
+ return GetThreadIdBySystemId(self, (uint)AnalyzeContext.CurrentThreadId, out id);
}
internal int SetCurrentThreadId(
@@ -944,7 +944,7 @@ internal int SetCurrentThreadId(
try
{
unchecked {
- _analyzeContext.CurrentThreadId = (int)DataReader.EnumerateAllThreads().ElementAt((int)id);
+ AnalyzeContext.CurrentThreadId = (int)DataReader.EnumerateAllThreads().ElementAt((int)id);
}
}
catch (ArgumentOutOfRangeException)
@@ -958,7 +958,7 @@ internal int GetCurrentThreadSystemId(
IntPtr self,
out uint sysId)
{
- sysId = (uint)_analyzeContext.CurrentThreadId;
+ sysId = (uint)AnalyzeContext.CurrentThreadId;
return S_OK;
}
@@ -1014,7 +1014,7 @@ internal unsafe int GetCurrentThreadTeb(
IntPtr self,
ulong* offset)
{
- uint threadId = (uint)_analyzeContext.CurrentThreadId;
+ uint threadId = (uint)AnalyzeContext.CurrentThreadId;
ulong teb = DataReader.GetThreadTeb(threadId);
Write(offset, teb);
return S_OK;
@@ -1102,7 +1102,7 @@ internal int GetRegister(
int index,
out ulong value)
{
- uint threadId = (uint)_analyzeContext.CurrentThreadId;
+ uint threadId = (uint)AnalyzeContext.CurrentThreadId;
if (!_registerService.GetRegisterValue(threadId, index, out value)) {
return E_FAIL;
}
diff --git a/src/SOS/Strike/hostcoreclr.cpp b/src/SOS/Strike/hostcoreclr.cpp
index a206226e61..67771d077b 100644
--- a/src/SOS/Strike/hostcoreclr.cpp
+++ b/src/SOS/Strike/hostcoreclr.cpp
@@ -235,7 +235,7 @@ HRESULT GetRuntimeDirectory(std::string& runtimeDirectory)
LPCSTR directory = g_ExtServices->GetCoreClrDirectory();
if (directory == NULL)
{
- ExtErr("Error: Runtime module (%s) not loaded yet\n", NETCORE_RUNTIME_DLL_NAME_A);
+ ExtErr("Error: Runtime module (%s) not loaded yet\n", GetRuntimeDllName());
return E_FAIL;
}
if (!GetAbsolutePath(directory, runtimeDirectory))
diff --git a/src/Tools/dotnet-dump/Commands/SetClrPathCommand.cs b/src/Tools/dotnet-dump/Commands/SetClrPathCommand.cs
new file mode 100644
index 0000000000..e59fd3bef8
--- /dev/null
+++ b/src/Tools/dotnet-dump/Commands/SetClrPathCommand.cs
@@ -0,0 +1,32 @@
+// 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.Repl;
+using System.CommandLine;
+
+namespace Microsoft.Diagnostics.Tools.Dump
+{
+ [Command(Name = "setclrpath", Platform = CommandPlatform.Linux | CommandPlatform.OSX, Help = "Set the path to load coreclr DAC/DBI files.")]
+ public class SetClrPath: CommandBase
+ {
+ public AnalyzeContext AnalyzeContext { get; set; }
+
+ [Argument(Name = "clrpath", Help = "Runtime directory path.")]
+ public string Argument { get; set; }
+
+ public override void Invoke()
+ {
+ if (Argument == null)
+ {
+ WriteLine("Load path for DAC/DBI: '{0}'", AnalyzeContext.RuntimeModuleDirectory ?? "");
+ }
+ else
+ {
+ AnalyzeContext.RuntimeModuleDirectory = Argument;
+ WriteLine("Set load path for DAC/DBI to '{0}'", AnalyzeContext.RuntimeModuleDirectory);
+ }
+ }
+ }
+}