Skip to content

Commit

Permalink
Add setclrpath command to dotnet-dump on linux. Mentioned in dotnet#624
Browse files Browse the repository at this point in the history
  • Loading branch information
mikem8361 committed Dec 12, 2019
1 parent cb626f0 commit ba3ad5c
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 60 deletions.
5 changes: 5 additions & 0 deletions src/Microsoft.Diagnostics.DebugServices/AnalyzeContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,10 @@ public AnalyzeContext()
/// Cancellation token for current command
/// </summary>
public CancellationToken CancellationToken { get; set; }

/// <summary>
/// Directory of the runtime module (coreclr.dll, libcoreclr.so, etc.)
/// </summary>
public string RuntimeModuleDirectory { get; set; }
}
}
53 changes: 50 additions & 3 deletions src/Microsoft.Diagnostics.Repl/Command/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,24 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;

namespace Microsoft.Diagnostics.Repl
{
/// <summary>
/// Base command option attribute.
/// OS Platforms to add command
/// </summary>
[Flags]
public enum CommandPlatform : byte
{
All = 0x00,
Windows = 0x01,
Linux = 0x02,
OSX = 0x04,
}

/// <summary>
/// Base command, option and argument class.
/// </summary>
public class BaseAttribute : Attribute
{
Expand All @@ -22,11 +35,45 @@ public class BaseAttribute : Attribute
public string Help;
}

/// <summary>
/// Base command and command alias class.
/// </summary>
public class CommandBaseAttribute : BaseAttribute
{
/// <summary>
/// Optional OS platform for the command
/// </summary>
public CommandPlatform Platform = CommandPlatform.All;

/// <summary>
/// Returns true if the command should be added.
/// </summary>
public bool IsValidPlatform()
{
if (Platform != CommandPlatform.All)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return (Platform & CommandPlatform.Windows) != 0;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return (Platform & CommandPlatform.Linux) != 0;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return (Platform & CommandPlatform.OSX) != 0;
}
}
return true;
}
}

/// <summary>
/// Marks the class as a Command.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class CommandAttribute : BaseAttribute
public class CommandAttribute : CommandBaseAttribute
{
/// <summary>
/// Sets the value of the CommandBase.AliasExpansion when the command is executed.
Expand All @@ -38,7 +85,7 @@ public class CommandAttribute : BaseAttribute
/// Adds an alias to the previous command attribute
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class CommandAliasAttribute : BaseAttribute
public class CommandAliasAttribute : CommandBaseAttribute
{
}

Expand Down
89 changes: 48 additions & 41 deletions src/Microsoft.Diagnostics.Repl/Command/CommandProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,63 +116,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 (commandAttribute.IsValidPlatform())
{
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 (commandAliasAttribute.IsValidPlatform())
{
if (command == null)
{
throw new ArgumentException($"No previous CommandAttribute for this CommandAliasAttribute: {type.Name}");
}
command.AddAlias(commandAliasAttribute.Name);
}
command.AddAlias(commandAliasAttribute.Name);
}
}
}
Expand Down
12 changes: 8 additions & 4 deletions src/SOS/SOS.Hosting/LLDBServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
22 changes: 11 additions & 11 deletions src/SOS/SOS.Hosting/SOSHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -203,7 +203,7 @@ public SOSHost(IServiceProvider serviceProvider)
DataTarget dataTarget = serviceProvider.GetService<DataTarget>();
DataReader = dataTarget.DataReader;
_console = serviceProvider.GetService<IConsoleService>();
_analyzeContext = serviceProvider.GetService<AnalyzeContext>();
AnalyzeContext = serviceProvider.GetService<AnalyzeContext>();
_memoryService = serviceProvider.GetService<MemoryService>();
_registerService = serviceProvider.GetService<RegisterService>();
_versionCache = new ReadVirtualCache(_memoryService);
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand All @@ -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)
Expand All @@ -958,7 +958,7 @@ internal int GetCurrentThreadSystemId(
IntPtr self,
out uint sysId)
{
sysId = (uint)_analyzeContext.CurrentThreadId;
sysId = (uint)AnalyzeContext.CurrentThreadId;
return S_OK;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/SOS/Strike/hostcoreclr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
32 changes: 32 additions & 0 deletions src/Tools/dotnet-dump/Commands/SetClrPathCommand.cs
Original file line number Diff line number Diff line change
@@ -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 ?? "<none>");
}
else
{
AnalyzeContext.RuntimeModuleDirectory = Argument;
WriteLine("Set load path for DAC/DBI to '{0}'", AnalyzeContext.RuntimeModuleDirectory);
}
}
}
}

0 comments on commit ba3ad5c

Please sign in to comment.