Add Diagnostic Ipc client (#271)
authorJohn Salem <josalem@microsoft.com>
Fri, 24 May 2019 04:10:11 +0000 (21:10 -0700)
committerGitHub <noreply@github.com>
Fri, 24 May 2019 04:10:11 +0000 (21:10 -0700)
* * 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

src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticHelpers.cs
src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcClient.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcCommands.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcHeader.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Tools.RuntimeClient/DiagnosticsIpc/IpcMessage.cs [new file with mode: 0644]
src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/EventPipeClient.cs
src/Microsoft.Diagnostics.Tools.RuntimeClient/Eventing/SessionConfiguration.cs
src/Tools/dotnet-dump/Dumper.cs

index 1161b1d09ec1efc55654f674519555139514281e..b34fb8df74cd7c1c9c9416ad53eb25f0f93c42ab 100644 (file)
@@ -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
         /// <summary>
         /// Controls the contents of the dump
         /// </summary>
-        public enum DumpType : int
+        public enum DumpType : uint
         {
             Normal = 1,
             WithHeap = 2,
@@ -29,7 +30,7 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient
         /// <param name="dumpName">Path and file name of core dump</param>
         /// <param name="dumpType">Type of dump</param>
         /// <param name="diagnostics">If true, log to console the dump generation diagnostics</param>
-        /// <returns>HRESULT</returns>
+        /// <returns>DiagnosticServerErrorCode</returns>
         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;
         }
 
         /// <summary>
@@ -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 (file)
index 0000000..edbf6cc
--- /dev/null
@@ -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;
+
+        /// <summary>
+        /// Get the OS Transport to be used for communicating with a dotnet process.
+        /// </summary>
+        /// <param name="processId">The PID of the dotnet process to get the transport for</param>
+        /// <returns>A System.IO.Stream wrapper around the transport</returns>
+        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);
+            }
+        }
+
+        /// <summary>
+        /// Sends a single DiagnosticIpc Message to the dotnet process with PID processId.
+        /// </summary>
+        /// <param name="processId">The PID of the dotnet process</param>
+        /// <param name="message">The DiagnosticsIpc Message to be sent</param>
+        /// <returns>The response DiagnosticsIpc Message from the dotnet process</returns>
+        public static IpcMessage SendMessage(int processId, IpcMessage message)
+        {
+            using (var stream = GetTransport(processId))
+            {
+                Write(stream, message);
+                return Read(stream);
+            }
+        }
+
+        /// <summary>
+        /// Sends a single DiagnosticIpc Message to the dotnet process with PID processId
+        /// and returns the Stream for reuse in Optional Continuations.
+        /// </summary>
+        /// <param name="processId">The PID of the dotnet process</param>
+        /// <param name="message">The DiagnosticsIpc Message to be sent</param>
+        /// <param name="response">out var for response message</param>
+        /// <returns>The response DiagnosticsIpc Message from the dotnet process</returns>
+        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 (file)
index 0000000..fbde8ee
--- /dev/null
@@ -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 (file)
index 0000000..64e6fe5
--- /dev/null
@@ -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 (file)
index 0000000..c39df39
--- /dev/null
@@ -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;
+            }
+        }
+    }
+}
index 71d5ae7419afb72b0d652ee3dbd64f7df814d85f..13ff42ff093ccfbde655cf4fc44acbd9dd94af99 100644 (file)
@@ -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;
-
-        /// <summary>
-        /// Send event pipe command.
-        /// </summary>
-        /// <param name="processId">runtime process id</param>
-        /// <param name="buffer">serialized command</param>
-        /// <returns>command result</returns>
-        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;
-                }
-            }
-        }
-
         /// <summary>
         /// Get the files associated with the opened IPC Ports for DotNet Core applications.
         /// </summary>
@@ -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;
             }
-        }
 
-        /// <summary>
-        /// Start tracing to file.
-        /// </summary>
-        /// <param name="processId">Runtime process to trace</param>
-        /// <param name="configuration">buffer size, file path and provider configuration</param>
-        /// <returns>session id</returns>
-        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;
         }
 
         /// <summary>
@@ -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;
             }
         }
     }
index 7045efba4e40f8f66e54aba9eae7bffe80d304f0..75ac59ecf04334e234a62ef02408a0c708ebbd03 100644 (file)
@@ -36,5 +36,32 @@ namespace Microsoft.Diagnostics.Tools.RuntimeClient
 
         private readonly FileInfo _outputPath;
         private readonly List<Provider> _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;
+        }
     }
 }
index 3b9a374e3ee895ffeed664b25f5c1ab5891ed7a3..a75a6215a7dc6c24a357f77dc321252cbc85ed6b 100644 (file)
@@ -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}");