[--pack]
[--profile <profile_name>]
[--providers <list-of-comma-separated-providers>]
+ [-f|--format <trace-file-format>]
Collects a diagnostic trace from a currently running process
--buffersize <Size>
Sets the size of the in-memory circular buffer in megabytes. Default 256 MB.
+ -f, --format
+ The format of the output trace file. This defualts to "netperf" on Windows and "speedscope" on other OSes.
+
Examples:
> dotnet trace collect --process-id 1902 --pack
Microsoft-Windows-DotNETRuntimeRundown | [The Rundown Provider](https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-providers#the-rundown-provider)<br>[CLR Rundown Keywords](https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-keywords-and-levels#rundown)
Microsoft-DotNETCore-SampleProfiler | Enable the sample profiler
+## `dotnet-trace` output formats
+
+You can change the output file format using the `-f|--format` option. You can currently choose between `netperf` (the default on Windows) and `speedscope` (the default on all other OSes). Speedscope files can be opened at https://www.speedscope.app.
+
+Note that all traces are transmitted in the `netperf` format and are converted after the trace is completed. Since some conversions may result in loss of data, the original `netperf` file is preserved next to the converted file.
+
## *dotnet-trace* help
```cmd
KeyValueArgs - A semicolon separated list of key=value
KeyValueArgs format: '[key1=value1][;key2=value2]'
- --buffersize <Size> Sets the size of the in-memory circular buffer
- in megabytes. Default 256 MB.
-```
+ --buffersize <Size>
+ Sets the size of the in-memory circular buffer in megabytes. Default 256 MB.
+
+ -f, --format
+ The format of the output trace file. This deafualts to "netperf" on Windows and "speedscope" on other OSes.
/// <param name="buffersize">Sets the size of the in-memory circular buffer in megabytes.</param>
/// <param name="providers">A list of EventPipe providers to be enabled. This is in the form 'Provider[,Provider]', where Provider is in the form: '(GUID|KnownProviderName)[:Flags[:Level][:KeyValueArgs]]', and KeyValueArgs is in the form: '[key1=value1][;key2=value2]'</param>
/// <param name="profile">A named pre-defined set of provider configurations that allows common tracing scenarios to be specified succinctly.</param>
+ /// <param name="format">The desired format of the created trace file.</param>
/// <returns></returns>
- public static async Task<int> Collect(IConsole console, int processId, string output, uint buffersize, string providers, string profile)
+ public static async Task<int> Collect(IConsole console, int processId, string output, uint buffersize, string providers, string profile, TraceFileFormat format)
{
try
{
Console.Out.WriteLine();
Console.Out.WriteLine("Trace completed.");
+ TraceFileFormatConverter.ConvertToFormat(format, output);
+
await Task.FromResult(0);
return sessionId != 0 ? 0 : 1;
}
CommonOptions.OutputPathOption(),
CommonOptions.ProvidersOption(),
ProfileOption(),
+ CommonOptions.FormatOption(),
},
- handler: System.CommandLine.Invocation.CommandHandler.Create<IConsole, int, string, uint, string, string>(Collect));
+ handler: System.CommandLine.Invocation.CommandHandler.Create<IConsole, int, string, uint, string, string, TraceFileFormat>(Collect));
public static Option ProfileOption() =>
new Option(
// See the LICENSE file in the project root for more information.
using System.CommandLine;
+using System.Runtime.InteropServices;
namespace Microsoft.Diagnostics.Tools.Trace
{
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);
+
+ public static TraceFileFormat DefaultTraceFileFormat =>
+ RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? TraceFileFormat.netperf : TraceFileFormat.speedscope;
+
+ public static Option FormatOption() =>
+ new Option(
+ aliases: new[] { "-f", "--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);
}
}
--- /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.IO;
+using Microsoft.Diagnostics.Symbols;
+using Microsoft.Diagnostics.Tracing;
+using Microsoft.Diagnostics.Tracing.Etlx;
+using Microsoft.Diagnostics.Tracing.Stacks;
+using Microsoft.Diagnostics.Tracing.Stacks.Formats;
+
+namespace Microsoft.Diagnostics.Tools.Trace
+{
+ internal enum TraceFileFormat { netperf, speedscope };
+
+ internal static class TraceFileFormatConverter
+ {
+ public static void ConvertToFormat(TraceFileFormat format, string fileToConvert)
+ {
+ switch (format)
+ {
+ case TraceFileFormat.netperf:
+ break;
+ case TraceFileFormat.speedscope:
+ Console.Out.WriteLine($"Converting to {format}...");
+ ConvertToSpeedscope(fileToConvert);
+ break;
+ default:
+ // Validation happened way before this, so we shoud never reach this...
+ throw new Exception($"Invalid TraceFileFormat \"{format}\"");
+ }
+ }
+
+ private static void ConvertToSpeedscope(string fileToConvert)
+ {
+ var symbolReader = new SymbolReader(System.IO.TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath };
+ var etlxFilePath = TraceLog.CreateFromEventPipeDataFile(fileToConvert);
+
+ var eventLog = new TraceLog(etlxFilePath);
+
+ try
+ {
+ var stackSource = new MutableTraceEventStackSource(eventLog)
+ {
+ OnlyManagedCodeStacks = true // EventPipe currently only has managed code stacks.
+ };
+
+ var computer = new SampleProfilerThreadTimeComputer(eventLog, symbolReader);
+ computer.GenerateThreadTimeStacks(stackSource);
+
+ var speedScopeFilePath = Path.ChangeExtension(fileToConvert, "speedscope.json");
+
+ SpeedScopeStackSourceWriter.WriteStackViewAsJson(stackSource, speedScopeFilePath);
+ }
+ finally
+ {
+ eventLog.Dispose();
+
+ if (File.Exists(etlxFilePath))
+ {
+ File.Delete(etlxFilePath);
+ }
+ }
+ }
+ }
+}