-
Notifications
You must be signed in to change notification settings - Fork 359
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
sywhang
merged 43 commits into
dotnet:master
from
sywhang:dev/suwhang/rcl-implementation
Dec 6, 2019
Merged
Changes from 38 commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
22030df
First batch of files
sywhang 3fde2a7
Make dotnet-counters use the diagnostics client library
sywhang d9efaff
Port dotnet-trace
sywhang 13c8306
Port dotnet-dump
sywhang f8667ff
Implement WriteDump
sywhang 54b78a6
Implement AttachProfiler
sywhang fa48772
Implement GetPublishedProcesses
sywhang 9c8dc4d
Implement dispose overrides
sywhang 614ba31
Port dotnet-gcdump
sywhang 142e938
Port dotnet-trace tests
sywhang e22532d
Default keyword is not ulong.max
sywhang 45886f1
Fix argument parser
sywhang 4dec226
Merge remote-tracking branch 'upstream/master' into dev/suwhang/rcl-i…
sywhang ccd1c17
Add diagnostics client library to diagnostics solution
sywhang 46f0402
Fix few bugs, start throwing relevant exceptions
sywhang 31aaa2a
Add more exception throws
sywhang 3b51ff0
Undo accidental change
sywhang f087d15
Fix build error, address PR feedback
sywhang 0642cad
remove TODO and add asserts on header parse
sywhang a0b3c57
Add exception handling for counters
sywhang 1a47b41
Add exception handling for dotnet-trace
sywhang 48333bd
Fix default keyword not being all fs when no keyword specified
sywhang bec51df
Add test for DiagnosticsClient library
sywhang 5151b30
Remove unused regex
sywhang d3cf367
Add TestHelpers to DiagnosticsClient test
sywhang 6fc7fb3
Added test runner, GetPublishedProcesses tests
sywhang 488e137
Fix test run on Linux
sywhang 3697a54
Add Tracee
sywhang 272a459
Rename test file
sywhang 81e446d
Dump test added
sywhang dba6940
Add EventPipeSession tests
sywhang bba974e
Add some more failure test
sywhang 810e67d
remove useless test
sywhang 21465f5
Add license headers
sywhang ac5c16d
add diagnostics client library test to project
sywhang 48892d4
Squashing some commits
sywhang 44e1163
Merge with master
sywhang 17eff82
Fix build
sywhang 22613bf
PR feedback
sywhang 233a8b5
more nits
sywhang adc565c
Merge branch 'master' into dev/suwhang/rcl-implementation
sywhang 968574d
Make public classes sealed
sywhang c61bcf9
fix build
sywhang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
170 changes: 170 additions & 0 deletions
170
src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
// 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.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Runtime.InteropServices; | ||
using System.Text.RegularExpressions; | ||
|
||
namespace Microsoft.Diagnostics.NETCore.Client | ||
{ | ||
/// <summary> | ||
/// This is a top-level class that contains methods to send various diagnostics command to the runtime. | ||
/// </summary> | ||
public class DiagnosticsClient | ||
{ | ||
private int _processId; | ||
private static string DiagnosticsPortPattern { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"^dotnet-diagnostic-(\d+)$" : @"^dotnet-diagnostic-(\d+)-(\d+)-socket$"; | ||
private static string IpcRootPath { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"\\.\pipe\" : Path.GetTempPath(); | ||
sywhang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
public DiagnosticsClient(int processId) | ||
{ | ||
_processId = processId; | ||
} | ||
|
||
/// <summary> | ||
/// Start tracing the application and return an EventPipeSession object | ||
/// </summary> | ||
/// <param name="providers">An IEnumerable containing the list of Providers to turn on.</param> | ||
/// <param name="requestRundown">If true, request rundown events from the runtime</param> | ||
/// <param name="circularBufferMB">The size of the runtime's buffer for collecting events in MB</param> | ||
/// <returns> | ||
/// An EventPipeSession object representing the EventPipe session that just started. | ||
/// </returns> | ||
public EventPipeSession StartEventPipeSession(IEnumerable<EventPipeProvider> providers, bool requestRundown=true, int circularBufferMB=256) | ||
{ | ||
return new EventPipeSession(_processId, providers, requestRundown, circularBufferMB); | ||
} | ||
|
||
/// <summary> | ||
/// Trigger a core dump generation. | ||
/// </summary> | ||
/// <param name="dumpType">Type of the dump to be generated</param> | ||
/// <param name="dumpPath">Full path to the dump to be generated. By default it is /tmp/coredump.{pid}</param> | ||
/// <param name="logDumpGeneration">When set to true, display the dump generation debug log to the console.</param> | ||
public void WriteDump(DumpType dumpType, string dumpPath, bool logDumpGeneration=false) | ||
{ | ||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) | ||
throw new PlatformNotSupportedException($"Unsupported operating system: {RuntimeInformation.OSDescription}"); | ||
|
||
if (string.IsNullOrEmpty(dumpPath)) | ||
throw new ArgumentNullException($"{nameof(dumpPath)} required"); | ||
|
||
var payload = SerializeCoreDump(dumpPath, dumpType, logDumpGeneration); | ||
var message = new IpcMessage(DiagnosticsServerCommandSet.Dump, (byte)DumpCommandId.GenerateCoreDump, payload); | ||
var response = IpcClient.SendMessage(_processId, message); | ||
var hr = 0; | ||
switch ((DiagnosticsServerCommandId)response.Header.CommandId) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: This |
||
{ | ||
case DiagnosticsServerCommandId.Error: | ||
hr = BitConverter.ToInt32(response.Payload, 0); | ||
throw new ServerErrorException($"Writing dump failed (HRESULT: 0x{hr:X8})"); | ||
case DiagnosticsServerCommandId.OK: | ||
return; | ||
default: | ||
throw new ServerErrorException($"Writing dump failed - server responded with unknown command"); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Attach a profiler. | ||
/// </summary> | ||
/// <param name="attachTimeout">Timeout for attaching the profiler</param> | ||
/// <param name="profilerGuid">Guid for the profiler to be attached</param> | ||
/// <param name="profilerPath">Path to the profiler to be attached</param> | ||
/// <param name="additionalData">Additional data to be passed to the profiler</param> | ||
public void AttachProfiler(TimeSpan attachTimeout, Guid profilerGuid, string profilerPath, byte[] additionalData=null) | ||
{ | ||
if (profilerGuid == null || profilerGuid == Guid.Empty) | ||
{ | ||
throw new ArgumentException($"{nameof(profilerGuid)} must be a valid Guid"); | ||
} | ||
|
||
if (String.IsNullOrEmpty(profilerPath)) | ||
{ | ||
throw new ArgumentException($"{nameof(profilerPath)} must be non-null"); | ||
} | ||
|
||
var header = new MessageHeader { | ||
sywhang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
RequestType = DiagnosticsMessageType.AttachProfiler, | ||
Pid = (uint)Process.GetCurrentProcess().Id, | ||
}; | ||
|
||
byte[] serializedConfiguration = SerializeProfilerAttach((uint)attachTimeout.TotalSeconds, profilerGuid, profilerPath, additionalData); | ||
var message = new IpcMessage(DiagnosticsServerCommandSet.Profiler, (byte)ProfilerCommandId.AttachProfiler, serializedConfiguration); | ||
var response = IpcClient.SendMessage(_processId, message); | ||
switch ((DiagnosticsServerCommandId)response.Header.CommandId) | ||
{ | ||
case DiagnosticsServerCommandId.Error: | ||
var hr = BitConverter.ToInt32(response.Payload, 0); | ||
throw new ServerErrorException($"Profiler attach failed (HRESULT: 0x{hr:X8})"); | ||
case DiagnosticsServerCommandId.OK: | ||
return; | ||
default: | ||
throw new ServerErrorException($"Profiler attach failed - server responded with unknown command"); | ||
} | ||
|
||
// TODO: the call to set up the pipe and send the message operates on a different timeout than attachTimeout, which is for the runtime. | ||
sywhang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// We should eventually have a configurable timeout for the message passing, potentially either separately from the | ||
// runtime timeout or respect attachTimeout as one total duration. | ||
} | ||
|
||
/// <summary> | ||
/// Get all the active processes that can be attached to. | ||
/// </summary> | ||
/// <returns> | ||
/// IEnumerable of all the active process IDs. | ||
/// </returns> | ||
public static IEnumerable<int> GetPublishedProcesses() | ||
{ | ||
return Directory.GetFiles(IpcRootPath) | ||
sywhang marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.Select(namedPipe => (new FileInfo(namedPipe)).Name) | ||
.Where(input => Regex.IsMatch(input, DiagnosticsPortPattern)) | ||
.Select(input => int.Parse(Regex.Match(input, DiagnosticsPortPattern).Groups[1].Value, NumberStyles.Integer)) | ||
.Distinct(); | ||
} | ||
|
||
private static byte[] SerializeCoreDump(string dumpName, DumpType dumpType, bool diagnostics) | ||
{ | ||
using (var stream = new MemoryStream()) | ||
using (var writer = new BinaryWriter(stream)) | ||
{ | ||
writer.WriteString(dumpName); | ||
writer.Write((uint)dumpType); | ||
writer.Write((uint)(diagnostics ? 1 : 0)); | ||
|
||
writer.Flush(); | ||
return stream.ToArray(); | ||
} | ||
} | ||
|
||
private static byte[] SerializeProfilerAttach(uint attachTimeout, Guid profilerGuid, string profilerPath, byte[] additionalData) | ||
{ | ||
using (var stream = new MemoryStream()) | ||
using (var writer = new BinaryWriter(stream)) | ||
{ | ||
writer.Write(attachTimeout); | ||
writer.Write(profilerGuid.ToByteArray()); | ||
writer.WriteString(profilerPath); | ||
|
||
if (additionalData == null) | ||
{ | ||
writer.Write(0); | ||
} | ||
else | ||
{ | ||
writer.Write(additionalData.Length); | ||
writer.Write(additionalData); | ||
} | ||
|
||
writer.Flush(); | ||
return stream.ToArray(); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I do not see a
virtual
method in this class (no plan on allowing inheritance), therefore I would make itsealed
.