Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Microsoft.Diagnostics.NETCore.Client Implementation #617

Merged
merged 43 commits into from
Dec 6, 2019
Merged
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
22030df
First batch of files
sywhang Nov 6, 2019
3fde2a7
Make dotnet-counters use the diagnostics client library
sywhang Nov 6, 2019
d9efaff
Port dotnet-trace
sywhang Nov 7, 2019
13c8306
Port dotnet-dump
sywhang Nov 7, 2019
f8667ff
Implement WriteDump
sywhang Nov 7, 2019
54b78a6
Implement AttachProfiler
sywhang Nov 7, 2019
fa48772
Implement GetPublishedProcesses
sywhang Nov 7, 2019
9c8dc4d
Implement dispose overrides
sywhang Nov 7, 2019
614ba31
Port dotnet-gcdump
sywhang Nov 7, 2019
142e938
Port dotnet-trace tests
sywhang Nov 7, 2019
e22532d
Default keyword is not ulong.max
sywhang Nov 7, 2019
45886f1
Fix argument parser
sywhang Nov 7, 2019
4dec226
Merge remote-tracking branch 'upstream/master' into dev/suwhang/rcl-i…
sywhang Nov 13, 2019
ccd1c17
Add diagnostics client library to diagnostics solution
sywhang Nov 13, 2019
46f0402
Fix few bugs, start throwing relevant exceptions
sywhang Nov 14, 2019
31aaa2a
Add more exception throws
sywhang Nov 14, 2019
3b51ff0
Undo accidental change
sywhang Nov 14, 2019
f087d15
Fix build error, address PR feedback
sywhang Nov 14, 2019
0642cad
remove TODO and add asserts on header parse
sywhang Nov 15, 2019
a0b3c57
Add exception handling for counters
sywhang Nov 15, 2019
1a47b41
Add exception handling for dotnet-trace
sywhang Nov 15, 2019
48333bd
Fix default keyword not being all fs when no keyword specified
sywhang Nov 15, 2019
bec51df
Add test for DiagnosticsClient library
sywhang Nov 15, 2019
5151b30
Remove unused regex
sywhang Nov 15, 2019
d3cf367
Add TestHelpers to DiagnosticsClient test
sywhang Nov 15, 2019
6fc7fb3
Added test runner, GetPublishedProcesses tests
sywhang Nov 18, 2019
488e137
Fix test run on Linux
sywhang Nov 18, 2019
3697a54
Add Tracee
sywhang Nov 18, 2019
272a459
Rename test file
sywhang Nov 18, 2019
81e446d
Dump test added
sywhang Nov 18, 2019
dba6940
Add EventPipeSession tests
sywhang Nov 18, 2019
bba974e
Add some more failure test
sywhang Nov 18, 2019
810e67d
remove useless test
sywhang Nov 18, 2019
21465f5
Add license headers
sywhang Nov 18, 2019
ac5c16d
add diagnostics client library test to project
sywhang Nov 18, 2019
48892d4
Squashing some commits
sywhang Nov 26, 2019
44e1163
Merge with master
sywhang Nov 26, 2019
17eff82
Fix build
sywhang Nov 26, 2019
22613bf
PR feedback
sywhang Dec 4, 2019
233a8b5
more nits
sywhang Dec 4, 2019
adc565c
Merge branch 'master' into dev/suwhang/rcl-implementation
sywhang Dec 4, 2019
968574d
Make public classes sealed
sywhang Dec 5, 2019
c61bcf9
fix build
sywhang Dec 5, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Port dotnet-trace
sywhang committed Nov 7, 2019

Unverified

This user has not yet uploaded their public signing key.
commit d9efaff311d8ab782fb97086a388a2c14d1c4dfb
Original file line number Diff line number Diff line change
@@ -33,15 +33,6 @@ public EventPipeSession StartEventPipeSession(IEnumerable<EventPipeProvider> pro
return new EventPipeSession(_processId, providers, requestRundown, circularBufferMB);
}

