Speedscope format (#171)
authorJohn Salem <josalem@microsoft.com>
Fri, 19 Apr 2019 18:15:28 +0000 (11:15 -0700)
committerGitHub <noreply@github.com>
Fri, 19 Apr 2019 18:15:28 +0000 (11:15 -0700)
* 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 <josalem@microsoft.com>
* Update src/Tools/dotnet-trace/CommandLine/Options/CommonOptions.cs

Co-Authored-By: josalem <josalem@microsoft.com>
* Fix typos

documentation/design-docs/dotnet-tools.md
documentation/dotnet-trace-instructions.md
src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs
src/Tools/dotnet-trace/CommandLine/Options/CommonOptions.cs
src/Tools/dotnet-trace/TraceFileFormatConverter.cs [new file with mode: 0644]

index 3d894e0021cac671075586896d23310a0ca23713..daa1ecd8efeebee7d4eddaacc5229f424477c8f4 100644 (file)
@@ -258,6 +258,7 @@ COLLECT
                          [--pack]
                          [--profile <profile_name>]
                          [--providers <list-of-comma-separated-providers>]
+                         [-f|--format <trace-file-format>]
 
     Collects a diagnostic trace from a currently running process
 
@@ -300,6 +301,9 @@ COLLECT
     --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
index 8bc7d8ac78393a63fdffdab441ab087fc0f517ae..f94bcc3474d3f32abac12080365a9702908f84f7 100644 (file)
@@ -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)<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
@@ -126,6 +132,8 @@ Options:
             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.
index 65d0e7e2350dc0141ad167da5735bd3da3210b98..392260e7dbb98bce165a8737841f6e570f69305c 100644 (file)
@@ -26,8 +26,9 @@ namespace Microsoft.Diagnostics.Tools.Trace
         /// <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
             {
@@ -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<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(
index 17743fb66cd626c470fd35bc4dfcd5a7e43aa587..a12a6d9e62ed512c17cc294d48c0fcb3662f995b 100644 (file)
@@ -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<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);
     }
 }
diff --git a/src/Tools/dotnet-trace/TraceFileFormatConverter.cs b/src/Tools/dotnet-trace/TraceFileFormatConverter.cs
new file mode 100644 (file)
index 0000000..66502c2
--- /dev/null
@@ -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);
+                }
+            }
+        }
+    }
+}