-
Notifications
You must be signed in to change notification settings - Fork 359
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add interactive dump "analyze" dump support to dotnet-dump project.
Use the System.CommandLine CommandProcessor for the new commands. Add "sos", "exit", "help", native "modules" and "setthread" commands.
- Loading branch information
Showing
12 changed files
with
529 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// -------------------------------------------------------------------- | ||
// | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// | ||
// -------------------------------------------------------------------- | ||
using Microsoft.Diagnostics.Runtime; | ||
using SOS; | ||
using System; | ||
using System.CommandLine; | ||
using System.Threading; | ||
|
||
namespace Microsoft.Diagnostic.Tools.Dump | ||
{ | ||
/// <summary> | ||
/// The the common context for analyze commands | ||
/// </summary> | ||
public class AnalyzeContext: ISOSHostContext | ||
{ | ||
readonly IConsole _console; | ||
ClrRuntime _runtime; | ||
|
||
public AnalyzeContext(IConsole console, DataTarget target, Action exit) | ||
{ | ||
_console = console; | ||
Target = target; | ||
Exit = exit; | ||
} | ||
|
||
/// <summary> | ||
/// ClrMD data target | ||
/// </summary> | ||
public DataTarget Target { get; } | ||
|
||
/// <summary> | ||
/// ClrMD runtime info | ||
/// </summary> | ||
public ClrRuntime Runtime | ||
{ | ||
get | ||
{ | ||
if (_runtime == null) | ||
{ | ||
if (Target.ClrVersions.Count != 1) | ||
{ | ||
throw new InvalidOperationException("More or less than 1 CLR version is present"); | ||
} | ||
_runtime = Target.ClrVersions[0].CreateRuntime(); | ||
} | ||
return _runtime; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Delegate to invoke to exit repl | ||
/// </summary> | ||
public Action Exit { get; } | ||
|
||
/// <summary> | ||
/// Current OS thread Id | ||
/// </summary> | ||
public int CurrentThreadId { get; set; } | ||
|
||
/// <summary> | ||
/// Cancellation token for current command | ||
/// </summary> | ||
public CancellationToken CancellationToken { get; set; } | ||
|
||
/// <summary> | ||
/// Console write function | ||
/// </summary> | ||
/// <param name="text"></param> | ||
void ISOSHostContext.Write(string text) | ||
{ | ||
_console.Out.Write(text); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
using Microsoft.Diagnostic.Repl; | ||
using Microsoft.Diagnostics.Runtime; | ||
using System.CommandLine; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Runtime.InteropServices; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.Diagnostic.Tools.Dump | ||
{ | ||
public class Analyzer | ||
{ | ||
private readonly ConsoleProvider _consoleProvider; | ||
private readonly CommandProcessor _commandProcessor; | ||
|
||
public Analyzer() | ||
{ | ||
_consoleProvider = new ConsoleProvider(); | ||
_commandProcessor = new CommandProcessor(new Assembly[] { typeof(Analyzer).Assembly }); | ||
} | ||
|
||
public async Task<int> Analyze(FileInfo dump_path, string[] command) | ||
{ | ||
_consoleProvider.Out.WriteLine($"Loading core dump: {dump_path} ..."); | ||
|
||
DataTarget target = null; | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { | ||
target = DataTarget.LoadCoreDump(dump_path.FullName); | ||
} | ||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { | ||
target = DataTarget.LoadCrashDump(dump_path.FullName, CrashDumpReader.ClrMD); | ||
} | ||
else { | ||
_consoleProvider.Error.WriteLine($"{RuntimeInformation.OSDescription} not supported"); | ||
return 1; | ||
} | ||
|
||
using (target) | ||
{ | ||
// Create common analyze context for commands | ||
var analyzeContext = new AnalyzeContext(_consoleProvider, target, _consoleProvider.Stop) { | ||
CurrentThreadId = unchecked((int)target.DataReader.EnumerateAllThreads().FirstOrDefault()) | ||
}; | ||
_commandProcessor.CommandContext = analyzeContext; | ||
|
||
// Automatically enable symbol server support on Linux and MacOS | ||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { | ||
await _commandProcessor.Parse("setsymbolserver -ms", _consoleProvider); | ||
} | ||
|
||
// Run the commands from the dotnet-dump command line | ||
if (command != null) | ||
{ | ||
foreach (string cmd in command) | ||
{ | ||
await _commandProcessor.Parse(cmd, _consoleProvider); | ||
} | ||
} | ||
|
||
// Start interactive command line processing | ||
await _consoleProvider.Start(async (string commandLine, CancellationToken cancellation) => { | ||
analyzeContext.CancellationToken = cancellation; | ||
await _commandProcessor.Parse(commandLine, _consoleProvider); | ||
}); | ||
} | ||
|
||
return 0; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
|
||
using Microsoft.Diagnostic.Repl; | ||
using System.CommandLine; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.Diagnostic.Tools.Dump | ||
{ | ||
[Command(Name = "exit", Help = "Exit interactive mode.")] | ||
public class ExitCommand : CommandBase | ||
{ | ||
public AnalyzeContext AnalyzeContext { get; set; } | ||
|
||
public override Task InvokeAsync() | ||
{ | ||
AnalyzeContext.Exit(); | ||
return Task.CompletedTask; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using Microsoft.Diagnostic.Repl; | ||
using System.CommandLine; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.Diagnostic.Tools.Dump | ||
{ | ||
[Command(Name = "help", Help = "Display help for a command.")] | ||
public class HelpCommand : CommandBase | ||
{ | ||
[Argument(Help = "Command to find help.")] | ||
public string Command { get; set; } | ||
|
||
public CommandProcessor CommandProcessor { get; set; } | ||
|
||
public override Task InvokeAsync() | ||
{ | ||
return Task.CompletedTask; | ||
} | ||
|
||
/// <summary> | ||
/// Get help builder interface | ||
/// </summary> | ||
/// <param name="helpBuilder">help builder</param> | ||
public Task InvokeAsync(IHelpBuilder helpBuilder) | ||
{ | ||
Command command = CommandProcessor.GetCommand(Command); | ||
if (command != null) { | ||
helpBuilder.Write(command); | ||
} | ||
else { | ||
Console.Error.WriteLine($"Help for {Command} not found."); | ||
} | ||
return Task.CompletedTask; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
|
||
using Microsoft.Diagnostic.Repl; | ||
using Microsoft.Diagnostics.Runtime; | ||
using System.CommandLine; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.Diagnostic.Tools.Dump | ||
{ | ||
[Command(Name = "modules", Help = "Displays the native modules in the process.")] | ||
[Command(Name = "lm")] | ||
public class ModulesCommand : CommandBase | ||
{ | ||
[Option(Name = "--verbose", Help = "Displays more details.")] | ||
[OptionAlias(Name = "-v")] | ||
public bool Verbose { get; set; } | ||
|
||
public AnalyzeContext AnalyzeContext { get; set; } | ||
|
||
public override Task InvokeAsync() | ||
{ | ||
foreach (ModuleInfo module in AnalyzeContext.Target.DataReader.EnumerateModules()) | ||
{ | ||
if (Verbose) | ||
{ | ||
WriteLine("{0}", module.FileName); | ||
WriteLine(" Address: {0:X16}", module.ImageBase); | ||
WriteLine(" FileSize: {0:X8}", module.FileSize); | ||
WriteLine(" TimeStamp: {0:X8}", module.TimeStamp); | ||
if (module.BuildId != null) { | ||
WriteLine(" BuildId: {0}", string.Concat(module.BuildId.Select((b) => b.ToString("x2")))); | ||
} | ||
WriteLine(" IsRuntime: {0}", module.IsRuntime); | ||
WriteLine(" IsManaged: {0}", module.IsManaged); | ||
} | ||
else | ||
{ | ||
WriteLine("{0:X16} {1:X8} {2}", module.ImageBase, module.FileSize, module.FileName); | ||
} | ||
} | ||
return Task.CompletedTask; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using Microsoft.Diagnostic.Repl; | ||
using SOS; | ||
using System; | ||
using System.CommandLine; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Runtime.InteropServices; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.Diagnostic.Tools.Dump | ||
{ | ||
[Command(Name = "clrstack", AliasExpansion = "ClrStack", Help = "Provides a stack trace of managed code only.")] | ||
[Command(Name = "clrthreads", AliasExpansion = "Threads", Help = "List the managed threads running.")] | ||
[Command(Name = "dumpasync", AliasExpansion = "DumpAsync", Help = "Displays info about async state machines on the garbage-collected heap.")] | ||
[Command(Name = "dumpclass", AliasExpansion = "DumpClass", Help = "Displays information about a EE class structure at the specified address.")] | ||
[Command(Name = "dumpdelegate", AliasExpansion = "DumpDelegate", Help = "Displays information about a delegate.")] | ||
[Command(Name = "dumpdomain", AliasExpansion = "DumpDomain", Help = "Displays information all the AppDomains and all assemblies within the domains.")] | ||
[Command(Name = "dumpheap", AliasExpansion = "DumpHeap", Help = "Displays info about the garbage-collected heap and collection statistics about objects.")] | ||
[Command(Name = "dumpil", AliasExpansion = "DumpIL", Help = "Displays the Microsoft intermediate language (MSIL) that is associated with a managed method.")] | ||
[Command(Name = "dumplog", AliasExpansion = "DumpLog", Help = "Writes the contents of an in-memory stress log to the specified file.")] | ||
[Command(Name = "dumpmd", AliasExpansion = "DumpMD", Help = "Displays information about a MethodDesc structure at the specified address.")] | ||
[Command(Name = "dumpmodule", AliasExpansion = "DumpModule", Help = "Displays information about a EE module structure at the specified address.")] | ||
[Command(Name = "dumpmt", AliasExpansion = "DumpMT", Help = "Displays information about a method table at the specified address.")] | ||
[Command(Name = "dumpobj", AliasExpansion = "DumpObj", Help = "Displays info about an object at the specified address.")] | ||
[Command(Name = "dumpstack", AliasExpansion = "DumpStack", Help = "Displays a native and managed stack trace.")] | ||
[Command(Name = "dso", AliasExpansion = "DumpStackObjects", Help = "Displays all managed objects found within the bounds of the current stack.")] | ||
[Command(Name = "eeheap", AliasExpansion = "EEHeap", Help = "Displays info about process memory consumed by internal runtime data structures.")] | ||
[Command(Name = "eestack", AliasExpansion = "EEStack", Help = "Runs dumpstack on all threads in the process.")] | ||
[Command(Name = "finalizequeue", AliasExpansion = "FinalizeQueue", Help = "Displays all objects registered for finalization.")] | ||
[Command(Name = "gcroot", AliasExpansion = "GCRoot", Help = "Displays info about references (or roots) to an object at the specified address.")] | ||
[Command(Name = "ip2md", AliasExpansion = "IP2MD", Help = "Displays the MethodDesc structure at the specified address in code that has been JIT-compiled.")] | ||
[Command(Name = "name2ee", AliasExpansion = "Name2EE", Help = "Displays the MethodTable structure and EEClass structure for the specified type or method in the specified module.")] | ||
[Command(Name = "pe", AliasExpansion = "PrintException", Help = "Displays and formats fields of any object derived from the Exception class at the specified address.")] | ||
[Command(Name = "syncblk", AliasExpansion = "SyncBlk", Help = "Displays the SyncBlock holder info.")] | ||
[Command(Name = "histclear", AliasExpansion = "HistClear", Help = "Releases any resources used by the family of Hist commands.")] | ||
[Command(Name = "histinit", AliasExpansion = "HistInit", Help = "Initializes the SOS structures from the stress log saved in the debuggee.")] | ||
[Command(Name = "histobj", AliasExpansion = "HistObj", Help = "Examines all stress log relocation records and displays the chain of garbage collection relocations that may have led to the address passed in as an argument.")] | ||
[Command(Name = "histobjfind", AliasExpansion = "HistObjFind", Help = "Displays all the log entries that reference an object at the specified address.")] | ||
[Command(Name = "histroot", AliasExpansion = "HistRoot", Help = "Displays information related to both promotions and relocations of the specified root.")] | ||
[Command(Name = "setsymbolserver", AliasExpansion = "SetSymbolServer", Help = "Enables the symbol server support ")] | ||
[Command(Name = "soshelp", AliasExpansion = "Help", Help = "Displays all available commands when no parameter is specified, or displays detailed help information about the specified command. soshelp <command>")] | ||
public class SOSCommand : CommandBase | ||
{ | ||
[Argument(Name = "arguments", Help = "Arguments to SOS command.")] | ||
public string[] Arguments { get; set; } | ||
|
||
public AnalyzeContext AnalyzeContext { get; set; } | ||
|
||
private SOSHost _sosHost; | ||
|
||
public override Task InvokeAsync() | ||
{ | ||
try { | ||
if (_sosHost == null) { | ||
_sosHost = new SOSHost(AnalyzeContext.Target.DataReader, AnalyzeContext); | ||
} | ||
string arguments = null; | ||
if (Arguments.Length > 0) { | ||
arguments = string.Concat(Arguments.Select((arg) => arg + " ")); | ||
} | ||
_sosHost.ExecuteCommand(AliasExpansion, arguments); | ||
} | ||
catch (Exception ex) when (ex is FileNotFoundException || ex is EntryPointNotFoundException || ex is InvalidOperationException) { | ||
Console.Error.WriteLine(ex.Message); | ||
} | ||
return Task.CompletedTask; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
|
||
using Microsoft.Diagnostic.Repl; | ||
using System.CommandLine; | ||
using System.Threading.Tasks; | ||
|
||
namespace Microsoft.Diagnostic.Tools.Dump | ||
{ | ||
[Command(Name = "setthread", Help = "Sets or displays the current thread id for the SOS commands.")] | ||
[Command(Name = "threads")] | ||
public class SetThreadCommand : CommandBase | ||
{ | ||
[Argument(Help = "The thread id to set, otherwise displays the current id.")] | ||
public int? ThreadId { get; set; } = null; | ||
|
||
public AnalyzeContext AnalyzeContext { get; set; } | ||
|
||
public override Task InvokeAsync() | ||
{ | ||
if (ThreadId.HasValue) | ||
{ | ||
AnalyzeContext.CurrentThreadId = ThreadId.Value; | ||
} | ||
else | ||
{ | ||
int index = 0; | ||
foreach (uint threadId in AnalyzeContext.Target.DataReader.EnumerateAllThreads()) | ||
{ | ||
WriteLine("{0}{1} 0x{2:X4} ({2})", threadId == AnalyzeContext.CurrentThreadId ? "*" : " ", index, threadId); | ||
index++; | ||
} | ||
} | ||
return Task.CompletedTask; | ||
} | ||
} | ||
} |
Oops, something went wrong.