[dotnet-trace] Adding --profile options and minor fixes. (#172)
authorJosé Rivero <jorive@microsoft.com>
Fri, 19 Apr 2019 06:12:37 +0000 (23:12 -0700)
committerGitHub <noreply@github.com>
Fri, 19 Apr 2019 06:12:37 +0000 (23:12 -0700)
* OnStop: rename pid -> processId
* Adding a Versions.props
* Update Collect output format. Added the process main module path.
* Adding profiles verb
* Adding --profile option.
* up-to-spec
* Renaming few ETW instances to EventPipe.

13 files changed:
documentation/dotnet-trace-instructions.md
src/Common.props
src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/Provider.cs
src/Microsoft.Diagnostics.Tools.RuntimeClient/Microsoft.Diagnostics.Tools.RuntimeClient.csproj
src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs
src/Tools/dotnet-trace/CommandLine/Commands/ProfilesCommandHandler.cs [new file with mode: 0644]
src/Tools/dotnet-trace/CommandLine/Commands/ProvidersCommandHandler.cs [deleted file]
src/Tools/dotnet-trace/CommandLine/Commands/StopCommandHandler.cs
src/Tools/dotnet-trace/Extensions.cs
src/Tools/dotnet-trace/Profile.cs [new file with mode: 0644]
src/Tools/dotnet-trace/Program.cs
src/Tools/dotnet-trace/dotnet-trace.csproj
src/Versions.props [new file with mode: 0644]

index 05f05d3ee490d5d55e61c146fc053c5fdd27dee6..8bc7d8ac78393a63fdffdab441ab087fc0f517ae 100644 (file)
@@ -47,7 +47,7 @@ Binder                          |                 4 |
 Loader                          |                 8 | Logging when modules actually get loaded and unloaded.
 Jit                             |                10 | Logging when Just in time (JIT) compilation occurs.
 NGen                            |                20 | Logging when precompiled native (NGEN) images are loaded.
-StartEnumeration                |                40 | Indicates that on attach or module load , a rundown of all existing methods should be done.
+StartEnumeration                |                40 | Indicates that on attach or module load, a rundown of all existing methods should be done.
 StopEnumeration                 |                80 | Indicates that on detach or process shutdown, a rundown of all existing methods should be done.
 Security                        |               400 | Events associated with validating security restrictions.
 AppDomainResourceManagement     |               800 | Events for logging resource consumption on an app-domain level granularity.
@@ -66,14 +66,14 @@ GCSampledObjectAllocationHigh   |            200000 | Enables allocation samplin
 GCHeapSurvivalAndMovement       |            400000 | Enables events associate with object movement or survival with each GC.
 GCHeapCollect                   |            800000 | Triggers a GC. Can pass a 64 bit value that will be logged with the GC Start event so you know which GC you actually triggered.
 GCHeapAndTypeNames              |           1000000 | Indicates that you want type names looked up and put into the events (not just meta-data tokens).
-GCHeapSnapshot                  |           1980001 | This provides the flags commonly needed to take a heap .NET Heap snapshot with ETW.
+GCHeapSnapshot                  |           1980001 | This provides the flags commonly needed to take a heap .NET Heap snapshot with EventPipe.<br>This is equivalent to `GC+Type+GCHeapDump+GCHeapCollect+GCHeapAndTypeNames`
 GCSampledObjectAllocationLow    |           2000000 | Enables allocation sampling with the 'slow' rate, Sample to limit to 5 allocations per second per type. This is reasonable for monitoring. Note that this DOES update the allocation path to be slower and only works if the process start with this on.
 GCAllObjectAllocation           |           2200000 | Turns on capturing the stack and type of object allocation made by the .NET Runtime. This is only supported after V4.5.3 (Late 2014) This can be very verbose and you should seriously using GCSampledObjectAllocationHigh instead (and GCSampledObjectAllocationLow for production scenarios).
 Stack                           |          40000000 | Also log the stack trace of events for which this is valuable.
 ThreadTransfer                  |          80000000 | This allows tracing work item transfer events (thread pool enqueue/dequeue/ioenqueue/iodequeue/a.o.).
 Debugger                        |         100000000 | .NET Debugger events
 Monitoring                      |         200000000 | Events intended for monitoring on an ongoing basis.
-Codesymbols                     |         400000000 | Events that will dump PDBs of dynamically generated assemblies to the ETW stream.
+Codesymbols                     |         400000000 | Events that will dump PDBs of dynamically generated assemblies to the EventPipe stream.
 Default                         |         4C14FCCBD | Recommend default flags (good compromise on verbosity).
 
 [source](https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/Parsers/ClrTraceEventParser.cs#L41)
index f3894f125125a108102336aca6f5ab9fd3cc5c47..30ac8ae166e7b39f180ba6e6b02c0fe2dfc513fb 100644 (file)
@@ -1,5 +1,7 @@
 <Project>
 
+  <Import Project="$(MSBuildThisFileDirectory)\Versions.props" />
+
   <PropertyGroup Label="Repository information">
     <RepositoryUrl>https://github.com/dotnet/diagnostics.git</RepositoryUrl>
     <RepositoryType>git</RepositoryType>
index c6ebfb27faeced742f524dd1c342549965f69416..04ef7e70f72f28d0187caf208c9f00ec92e17cbf 100644 (file)
@@ -30,5 +30,8 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient
         public string Name { get; }
 
         public string FilterData { get; }
+
+        public override string ToString() =>
+            $"{Name}:0x{Keywords:X16}:{(uint)EventLevel}{(FilterData == null ? "" : $":{FilterData}")}";
     }
 }
