From d7a9d27f41984eea6c9af429e742fd4b407f6c3c Mon Sep 17 00:00:00 2001 From: John Salem Date: Thu, 23 May 2019 21:10:11 -0700 Subject: [PATCH] Add Diagnostic Ipc client (#271) * * Add infrastructure for a new IPC client in line with the propsed spec * update EventPipeClient to use new IpcClient * Update DiagnosticHelpers to use IpcClient * * fix typo * change to use uint32 instead of INT for generate core dump command * * remove dead code * * bring error codes in line with the now checked0in spec * Remove extra error code * Rename Miscellaneous to Dump for command sets and Ids * fix typing issue * * update profiler attach to use IpcClient * * Change errors to be int32 HRESULTS * * remove unnecessary todo * simplify size checking * * add ASSERT that MAGIC is always the correct length * * Fix using statement --- .../DiagnosticHelpers.cs | 99 +++++----- .../DiagnosticsIpc/IpcClient.cs | 108 +++++++++++ .../DiagnosticsIpc/IpcCommands.cs | 37 ++++ .../DiagnosticsIpc/IpcHeader.cs | 73 +++++++ .../DiagnosticsIpc/IpcMessage.cs | 58 ++++++ .../Eventing/EventPipeClient.cs | 179 +++--------------- .../Eventing/SessionConfiguration.cs | 27 +++ src/Tools/dotnet-dump/Dumper.cs | 2 +- 8 files changed, 380 insertions(+), 203 deletions(-) create mode 100644 src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcClient.cs create mode 100644 src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcCommands.cs create mode 100644 src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcHeader.cs create mode 100644 src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcMessage.cs diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticHelpers.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticHelpers.cs index 1161b1d09..b34fb8df7 100644 --- a/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticHelpers.cs +++ b/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticHelpers.cs @@ -2,6 +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.DiagnosticsIpc; using System; using System.Diagnostics; using System.IO; @@ -14,7 +15,7 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient /// /// Controls the contents of the dump /// - public enum DumpType : int + public enum DumpType : uint { Normal = 1, WithHeap = 2, @@ -29,7 +30,7 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient /// Path and file name of core dump /// Type of dump /// If true, log to console the dump generation diagnostics - /// HRESULT + /// DiagnosticServerErrorCode public static int GenerateCoreDump(int processId, string dumpName, DumpType dumpType, bool diagnostics) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) @@ -38,16 +39,24 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient if (string.IsNullOrEmpty(dumpName)) throw new ArgumentNullException($"{nameof(dumpName)} required"); - var header = new MessageHeader { - RequestType = DiagnosticMessageType.GenerateCoreDump, - Pid = (uint)Process.GetCurrentProcess().Id, - }; - byte[] serializedConfiguration; - using (var stream = new MemoryStream()) - serializedConfiguration = SerializeCoreDump(header, stream, dumpName, dumpType, diagnostics); + var payload = SerializeCoreDump(dumpName, dumpType, diagnostics); + var message = new IpcMessage(DiagnosticServerCommandSet.Dump, (byte)DumpCommandId.GenerateCoreDump, payload); - return (int)EventPipeClient.SendCommand(processId, serializedConfiguration); + var response = IpcClient.SendMessage(processId, message); + + var hr = 0; + switch ((DiagnosticServerCommandId)response.Header.CommandId) + { + case DiagnosticServerCommandId.Error: + case DiagnosticServerCommandId.OK: + hr = BitConverter.ToInt32(response.Payload); + break; + default: + return -1; + } + + return hr; } /// @@ -76,66 +85,64 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient Pid = (uint)Process.GetCurrentProcess().Id, }; - byte[] serializedConfiguration; - using (var stream = new MemoryStream()) + byte[] serializedConfiguration = SerializeProfilerAttach(attachTimeout, profilerGuid, profilerPath, additionalData); + var message = new IpcMessage(DiagnosticServerCommandSet.Profiler, (byte)ProfilerCommandId.AttachProfiler, serializedConfiguration); + + var response = IpcClient.SendMessage(processId, message); + + var hr = 0; + switch ((DiagnosticServerCommandId)response.Header.CommandId) { - serializedConfiguration = SerializeProfilerAttach(header, stream, attachTimeout, profilerGuid, profilerPath, additionalData); + case DiagnosticServerCommandId.Error: + case DiagnosticServerCommandId.OK: + hr = BitConverter.ToInt32(response.Payload); + break; + default: + hr = -1; + break; } // TODO: the call to set up the pipe and send the message operates on a different timeout than attachTimeout, which is for the runtime. // 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. - return (int)EventPipeClient.SendCommand(processId, serializedConfiguration); - + return hr; } - private static byte[] SerializeProfilerAttach(MessageHeader header, MemoryStream stream, uint attachTimeout, Guid profilerGuid, string profilerPath, byte[] additionalData) + private static byte[] SerializeProfilerAttach(uint attachTimeout, Guid profilerGuid, string profilerPath, byte[] additionalData) { - using (var bw = new BinaryWriter(stream)) + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) { - bw.Write((uint)header.RequestType); - bw.Write(header.Pid); - - bw.Write(attachTimeout); - bw.Write(profilerGuid.ToByteArray()); - bw.WriteString(profilerPath); + writer.Write(attachTimeout); + writer.Write(profilerGuid.ToByteArray()); + writer.WriteString(profilerPath); if (additionalData == null) { - bw.Write(0); + writer.Write(0); } else { - bw.Write(additionalData.Length); - bw.Write(additionalData); + writer.Write(additionalData.Length); + writer.Write(additionalData); } - bw.Flush(); - stream.Position = 0; - - var bytes = new byte[stream.Length]; - stream.Read(bytes, 0, bytes.Length); - return bytes; + writer.Flush(); + return stream.ToArray(); } } - private static byte[] SerializeCoreDump(MessageHeader header, Stream stream, string dumpName, DumpType dumpType, bool diagnostics) + private static byte[] SerializeCoreDump(string dumpName, DumpType dumpType, bool diagnostics) { - using (var bw = new BinaryWriter(stream)) + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) { - bw.Write((uint)header.RequestType); - bw.Write(header.Pid); - - bw.WriteString(dumpName); - bw.Write((int)dumpType); - bw.Write(diagnostics ? 1 : 0); - - bw.Flush(); - stream.Position = 0; + writer.WriteString(dumpName); + writer.Write((uint)dumpType); + writer.Write((uint)(diagnostics ? 1 : 0)); - var bytes = new byte[stream.Length]; - stream.Read(bytes, 0, bytes.Length); - return bytes; + writer.Flush(); + return stream.ToArray(); } } } diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcClient.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcClient.cs new file mode 100644 index 000000000..edbf6cc48 --- /dev/null +++ b/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcClient.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Pipes; +using System.Linq; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Text; +using System.Text.RegularExpressions; + +namespace Microsoft.Diagnostics.Tools.RuntimeClient.DiagnosticsIpc +{ + public class IpcClient + { + private static string DiagnosticPortPattern { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"^dotnetcore-diagnostic-(\d+)$" : @"^dotnetcore-diagnostic-(\d+)-(\d+)-socket$"; + + private static string IpcRootPath { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"\\.\pipe\" : Path.GetTempPath(); + + private static double ConnectTimeoutMilliseconds { get; } = TimeSpan.FromSeconds(3).TotalMilliseconds; + + /// + /// Get the OS Transport to be used for communicating with a dotnet process. + /// + /// The PID of the dotnet process to get the transport for + /// A System.IO.Stream wrapper around the transport + private static Stream GetTransport(int processId) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + string pipeName = $"dotnetcore-diagnostic-{processId}"; + var namedPipe = new NamedPipeClientStream( + ".", pipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Impersonation); + namedPipe.Connect((int)ConnectTimeoutMilliseconds); + return namedPipe; + } + else + { + string ipcPort = Directory.GetFiles(IpcRootPath) // Try best match. + .Select(namedPipe => (new FileInfo(namedPipe)).Name) + .SingleOrDefault(input => Regex.IsMatch(input, $"^dotnetcore-diagnostic-{processId}-(\\d+)-socket$")); + if (ipcPort == null) + { + throw new PlatformNotSupportedException($"Process {processId} not running compatible .NET Core runtime"); + } + string path = Path.Combine(Path.GetTempPath(), ipcPort); + var remoteEP = new UnixDomainSocketEndPoint(path); + + var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); + socket.Connect(remoteEP); + return new NetworkStream(socket); + } + } + + /// + /// Sends a single DiagnosticIpc Message to the dotnet process with PID processId. + /// + /// The PID of the dotnet process + /// The DiagnosticsIpc Message to be sent + /// The response DiagnosticsIpc Message from the dotnet process + public static IpcMessage SendMessage(int processId, IpcMessage message) + { + using (var stream = GetTransport(processId)) + { + Write(stream, message); + return Read(stream); + } + } + + /// + /// Sends a single DiagnosticIpc Message to the dotnet process with PID processId + /// and returns the Stream for reuse in Optional Continuations. + /// + /// The PID of the dotnet process + /// The DiagnosticsIpc Message to be sent + /// out var for response message + /// The response DiagnosticsIpc Message from the dotnet process + public static Stream SendMessage(int processId, IpcMessage message, out IpcMessage response) + { + var stream = GetTransport(processId); + Write(stream, message); + response = Read(stream); + return stream; + } + + private static void Write(Stream stream, byte[] buffer) + { + using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) + { + writer.Write(buffer); + } + } + + private static void Write(Stream stream, IpcMessage message) + { + using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) + { + writer.Write(message.Serialize()); + } + } + + + private static IpcMessage Read(Stream stream) + { + return IpcMessage.Parse(stream); + } + } +} diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcCommands.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcCommands.cs new file mode 100644 index 000000000..fbde8eee2 --- /dev/null +++ b/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcCommands.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Diagnostics.Tools.RuntimeClient.DiagnosticsIpc +{ + public enum DiagnosticServerCommandSet : byte + { + Dump = 0x01, + EventPipe = 0x02, + Profiler = 0x03, + + Server = 0xFF, + } + + public enum DiagnosticServerCommandId : byte + { + OK = 0x00, + Error = 0xFF, + } + + public enum EventPipeCommandId : byte + { + StopTracing = 0x01, + CollectTracing = 0x02, + } + + public enum DumpCommandId : byte + { + GenerateCoreDump = 0x01, + } + + public enum ProfilerCommandId : byte + { + AttachProfiler = 0x01, + } +} diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcHeader.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcHeader.cs new file mode 100644 index 000000000..64e6fe5c0 --- /dev/null +++ b/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcHeader.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.Diagnostics.Tools.RuntimeClient.DiagnosticsIpc +{ + public class IpcHeader + { + IpcHeader() { } + + public IpcHeader(DiagnosticServerCommandSet commandSet, byte commandId) + { + CommandSet = (byte)commandSet; + CommandId = commandId; + } + + // the number of bytes for the DiagnosticsIpc::IpcHeader type in native code + public static readonly UInt16 HeaderSizeInBytes = 20; + private static readonly UInt16 MagicSizeInBytes = 14; + + public byte[] Magic = ASCIIEncoding.ASCII.GetBytes("DOTNET_IPC_V1" + '\0'); // byte[14] in native code + public UInt16 Size = HeaderSizeInBytes; + public byte CommandSet; + public byte CommandId; + public UInt16 Reserved = 0x0000; + + + // Helper expression to quickly get V1 magic string for comparison + // should be 14 bytes long + public static byte[] DOTNET_IPC_V1 => ASCIIEncoding.ASCII.GetBytes("DOTNET_IPC_V1" + '\0'); + + public byte[] Serialize() + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + writer.Write(Magic); + Debug.Assert(Magic.Length == MagicSizeInBytes); + writer.Write(Size); + writer.Write(CommandSet); + writer.Write(CommandId); + writer.Write((UInt16)0x0000); + writer.Flush(); + return stream.ToArray(); + } + } + + public static IpcHeader TryParse(BinaryReader reader) + { + IpcHeader header = new IpcHeader + { + Magic = reader.ReadBytes(14), + Size = reader.ReadUInt16(), + CommandSet = reader.ReadByte(), + CommandId = reader.ReadByte(), + Reserved = reader.ReadUInt16() + }; + + // TODO: Validate it is correct! + + return header; + } + + override public string ToString() + { + return $"{{ Magic={Magic}; Size={Size}; CommandSet={CommandSet}; CommandId={CommandId}; Reserved={Reserved} }}"; + } + } +} diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcMessage.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcMessage.cs new file mode 100644 index 000000000..c39df398c --- /dev/null +++ b/src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcMessage.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Microsoft.Diagnostics.Tools.RuntimeClient.DiagnosticsIpc +{ + public class IpcMessage + { + public IpcMessage() + { } + + public IpcMessage(IpcHeader header, byte[] payload) + { + Payload = payload; + Header = header; + } + + internal IpcMessage(DiagnosticServerCommandSet commandSet, byte commandId, byte[] payload = null) + { + Header = new IpcHeader(commandSet, commandId); + Payload = payload; + } + + public byte[] Payload { get; private set; } = null; + public IpcHeader Header { get; private set; } = default; + + public byte[] Serialize() + { + byte[] serializedData = null; + // Verify things will fit in the size capacity + Header.Size = checked((UInt16)(IpcHeader.HeaderSizeInBytes + Payload.Length)); ; + byte[] headerBytes = Header.Serialize(); + + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + writer.Write(headerBytes); + writer.Write(Payload); + writer.Flush(); + serializedData = stream.ToArray(); + } + + return serializedData; + } + + public static IpcMessage Parse(Stream stream) + { + IpcMessage message = new IpcMessage(); + using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) + { + message.Header = IpcHeader.TryParse(reader); + message.Payload = reader.ReadBytes(message.Header.Size - IpcHeader.HeaderSizeInBytes); + return message; + } + } + } +} diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/EventPipeClient.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/EventPipeClient.cs index 71d5ae741..13ff42ff0 100644 --- a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/EventPipeClient.cs +++ b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/EventPipeClient.cs @@ -2,6 +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.DiagnosticsIpc; using System; using System.Collections.Generic; using System.Diagnostics; @@ -22,52 +23,6 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient private static string IpcRootPath { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @"\\.\pipe\" : Path.GetTempPath(); - private static double ConnectTimeoutMilliseconds { get; } = TimeSpan.FromSeconds(3).TotalMilliseconds; - - /// - /// Send event pipe command. - /// - /// runtime process id - /// serialized command - /// command result - public static ulong SendCommand(int processId, byte[] buffer) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - string pipeName = $"dotnetcore-diagnostic-{processId}"; - using (var namedPipe = new NamedPipeClientStream( - ".", pipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Impersonation)) - { - namedPipe.Connect((int)ConnectTimeoutMilliseconds); - namedPipe.Write(buffer, 0, buffer.Length); - - return new BinaryReader(namedPipe).ReadUInt64(); - } - } - else - { - string ipcPort = Directory.GetFiles(IpcRootPath) // Try best match. - .Select(namedPipe => (new FileInfo(namedPipe)).Name) - .SingleOrDefault(input => Regex.IsMatch(input, $"^dotnetcore-diagnostic-{processId}-(\\d+)-socket$")); - if (ipcPort == null) - { - throw new PlatformNotSupportedException($"Process {processId} not running compatible .NET Core runtime"); - } - string path = Path.Combine(Path.GetTempPath(), ipcPort); - var remoteEP = new UnixDomainSocketEndPoint(path); - - using (var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)) - { - socket.Connect(remoteEP); - socket.Send(buffer); - - var content = new byte[sizeof(ulong)]; - int nReceivedBytes = socket.Receive(content); - return (nReceivedBytes == sizeof(ulong)) ? BitConverter.ToUInt64(content, 0) : 0; - } - } - } - /// /// Get the files associated with the opened IPC Ports for DotNet Core applications. /// @@ -93,74 +48,23 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient public static Stream CollectTracing(int processId, SessionConfiguration configuration, out ulong sessionId) { sessionId = 0; + var message = new IpcMessage(DiagnosticServerCommandSet.EventPipe, (byte)EventPipeCommandId.CollectTracing, configuration.Serialize()); + var stream = IpcClient.SendMessage(processId, message, out var response); - var header = new MessageHeader { - RequestType = DiagnosticMessageType.CollectEventPipeTracing, - Pid = (uint)Process.GetCurrentProcess().Id, - }; - - byte[] serializedConfiguration; - using (var stream = new MemoryStream()) - serializedConfiguration = Serialize(header, configuration, stream); - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - string pipeName = $"dotnetcore-diagnostic-{processId}"; - var namedPipe = new NamedPipeClientStream( - ".", pipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Impersonation); - namedPipe.Connect((int)ConnectTimeoutMilliseconds); - - // Request start-collection - namedPipe.Write(serializedConfiguration, 0, serializedConfiguration.Length); - - sessionId = new BinaryReader(namedPipe).ReadUInt64(); - return namedPipe; - } - else + switch ((DiagnosticServerCommandId)response.Header.CommandId) { - // TODO: Determine ApplicationGroupId - string ipcPort = Directory.GetFiles(IpcRootPath) // Try best match. - .Select(namedPipe => (new FileInfo(namedPipe)).Name) - .SingleOrDefault(input => Regex.IsMatch(input, $"^dotnetcore-diagnostic-{processId}-(\\d+)-socket$")); - if (ipcPort == null) - { - throw new PlatformNotSupportedException($"Process {processId} not running compatible .NET Core runtime"); - } - string path = Path.Combine(Path.GetTempPath(), ipcPort); - var remoteEP = new UnixDomainSocketEndPoint(path); - - var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); - socket.Connect(remoteEP); - - // Request start-collection - socket.Send(serializedConfiguration); - - var content = new byte[sizeof(ulong)]; - int nReceivedBytes = socket.Receive(content); - sessionId = (nReceivedBytes == sizeof(ulong)) ? BitConverter.ToUInt64(content, 0) : 0; - - return new NetworkStream(socket, FileAccess.Read, true); + case DiagnosticServerCommandId.OK: + sessionId = BitConverter.ToUInt64(response.Payload); + break; + case DiagnosticServerCommandId.Error: + // bad... + var hr = BitConverter.ToInt32(response.Payload); + throw new Exception($"Session start FAILED 0x{hr:X8}"); + default: + break; } - } - /// - /// Start tracing to file. - /// - /// Runtime process to trace - /// buffer size, file path and provider configuration - /// session id - public static ulong StartTracingToFile(int processId, SessionConfiguration configuration) - { - var header = new MessageHeader { - RequestType = DiagnosticMessageType.StartEventPipeTracing, - Pid = (uint)Process.GetCurrentProcess().Id, - }; - - byte[] serializedConfiguration; - using (var stream = new MemoryStream()) - serializedConfiguration = Serialize(header, configuration, stream); - - return SendCommand(processId, serializedConfiguration); + return stream; } /// @@ -174,55 +78,18 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient if (sessionId == 0) return sessionId; // TODO: Throw here instead? - var header = new MessageHeader { - RequestType = DiagnosticMessageType.StopEventPipeTracing, - Pid = (uint)Process.GetCurrentProcess().Id, - }; + byte[] payload = BitConverter.GetBytes(sessionId); - byte[] sessionIdInBytes; - using (var stream = new MemoryStream()) - { - using (var sw = new BinaryWriter(stream)) - { - sw.Write((uint)header.RequestType); - sw.Write(header.Pid); + var response = IpcClient.SendMessage(processId, new IpcMessage(DiagnosticServerCommandSet.EventPipe, (byte)EventPipeCommandId.StopTracing, payload)); - sw.Write(sessionId); - sw.Flush(); - sessionIdInBytes = stream.ToArray(); - } - } - - return SendCommand(processId, sessionIdInBytes); - } - - private static byte[] Serialize(MessageHeader header, SessionConfiguration configuration, Stream stream) - { - using (var bw = new BinaryWriter(stream)) + switch ((DiagnosticServerCommandId)response.Header.CommandId) { - bw.Write((uint)header.RequestType); - bw.Write(header.Pid); - - bw.Write(configuration.CircularBufferSizeInMB); - - bw.WriteString(configuration.OutputPath); - - bw.Write(configuration.Providers.Count()); - foreach (var provider in configuration.Providers) - { - bw.Write(provider.Keywords); - bw.Write((uint)provider.EventLevel); - - bw.WriteString(provider.Name); - bw.WriteString(provider.FilterData); - } - - bw.Flush(); - stream.Position = 0; - - var bytes = new byte[stream.Length]; - stream.Read(bytes, 0, bytes.Length); - return bytes; + case DiagnosticServerCommandId.OK: + return BitConverter.ToUInt64(response.Payload); + case DiagnosticServerCommandId.Error: + return 0; + default: + return 0; } } } diff --git a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs index 7045efba4..75ac59ecf 100644 --- a/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs +++ b/src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs @@ -36,5 +36,32 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient private readonly FileInfo _outputPath; private readonly List _providers; + + public byte[] Serialize() + { + byte[] serializedData = null; + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + writer.Write(CircularBufferSizeInMB); + + writer.WriteString(OutputPath); + + writer.Write(Providers.Count()); + foreach (var provider in Providers) + { + writer.Write(provider.Keywords); + writer.Write((uint)provider.EventLevel); + + writer.WriteString(provider.Name); + writer.WriteString(provider.FilterData); + } + + writer.Flush(); + serializedData = stream.ToArray(); + } + + return serializedData; + } } } diff --git a/src/Tools/dotnet-dump/Dumper.cs b/src/Tools/dotnet-dump/Dumper.cs index 3b9a374e3..a75a6215a 100644 --- a/src/Tools/dotnet-dump/Dumper.cs +++ b/src/Tools/dotnet-dump/Dumper.cs @@ -60,7 +60,7 @@ namespace Microsoft.Diagnostic.Tools.Dump DiagnosticHelpers.DumpType dumpType = type == DumpTypeOption.Heap ? DiagnosticHelpers.DumpType.WithHeap : DiagnosticHelpers.DumpType.Normal; // Send the command to the runtime to initiate the core dump - int hr = DiagnosticHelpers.GenerateCoreDump(processId, output, dumpType, diagnostics: false); + var hr = DiagnosticHelpers.GenerateCoreDump(processId, output, dumpType, diagnostics: false); if (hr != 0) { throw new InvalidOperationException($"Core dump generation FAILED 0x{hr:X8}"); -- 2.34.1