From: John Salem Date: Fri, 19 Apr 2019 18:15:28 +0000 (-0700) Subject: Speedscope format (#171) X-Git-Tag: submit/tizen/20190813.035844~29 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2c58b2c2f6431904ec1a63e8c3806b74c8c7874f;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Speedscope format (#171) * Incorporate code from closed PR #114 to enable speedscope output: * adds format option * Actually add the converter... * update dotnet-trace instructions * Expanded on output format section of instructions * Change protection level of TraceFileFormatConverter * Updates in response to feedback * Update src/Tools/dotnet-trace/TraceFileFormatConverter.cs Co-Authored-By: josalem * Update src/Tools/dotnet-trace/CommandLine/Options/CommonOptions.cs Co-Authored-By: josalem * Fix typos --- diff --git a/documentation/design-docs/dotnet-tools.md b/documentation/design-docs/dotnet-tools.md index 3d894e002..daa1ecd8e 100644 --- a/documentation/design-docs/dotnet-tools.md +++ b/documentation/design-docs/dotnet-tools.md @@ -258,6 +258,7 @@ COLLECT [--pack] [--profile ] [--providers ] + [-f|--format ] Collects a diagnostic trace from a currently running process @@ -300,6 +301,9 @@ COLLECT --buffersize 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 diff --git a/documentation/dotnet-trace-instructions.md b/documentation/dotnet-trace-instructions.md index 8bc7d8ac7..f94bcc347 100644 --- a/documentation/dotnet-trace-instructions.md +++ b/documentation/dotnet-trace-instructions.md @@ -86,6 +86,12 @@ Microsoft-Windows-DotNETRuntime | [The Runtime Provider](https://docs.mi Microsoft-Windows-DotNETRuntimeRundown | [The Rundown Provider](https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-providers#the-rundown-provider)
[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 @@ -126,6 +132,8 @@ Options: KeyValueArgs - A semicolon separated list of key=value KeyValueArgs format: '[key1=value1][;key2=value2]' - --buffersize Sets the size of the in-memory circular buffer - in megabytes. Default 256 MB. -``` + --buffersize + 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. diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs b/src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs index 65d0e7e23..392260e7d 100644 --- a/src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs +++ b/src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs @@ -26,8 +26,9 @@ namespace Microsoft.Diagnostics.Tools.Trace /// Sets the size of the in-memory circular buffer in megabytes. /// 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]' /// A named pre-defined set of provider configurations that allows common tracing scenarios to be specified succinctly. + /// The desired format of the created trace file. /// - public static async Task Collect(IConsole console, int processId, string output, uint buffersize, string providers, string profile) + public static async Task Collect(IConsole console, int processId, string output, uint buffersize, string providers, string profile, TraceFileFormat format) { try { @@ -109,6 +110,8 @@ namespace Microsoft.Diagnostics.Tools.Trace Console.Out.WriteLine(); Console.Out.WriteLine("Trace completed."); + TraceFileFormatConverter.ConvertToFormat(format, output); + await Task.FromResult(0); return sessionId != 0 ? 0 : 1; } @@ -174,8 +177,9 @@ namespace Microsoft.Diagnostics.Tools.Trace CommonOptions.OutputPathOption(), CommonOptions.ProvidersOption(), ProfileOption(), + CommonOptions.FormatOption(), }, - handler: System.CommandLine.Invocation.CommandHandler.Create(Collect)); + handler: System.CommandLine.Invocation.CommandHandler.Create(Collect)); public static Option ProfileOption() => new Option( diff --git a/src/Tools/dotnet-trace/CommandLine/Options/CommonOptions.cs b/src/Tools/dotnet-trace/CommandLine/Options/CommonOptions.cs index 17743fb66..a12a6d9e6 100644 --- a/src/Tools/dotnet-trace/CommandLine/Options/CommonOptions.cs +++ b/src/Tools/dotnet-trace/CommandLine/Options/CommonOptions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.CommandLine; +using System.Runtime.InteropServices; namespace Microsoft.Diagnostics.Tools.Trace { @@ -37,5 +38,15 @@ namespace Microsoft.Diagnostics.Tools.Trace description: $"Sets the size of the in-memory circular buffer in megabytes. Default {DefaultCircularBufferSizeInMB} MB", argument: new Argument(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(defaultValue: DefaultTraceFileFormat) { Name = "trace-file-format" }, + isHidden: false); } } diff --git a/src/Tools/dotnet-trace/TraceFileFormatConverter.cs b/src/Tools/dotnet-trace/TraceFileFormatConverter.cs new file mode 100644 index 000000000..66502c21c --- /dev/null +++ b/src/Tools/dotnet-trace/TraceFileFormatConverter.cs @@ -0,0 +1,67 @@ +// 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); + } + } + } + } +}