index 48ebe8cf3ef8c79025a7b3b67fa759306633ee3e..4bef423b9690608c0a37026f6d18be1d99e6d25c 100644 (file)
@@ -19,7 +19,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.40" />
+    <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="$(MicrosoftDiagnosticsTracingLibraryVersion)" />
   </ItemGroup>
 
 </Project>
index 96ec653227b30a9d39b2069e11e727696d838aa4..65d0e7e2350dc0141ad167da5735bd3da3210b98 100644 (file)
@@ -4,11 +4,12 @@
 
 using Microsoft.Diagnostics.Tools.RuntimeClient;
 using System;
+using System.Collections.Generic;
 using System.CommandLine;
-using System.CommandLine.Invocation;
 using System.CommandLine.Rendering;
 using System.Diagnostics;
 using System.IO;
+using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -24,8 +25,9 @@ namespace Microsoft.Diagnostics.Tools.Trace
         /// <param name="output">The output path for the collected trace data.</param>
         /// <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>
         /// <returns></returns>
-        public static async Task<int> Collect(IConsole console, int processId, string output, uint buffersize, string providers)
+        public static async Task<int> Collect(IConsole console, int processId, string output, uint buffersize, string providers, string profile)
         {
             try
             {
@@ -33,11 +35,25 @@ namespace Microsoft.Diagnostics.Tools.Trace
                     throw new ArgumentNullException(nameof(output));
                 if (processId <= 0)
                     throw new ArgumentException(nameof(processId));
+                if (profile == null)
+                    throw new ArgumentNullException(nameof(profile));
 
+                var selectedProfile = ProfilesCommandHandler.DotNETRuntimeProfiles
+                    .FirstOrDefault(p => p.Name.Equals(profile, StringComparison.OrdinalIgnoreCase));
+                if (selectedProfile == null)
+                    throw new ArgumentException($"Invalid profile name: {profile}");
+
+                var providerCollection = Extensions.ToProviders(providers);
+                if (selectedProfile.Providers != null)
+                    providerCollection.AddRange(selectedProfile.Providers);
+
+                PrintProviders(providerCollection);
+
+                var process = Process.GetProcessById(processId);
                 var configuration = new SessionConfiguration(
                     circularBufferSizeMB: buffersize,
                     outputPath: null, // Not used on the streaming scenario.
-                    Extensions.ToProviders(providers));
+                    providers: providerCollection);
 
                 var shouldExit = new ManualResetEvent(false);
 
@@ -54,7 +70,8 @@ namespace Microsoft.Diagnostics.Tools.Trace
                     var collectingTask = new Task(() => {
                         using (var fs = new FileStream(output, FileMode.Create, FileAccess.Write))
                         {
-                            Console.Out.WriteLine($"Recording tracing session to: {fs.Name}");
+                            Console.Out.WriteLine($"Process     : {process.MainModule.FileName}");
+                            Console.Out.WriteLine($"Output File : {fs.Name}");
                             Console.Out.WriteLine($"\tSession Id: 0x{sessionId:X16}");
                             lineToClear = Console.CursorTop;
 
@@ -75,7 +92,7 @@ namespace Microsoft.Diagnostics.Tools.Trace
                     });
                     collectingTask.Start();
 
-                    Console.Out.WriteLine("press <Enter> or <Ctrl+c> to exit...");
+                    Console.Out.WriteLine("Press <Enter> or <Ctrl+C> to exit...");
                     System.Console.CancelKeyPress += (sender, args) => {
                         args.Cancel = true;
                         shouldExit.Set();
@@ -102,6 +119,14 @@ namespace Microsoft.Diagnostics.Tools.Trace
             }
         }
 
+        [Conditional("DEBUG")]
+        private static void PrintProviders(IReadOnlyList<Provider> providers)
+        {
+            Console.Out.WriteLine("Enabling the following providers");
+            foreach (var provider in providers)
+                Console.Out.WriteLine($"\t{provider.ToString()}");
+        }
+
         private static int prevBufferWidth = 0;
         private static string clearLineString = "";
         private static int lineToClear = 0;
@@ -148,7 +173,15 @@ namespace Microsoft.Diagnostics.Tools.Trace
                     CommonOptions.CircularBufferOption(),
                     CommonOptions.OutputPathOption(),
                     CommonOptions.ProvidersOption(),
+                    ProfileOption(),
                 },
