From: Mike McLaughlin Date: Sun, 17 Feb 2019 20:14:28 +0000 (-0800) Subject: Add interactive dump "analyze" dump support to dotnet-dump project. X-Git-Tag: submit/tizen/20190813.035844~56^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6d59be78b9ccd231d1f34f1e1b6017d7b9f2a9db;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git 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. --- diff --git a/src/Tools/dotnet-dump/AnalyzeContext.cs b/src/Tools/dotnet-dump/AnalyzeContext.cs new file mode 100644 index 000000000..52315aa84 --- /dev/null +++ b/src/Tools/dotnet-dump/AnalyzeContext.cs @@ -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 +{ + /// + /// The the common context for analyze commands + /// + public class AnalyzeContext: ISOSHostContext + { + readonly IConsole _console; + ClrRuntime _runtime; + + public AnalyzeContext(IConsole console, DataTarget target, Action exit) + { + _console = console; + Target = target; + Exit = exit; + } + + /// + /// ClrMD data target + /// + public DataTarget Target { get; } + + /// + /// ClrMD runtime info + /// + 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; + } + } + + /// + /// Delegate to invoke to exit repl + /// + public Action Exit { get; } + + /// + /// Current OS thread Id + /// + public int CurrentThreadId { get; set; } + + /// + /// Cancellation token for current command + /// + public CancellationToken CancellationToken { get; set; } + + /// + /// Console write function + /// + /// + void ISOSHostContext.Write(string text) + { + _console.Out.Write(text); + } + } +} \ No newline at end of file diff --git a/src/Tools/dotnet-dump/Analyzer.cs b/src/Tools/dotnet-dump/Analyzer.cs new file mode 100644 index 000000000..480f57bd6 --- /dev/null +++ b/src/Tools/dotnet-dump/Analyzer.cs @@ -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 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; + } + } +} diff --git a/src/Tools/dotnet-dump/Commands/ExitCommand.cs b/src/Tools/dotnet-dump/Commands/ExitCommand.cs new file mode 100644 index 000000000..704173de1 --- /dev/null +++ b/src/Tools/dotnet-dump/Commands/ExitCommand.cs @@ -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; + } + } +} diff --git a/src/Tools/dotnet-dump/Commands/HelpCommand.cs b/src/Tools/dotnet-dump/Commands/HelpCommand.cs new file mode 100644 index 000000000..eef951401 --- /dev/null +++ b/src/Tools/dotnet-dump/Commands/HelpCommand.cs @@ -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; + } + + /// + /// Get help builder interface + /// + /// help builder + 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; + } + } +} diff --git a/src/Tools/dotnet-dump/Commands/ModulesCommand.cs b/src/Tools/dotnet-dump/Commands/ModulesCommand.cs new file mode 100644 index 000000000..e80590459 --- /dev/null +++ b/src/Tools/dotnet-dump/Commands/ModulesCommand.cs @@ -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; + } + } +} diff --git a/src/Tools/dotnet-dump/Commands/SOSCommand.cs b/src/Tools/dotnet-dump/Commands/SOSCommand.cs new file mode 100644 index 000000000..fb111716a --- /dev/null +++ b/src/Tools/dotnet-dump/Commands/SOSCommand.cs @@ -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 ")] + 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; + } + } +} diff --git a/src/Tools/dotnet-dump/Commands/SetThreadCommand.cs b/src/Tools/dotnet-dump/Commands/SetThreadCommand.cs new file mode 100644 index 000000000..35b10da8d --- /dev/null +++ b/src/Tools/dotnet-dump/Commands/SetThreadCommand.cs @@ -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; + } + } +} diff --git a/src/Tools/dotnet-dump/Dumper.Linux.cs b/src/Tools/dotnet-dump/Dumper.Linux.cs index 58ad219f2..db3f7fd3f 100644 --- a/src/Tools/dotnet-dump/Dumper.Linux.cs +++ b/src/Tools/dotnet-dump/Dumper.Linux.cs @@ -4,39 +4,39 @@ using System.Diagnostics; using System.Threading.Tasks; using System.IO; -namespace Microsoft.Diagnostics.Tools.Dump +namespace Microsoft.Diagnostic.Tools.Dump { - public static partial class Dumper + public partial class Dumper { private static class Linux { internal static async Task CollectDumpAsync(Process process, string fileName) { // We don't work on WSL :( - var ostype = await File.ReadAllTextAsync("/proc/sys/kernel/osrelease"); - if(ostype.Contains("Microsoft")) + string ostype = await File.ReadAllTextAsync("/proc/sys/kernel/osrelease"); + if (ostype.Contains("Microsoft")) { throw new PlatformNotSupportedException("Cannot collect memory dumps from Windows Subsystem for Linux."); } // First step is to find the .NET runtime. To do this we look for coreclr.so - var coreclr = process.Modules.Cast().FirstOrDefault(m => string.Equals(m.ModuleName, "libcoreclr.so")); - if(coreclr == null) + ProcessModule coreclr = process.Modules.Cast().FirstOrDefault(m => string.Equals(m.ModuleName, "libcoreclr.so")); + if (coreclr == null) { throw new NotSupportedException("Unable to locate .NET runtime associated with this process!"); } // Find createdump next to that file - var runtimeDirectory = Path.GetDirectoryName(coreclr.FileName); - var createDumpPath = Path.Combine(runtimeDirectory, "createdump"); - if(!File.Exists(createDumpPath)) + string runtimeDirectory = Path.GetDirectoryName(coreclr.FileName); + string createDumpPath = Path.Combine(runtimeDirectory, "createdump"); + if (!File.Exists(createDumpPath)) { throw new NotSupportedException($"Unable to locate 'createdump' tool in '{runtimeDirectory}'"); } // Create the dump - var exitCode = await CreateDumpAsync(createDumpPath, fileName, process.Id); - if(exitCode != 0) + int exitCode = await CreateDumpAsync(createDumpPath, fileName, process.Id); + if (exitCode != 0) { throw new Exception($"createdump exited with non-zero exit code: {exitCode}"); } @@ -50,10 +50,10 @@ namespace Microsoft.Diagnostics.Tools.Dump StartInfo = new ProcessStartInfo() { FileName = exePath, - Arguments = $"-f {fileName} {processId}", - RedirectStandardError = true, - RedirectStandardOutput = true, - RedirectStandardInput = true, + Arguments = $"--diag -f {fileName} {processId}", + //RedirectStandardError = true, + //RedirectStandardOutput = true, + //RedirectStandardInput = true, }, EnableRaisingEvents = true, }; diff --git a/src/Tools/dotnet-dump/Dumper.Windows.cs b/src/Tools/dotnet-dump/Dumper.Windows.cs index 89350b7c2..a78626d82 100644 --- a/src/Tools/dotnet-dump/Dumper.Windows.cs +++ b/src/Tools/dotnet-dump/Dumper.Windows.cs @@ -5,9 +5,9 @@ using System.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; -namespace Microsoft.Diagnostics.Tools.Dump +namespace Microsoft.Diagnostic.Tools.Dump { - public static partial class Dumper + public partial class Dumper { private static class Windows { diff --git a/src/Tools/dotnet-dump/Dumper.cs b/src/Tools/dotnet-dump/Dumper.cs index fc412e4ff..cede967d1 100644 --- a/src/Tools/dotnet-dump/Dumper.cs +++ b/src/Tools/dotnet-dump/Dumper.cs @@ -1,26 +1,60 @@ using System; +using System.CommandLine; using System.Diagnostics; +using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; -namespace Microsoft.Diagnostics.Tools.Dump +namespace Microsoft.Diagnostic.Tools.Dump { - public static partial class Dumper + public partial class Dumper { - public static Task CollectDumpAsync(Process process, string fileName) + public Dumper() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return Windows.CollectDumpAsync(process, fileName); + } + + public async Task Collect(IConsole console, int processId, string outputDirectory) + { + if (processId == 0) { + console.Error.WriteLine("ProcessId is required."); + return 1; } - else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + + // System.CommandLine has a bug in the default value handling + if (outputDirectory == null) { + outputDirectory = Directory.GetCurrentDirectory(); + } + + // Get the process + Process process = null; + try { - return Linux.CollectDumpAsync(process, fileName); + process = Process.GetProcessById(processId); } - else + catch (Exception ex) when (ex is ArgumentException || ex is InvalidOperationException) { - throw new PlatformNotSupportedException("Can't collect a memory dump on this platform."); + console.Error.WriteLine($"Invalid process id: {processId}"); + return 1; } + + // Generate the file name + string fileName = Path.Combine(outputDirectory, $"{process.ProcessName}-{process.Id}-{DateTime.Now:yyyyMMdd-HHmmss-fff}.dmp"); + + console.Out.WriteLine($"Collecting memory dump for {process.ProcessName} (ID: {process.Id}) ..."); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + await Windows.CollectDumpAsync(process, fileName); + } + else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { + await Linux.CollectDumpAsync(process, fileName); + } + else { + console.Error.WriteLine($"Unsupported operating system {RuntimeInformation.OSDescription}"); + return 1; + } + + console.Out.WriteLine($"Dump saved to {fileName}"); + return 0; } } } diff --git a/src/Tools/dotnet-dump/Program.cs b/src/Tools/dotnet-dump/Program.cs index cdb386411..8509b5c9c 100644 --- a/src/Tools/dotnet-dump/Program.cs +++ b/src/Tools/dotnet-dump/Program.cs @@ -1,61 +1,59 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; +using System.CommandLine; +using System.CommandLine.Builder; +using System.CommandLine.Invocation; using System.IO; -using System.Runtime.InteropServices; using System.Threading.Tasks; -using McMaster.Extensions.CommandLineUtils; -using Microsoft.Internal.Utilities; -namespace Microsoft.Diagnostics.Tools.Dump +namespace Microsoft.Diagnostic.Tools.Dump { - [Command(Name = "dotnet-dump", Description = "Captures memory dumps of .NET processes")] - internal class Program + class Program { - [Required(ErrorMessage = "You must provide a process ID to be dumped.")] - [Option("-p|--process-id ", Description = "The ID of the process to collect a memory dump for")] - public int ProcessId { get; set; } - - [Option("-o|--output ", Description = "The directory to write the dump to. Defaults to the current working directory.")] - public string OutputDir { get; set; } - - public async Task OnExecute(IConsole console, CommandLineApplication app) + public static Task Main(string[] args) { - if (string.IsNullOrEmpty(OutputDir)) - { - OutputDir = Directory.GetCurrentDirectory(); - } - - // Get the process - var process = Process.GetProcessById(ProcessId); - - // Generate the file name - var fileName = Path.Combine(OutputDir, $"{process.ProcessName}-{process.Id}-{DateTime.Now:yyyyMMdd-HHmmss-fff}.dmp"); + var parser = new CommandLineBuilder() + .AddCommand(CollectCommand()) + .AddCommand(AnalyzeCommand()) + .UseDefaults() + .Build(); - console.WriteLine($"Collecting memory dump for {process.ProcessName} (ID: {process.Id}) ..."); - await Dumper.CollectDumpAsync(process, fileName); - console.WriteLine($"Dump saved to {fileName}"); - - return 0; + return parser.InvokeAsync(args); } - private static int Main(string[] args) - { - DebugUtil.WaitForDebuggerIfRequested(ref args); - - try - { - return CommandLineApplication.Execute(args); - } - catch(PlatformNotSupportedException ex) - { - Console.Error.WriteLine(ex.Message); - return 1; - } - catch (OperationCanceledException) - { - return 0; - } - } + private static Command CollectCommand() => + new Command( + "collect", + "Captures memory dumps of .NET processes.", + new Option[] { ProcessIdOption(), OutputOption() }, + handler: CommandHandler.Create(new Dumper().Collect)); + + private static Option ProcessIdOption() => + new Option( + new[] { "-p", "--process-id" }, + "The ID of the process to collect a memory dump.", + new Argument { Name = "processId" }); + + private static Option OutputOption() => + new Option( + new[] { "-o", "--output" }, + "The directory to write the dump. Defaults to the current working directory.", + new Argument(Directory.GetCurrentDirectory()) { Name = "directory" }); + + private static Command AnalyzeCommand() => + new Command( + "analyze", + "Start interactive dump analyze.", + new Option[] { RunCommand() }, argument: DumpPath(), + handler: CommandHandler.Create(new Analyzer().Analyze)); + + private static Argument DumpPath() => + new Argument { + Name = "dump_path", + Description = "Name of the dump file to analyze." }.ExistingOnly(); + + private static Option RunCommand() => + new Option( + new[] { "-c", "--command" }, + "Run the command on start.", + new Argument() { Name = "command" }); } } diff --git a/src/Tools/dotnet-dump/dotnet-dump.csproj b/src/Tools/dotnet-dump/dotnet-dump.csproj index b8453cade..408ce5dd9 100644 --- a/src/Tools/dotnet-dump/dotnet-dump.csproj +++ b/src/Tools/dotnet-dump/dotnet-dump.csproj @@ -2,23 +2,83 @@ Exe - netcoreapp2.1 - Microsoft.Diagnostics.Tools.Dump - + 2.1.0 + win-x64;win-x86;osx-x64 + + $(VersionPrefix) + $(VersionPrefix) + dotnet-dump + Microsoft.Diagnostic.Tools.Dump + Diagnostic dump collect and analyze tool + Diagnostic + $(Description) + + $(OutputPath) + $(ArtifactsBinDir)\SOS.NETCore\$(Configuration)\netstandard2.0\publish\*.dll - - + - + - + + + + <_PackageFiles Include="$(SOSNETCoreBinaries)"> + None + tools/netcoreapp2.1/any/win-x64 + + <_PackageFiles Include="$(ArtifactsBinDir)\Windows_NT.x64.$(Configuration)\sos.dll"> + None + tools/netcoreapp2.1/any/win-x64 + + <_PackageFiles Include="$(SOSNETCoreBinaries)"> + None + tools/netcoreapp2.1/any/win-x86 + + <_PackageFiles Include="$(ArtifactsBinDir)\Windows_NT.x86.$(Configuration)\sos.dll"> + None + tools/netcoreapp2.1/any/win-x86 + + <_PackageFiles Include="$(SOSNETCoreBinaries)"> + None + tools/netcoreapp2.1/any/linux-x64 + + <_PackageFiles Include="$(ArtifactsBinDir)\Linux.x64.$(Configuration)\libsosplugin.so"> + None + tools/netcoreapp2.1/any/linux-x64 + + <_PackageFiles Include="$(ArtifactsBinDir)\Linux.x64.$(Configuration)\libsos.so"> + None + tools/netcoreapp2.1/any/linux-x64 + + <_PackageFiles Include="$(ArtifactsBinDir)\Linux.x64.$(Configuration)\sosdocsunix.txt"> + None + tools/netcoreapp2.1/any/linux-x64 + + <_PackageFiles Include="$(SOSNETCoreBinaries)"> + None + tools/netcoreapp2.1/any/osx-x64 + + <_PackageFiles Include="$(ArtifactsBinDir)\OSX.x64.$(Configuration)\libsosplugin.dylib"> + None + tools/netcoreapp2.1/any/osx-x64 + + <_PackageFiles Include="$(ArtifactsBinDir)\OSX.x64.$(Configuration)\libsos.dylib"> + None + tools/netcoreapp2.1/any/osx-x64 + + <_PackageFiles Include="$(ArtifactsBinDir)\OSX.x64.$(Configuration)\sosdocsunix.txt"> + None + tools/netcoreapp2.1/any/osx-x64 + +