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.
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)
<Project>
+ <Import Project="$(MSBuildThisFileDirectory)\Versions.props" />
+
<PropertyGroup Label="Repository information">
<RepositoryUrl>https://github.com/dotnet/diagnostics.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
public string Name { get; }
public string FilterData { get; }
+
+ public override string ToString() =>
+ $"{Name}:0x{Keywords:X16}:{(uint)EventLevel}{(FilterData == null ? "" : $":{FilterData}")}";
}
}
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.40" />
+ <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="$(MicrosoftDiagnosticsTracingLibraryVersion)" />
</ItemGroup>
</Project>
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;
/// <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
{
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);
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;
});
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();
}
}
+ [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;
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);
}
}
--- /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.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."),
+ };
+ }
+}
+++ /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.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"),
- };
- }
-}
{
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;
{
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)
--- /dev/null
+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; }
+ }
+}
{
var parser = new CommandLineBuilder()
#if DEBUG
- .AddCommand(ProvidersCommandHandler.KnownProvidersCommand())
.AddCommand(StopCommandHandler.StopCommand())
#endif
.AddCommand(CollectCommandHandler.CollectCommand())
.AddCommand(EndPointsCommandHandler.ActivePortsCommand())
+ .AddCommand(ProfilesCommandHandler.ProfilesCommand())
.UseDefaults()
.Build();
</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>
--- /dev/null
+<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>