/// <summary>
/// Stop the EventPipe session provided as an argument.
/// </summary>
/// <param name="session">The EventPipeSession to be stopped.</param>
public void StopEventPipeSession(EventPipeSession session)
{

}

/// <summary>
/// Trigger a core dump generation.
/// </summary>
Original file line number Diff line number Diff line change
@@ -67,6 +67,10 @@ public override int GetHashCode()

internal string GetArgumentString()
{
if (Arguments == null)
{
return "";
}
StringBuilder sb = new StringBuilder();
sywhang marked this conversation as resolved.
Show resolved Hide resolved
foreach(var argument in Arguments)
{
Original file line number Diff line number Diff line change
@@ -14,9 +14,11 @@ public class EventPipeSession : IDisposable
private bool _requestRundown;
private int _circularBufferMB;
private long _sessionId;
private int _processId;

internal EventPipeSession(int processId, IEnumerable<EventPipeProvider> providers, bool requestRundown, int circularBufferMB)
{
_processId = processId;
_providers = providers;
_requestRundown = requestRundown;
_circularBufferMB = circularBufferMB;
@@ -47,7 +49,24 @@ internal EventPipeSession(int processId, IEnumerable<EventPipeProvider> provider
public void Stop()
{
// TODO
return;
if (_sessionId == 0)
return; // TODO: Throw here instead?

byte[] payload = BitConverter.GetBytes(_sessionId);

var response = IpcClient.SendMessage(_processId, new IpcMessage(DiagnosticsServerCommandSet.EventPipe, (byte)EventPipeCommandId.StopTracing, payload));

switch ((DiagnosticsServerCommandId)response.Header.CommandId)
{
case DiagnosticsServerCommandId.OK:
return;
case DiagnosticsServerCommandId.Error:
//TODO: THROW HERE?
return;
default:
//TODO: THROW HERE?
return;
}
}

public void Dispose()
135 changes: 67 additions & 68 deletions src/Tools/dotnet-trace/CommandLine/Commands/CollectCommand.cs
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
// 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 Microsoft.Diagnostics.NETCore.Client;
using System;
using System.Collections.Generic;
using System.CommandLine;
@@ -59,7 +59,7 @@ private static async Task<int> Collect(CancellationToken ct, IConsole console, i
Dictionary<string, string> enabledBy = new Dictionary<string, string>();

var providerCollection = Extensions.ToProviders(providers);
foreach (Provider providerCollectionProvider in providerCollection)
foreach (EventPipeProvider providerCollectionProvider in providerCollection)
{
enabledBy[providerCollectionProvider.Name] = "--providers ";
}
@@ -87,11 +87,6 @@ private static async Task<int> Collect(CancellationToken ct, IConsole console, i
PrintProviders(providerCollection, enabledBy);

var process = Process.GetProcessById(processId);
var configuration = new SessionConfiguration(
circularBufferSizeMB: buffersize,
format: EventPipeSerializationFormat.NetTrace,
providers: providerCollection);

var shouldExit = new ManualResetEvent(false);
var shouldStopAfterDuration = duration != default(TimeSpan);
var failed = false;
@@ -100,79 +95,81 @@ private static async Task<int> Collect(CancellationToken ct, IConsole console, i

ct.Register(() => shouldExit.Set());

ulong sessionId = 0;
using (Stream stream = EventPipeClient.CollectTracing(processId, configuration, out sessionId))
using (VirtualTerminalMode vTermMode = VirtualTerminalMode.TryEnable())
var diagnosticsClient = new DiagnosticsClient(processId);
using (EventPipeSession session = diagnosticsClient.StartEventPipeSession(providerCollection, true))
{
if (sessionId == 0)
{
Console.Error.WriteLine("Unable to create session.");
return ErrorCodes.SessionCreationError;
}

if (shouldStopAfterDuration)
using (VirtualTerminalMode vTermMode = VirtualTerminalMode.TryEnable())
{
durationTimer = new System.Timers.Timer(duration.TotalMilliseconds);
durationTimer.Elapsed += (s, e) => shouldExit.Set();
durationTimer.AutoReset = false;
}
if (session == null)
{
Console.Error.WriteLine("Unable to create session.");
return ErrorCodes.SessionCreationError;
}

var collectingTask = new Task(() =>
{
try
if (shouldStopAfterDuration)
{
var stopwatch = new Stopwatch();
durationTimer?.Start();
stopwatch.Start();
durationTimer = new System.Timers.Timer(duration.TotalMilliseconds);
durationTimer.Elapsed += (s, e) => shouldExit.Set();
durationTimer.AutoReset = false;
}

using (var fs = new FileStream(output.FullName, FileMode.Create, FileAccess.Write))
var collectingTask = new Task(() =>
{
try
{
Console.Out.WriteLine($"Process : {process.MainModule.FileName}");
Console.Out.WriteLine($"Output File : {fs.Name}");
if (shouldStopAfterDuration)
Console.Out.WriteLine($"Trace Duration : {duration.ToString(@"dd\:hh\:mm\:ss")}");

Console.Out.WriteLine("\n\n");
var buffer = new byte[16 * 1024];
var stopwatch = new Stopwatch();
durationTimer?.Start();
stopwatch.Start();

while (true)
using (var fs = new FileStream(output.FullName, FileMode.Create, FileAccess.Write))
{
int nBytesRead = stream.Read(buffer, 0, buffer.Length);
if (nBytesRead <= 0)
break;
fs.Write(buffer, 0, nBytesRead);
lineToClear = Console.CursorTop-1;
ResetCurrentConsoleLine(vTermMode.IsEnabled);
Console.Out.WriteLine($"[{stopwatch.Elapsed.ToString(@"dd\:hh\:mm\:ss")}]\tRecording trace {GetSize(fs.Length)}");
Console.Out.WriteLine("Press <Enter> or <Ctrl+C> to exit...");
Debug.WriteLine($"PACKET: {Convert.ToBase64String(buffer, 0, nBytesRead)} (bytes {nBytesRead})");
Console.Out.WriteLine($"Process : {process.MainModule.FileName}");
Console.Out.WriteLine($"Output File : {fs.Name}");
if (shouldStopAfterDuration)
Console.Out.WriteLine($"Trace Duration : {duration.ToString(@"dd\:hh\:mm\:ss")}");

Console.Out.WriteLine("\n\n");
var buffer = new byte[16 * 1024];

while (true)
{
int nBytesRead = session.EventStream.Read(buffer, 0, buffer.Length);
if (nBytesRead <= 0)
break;
fs.Write(buffer, 0, nBytesRead);
lineToClear = Console.CursorTop-1;
ResetCurrentConsoleLine(vTermMode.IsEnabled);
Console.Out.WriteLine($"[{stopwatch.Elapsed.ToString(@"dd\:hh\:mm\:ss")}]\tRecording trace {GetSize(fs.Length)}");
Console.Out.WriteLine("Press <Enter> or <Ctrl+C> to exit...");
Debug.WriteLine($"PACKET: {Convert.ToBase64String(buffer, 0, nBytesRead)} (bytes {nBytesRead})");
}
}
}
}
catch (Exception ex)
catch (Exception ex)
{
failed = true;
Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
}
finally
{
terminated = true;
shouldExit.Set();
}
});
collectingTask.Start();

do
{
failed = true;
Console.Error.WriteLine($"[ERROR] {ex.ToString()}");
}
finally
while (!Console.KeyAvailable && !shouldExit.WaitOne(250)) { }
} while (!shouldExit.WaitOne(0) && Console.ReadKey(true).Key != ConsoleKey.Enter);

if (!terminated)
{
terminated = true;
shouldExit.Set();
durationTimer?.Stop();
session.Stop();
}
});
collectingTask.Start();

do
{
while (!Console.KeyAvailable && !shouldExit.WaitOne(250)) { }
} while (!shouldExit.WaitOne(0) && Console.ReadKey(true).Key != ConsoleKey.Enter);

if (!terminated)
{
durationTimer?.Stop();
EventPipeClient.StopTracing(processId, sessionId);
await collectingTask;
}
await collectingTask;
}

Console.Out.WriteLine();
@@ -190,7 +187,7 @@ private static async Task<int> Collect(CancellationToken ct, IConsole console, i
}
}

private static void PrintProviders(IReadOnlyList<Provider> providers, Dictionary<string, string> enabledBy)
private static void PrintProviders(IReadOnlyList<EventPipeProvider> providers, Dictionary<string, string> enabledBy)
{
Console.Out.WriteLine("");
Console.Out.Write(String.Format("{0, -40}","Provider Name")); // +4 is for the tab
@@ -199,10 +196,12 @@ private static void PrintProviders(IReadOnlyList<Provider> providers, Dictionary
Console.Out.Write("Enabled By\n");
foreach (var provider in providers)
{
Console.Out.WriteLine(String.Format("{0, -80}", $"{provider.ToDisplayString()}") + $"{enabledBy[provider.Name]}");
Console.Out.WriteLine(String.Format("{0, -80}", $"{GetProviderDisplayString(provider)}") + $"{enabledBy[provider.Name]}");
}
Console.Out.WriteLine();
}
private static string GetProviderDisplayString(EventPipeProvider provider) =>
String.Format("{0, -40}", provider.Name) + String.Format("0x{0, -18}", $"{provider.Keywords:X16}") + String.Format("{0, -8}", provider.EventLevel.ToString() + $"({(int)provider.EventLevel})");

private static int prevBufferWidth = 0;
private static string clearLineString = "";
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
// 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 Microsoft.Diagnostics.NETCore.Client;
using System;
using System.IO;
using System.CommandLine;
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@
// 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 Microsoft.Internal.Common.Commands;
using System;
using System.CommandLine;
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
// 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 Microsoft.Diagnostics.NETCore.Client;
using Microsoft.Diagnostics.Tracing.Parsers;
using System;
using System.Collections.Generic;
@@ -39,35 +39,35 @@ public static Command ListProfilesCommand() =>
handler: CommandHandler.Create<IConsole>(GetProfiles),
isHidden: false);

// FIXME: Read from a config file!
internal static IEnumerable<Profile> DotNETRuntimeProfiles { get; } = new[] {
new Profile(
"cpu-sampling",
new Provider[] {
new Provider("Microsoft-DotNETCore-SampleProfiler"),
new Provider("Microsoft-Windows-DotNETRuntime", (ulong)ClrTraceEventParser.Keywords.Default, EventLevel.Informational),
new EventPipeProvider[] {
new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational),
new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational, (long)ClrTraceEventParser.Keywords.Default)
},
"Useful for tracking CPU usage and general .NET runtime information. This is the default option if no profile or providers are specified."),
new Profile(
"gc-verbose",
new Provider[] {
new Provider(
new EventPipeProvider[] {
new EventPipeProvider(
name: "Microsoft-Windows-DotNETRuntime",
keywords: (ulong)ClrTraceEventParser.Keywords.GC |
(ulong)ClrTraceEventParser.Keywords.GCHandle |
(ulong)ClrTraceEventParser.Keywords.Exception,
eventLevel: EventLevel.Verbose
eventLevel: EventLevel.Verbose,
keywords: (long)ClrTraceEventParser.Keywords.GC |
(long)ClrTraceEventParser.Keywords.GCHandle |
(long)ClrTraceEventParser.Keywords.Exception
),
},
"Tracks GC collections and samples object allocations."),
new Profile(
"gc-collect",
new Provider[] {
new Provider(
new EventPipeProvider[] {
new EventPipeProvider(
name: "Microsoft-Windows-DotNETRuntime",
keywords: (ulong)ClrTraceEventParser.Keywords.GC |
(ulong)ClrTraceEventParser.Keywords.Exception,
eventLevel: EventLevel.Informational),
eventLevel: EventLevel.Informational,
keywords: (long)ClrTraceEventParser.Keywords.GC |
(long)ClrTraceEventParser.Keywords.Exception
)
},
"Tracks GC collections only at very low overhead."),
};
48 changes: 0 additions & 48 deletions src/Tools/dotnet-trace/CommandLine/Commands/StopCommandHandler.cs

This file was deleted.

Loading