-                handler: CommandHandler.Create<IConsole, int, string, uint, string>(Collect));
+                handler: System.CommandLine.Invocation.CommandHandler.Create<IConsole, int, string, uint, string, string>(Collect));
+
+        public 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: "runtime-basic") { Name = "profile_name" }, // TODO: Can we specify an actual type?
+                isHidden: false);
     }
 }
diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/ProfilesCommandHandler.cs b/src/Tools/dotnet-trace/CommandLine/Commands/ProfilesCommandHandler.cs
new file mode 100644 (file)
index 0000000..a6a9b04
--- /dev/null
@@ -0,0 +1,71 @@
+// 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.Diagnostics.Tools.RuntimeClient;
+using System;
+using System.Collections.Generic;
+using System.CommandLine;
+using System.CommandLine.Invocation;
+using System.Diagnostics.Tracing;
+using System.Threading.Tasks;
+
+namespace Microsoft.Diagnostics.Tools.Trace
+{
+    internal sealed class ProfilesCommandHandler
+    {
+        public static async Task<int> GetProfiles(IConsole console)
+        {
+            try
+            {
+                foreach (var profile in DotNETRuntimeProfiles)
+                    Console.Out.WriteLine($"\t{profile.Name,-16} - {profile.Description}");
+
+                await Task.FromResult(0);
+                return 0;
+            }
+            catch (Exception ex)
+            {
+                Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
+                return 1;
+            }
+        }
+
+        public static Command ProfilesCommand() =>
+            new Command(
+                name: "profiles",
+                description: "List pre-defined set of provider aliases that allows common tracing scenarios to be specified.",
+                handler: CommandHandler.Create<IConsole>(GetProfiles),
+                isHidden: false);
+
+        // FIXME: Read from a config file!
+        internal static IEnumerable<Profile> DotNETRuntimeProfiles { get; } = new[] {
+            new Profile(
+                "runtime-basic",
+                new Provider[] {
+                    new Provider("Microsoft-DotNETCore-SampleProfiler"),
+                    new Provider("Microsoft-Windows-DotNETRuntime", 0x00000004C14FCCBD, EventLevel.Informational),
+                },
+                "Useful for tracking CPU usage and general runtime information. This the default option if no profile is specified."),
+            new Profile(
+                "gc",
+                new Provider[] {
+                    new Provider("Microsoft-DotNETCore-SampleProfiler"),
+                    new Provider("Microsoft-Windows-DotNETRuntime", 0x0000000000000001, EventLevel.Verbose),
+                },
+                "Tracks allocation and collection performance."),
+            new Profile(
+                "gc-collect",
+                new Provider[] {
+                    new Provider("Microsoft-DotNETCore-SampleProfiler"),
+                    new Provider("Microsoft-Windows-DotNETRuntime", 0x0000000000000001, EventLevel.Informational),
+                },
+                "Tracks GC collection only at very low overhead."),
+
+            new Profile(
+                "none",
+                null,
+                "Tracks nothing. Only providers specified by the --providers option will be available."),
+        };
+    }
+}
diff --git a/src/Tools/dotnet-trace/CommandLine/Commands/ProvidersCommandHandler.cs b/src/Tools/dotnet-trace/CommandLine/Commands/ProvidersCommandHandler.cs
deleted file mode 100644 (file)
index 4900efc..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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.Diagnostics.Tools.RuntimeClient;
-using System;
-using System.Collections.Generic;
-using System.CommandLine;
-using System.CommandLine.Invocation;
-using System.Diagnostics.Tracing;
-using System.Threading.Tasks;
-
-namespace Microsoft.Diagnostics.Tools.Trace
-{
-    internal static class ProvidersCommandHandler
-    {
-        public static async Task<int> KnownProviders(IConsole console)
-        {
-            try
-            {
-                foreach (var provider in CommonProviders)
-                {
-                    var filterData = provider.FilterData == null ? "" : $":{provider.FilterData}";
-                    Console.Out.WriteLine($"Provider={provider.Name}:0x{provider.Keywords:X16}:{(uint)provider.EventLevel}{filterData}");
-                }
-
-                await Task.FromResult(0);
-                return 0;
-            }
-            catch (Exception ex)
-            {
-                Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
-                return 1;
-            }
-        }
-
-        public static Command KnownProvidersCommand() =>
-            new Command(
-                name: "knownproviders",
-                description: "List known tracing flags.",
-                handler: CommandHandler.Create<IConsole>(KnownProviders),
-                isHidden: true);
-
-        private static IEnumerable<Provider> CommonProviders { get; } = new[] {
-            new Provider("Microsoft-Windows-DotNETRuntime", 0x0000000000000001, EventLevel.Informational),  // ClrGC
-            new Provider("Microsoft-Windows-DotNETRuntime", 0x0000000000000002, EventLevel.Informational),  // ClrThreadPool
-            new Provider("Microsoft-Windows-DotNETRuntime", 0x00000000FFFFFFFF, EventLevel.Verbose),        // ClrAll
-            new Provider("Microsoft-Windows-DotNETRuntime", 0x00000004C14FCCBD, EventLevel.Informational),  //
-            new Provider("Microsoft-Windows-DotNETRuntimeRundown"),
-        };
-    }
-}
index 530a62dfbe4e75a530f945bc858c8c2fb7625a40..073ffb40588d1544510ee455ba163d496a0c44ac 100644 (file)
@@ -12,11 +12,11 @@ namespace Microsoft.Diagnostics.Tools.Trace
 {
     internal static class StopCommandHandler
     {
-        public static async Task<int> Stop(IConsole console, int pid, ulong sessionId)
+        public static async Task<int> Stop(IConsole console, int processId, ulong sessionId)
         {
             try
             {
-                EventPipeClient.StopTracing(pid, sessionId);
+                EventPipeClient.StopTracing(processId, sessionId);
 
                 await Task.FromResult(0);
                 return sessionId != 0 ? 0 : 1;
index bc58a64f38253ec1a186755bfbc3ac41bcc4f1c4..8c60cc20fff3f72420f77b84675a53d4dddf0f01 100644 (file)
@@ -12,13 +12,13 @@ namespace Microsoft.Diagnostics.Tools.Trace
 {
     internal static class Extensions
     {
-        public static IReadOnlyCollection<Provider> ToProviders(string providers)
+        public static List<Provider> ToProviders(string providers)
         {
             if (string.IsNullOrWhiteSpace(providers))
                 throw new ArgumentNullException(nameof(providers));
             return providers.Split(',')
                 .Select(ToProvider)
-                .ToArray();
+                .ToList();
         }
 
         private static Provider ToProvider(string provider)
diff --git a/src/Tools/dotnet-trace/Profile.cs b/src/Tools/dotnet-trace/Profile.cs
new file mode 100644 (file)
index 0000000..fcafa27
--- /dev/null
@@ -0,0 +1,22 @@
+using Microsoft.Diagnostics.Tools.RuntimeClient;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Diagnostics.Tools.Trace
+{
+    internal sealed class Profile
+    {
+        public Profile(string name, IEnumerable<Provider> providers, string description)
+        {
+            Name = name;
+            Providers = providers == null ? Enumerable.Empty<Provider>() : new List<Provider>(providers).AsReadOnly();
+            Description = description;
+        }
+
+        public string Name { get; }
+
+        public IEnumerable<Provider> Providers { get; }
+
+        public string Description { get; }
+    }
+}
index cce8fbf2ba299257bb6cacdefb054e6a25de8765..be774b562d94cad9b8c2151c183d880992493442 100644 (file)
@@ -14,11 +14,11 @@ namespace Microsoft.Diagnostics.Tools.Trace
         {
             var parser = new CommandLineBuilder()
 #if DEBUG
-                .AddCommand(ProvidersCommandHandler.KnownProvidersCommand())
                 .AddCommand(StopCommandHandler.StopCommand())
 #endif
                 .AddCommand(CollectCommandHandler.CollectCommand())
                 .AddCommand(EndPointsCommandHandler.ActivePortsCommand())
+                .AddCommand(ProfilesCommandHandler.ProfilesCommand())
                 .UseDefaults()
                 .Build();
 
index dda69cc5b1f2846f19a8da4f18b14b658bb04f40..a8fa5598f867bdf90ef01c4786d30b24ccee53bf 100644 (file)
@@ -22,8 +22,8 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="System.CommandLine.Experimental" Version="0.2.0-alpha.19179.1" />
-    <PackageReference Include="System.CommandLine.Rendering" Version="0.2.0-alpha.19179.1" />
+    <PackageReference Include="System.CommandLine.Experimental" Version="$(SystemCommandLineExperimentalVersion)" />
+    <PackageReference Include="System.CommandLine.Rendering" Version="$(SystemCommandLineRenderingVersion)" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/Versions.props b/src/Versions.props
new file mode 100644 (file)
index 0000000..4d4f5c7
--- /dev/null
@@ -0,0 +1,7 @@
+<Project>
+  <PropertyGroup>
+    <MicrosoftDiagnosticsTracingLibraryVersion>2.0.40</MicrosoftDiagnosticsTracingLibraryVersion>
+    <SystemCommandLineExperimentalVersion>0.2.0-alpha.19213.1</SystemCommandLineExperimentalVersion>
+    <SystemCommandLineRenderingVersion>0.2.0-alpha.19213.1</SystemCommandLineRenderingVersion>
+  </PropertyGroup>
+</Project>