EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventPipe.UnitTests", "src\tests\eventpipe\EventPipe.UnitTests.csproj", "{CED9ABBA-861E-4C0A-9359-22351208EF27}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{298AE119-6625-4604-BDE5-0765DC34C856}"
+ ProjectSection(SolutionItems) = preProject
+ src\Tools\Common\CommandExtensions.cs = src\Tools\Common\CommandExtensions.cs
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Commands", "Commands", "{C457CBCD-3A8D-4402-9A2B-693A0390D3F9}"
+ ProjectSection(SolutionItems) = preProject
+ src\Tools\Common\Commands\ProcessStatus.cs = src\Tools\Common\Commands\ProcessStatus.cs
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|Any CPU = Checked|Any CPU
{6F4DD2F8-1C7B-4A87-B7E5-1BEE9F5AC128} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
{C79D6069-2C18-48CB-846E-71F7168C2F7D} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
{CED9ABBA-861E-4C0A-9359-22351208EF27} = {03479E19-3F18-49A6-910A-F5041E27E7C0}
+ {298AE119-6625-4604-BDE5-0765DC34C856} = {B62728C8-1267-4043-B46F-5537BBAEC692}
+ {C457CBCD-3A8D-4402-9A2B-693A0390D3F9} = {298AE119-6625-4604-BDE5-0765DC34C856}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46465737-C938-44FC-BE1A-4CE139EBB5E0}
<MicrosoftDiagnosticsRuntimeVersion>1.1.46104</MicrosoftDiagnosticsRuntimeVersion>
<MicrosoftDiaSymReaderNativePackageVersion>1.7.0</MicrosoftDiaSymReaderNativePackageVersion>
<MicrosoftDiagnosticsTracingTraceEventVersion>2.0.44</MicrosoftDiagnosticsTracingTraceEventVersion>
- <SystemCommandLineExperimentalVersion>0.2.0-alpha.19254.1</SystemCommandLineExperimentalVersion>
- <SystemCommandLineRenderingVersion>0.2.0-alpha.19254.1</SystemCommandLineRenderingVersion>
+ <SystemCommandLineExperimentalVersion>0.3.0-alpha.19602.1</SystemCommandLineExperimentalVersion>
+ <SystemCommandLineRenderingVersion>0.3.0-alpha.19602.1</SystemCommandLineRenderingVersion>
<SystemMemoryVersion>4.5.3</SystemMemoryVersion>
<XUnitVersion>2.4.1</XUnitVersion>
<XUnitAbstractionsVersion>2.0.3</XUnitAbstractionsVersion>
{
command = new Command(commandAttribute.Name, commandAttribute.Help);
var properties = new List<(PropertyInfo, Option)>();
- PropertyInfo argument = null;
+ var arguments = new List<(PropertyInfo, Argument)>();
foreach (PropertyInfo property in type.GetProperties().Where(p => p.CanWrite))
{
var argumentAttribute = (ArgumentAttribute)property.GetCustomAttributes(typeof(ArgumentAttribute), inherit: false).SingleOrDefault();
if (argumentAttribute != null)
{
- if (argument != null) {
- throw new ArgumentException($"More than one ArgumentAttribute in command class: {type.Name}");
- }
IArgumentArity arity = property.PropertyType.IsArray ? ArgumentArity.ZeroOrMore : ArgumentArity.ZeroOrOne;
- command.Argument = new Argument {
+ var argument = new Argument {
Name = argumentAttribute.Name ?? property.Name.ToLowerInvariant(),
Description = argumentAttribute.Help,
ArgumentType = property.PropertyType,
Arity = arity
};
- argument = property;
+ command.AddArgument(argument);
+ arguments.Add((property, argument));
}
else
{
var optionAttribute = (OptionAttribute)property.GetCustomAttributes(typeof(OptionAttribute), inherit: false).SingleOrDefault();
if (optionAttribute != null)
{
- var option = new Option(optionAttribute.Name ?? BuildAlias(property.Name), optionAttribute.Help, new Argument { ArgumentType = property.PropertyType });
+ var option = new Option(optionAttribute.Name ?? BuildAlias(property.Name), optionAttribute.Help) {
+ Argument = new Argument { ArgumentType = property.PropertyType }
+ };
command.AddOption(option);
properties.Add((property, option));
}
}
- var handler = new Handler(this, commandAttribute.AliasExpansion, argument, properties, type);
+ var handler = new Handler(this, commandAttribute.AliasExpansion, arguments, properties, type);
_commandHandlers.Add(command.Name, handler);
command.Handler = handler;
{
private readonly CommandProcessor _commandProcessor;
private readonly string _aliasExpansion;
- private readonly PropertyInfo _argument;
+ private readonly IEnumerable<(PropertyInfo Property, Argument Argument)> _arguments;
private readonly IEnumerable<(PropertyInfo Property, Option Option)> _properties;
private readonly ConstructorInfo _constructor;
private readonly MethodInfo _methodInfo;
private readonly MethodInfo _methodInfoHelp;
- public Handler(CommandProcessor commandProcessor, string aliasExpansion, PropertyInfo argument, IEnumerable<(PropertyInfo, Option)> properties, Type type)
+ public Handler(CommandProcessor commandProcessor, string aliasExpansion, IEnumerable<(PropertyInfo, Argument)> arguments, IEnumerable<(PropertyInfo, Option)> properties, Type type)
{
_commandProcessor = commandProcessor;
_aliasExpansion = aliasExpansion;
- _argument = argument;
+ _arguments = arguments;
_properties = properties;
_constructor = type.GetConstructors().SingleOrDefault((info) => info.GetParameters().Length == 0) ??
property.Property.SetValue(instance, value);
}
- if (context != null && _argument != null)
+ if (context != null)
{
- object value = null;
- ArgumentResult result = context.ParseResult.CommandResult.ArgumentResult;
- switch (result)
+ IEnumerable<ArgumentResult> argumentResults = context.ParseResult.CommandResult.Children.OfType<ArgumentResult>();
+
+ foreach ((PropertyInfo Property, Argument Argument) argument in _arguments)
{
- case SuccessfulArgumentResult successful:
- value = successful.Value;
- break;
- case FailedArgumentResult failed:
- throw new InvalidOperationException(failed.ErrorMessage);
+ ArgumentResult argumentResult = argumentResults.Where((result) => result.Argument == argument.Argument).SingleOrDefault();
+ if (argumentResult != null)
+ {
+ object value = argumentResult.GetValueOrDefault();
+ argument.Property.SetValue(instance, value);
+ }
}
- _argument.SetValue(instance, value);
}
}
--- /dev/null
+// 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.CommandLine;
+using System.CommandLine.Invocation;
+
+namespace Microsoft.Tools.Common
+{
+ public static class CommandExtenions
+ {
+ /// <summary>
+ /// Allows the command handler to be included in the collection initializer.
+ /// </summary>
+ public static void Add(this Command command, ICommandHandler handler)
+ {
+ command.Handler = handler;
+ }
+ }
+}
+++ /dev/null
-// 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.Runtime.Serialization;
-
-namespace Microsoft.Internal.Utilities
-{
- public class CommandLineException : Exception
- {
- public CommandLineException()
- {
- }
-
- public CommandLineException(string message) : base(message)
- {
- }
-
- public CommandLineException(string message, Exception innerException) : base(message, innerException)
- {
- }
-
- protected CommandLineException(SerializationInfo info, StreamingContext context) : base(info, context)
- {
- }
- }
-}
// 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.NETCore.Client;
using System;
using System.CommandLine;
-using System.Diagnostics;
+using System.CommandLine.Invocation;
using System.Linq;
using System.Text;
-
-using Microsoft.Diagnostics.NETCore.Client;
+using Process = System.Diagnostics.Process;
namespace Microsoft.Internal.Common.Commands
{
public class ProcessStatusCommandHandler
{
+ public static Command ProcessStatusCommand(string description) =>
+ new Command(name: "ps", description)
+ {
+ Handler = CommandHandler.Create<IConsole>(PrintProcessStatus)
+ };
+
/// <summary>
/// Print the current list of available .NET core processes for diagnosis and their statuses
/// </summary>
+++ /dev/null
-// 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.Threading;
-using System.Threading.Tasks;
-using McMaster.Extensions.CommandLineUtils;
-
-namespace Microsoft.Internal.Utilities
-{
- public static class ConsoleCancellationExtensions
- {
- public static CancellationToken GetCtrlCToken(this IConsole console)
- {
- var cts = new CancellationTokenSource();
- console.CancelKeyPress += (sender, args) =>
- {
- if (cts.IsCancellationRequested)
- {
- // Terminate forcibly, the user pressed Ctrl-C a second time
- args.Cancel = false;
- }
- else
- {
- // Don't terminate, just trip the token
- args.Cancel = true;
- cts.Cancel();
- }
- };
- return cts.Token;
- }
-
- public static Task WaitForCtrlCAsync(this IConsole console)
- {
- var tcs = new TaskCompletionSource<object>();
- console.CancelKeyPress += (sender, args) =>
- {
- // Don't terminate, just trip the task
- args.Cancel = true;
- tcs.TrySetResult(null);
- };
- return tcs.Task;
- }
- }
-}
+++ /dev/null
-// 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.Diagnostics;
-using System.Linq;
-
-namespace Microsoft.Internal.Utilities
-{
- internal static class DebugUtil
- {
- [Conditional("DEBUG")]
- public static void WaitForDebuggerIfRequested(ref string[] args)
- {
- if (args.Any(a => a == "--debug"))
- {
- args = args.Where(a => a != "--debug").ToArray();
- Console.WriteLine($"Ready for debugger to attach. Process ID: {Process.GetCurrentProcess().Id}.");
- Console.WriteLine("Press ENTER to continue.");
- Console.ReadLine();
- }
- }
- }
-}
// 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.NETCore.Client;
+using Microsoft.Internal.Common.Commands;
+using Microsoft.Tools.Common;
using System;
+using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Binding;
using System.CommandLine.Builder;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using System.Collections.Generic;
-using Microsoft.Diagnostics.NETCore.Client;
-using Microsoft.Internal.Common.Commands;
namespace Microsoft.Diagnostics.Tools.Counters
{
private static Command MonitorCommand() =>
new Command(
- "monitor",
- "Start monitoring a .NET application",
- new Option[] { ProcessIdOption(), RefreshIntervalOption() },
- argument: CounterList(),
- handler: CommandHandler.Create<CancellationToken, List<string>, IConsole, int, int>(new CounterMonitor().Monitor));
+ name: "monitor",
+ description: "Start monitoring a .NET application")
+ {
+ // Handler
+ CommandHandler.Create<CancellationToken, List<string>, IConsole, int, int>(new CounterMonitor().Monitor),
+ // Arguments and Options
+ CounterList(), ProcessIdOption(), RefreshIntervalOption()
+ };
private static Command CollectCommand() =>
new Command(
- "collect",
- "Monitor counters in a .NET application and export the result into a file",
- new Option[] { ProcessIdOption(), RefreshIntervalOption(), ExportFormatOption(), ExportFileNameOption() },
- argument: CounterList(),
- handler: HandlerDescriptor.FromDelegate((ExportDelegate)new CounterMonitor().Collect).GetCommandHandler());
+ name: "collect",
+ description: "Monitor counters in a .NET application and export the result into a file")
+ {
+ // Handler
+ HandlerDescriptor.FromDelegate((ExportDelegate)new CounterMonitor().Collect).GetCommandHandler(),
+ // Arguments and Options
+ CounterList(), ProcessIdOption(), RefreshIntervalOption(), ExportFormatOption(), ExportFileNameOption()
+ };
private static Option ProcessIdOption() =>
new Option(
- new[] { "-p", "--process-id" },
- "The ID of the process that will be monitored.",
- new Argument<int> { Name = "pid" });
+ aliases: new[] { "-p", "--process-id" },
+ description: "The process id that will be monitored.")
+ {
+ Argument = new Argument<int>(name: "pid")
+ };
private static Option RefreshIntervalOption() =>
new Option(
- new[] { "--refresh-interval" },
- "The number of seconds to delay between updating the displayed counters.",
- new Argument<int>(defaultValue: 1) { Name = "refresh-interval" });
+ alias: "--refresh-interval",
+ description: "The number of seconds to delay between updating the displayed counters.")
+ {
+ Argument = new Argument<int>(name: "refresh-interval", defaultValue: 1)
+ };
- private static Option ExportFormatOption() =>
+ private static Option ExportFormatOption() =>
new Option(
- new[] { "--format" },
- "The format of exported counter data.",
- new Argument<CountersExportFormat>(defaultValue: CountersExportFormat.csv) { Name = "format" });
+ alias: "--format",
+ description: "The format of exported counter data.")
+ {
+ Argument = new Argument<CountersExportFormat>(name: "format", defaultValue: CountersExportFormat.csv)
+ };
- private static Option ExportFileNameOption() =>
+ private static Option ExportFileNameOption() =>
new Option(
- new[] { "-o", "--output" },
- "The output file name.",
- new Argument<string>(defaultValue: "counter") { Name = "output" });
+ aliases: new[] { "-o", "--output" },
+ description: "The output file name.")
+ {
+ Argument = new Argument<string>(name: "output", defaultValue: "counter")
+ };
private static Argument CounterList() =>
- new Argument<List<string>> {
- Name = "counter_list",
- Description = @"A space separated list of counters. Counters can be specified provider_name[:counter_name].
- If the provider_name is used without a qualifying counter_name then all counters will be shown. To discover
- provider and counter names, use the list command.
- .",
+ new Argument<List<string>>(name: "counter_list", defaultValue: new List<string>())
+ {
+ Description = @"A space separated list of counters. Counters can be specified provider_name[:counter_name]. If the provider_name is used without a qualifying counter_name then all counters will be shown. To discover provider and counter names, use the list command.",
Arity = ArgumentArity.ZeroOrMore
};
private static Command ListCommand() =>
new Command(
- "list",
- "Display a list of counter names and descriptions, grouped by provider.",
- new Option[] { },
- handler: CommandHandler.Create<IConsole>(List));
-
- private static Command ProcessStatusCommand() =>
- new Command(
- "ps",
- "Display a list of dotnet processes that can be monitored.",
- new Option[] { },
- handler: CommandHandler.Create<IConsole>(ProcessStatusCommandHandler.PrintProcessStatus));
+ name: "list",
+ description: "Display a list of counter names and descriptions, grouped by provider.")
+ {
+ Handler = CommandHandler.Create<IConsole>(List)
+ };
public static int List(IConsole console)
{
.AddCommand(MonitorCommand())
.AddCommand(CollectCommand())
.AddCommand(ListCommand())
- .AddCommand(ProcessStatusCommand())
+ .AddCommand(ProcessStatusCommandHandler.ProcessStatusCommand("Lists the dotnet processes that can be monitored"))
.UseDefaults()
.Build();
return parser.InvokeAsync(args);
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)..\dotnet-trace\Extensions.cs" Link="Extensions.cs" />
+ <Compile Include="..\Common\CommandExtensions.cs" Link="CommandExtensions.cs" />
<Compile Include="..\Common\Commands\ProcessStatus.cs" Link="ProcessStatus.cs" />
</ItemGroup>
// Run the commands from the dotnet-dump command line
if (command != null)
{
- foreach (string cmd in command) {
+ foreach (string cmd in command)
+ {
await _commandProcessor.Parse(cmd);
}
}
{
try {
string arguments = null;
- if (Arguments.Length > 0) {
+ if (Arguments != null && Arguments.Length > 0) {
arguments = string.Concat(Arguments.Select((arg) => arg + " "));
}
SOSHost.ExecuteCommand(AliasExpansion, arguments);
// 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.NETCore.Client;
+using Microsoft.Internal.Common.Commands;
+using Microsoft.Tools.Common;
using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Invocation;
using System.IO;
using System.Threading.Tasks;
-using Microsoft.Diagnostics.NETCore.Client;
-using Microsoft.Internal.Common.Commands;
namespace Microsoft.Diagnostics.Tools.Dump
{
var parser = new CommandLineBuilder()
.AddCommand(CollectCommand())
.AddCommand(AnalyzeCommand())
- .AddCommand(ProcessStatusCommand())
+ .AddCommand(ProcessStatusCommandHandler.ProcessStatusCommand("Lists the dotnet processes that dumps can be collected"))
.UseDefaults()
.Build();
}
private static Command CollectCommand() =>
- new Command(
- "collect",
- "Capture dumps from a process",
- new Option[] { ProcessIdOption(), OutputOption(), DiagnosticLoggingOption(), TypeOption() },
- handler: CommandHandler.Create<IConsole, int, string, bool, Dumper.DumpTypeOption>(new Dumper().Collect));
+ new Command( name: "collect", description: "Capture dumps from a process")
+ {
+ // Handler
+ CommandHandler.Create<IConsole, int, string, bool, Dumper.DumpTypeOption>(new Dumper().Collect),
+ // Options
+ ProcessIdOption(), OutputOption(), DiagnosticLoggingOption(), TypeOption()
+ };
private static Option ProcessIdOption() =>
new Option(
- new[] { "-p", "--process-id" },
- "The process to collect a memory dump from.",
- new Argument<int> { Name = "pid" });
+ aliases: new[] { "-p", "--process-id" },
+ description: "The process id to collect a memory dump.")
+ {
+ Argument = new Argument<int>(name: "pid")
+ };
private static Option OutputOption() =>
- new Option(
- new[] { "-o", "--output" },
- @"The path where collected dumps should be written. Defaults to '.\dump_YYYYMMDD_HHMMSS.dmp' on Windows and
-'./core_YYYYMMDD_HHMMSS' on Linux where YYYYMMDD is Year/Month/Day and HHMMSS is Hour/Minute/Second. Otherwise, it is the full
-path and file name of the dump.",
- new Argument<string>() { Name = "output_dump_path" });
+ new Option(
+ aliases: new[] { "-o", "--output" },
+ description: @"The path where collected dumps should be written. Defaults to '.\dump_YYYYMMDD_HHMMSS.dmp' on Windows and './core_YYYYMMDD_HHMMSS'
+on Linux where YYYYMMDD is Year/Month/Day and HHMMSS is Hour/Minute/Second. Otherwise, it is the full path and file name of the dump.")
+ {
+ Argument = new Argument<string>(name: "output_dump_path")
+ };
private static Option DiagnosticLoggingOption() =>
new Option(
- new[] { "--diag" },
- "Enable dump collection diagnostic logging.",
- new Argument<bool> { Name = "diag" });
+ alias: "--diag",
+ description: "Enable dump collection diagnostic logging.")
+ {
+ Argument = new Argument<bool>(name: "diag")
+ };
private static Option TypeOption() =>
new Option(
- "--type",
- @"The dump type determines the kinds of information that are collected from the process. There are two types:
-
-heap - A large and relatively comprehensive dump containing module lists, thread lists, all stacks,
- exception information, handle information, and all memory except for mapped images.
-mini - A small dump containing module lists, thread lists, exception information and all stacks.
-
-If not specified 'heap' is the default.",
- new Argument<Dumper.DumpTypeOption>(Dumper.DumpTypeOption.Heap) { Name = "dump_type" });
+ alias: "--type",
+ description: @"The dump type determines the kinds of information that are collected from the process. There are two types: heap - A large and
+relatively comprehensive dump containing module lists, thread lists, all stacks, exception information, handle information, and all memory except for mapped
+images. mini - A small dump containing module lists, thread lists, exception information and all stacks. If not specified 'heap' is the default.")
+ {
+ Argument = new Argument<Dumper.DumpTypeOption>(name: "dump_type", defaultValue: Dumper.DumpTypeOption.Heap)
+ };
private static Command AnalyzeCommand() =>
new Command(
- "analyze",
- "Starts an interactive shell with debugging commands to explore a dump",
- new Option[] { RunCommand() }, argument: DumpPath(),
- handler: CommandHandler.Create<FileInfo, string[]>(new Analyzer().Analyze));
+ name: "analyze",
+ description: "Starts an interactive shell with debugging commands to explore a dump")
+ {
+ // Handler
+ CommandHandler.Create<FileInfo, string[]>(new Analyzer().Analyze),
+ // Arguments and Options
+ DumpPath(),
+ RunCommand()
+ };
private static Argument DumpPath() =>
- new Argument<FileInfo> {
- Name = "dump_path",
- Description = "Name of the dump file to analyze." }.ExistingOnly();
+ new Argument<FileInfo>(
+ 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<string[]>() { Name = "command", Arity = ArgumentArity.ZeroOrMore });
-
- private static Command ProcessStatusCommand() =>
- new Command(
- "ps",
- "Display a list of dotnet processes to create dump from",
- new Option[] { },
- handler: CommandHandler.Create<IConsole>(ProcessStatusCommandHandler.PrintProcessStatus));
+ aliases: new[] { "-c", "--command" },
+ description: "Run the command on start.")
+ {
+ Argument = new Argument<string[]>(name: "command", defaultValue: new string[0]) { Arity = ArgumentArity.ZeroOrMore }
+ };
}
}
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Common\CommandExtensions.cs" Link="CommandExtensions.cs" />
<Compile Include="..\Common\Commands\ProcessStatus.cs" Link="ProcessStatus.cs" />
</ItemGroup>
// 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.Tools.Common;
using System;
-using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Binding;
-using System.CommandLine.Rendering;
-using System.Diagnostics;
using System.IO;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public static Command CollectCommand() =>
new Command(
name: "collect",
- description: "Collects a diagnostic trace from a currently running process",
- symbols: new Option[] {
- ProcessIdOption(),
- OutputPathOption(),
- VerboseOption(),
- TimeoutOption()
- },
- handler: HandlerDescriptor.FromDelegate((CollectDelegate)Collect).GetCommandHandler());
+ description: "Collects a diagnostic trace from a currently running process")
+ {
+ // Handler
+ HandlerDescriptor.FromDelegate((CollectDelegate)Collect).GetCommandHandler(),
+ // Options
+ ProcessIdOption(), OutputPathOption(), VerboseOption(), TimeoutOption()
+ };
public static Option ProcessIdOption() =>
new Option(
aliases: new[] { "-p", "--process-id" },
- description: "The process to collect the trace from",
- argument: new Argument<int>(defaultValue: 0) { Name = "pid" },
- isHidden: false);
+ description: "The process id to collect the trace.")
+ {
+ Argument = new Argument<int>(name: "pid", defaultValue: 0),
+ };
private static Option OutputPathOption() =>
new Option(
aliases: new[] { "-o", "--output" },
- description: $@"The path where collected gcdumps should be written. Defaults to '.\YYYYMMDD_HHMMSS_<pid>.gcdump' where YYYYMMDD is Year/Month/Day and HHMMSS is Hour/Minute/Second. Otherwise, it is the full path and file name of the dump.",
- argument: new Argument<string>(defaultValue: "") { Name = "gcdump-file-path" },
- isHidden: false);
+ description: $@"The path where collected gcdumps should be written. Defaults to '.\YYYYMMDD_HHMMSS_<pid>.gcdump' where YYYYMMDD is Year/Month/Day and HHMMSS is Hour/Minute/Second. Otherwise, it is the full path and file name of the dump.")
+ {
+ Argument = new Argument<string>(name: "gcdump-file-path", defaultValue: "")
+ };
private static Option VerboseOption() =>
new Option(
aliases: new[] { "-v", "--verbose" },
- description: $"Output the log while collecting the gcdump",
- argument: new Argument<bool>(defaultValue: false) { Name = "verbose" },
- isHidden: false);
+ description: $"Output the log while collecting the gcdump.")
+ {
+ Argument = new Argument<bool>(name: "verbose", defaultValue: false)
+ };
private static int DefaultTimeout = 30;
private static Option TimeoutOption() =>
new Option(
aliases: new[] { "-t", "--timeout" },
- description: $"Give up on collecting the gcdump if it takes longer than this many seconds. The default value is {DefaultTimeout}s",
- argument: new Argument<int>(defaultValue: DefaultTimeout) { Name = "timeout" },
- isHidden: false);
+ description: $"Give up on collecting the gcdump if it takes longer than this many seconds. The default value is {DefaultTimeout}s.")
+ {
+ Argument = new Argument<int>(name: "timeout", defaultValue: DefaultTimeout)
+ };
}
}
+++ /dev/null
-// 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.Internal.Common.Commands;
-using System;
-using System.CommandLine;
-using System.Threading.Tasks;
-
-namespace Microsoft.Diagnostics.Tools.GCDump
-{
- internal static class ListProcessesCommandHandler
- {
- public static async Task<int> GetActivePorts(IConsole console)
- {
- ProcessStatusCommandHandler.PrintProcessStatus(console);
- await Task.FromResult(0);
- return 0;
- }
-
- public static Command ProcessStatusCommand() =>
- new Command(
- name: "ps",
- description: "Lists dotnet processes that can be attached to.",
- handler: System.CommandLine.Invocation.CommandHandler.Create<IConsole>(GetActivePorts),
- isHidden: false);
- }
-}
// 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.Internal.Common.Commands;
using System.CommandLine.Builder;
using System.CommandLine.Invocation;
using System.Threading.Tasks;
{
var parser = new CommandLineBuilder()
.AddCommand(CollectCommandHandler.CollectCommand())
- .AddCommand(ListProcessesCommandHandler.ProcessStatusCommand())
+ .AddCommand(ProcessStatusCommandHandler.ProcessStatusCommand("Lists the dotnet processes that gcdumps can be collected"))
.UseDefaults()
.Build();
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Common\CommandExtensions.cs" Link="CommandExtensions.cs" />
<Compile Include="..\Common\Commands\ProcessStatus.cs" Link="ProcessStatus.cs" />
</ItemGroup>
// 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.Tools.Common;
using SOS;
-using System;
using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Invocation;
-using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
private static Command InstallCommand() =>
new Command(
- "install",
- "Installs SOS and configures LLDB to load it on startup.",
- new Option[] { ArchitectureOption() },
- handler: CommandHandler.Create<IConsole, Architecture?>((console, architecture) => InvokeAsync(console, architecture, install: true)));
+ name: "install",
+ description: "Installs SOS and configures LLDB to load it on startup.")
+ {
+ // Handler
+ CommandHandler.Create<IConsole, Architecture?>((console, architecture) => InvokeAsync(console, architecture, install: true)),
+ // Options
+ ArchitectureOption()
+ };
private static Option ArchitectureOption() =>
new Option(
- new[] { "--architecture" },
- "The process to collect a memory dump from.",
- new Argument<Architecture>() { Name = "architecture" });
+ alias: "--architecture",
+ description: "The processor architecture to install.")
+ {
+ Argument = new Argument<Architecture>(name: "architecture")
+ };
private static Command UninstallCommand() =>
new Command(
- "uninstall",
- "Uninstalls SOS and reverts any configuration changes to LLDB.",
- handler: CommandHandler.Create<IConsole>((console) => InvokeAsync(console, architecture: null, install: false)));
+ name: "uninstall",
+ description: "Uninstalls SOS and reverts any configuration changes to LLDB.")
+ {
+ Handler = CommandHandler.Create<IConsole>((console) => InvokeAsync(console, architecture: null, install: false))
+ };
private static Task<int> InvokeAsync(IConsole console, Architecture? architecture, bool install)
{
<PackageReleaseNotes>$(Description)</PackageReleaseNotes>
<SOSPackagePathPrefix>tools/$(TargetFramework)/any</SOSPackagePathPrefix>
</PropertyGroup>
+ <ItemGroup>
+ <Compile Include="..\Common\CommandExtensions.cs" Link="CommandExtensions.cs" />
+ </ItemGroup>
<ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\SOS\SOS.InstallHelper\SOS.InstallHelper.csproj" />
// See the LICENSE file in the project root for more information.
using Microsoft.Diagnostics.NETCore.Client;
+using Microsoft.Tools.Common;
using System;
using System.Collections.Generic;
using System.CommandLine;
public static Command CollectCommand() =>
new Command(
name: "collect",
- description: "Collects a diagnostic trace from a currently running process",
- symbols: new Option[] {
- CommonOptions.ProcessIdOption(),
- CircularBufferOption(),
- OutputPathOption(),
- ProvidersOption(),
- ProfileOption(),
- CommonOptions.FormatOption(),
- DurationOption()
- },
- handler: HandlerDescriptor.FromDelegate((CollectDelegate)Collect).GetCommandHandler());
+ description: "Collects a diagnostic trace from a currently running process")
+ {
+ // Handler
+ HandlerDescriptor.FromDelegate((CollectDelegate)Collect).GetCommandHandler(),
+ // Options
+ CommonOptions.ProcessIdOption(),
+ CircularBufferOption(),
+ OutputPathOption(),
+ ProvidersOption(),
+ ProfileOption(),
+ CommonOptions.FormatOption(),
+ DurationOption()
+ };
private static uint DefaultCircularBufferSizeInMB => 256;
private static Option CircularBufferOption() =>
new Option(
alias: "--buffersize",
- description: $"Sets the size of the in-memory circular buffer in megabytes. Default {DefaultCircularBufferSizeInMB} MB.",
- argument: new Argument<uint>(defaultValue: DefaultCircularBufferSizeInMB) { Name = "size" },
- isHidden: false);
+ description: $"Sets the size of the in-memory circular buffer in megabytes. Default {DefaultCircularBufferSizeInMB} MB.")
+ {
+ Argument = new Argument<uint>(name: "size", defaultValue: DefaultCircularBufferSizeInMB)
+ };
public static string DefaultTraceName => "trace.nettrace";
private static Option OutputPathOption() =>
new Option(
aliases: new[] { "-o", "--output" },
- description: $"The output path for the collected trace data. If not specified it defaults to '{DefaultTraceName}'",
- argument: new Argument<FileInfo>(defaultValue: new FileInfo(DefaultTraceName)) { Name = "trace-file-path" },
- isHidden: false);
+ description: $"The output path for the collected trace data. If not specified it defaults to '{DefaultTraceName}'.")
+ {
+ Argument = new Argument<FileInfo>(name: "trace-file-path", defaultValue: new FileInfo(DefaultTraceName))
+ };
private static Option ProvidersOption() =>
new Option(
alias: "--providers",
- description: @"A list of EventPipe providers to be enabled. This is in the form 'Provider[,Provider]', where Provider is in the form: 'KnownProviderName[:Flags[:Level][:KeyValueArgs]]', and KeyValueArgs is in the form: '[key1=value1][;key2=value2]'. These providers are in addition to any providers implied by the --profile argument. If there is any discrepancy for a particular provider, the configuration here takes precedence over the implicit configuration from the profile.",
- argument: new Argument<string>(defaultValue: "") { Name = "list-of-comma-separated-providers" }, // TODO: Can we specify an actual type?
- isHidden: false);
+ description: @"A list of EventPipe providers to be enabled. This is in the form 'Provider[,Provider]', where Provider is in the form: 'KnownProviderName[:Flags[:Level][:KeyValueArgs]]', and KeyValueArgs is in the form: '[key1=value1][;key2=value2]'. These providers are in addition to any providers implied by the --profile argument. If there is any discrepancy for a particular provider, the configuration here takes precedence over the implicit configuration from the profile.")
+ {
+ Argument = new Argument<string>(name: "list-of-comma-separated-providers", defaultValue: "") // TODO: Can we specify an actual type?
+ };
private static Option ProfileOption() =>
new Option(
alias: "--profile",
- description: @"A named pre-defined set of provider configurations that allows common tracing scenarios to be specified succinctly.",
- argument: new Argument<string>(defaultValue: "") { Name = "profile-name" },
- isHidden: false);
+ description: @"A named pre-defined set of provider configurations that allows common tracing scenarios to be specified succinctly.")
+ {
+ Argument = new Argument<string>(name: "profile-name", defaultValue: "")
+ };
private static Option DurationOption() =>
new Option(
alias: "--duration",
- description: @"When specified, will trace for the given timespan and then automatically stop the trace. Provided in the form of dd:hh:mm:ss.",
- argument: new Argument<TimeSpan>(defaultValue: default(TimeSpan)) { Name = "duration-timespan" },
- isHidden: true);
+ description: @"When specified, will trace for the given timespan and then automatically stop the trace. Provided in the form of dd:hh:mm:ss.")
+ {
+ Argument = new Argument<TimeSpan>(name: "duration-timespan", defaultValue: default),
+ IsHidden = true
+ };
}
}
// 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.NETCore.Client;
+using Microsoft.Tools.Common;
using System;
-using System.IO;
using System.CommandLine;
using System.CommandLine.Builder;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading.Tasks;
+using System.IO;
namespace Microsoft.Diagnostics.Tools.Trace
{
public static Command ConvertCommand() =>
new Command(
name: "convert",
- description: "Converts traces to alternate formats for use with alternate trace analysis tools. Can only convert from the nettrace format.",
- argument: (new Argument<FileInfo>(defaultValue: new FileInfo(CollectCommandHandler.DefaultTraceName)) {
- Name = "input-filename",
- Description = $"Input trace file to be converted. Defaults to '{CollectCommandHandler.DefaultTraceName}'."
- }).ExistingOnly(),
- symbols: new Option[] {
- CommonOptions.ConvertFormatOption(),
- OutputOption()
- },
- handler: System.CommandLine.Invocation.CommandHandler.Create<IConsole, FileInfo, TraceFileFormat, FileInfo>(ConvertFile),
- isHidden: false
- );
+ description: "Converts traces to alternate formats for use with alternate trace analysis tools. Can only convert from the nettrace format")
+ {
+ // Handler
+ System.CommandLine.Invocation.CommandHandler.Create<IConsole, FileInfo, TraceFileFormat, FileInfo>(ConvertFile),
+ // Arguments and Options
+ InputFileArgument(),
+ CommonOptions.ConvertFormatOption(),
+ OutputOption(),
+ };
- public static Option OutputOption() =>
+ private static Argument InputFileArgument() =>
+ new Argument<FileInfo>(name: "input-filename", defaultValue: new FileInfo(CollectCommandHandler.DefaultTraceName))
+ {
+ Description = $"Input trace file to be converted. Defaults to '{CollectCommandHandler.DefaultTraceName}'."
+ }.ExistingOnly();
+
+ private static Option OutputOption() =>
new Option(
- aliases: new [] { "-o", "--output" },
- description: "Output filename. Extension of target format will be added.",
- argument: new Argument<FileInfo>() { Name = "output-filename" },
- isHidden: false
- );
+ aliases: new[] { "-o", "--output" },
+ description: "Output filename. Extension of target format will be added.")
+ {
+ Argument = new Argument<FileInfo>(name: "output-filename")
+ };
}
}
+++ /dev/null
-// 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.Internal.Common.Commands;
-using System;
-using System.CommandLine;
-using System.Threading.Tasks;
-
-namespace Microsoft.Diagnostics.Tools.Trace
-{
- internal static class ListProcessesCommandHandler
- {
- public static async Task<int> GetActivePorts(IConsole console)
- {
- ProcessStatusCommandHandler.PrintProcessStatus(console);
- await Task.FromResult(0);
- return 0;
- }
-
- public static Command ListProcessesCommand() =>
- new Command(
- name: "ps",
- description: "Lists dotnet processes that can be attached to.",
- handler: System.CommandLine.Invocation.CommandHandler.Create<IConsole>(GetActivePorts),
- isHidden: false);
- }
-}
public static Command ListProfilesCommand() =>
new Command(
name: "list-profiles",
- description: "Lists pre-built tracing profiles with a description of what providers and filters are in each profile.",
- handler: CommandHandler.Create<IConsole>(GetProfiles),
- isHidden: false);
+ description: "Lists pre-built tracing profiles with a description of what providers and filters are in each profile")
+ {
+ Handler = CommandHandler.Create<IConsole>(GetProfiles),
+ };
internal static IEnumerable<Profile> DotNETRuntimeProfiles { get; } = new[] {
new Profile(
// See the LICENSE file in the project root for more information.
using System.CommandLine;
-using System.IO;
-using System.Runtime.InteropServices;
namespace Microsoft.Diagnostics.Tools.Trace
{
public static Option ProcessIdOption() =>
new Option(
aliases: new[] { "-p", "--process-id" },
- description: "The process to collect the trace from",
- argument: new Argument<int> { Name = "pid" },
- isHidden: false);
+ description: "The process id to collect the trace.")
+ {
+ Argument = new Argument<int>(name: "pid")
+ };
public static TraceFileFormat DefaultTraceFileFormat => TraceFileFormat.NetTrace;
public static Option FormatOption() =>
new Option(
- aliases: new[] { "--format" },
- description: $"Sets the output format for the trace file. Default is {DefaultTraceFileFormat}",
- argument: new Argument<TraceFileFormat>(defaultValue: DefaultTraceFileFormat) { Name = "trace-file-format" },
- isHidden: false);
+ alias: "--format",
+ description: $"Sets the output format for the trace file. Default is {DefaultTraceFileFormat}.")
+ {
+ Argument = new Argument<TraceFileFormat>(name: "trace-file-format", defaultValue: DefaultTraceFileFormat)
+ };
public static Option ConvertFormatOption() =>
new Option(
- aliases: new[] { "--format" },
- description: $"Sets the output format for the trace file conversion.",
- argument: new Argument<TraceFileFormat> { Name = "trace-file-format" },
- isHidden: false);
+ alias: "--format",
+ description: $"Sets the output format for the trace file conversion.")
+ {
+ Argument = new Argument<TraceFileFormat>(name: "trace-file-format")
+ };
}
}
// 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.Internal.Common.Commands;
using System.CommandLine.Builder;
using System.CommandLine.Invocation;
using System.Threading.Tasks;
{
var parser = new CommandLineBuilder()
.AddCommand(CollectCommandHandler.CollectCommand())
- .AddCommand(ListProcessesCommandHandler.ListProcessesCommand())
+ .AddCommand(ProcessStatusCommandHandler.ProcessStatusCommand("Lists the dotnet processes that traces can be collected"))
.AddCommand(ListProfilesCommandHandler.ListProfilesCommand())
.AddCommand(ConvertCommandHandler.ConvertCommand())
.UseDefaults()
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Common\CommandExtensions.cs" Link="CommandExtensions.cs" />
<Compile Include="..\Common\Commands\ProcessStatus.cs" Link="ProcessStatus.cs" />
</ItemGroup>