public static async Task <bool> TEST_TracesHaveRelevantEvents() { bool fSuccess = true; string serverName = ReverseServer.MakeServerAddress(); Logger.logger.Log($"Server name is '{serverName}'"); var server = new ReverseServer(serverName); using var memoryStream = new MemoryStream(); Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: new Dictionary <string, string> { { Utils.DiagnosticPortsEnvKey, $"{serverName}" } }, duringExecution: async(pid) => { Stream stream = await server.AcceptAsync(); IpcAdvertise advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); var config = new SessionConfiguration( circularBufferSizeMB: 1000, format: EventPipeSerializationFormat.NetTrace, providers: new List <Provider> { new Provider("Microsoft-Windows-DotNETRuntimePrivate", 0x80000000, EventLevel.Verbose) }); Logger.logger.Log("Starting EventPipeSession over standard connection"); using Stream eventStream = EventPipeClient.CollectTracing(pid, config, out var sessionId); Logger.logger.Log($"Started EventPipeSession over standard connection with session id: 0x{sessionId:x}"); Task readerTask = eventStream.CopyToAsync(memoryStream); Logger.logger.Log($"Send ResumeRuntime Diagnostics IPC Command"); // send ResumeRuntime command (0x04=ProcessCommandSet, 0x01=ResumeRuntime commandid) var message = new IpcMessage(0x04, 0x01); Logger.logger.Log($"Sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"received: {response.ToString()}"); await Task.Delay(TimeSpan.FromSeconds(2)); Logger.logger.Log("Stopping EventPipeSession over standard connection"); EventPipeClient.StopTracing(pid, sessionId); await readerTask; Logger.logger.Log("Stopped EventPipeSession over standard connection"); } ); fSuccess &= await subprocessTask; memoryStream.Seek(0, SeekOrigin.Begin); using var source = new EventPipeEventSource(memoryStream); var parser = new ClrPrivateTraceEventParser(source); bool isStartupEventPresent = false; parser.StartupEEStartupStart += (eventData) => isStartupEventPresent = true; source.Process(); Logger.logger.Log($"isStartupEventPresent: {isStartupEventPresent}"); return(isStartupEventPresent && fSuccess); }
/// <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"); } 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"); } // 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. }
/// <summary> /// Initiate a core dump in the target process runtime. /// </summary> /// <param name="processId">.NET Core process id</param> /// <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>DiagnosticsServerErrorCode</returns> public static int GenerateCoreDump(int processId, string dumpName, DumpType dumpType, bool diagnostics) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { throw new PlatformNotSupportedException($"Unsupported operating system: {RuntimeInformation.OSDescription}"); } if (string.IsNullOrEmpty(dumpName)) { throw new ArgumentNullException($"{nameof(dumpName)} required"); } var payload = SerializeCoreDump(dumpName, dumpType, diagnostics); var message = new IpcMessage(DiagnosticsServerCommandSet.Dump, (byte)DumpCommandId.GenerateCoreDump, payload); var response = IpcClient.SendMessage(processId, message); var hr = 0; switch ((DiagnosticsServerCommandId)response.Header.CommandId) { case DiagnosticsServerCommandId.Error: case DiagnosticsServerCommandId.OK: hr = BitConverter.ToInt32(response.Payload, 0); break; default: return(-1); } return(hr); }
public static int Main(string[] args) { Process currentProcess = Process.GetCurrentProcess(); int pid = currentProcess.Id; Logger.logger.Log($"Test PID: {pid}"); var testEnvPairs = new Dictionary <string, string> { { "TESTKEY1", "TESTVAL1" }, { "TESTKEY2", "TESTVAL2" }, { "TESTKEY3", "__TEST__VAL=;--3" } }; foreach (var(key, val) in testEnvPairs) { System.Environment.SetEnvironmentVariable(key, val); } Stream stream = ConnectionHelper.GetStandardTransport(pid); // 0x04 = ProcessCommandSet, 0x02 = ProcessInfo var processInfoMessage = new IpcMessage(0x04, 0x02); Logger.logger.Log($"Wrote: {processInfoMessage}"); Stream continuationStream = IpcClient.SendMessage(stream, processInfoMessage, out IpcMessage response); Logger.logger.Log($"Received: {response}"); Utils.Assert(response.Header.CommandSet == 0xFF, $"Response must have Server command set. Expected: 0xFF, Received: 0x{response.Header.CommandSet:X2}"); // server Utils.Assert(response.Header.CommandId == 0x00, $"Response must have OK command id. Expected: 0x00, Received: 0x{response.Header.CommandId:X2}"); // OK UInt32 continuationSizeInBytes = BitConverter.ToUInt32(response.Payload[0..4]);
public static int Main(string[] args) { Process currentProcess = Process.GetCurrentProcess(); int pid = currentProcess.Id; Logger.logger.Log($"Test PID: {pid}"); Stream stream = ConnectionHelper.GetStandardTransport(pid); // 0x04 = ProcessCommandSet, 0x04 = ProcessInfo2 var processInfoMessage = new IpcMessage(0x04, 0x04); Logger.logger.Log($"Wrote: {processInfoMessage}"); IpcMessage response = IpcClient.SendMessage(stream, processInfoMessage); Logger.logger.Log($"Received: <omitted>"); Utils.Assert(response.Header.CommandSet == 0xFF, $"Response must have Server command set. Expected: 0xFF, Received: 0x{response.Header.CommandSet:X2}"); // server Utils.Assert(response.Header.CommandId == 0x00, $"Response must have OK command id. Expected: 0x00, Received: 0x{response.Header.CommandId:X2}"); // OK // Parse payload // uint64_t ProcessId; // GUID RuntimeCookie; // LPCWSTR CommandLine; // LPCWSTR OS; // LPCWSTR Arch; int totalSize = response.Payload.Length; Logger.logger.Log($"Total size of Payload = {totalSize} bytes"); // VALIDATE PID int start = 0; int end = start + 8 /* sizeof(uint63_t) */; UInt64 processId = BitConverter.ToUInt64(response.Payload[start..end]);
public void SetStartupProfiler(Guid profilerGuid, string profilerPath) { MethodInfo startupProfiler = typeof(DiagnosticsClient).GetMethod("SetStartupProfiler", BindingFlags.Public); if (startupProfiler != null) { throw new Exception("You updated DiagnosticsClient to a version that supports SetStartupProfiler, please remove this and use the real code."); } Console.WriteLine("Sending startup profiler message."); using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { writer.Write(profilerGuid.ToByteArray()); writer.WriteString(profilerPath); writer.Flush(); byte[] payload = stream.ToArray(); var message = new IpcMessage(0x03, 0x02, payload); Console.WriteLine($"Sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(ConnectionHelper.GetStandardTransport(_processId), message); Console.WriteLine($"Received: {response.ToString()}"); } Console.WriteLine("Finished sending startup profiler message."); }
private bool disposedValue = false; // To detect redundant calls internal EventPipeSession(int processId, IEnumerable <EventPipeProvider> providers, bool requestRundown, int circularBufferMB) { _processId = processId; _providers = providers; _requestRundown = requestRundown; _circularBufferMB = circularBufferMB; var config = new EventPipeSessionConfiguration(circularBufferMB, EventPipeSerializationFormat.NetTrace, providers, requestRundown); var message = new IpcMessage(DiagnosticsServerCommandSet.EventPipe, (byte)EventPipeCommandId.CollectTracing2, config.SerializeV2()); EventStream = IpcClient.SendMessage(processId, message, out var response); switch ((DiagnosticsServerCommandId)response.Header.CommandId) { case DiagnosticsServerCommandId.OK: _sessionId = BitConverter.ToInt64(response.Payload, 0); break; case DiagnosticsServerCommandId.Error: var hr = BitConverter.ToInt32(response.Payload, 0); throw new ServerErrorException($"EventPipe session start failed (HRESULT: 0x{hr:X8})"); default: throw new ServerErrorException($"EventPipe session start failed - Server responded with unknown command"); } }
public static int Main(string[] args) { if (args.Length > 0 && args[0].Equals("RunTest", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("In RunTest, exiting."); return(100); } string serverName = ReverseServer.MakeServerAddress(); Task backgroundTask = Task.Run(() => { ReverseServer server = null; try { Task task = Task.Run(async() => { server = new ReverseServer(serverName); using (Stream serverStream = await server.AcceptAsync()) { IpcAdvertise advertise = IpcAdvertise.Parse(serverStream); Console.WriteLine($"Got IpcAdvertise: {advertise}"); int processId = (int)advertise.ProcessId; // While we are paused in startup send the profiler startup command string profilerPath = GetProfilerPath(); DiagnosticsIPCWorkaround client = new DiagnosticsIPCWorkaround(processId); client.SetStartupProfiler(ReverseStartupProfilerGuid, profilerPath); // Resume runtime message IpcMessage resumeMessage = new IpcMessage(0x04, 0x01); Console.WriteLine($"Sent resume runtime message: {resumeMessage.ToString()}"); IpcMessage resumeResponse = IpcClient.SendMessage(serverStream, resumeMessage); Logger.logger.Log($"Received: {resumeResponse.ToString()}"); } }); task.Wait(); } catch (Exception e) { Console.WriteLine($"ReverseServer saw exception {e.Message}"); Console.WriteLine(e.StackTrace); Console.WriteLine($"Inner exception {e.InnerException?.Message}"); Console.WriteLine(e.InnerException?.StackTrace); } finally { server?.Shutdown(); } }); return(ProfilerTestRunner.Run(profileePath: System.Reflection.Assembly.GetExecutingAssembly().Location, testName: "ReverseStartup", profilerClsid: Guid.Empty, profileeOptions: ProfileeOptions.NoStartupAttach | ProfileeOptions.ReverseDiagnosticsMode, reverseServerName: serverName)); }
public static async Task <bool> TEST_CanGetProcessInfo2WhileSuspended() { bool fSuccess = true; Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: new Dictionary <string, string> { { Utils.DiagnosticPortSuspend, "1" } }, duringExecution: (int pid) => { Stream stream = ConnectionHelper.GetStandardTransport(pid); // 0x04 = ProcessCommandSet, 0x04 = ProcessInfo2 var processInfoMessage = new IpcMessage(0x04, 0x04); Logger.logger.Log($"Wrote: {processInfoMessage}"); IpcMessage response = IpcClient.SendMessage(stream, processInfoMessage); Logger.logger.Log($"Received: [{response.Payload.Select(b => b.ToString("X2") + " ").Aggregate(string.Concat)}]"); ProcessInfo2 processInfo2 = ProcessInfo2.TryParse(response.Payload); Utils.Assert(String.IsNullOrEmpty(processInfo2.ManagedEntrypointAssemblyName)); // send resume command on this connection var message = new IpcMessage(0x04, 0x01); Logger.logger.Log($"Sent: {message.ToString()}"); response = IpcClient.SendMessage(ConnectionHelper.GetStandardTransport(pid), message); Logger.logger.Log($"Received: {response.ToString()}"); return(Task.FromResult(true)); } ); fSuccess &= await subprocessTask; return(fSuccess); }
public static async Task <bool> TEST_RuntimeResumesExecutionWithCommand() { bool fSuccess = true; string serverName = ReverseServer.MakeServerAddress(); Logger.logger.Log($"Server name is '{serverName}'"); var server = new ReverseServer(serverName); Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: new Dictionary <string, string> { { Utils.DiagnosticPortsEnvKey, $"{serverName}" } }, duringExecution: async(_) => { Stream stream = await server.AcceptAsync(); IpcAdvertise advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); // send ResumeRuntime command (0x04=ProcessCommandSet, 0x01=ResumeRuntime commandid) var message = new IpcMessage(0x04, 0x01); Logger.logger.Log($"Sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"received: {response.ToString()}"); } ); fSuccess &= await subprocessTask; return(fSuccess); }
/// <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) { 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"); } }
public static async Task <bool> TEST_DisabledCommandsError() { bool fSuccess = true; string serverName = ReverseServer.MakeServerAddress(); Logger.logger.Log($"Server name is '{serverName}'"); var server = new ReverseServer(serverName); using var memoryStream1 = new MemoryStream(); using var memoryStream2 = new MemoryStream(); using var memoryStream3 = new MemoryStream(); Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: new Dictionary <string, string> { { Utils.DiagnosticPortsEnvKey, $"{serverName}" } }, duringExecution: async(pid) => { Stream stream = await server.AcceptAsync(); IpcAdvertise advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); Logger.logger.Log($"Send profiler attach Diagnostics IPC Command"); // send profiler attach command (0x03=ProfilerCommandId, 0x01=attach commandid) var message = new IpcMessage(0x03, 0x01); Logger.logger.Log($"Sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"received: {response.ToString()}"); if (response.Header.CommandSet != 0xFF && response.Header.CommandId != 0xFF) { throw new Exception("Command did not fail!"); } stream = await server.AcceptAsync(); advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); Logger.logger.Log($"Send ResumeRuntime Diagnostics IPC Command"); // send ResumeRuntime command (0x04=ProcessCommandSet, 0x01=ResumeRuntime commandid) message = new IpcMessage(0x04, 0x01); Logger.logger.Log($"Sent: {message.ToString()}"); response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"received: {response.ToString()}"); } ); fSuccess &= await subprocessTask; return(fSuccess); }
///<summary> /// Stops the given session ///</summary> public void Stop() { Debug.Assert(_sessionId > 0); 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: var hr = BitConverter.ToInt32(response.Payload, 0); throw new ServerErrorException($"EventPipe session stop failed (HRESULT: 0x{hr:X8})"); default: throw new ServerErrorException($"EventPipe session stop failed - Server responded with unknown command"); } }
/// <summary> /// Attach a profiler to the target process runtime. /// </summary> /// <param name="processId">.NET Core process id</param> /// <param name="attachTimeout">The timeout (in ms) for the runtime to wait while attempting to attach.</param> /// <param name="profilerGuid">CLSID of the profiler to load</param> /// <param name="profilerPath">Path to the profiler library on disk</param> /// <param name="additionalData">additional data to pass to the profiler on attach</param> /// <returns>HRESULT</returns> public static int AttachProfiler(int processId, uint attachTimeout, Guid profilerGuid, string profilerPath, byte[] additionalData) { 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 { RequestType = DiagnosticsMessageType.AttachProfiler, Pid = (uint)Process.GetCurrentProcess().Id, }; byte[] serializedConfiguration = SerializeProfilerAttach(attachTimeout, profilerGuid, profilerPath, additionalData); var message = new IpcMessage(DiagnosticsServerCommandSet.Profiler, (byte)ProfilerCommandId.AttachProfiler, serializedConfiguration); var response = IpcClient.SendMessage(processId, message); var hr = 0; switch ((DiagnosticsServerCommandId)response.Header.CommandId) { case DiagnosticsServerCommandId.Error: case DiagnosticsServerCommandId.OK: hr = BitConverter.ToInt32(response.Payload, 0); 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(hr); }
/// <summary> /// Turn off EventPipe logging session for the specified process Id. /// </summary> /// <param name="processId">Process Id to turn off logging session.</param> /// <param name="sessionId">EventPipe session Id to turn off.</param> /// <returns>It returns sessionId if success, otherwise 0.</returns> public static ulong StopTracing(int processId, ulong sessionId) { if (sessionId == 0) { return(sessionId); // 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(BitConverter.ToUInt64(response.Payload)); case DiagnosticsServerCommandId.Error: return(0); default: return(0); } }
public static async Task <bool> TEST_AdvertiseAndProcessInfoCookiesMatch() { bool fSuccess = true; string serverName = ReverseServer.MakeServerAddress(); Logger.logger.Log($"Server name is '{serverName}'"); var server = new ReverseServer(serverName); using var memoryStream = new MemoryStream(); Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: new Dictionary <string, string> { { Utils.DiagnosticPortsEnvKey, $"{serverName},nosuspend" } }, duringExecution: async(pid) => { Stream stream = await server.AcceptAsync(); IpcAdvertise advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); Logger.logger.Log($"Send ProcessInfo Diagnostics IPC Command"); // send ProcessInfo command (0x04=ProcessCommandSet, 0x00=ProcessInfo commandid) var message = new IpcMessage(0x04, 0x00); Logger.logger.Log($"Sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"received: {response.ToString()}"); ProcessInfo info = ProcessInfo.TryParse(response.Payload); Logger.logger.Log($"ProcessInfo: {{ id={info.ProcessId}, cookie={info.RuntimeCookie}, cmdline={info.Commandline}, OS={info.OS}, arch={info.Arch} }}"); Utils.Assert(info.RuntimeCookie.Equals(advertise.RuntimeInstanceCookie), $"The runtime cookie reported by ProcessInfo and Advertise must match. ProcessInfo: {info.RuntimeCookie.ToString()}, Advertise: {advertise.RuntimeInstanceCookie.ToString()}"); Logger.logger.Log($"ProcessInfo and Advertise Cookies are equal"); } ); fSuccess &= await subprocessTask; return(fSuccess); }
public static int Main(string[] args) { if (args.Length >= 1) { Console.Out.WriteLine("Subprocess started! Waiting for input..."); var input = Console.In.ReadLine(); // will block until data is sent across stdin Console.Out.WriteLine($"Received '{input}'. Exiting..."); return(0); } var testEnvPairs = new Dictionary <string, string> { { "TESTKEY1", "TESTVAL1" }, { "TESTKEY2", "TESTVAL2" }, { "TESTKEY3", "__TEST__VAL=;--3" } }; Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: testEnvPairs, duringExecution: (int pid) => { Logger.logger.Log($"Test PID: {pid}"); Stream stream = ConnectionHelper.GetStandardTransport(pid); // 0x04 = ProcessCommandSet, 0x02 = ProcessInfo var processInfoMessage = new IpcMessage(0x04, 0x02); Logger.logger.Log($"Wrote: {processInfoMessage}"); Stream continuationStream = IpcClient.SendMessage(stream, processInfoMessage, out IpcMessage response); Logger.logger.Log($"Received: {response}"); Utils.Assert(response.Header.CommandSet == 0xFF, $"Response must have Server command set. Expected: 0xFF, Received: 0x{response.Header.CommandSet:X2}"); // server Utils.Assert(response.Header.CommandId == 0x00, $"Response must have OK command id. Expected: 0x00, Received: 0x{response.Header.CommandId:X2}"); // OK UInt32 continuationSizeInBytes = BitConverter.ToUInt32(response.Payload[0..4]); Logger.logger.Log($"continuation size: {continuationSizeInBytes} bytes"); UInt16 future = BitConverter.ToUInt16(response.Payload[4..]);
/// <summary> /// Start trace collection. /// </summary> /// <param name="processId">Runtime process to trace</param> /// <param name="configuration">buffer size and provider configuration</param> /// <param name="sessionId">session id</param> /// <returns>Stream</returns> public static Stream CollectTracing(int processId, SessionConfiguration configuration, out ulong sessionId) { sessionId = 0; var message = new IpcMessage(DiagnosticsServerCommandSet.EventPipe, (byte)EventPipeCommandId.CollectTracing, configuration.Serialize()); var stream = IpcClient.SendMessage(processId, message, out var response); switch ((DiagnosticsServerCommandId)response.Header.CommandId) { case DiagnosticsServerCommandId.OK: sessionId = BitConverter.ToUInt64(response.Payload); break; case DiagnosticsServerCommandId.Error: // bad... var hr = BitConverter.ToInt32(response.Payload); throw new Exception($"Session start FAILED 0x{hr:X8}"); default: break; } return(stream); }
public bool SetEnvironmentVariable(string name, string val) { MethodInfo setEnvironmentVariable = typeof(DiagnosticsClient).GetMethod("SetEnvironmentVariable", BindingFlags.Public); if (setEnvironmentVariable != null) { throw new Exception("You updated DiagnosticsClient to a version that supports SetEnvironmentVariable, please remove this and use the real code."); } Console.WriteLine($"Sending SetEnvironmentVariable message name={name} value={val ?? "NULL"}."); using (var stream = new MemoryStream()) using (var writer = new BinaryWriter(stream)) { writer.WriteString(name); writer.WriteString(val); writer.Flush(); byte[] payload = stream.ToArray(); var message = new IpcMessage(0x04, 0x03, payload); Console.WriteLine($"Sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(ConnectionHelper.GetStandardTransport(_processId), message); Console.WriteLine($"Received: {response.ToString()}"); if (response.Header.CommandSet != 255 || response.Header.CommandId != 0) { Console.WriteLine($"SetEnvironmentVariable failed."); return(false); } } Console.WriteLine($"Finished sending SetEnvironmentVariable message."); return(true); }
public static async Task <bool> TEST_MultipleConnectPortsSuspend() { bool fSuccess = true; var serverAndNames = new List <(ReverseServer, string)>(); string dotnetDiagnosticPorts = ""; for (int i = 0; i < s_NumberOfPorts; i++) { string serverName = ReverseServer.MakeServerAddress(); var server = new ReverseServer(serverName); Logger.logger.Log($"Server {i} address is '{serverName}'"); serverAndNames.Add((server, serverName)); dotnetDiagnosticPorts += $"{serverName};"; } Logger.logger.Log($"export DOTNET_DiagnosticPorts={dotnetDiagnosticPorts}"); var advertisements = new List <IpcAdvertise>(); Object sync = new Object(); int subprocessId = -1; Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: new Dictionary <string, string> { { Utils.DiagnosticPortsEnvKey, dotnetDiagnosticPorts } }, duringExecution: async(int pid) => { subprocessId = pid; // Create an eventpipe session that will tell us when // the EEStartupStarted event happens. This will tell us // the the runtime has been resumed. This should only happen // AFTER all suspend ports have sent the resume command. var config = new SessionConfiguration( circularBufferSizeMB: 1000, format: EventPipeSerializationFormat.NetTrace, providers: new List <Provider> { new Provider("Microsoft-Windows-DotNETRuntimePrivate", 0x80000000, EventLevel.Verbose), // workaround for https://github.com/dotnet/runtime/issues/44072 which happens because the // above provider only sends 2 events and that can cause EventPipeEventSource (from TraceEvent) // to not dispatch the events if the EventBlock is a size not divisible by 8 (the reading alignment in TraceEvent). // Adding this provider keeps data flowing over the pipe so the reader doesn't get stuck waiting for data // that won't come otherwise. new Provider("Microsoft-DotNETCore-SampleProfiler") }); Logger.logger.Log("Starting EventPipeSession over standard connection"); using Stream eventStream = EventPipeClient.CollectTracing(pid, config, out var sessionId); Logger.logger.Log($"Started EventPipeSession over standard connection with session id: 0x{sessionId:x}"); var mre = new ManualResetEvent(false); Task readerTask = Task.Run(async() => { Logger.logger.Log($"Creating EventPipeEventSource"); using var source = new EventPipeEventSource(eventStream); var parser = new ClrPrivateTraceEventParser(source); parser.StartupEEStartupStart += (eventData) => mre.Set(); Logger.logger.Log($"Created EventPipeEventSource"); Logger.logger.Log($"Starting processing"); await Task.Run(() => source.Process()); Logger.logger.Log($"Finished processing"); }); for (int i = 0; i < s_NumberOfPorts; i++) { fSuccess &= !mre.WaitOne(0); Logger.logger.Log($"Runtime HAS NOT resumed (expects: true): {fSuccess}"); var(server, _) = serverAndNames[i]; int serverIndex = i; Stream stream = await server.AcceptAsync(); IpcAdvertise advertise = IpcAdvertise.Parse(stream); lock (sync) advertisements.Add(advertise); Logger.logger.Log($"Server {serverIndex} got advertise {advertise.ToString()}"); // send resume command on this connection var message = new IpcMessage(0x04, 0x01); Logger.logger.Log($"Port {serverIndex} sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"Port {serverIndex} received: {response.ToString()}"); } Logger.logger.Log($"Waiting on EEStartupStarted event"); mre.WaitOne(); Logger.logger.Log($"Saw EEStartupStarted Event"); Logger.logger.Log($"Stopping EventPipeSession"); EventPipeClient.StopTracing(pid, sessionId); await readerTask; Logger.logger.Log($"Stopped EventPipeSession"); // runtime should have resumed now fSuccess &= mre.WaitOne(0); Logger.logger.Log($"Runtime HAS resumed (expects: true): {fSuccess}"); } ); fSuccess &= await subprocessTask; foreach (var(server, _) in serverAndNames) { server.Shutdown(); } if (advertisements.Count() > 0) { Guid referenceCookie = advertisements[0].RuntimeInstanceCookie; foreach (var adv in advertisements) { fSuccess &= (int)adv.ProcessId == subprocessId; fSuccess &= adv.RuntimeInstanceCookie.Equals(referenceCookie); } } else { fSuccess &= false; } return(fSuccess); }
public static async Task <bool> TEST_SuspendDefaultPort() { bool fSuccess = true; int subprocessId = -1; Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: new Dictionary <string, string> { { Utils.DiagnosticPortSuspend, "1" } }, duringExecution: async(int pid) => { subprocessId = pid; // Create an eventpipe session that will tell us when // the EEStartupStarted event happens. This will tell us // the the runtime has been resumed. This should only happen // AFTER all suspend ports have sent the resume command. var config = new SessionConfiguration( circularBufferSizeMB: 1000, format: EventPipeSerializationFormat.NetTrace, providers: new List <Provider> { new Provider("Microsoft-Windows-DotNETRuntimePrivate", 0x80000000, EventLevel.Verbose), // workaround for https://github.com/dotnet/runtime/issues/44072 which happens because the // above provider only sends 2 events and that can cause EventPipeEventSource (from TraceEvent) // to not dispatch the events if the EventBlock is a size not divisible by 8 (the reading alignment in TraceEvent). // Adding this provider keeps data flowing over the pipe so the reader doesn't get stuck waiting for data // that won't come otherwise. new Provider("Microsoft-DotNETCore-SampleProfiler") }); Logger.logger.Log("Starting EventPipeSession over standard connection"); using Stream eventStream = EventPipeClient.CollectTracing(pid, config, out var sessionId); Logger.logger.Log($"Started EventPipeSession over standard connection with session id: 0x{sessionId:x}"); var mre = new ManualResetEvent(false); Task readerTask = Task.Run(async() => { Logger.logger.Log($"Creating EventPipeEventSource"); using var source = new EventPipeEventSource(eventStream); var parser = new ClrPrivateTraceEventParser(source); parser.StartupEEStartupStart += (eventData) => mre.Set(); Logger.logger.Log($"Created EventPipeEventSource"); Logger.logger.Log($"Starting processing"); await Task.Run(() => source.Process()); Logger.logger.Log($"Finished processing"); }); fSuccess &= !mre.WaitOne(0); Logger.logger.Log($"Runtime HAS NOT resumed (expects: true): {fSuccess}"); // send resume command on this connection var message = new IpcMessage(0x04, 0x01); Logger.logger.Log($"Sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(ConnectionHelper.GetStandardTransport(pid), message); Logger.logger.Log($"Received: {response.ToString()}"); Logger.logger.Log($"Waiting for EEStartupStarted event"); mre.WaitOne(); Logger.logger.Log($"Saw EEStartupStarted event!"); Logger.logger.Log($"Stopping EventPipeSession"); EventPipeClient.StopTracing(pid, sessionId); await readerTask; Logger.logger.Log($"Stopped EventPipeSession"); // runtime should have resumed now fSuccess &= mre.WaitOne(0); Logger.logger.Log($"Runtime HAS resumed (expects: true): {fSuccess}"); } ); fSuccess &= await subprocessTask; return(fSuccess); }
public static async Task <bool> TEST_MultipleSessionsCanBeStartedWhilepaused() { bool fSuccess = true; string serverName = ReverseServer.MakeServerAddress(); Logger.logger.Log($"Server name is '{serverName}'"); var server = new ReverseServer(serverName); using var memoryStream1 = new MemoryStream(); using var memoryStream2 = new MemoryStream(); using var memoryStream3 = new MemoryStream(); Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: new Dictionary <string, string> { { Utils.DiagnosticsMonitorAddressEnvKey, serverName } }, duringExecution: async(pid) => { Stream stream = await server.AcceptAsync(); IpcAdvertise advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); var config = new SessionConfiguration( circularBufferSizeMB: 1000, format: EventPipeSerializationFormat.NetTrace, providers: new List <Provider> { new Provider("Microsoft-Windows-DotNETRuntimePrivate", 0x80000000, EventLevel.Verbose) }); Logger.logger.Log("Starting EventPipeSession over standard connection"); using Stream eventStream1 = EventPipeClient.CollectTracing(pid, config, out var sessionId1); Logger.logger.Log($"Started EventPipeSession over standard connection with session id: 0x{sessionId1:x}"); Task readerTask1 = eventStream1.CopyToAsync(memoryStream1); Logger.logger.Log("Starting EventPipeSession over standard connection"); using Stream eventStream2 = EventPipeClient.CollectTracing(pid, config, out var sessionId2); Logger.logger.Log($"Started EventPipeSession over standard connection with session id: 0x{sessionId2:x}"); Task readerTask2 = eventStream2.CopyToAsync(memoryStream2); Logger.logger.Log("Starting EventPipeSession over standard connection"); using Stream eventStream3 = EventPipeClient.CollectTracing(pid, config, out var sessionId3); Logger.logger.Log($"Started EventPipeSession over standard connection with session id: 0x{sessionId3:x}"); Task readerTask3 = eventStream3.CopyToAsync(memoryStream3); Logger.logger.Log($"Send ResumeRuntime Diagnostics IPC Command"); // send ResumeRuntime command (0xFF=ServerCommandSet, 0x01=ResumeRuntime commandid) var message = new IpcMessage(0xFF, 0x01); Logger.logger.Log($"Sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"received: {response.ToString()}"); await Task.Delay(TimeSpan.FromSeconds(2)); Logger.logger.Log("Stopping EventPipeSession over standard connection"); EventPipeClient.StopTracing(pid, sessionId1); EventPipeClient.StopTracing(pid, sessionId2); EventPipeClient.StopTracing(pid, sessionId3); await readerTask1; await readerTask2; await readerTask3; Logger.logger.Log("Stopped EventPipeSession over standard connection"); } ); fSuccess &= await Utils.WaitTillTimeout(subprocessTask, TimeSpan.FromMinutes(1)); int nStartupEventsSeen = 0; memoryStream1.Seek(0, SeekOrigin.Begin); using (var source = new EventPipeEventSource(memoryStream1)) { var parser = new ClrPrivateTraceEventParser(source); parser.StartupEEStartupStart += (eventData) => nStartupEventsSeen++; source.Process(); } memoryStream2.Seek(0, SeekOrigin.Begin); using (var source = new EventPipeEventSource(memoryStream2)) { var parser = new ClrPrivateTraceEventParser(source); parser.StartupEEStartupStart += (eventData) => nStartupEventsSeen++; source.Process(); } memoryStream3.Seek(0, SeekOrigin.Begin); using (var source = new EventPipeEventSource(memoryStream3)) { var parser = new ClrPrivateTraceEventParser(source); parser.StartupEEStartupStart += (eventData) => nStartupEventsSeen++; source.Process(); } Logger.logger.Log($"nStartupEventsSeen: {nStartupEventsSeen}"); return(nStartupEventsSeen == 3 && fSuccess); }
public static async Task <bool> TEST_ProcessInfoBeforeAndAfterSuspension() { // This test only applies to platforms where the PAL is used if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return(true); } // This test only applies to CoreCLR (this checks if we're running on Mono) if (Type.GetType("Mono.RuntimeStructs") != null) { return(true); } bool fSuccess = true; string serverName = ReverseServer.MakeServerAddress(); Logger.logger.Log($"Server name is '{serverName}'"); var server = new ReverseServer(serverName); using var memoryStream1 = new MemoryStream(); using var memoryStream2 = new MemoryStream(); using var memoryStream3 = new MemoryStream(); Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: new Dictionary <string, string> { { Utils.DiagnosticPortsEnvKey, $"{serverName}" } }, duringExecution: async(pid) => { Process currentProcess = Process.GetCurrentProcess(); Stream stream = await server.AcceptAsync(); IpcAdvertise advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); Logger.logger.Log($"Get ProcessInfo while process is suspended"); // 0x04 = ProcessCommandSet, 0x04 = ProcessInfo2 var message = new IpcMessage(0x04, 0x04); Logger.logger.Log($"Sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"received: {response.ToString()}"); ProcessInfo2 pi2Before = ProcessInfo2.TryParse(response.Payload); Utils.Assert(pi2Before.Commandline.Equals(currentProcess.MainModule.FileName), $"Before resuming, the commandline should be the mock value of the host executable path '{currentProcess.MainModule.FileName}'. Observed: '{pi2Before.Commandline}'"); // recycle stream = await server.AcceptAsync(); advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); // Start EP session to know when runtime is resumed var config = new SessionConfiguration( circularBufferSizeMB: 1000, format: EventPipeSerializationFormat.NetTrace, providers: new List <Provider> { new Provider("Microsoft-Windows-DotNETRuntimePrivate", 0x80000000, EventLevel.Verbose), new Provider("Microsoft-DotNETCore-SampleProfiler") }); Logger.logger.Log("Starting EventPipeSession over standard connection"); using Stream eventStream = EventPipeClient.CollectTracing(pid, config, out var sessionId); Logger.logger.Log($"Started EventPipeSession over standard connection with session id: 0x{sessionId:x}"); TaskCompletionSource <bool> runtimeResumed = new(false, TaskCreationOptions.RunContinuationsAsynchronously); var eventPipeTask = Task.Run(() => { Logger.logger.Log("Creating source"); using var source = new EventPipeEventSource(eventStream); var parser = new ClrPrivateTraceEventParser(source); parser.StartupEEStartupStart += (_) => runtimeResumed.SetResult(true); source.Process(); Logger.logger.Log("stopping processing"); }); Logger.logger.Log($"Send ResumeRuntime Diagnostics IPC Command"); // send ResumeRuntime command (0x04=ProcessCommandSet, 0x01=ResumeRuntime commandid) message = new IpcMessage(0x04, 0x01); Logger.logger.Log($"Sent: {message.ToString()}"); response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"received: {response.ToString()}"); // recycle stream = await server.AcceptAsync(); advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); // wait a little bit to make sure the runtime of the target is fully up, i.e., g_EEStarted == true // on resource constrained CI machines this may not be instantaneous Logger.logger.Log($"awaiting resume"); await Utils.WaitTillTimeout(runtimeResumed.Task, TimeSpan.FromSeconds(10)); Logger.logger.Log($"resumed"); // await Task.Delay(TimeSpan.FromSeconds(1)); Logger.logger.Log("Stopping EventPipeSession over standard connection"); EventPipeClient.StopTracing(pid, sessionId); Logger.logger.Log($"Await reader task"); await eventPipeTask; Logger.logger.Log("Stopped EventPipeSession over standard connection"); ProcessInfo2 pi2After = default; // The timing is not exact. There is a small window after resuming where the mock // value is still present. Retry several times to catch it. var retryTask = Task.Run(async() => { int i = 0; do { Logger.logger.Log($"Get ProcessInfo after resumption: attempt {i++}"); // 0x04 = ProcessCommandSet, 0x04 = ProcessInfo2 message = new IpcMessage(0x04, 0x04); Logger.logger.Log($"Sent: {message.ToString()}"); response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"received: {response.ToString()}"); pi2After = ProcessInfo2.TryParse(response.Payload); // recycle stream = await server.AcceptAsync(); advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); } while (pi2After.Commandline.Equals(pi2Before.Commandline)); }); await Utils.WaitTillTimeout(retryTask, TimeSpan.FromSeconds(10)); Utils.Assert(!pi2After.Commandline.Equals(pi2Before.Commandline), $"After resuming, the commandline should be the correct value. Observed: Before='{pi2Before.Commandline}' After='{pi2After.Commandline}'"); } ); fSuccess &= await subprocessTask; return(fSuccess); }
public static async Task <bool> TEST_CanStartAndStopSessionWhilepaused() { bool fSuccess = true; string serverName = ReverseServer.MakeServerAddress(); Logger.logger.Log($"Server name is '{serverName}'"); var server = new ReverseServer(serverName); using var memoryStream1 = new MemoryStream(); using var memoryStream2 = new MemoryStream(); using var memoryStream3 = new MemoryStream(); Task <bool> subprocessTask = Utils.RunSubprocess( currentAssembly: Assembly.GetExecutingAssembly(), environment: new Dictionary <string, string> { { Utils.DiagnosticPortsEnvKey, $"{serverName}" } }, duringExecution: async(pid) => { Stream stream = await server.AcceptAsync(); IpcAdvertise advertise = IpcAdvertise.Parse(stream); Logger.logger.Log(advertise.ToString()); var config = new SessionConfiguration( circularBufferSizeMB: 1000, format: EventPipeSerializationFormat.NetTrace, providers: new List <Provider> { new Provider("Microsoft-Windows-DotNETRuntime", UInt64.MaxValue, EventLevel.Verbose) }); Logger.logger.Log("Starting EventPipeSession over standard connection"); using Stream eventStream1 = EventPipeClient.CollectTracing(pid, config, out var sessionId1); Logger.logger.Log($"Started EventPipeSession over standard connection with session id: 0x{sessionId1:x}"); Task readerTask1 = eventStream1.CopyToAsync(memoryStream1); Logger.logger.Log("Starting EventPipeSession over standard connection"); using Stream eventStream2 = EventPipeClient.CollectTracing(pid, config, out var sessionId2); Logger.logger.Log($"Started EventPipeSession over standard connection with session id: 0x{sessionId2:x}"); Task readerTask2 = eventStream2.CopyToAsync(memoryStream2); Logger.logger.Log("Starting EventPipeSession over standard connection"); using Stream eventStream3 = EventPipeClient.CollectTracing(pid, config, out var sessionId3); Logger.logger.Log($"Started EventPipeSession over standard connection with session id: 0x{sessionId3:x}"); Task readerTask3 = eventStream3.CopyToAsync(memoryStream3); await Task.Delay(TimeSpan.FromSeconds(1)); Logger.logger.Log("Stopping EventPipeSession over standard connection"); EventPipeClient.StopTracing(pid, sessionId1); EventPipeClient.StopTracing(pid, sessionId2); EventPipeClient.StopTracing(pid, sessionId3); await readerTask1; await readerTask2; await readerTask3; Logger.logger.Log("Stopped EventPipeSession over standard connection"); Logger.logger.Log($"Send ResumeRuntime Diagnostics IPC Command"); // send ResumeRuntime command (0x04=ProcessCommandSet, 0x01=ResumeRuntime commandid) var message = new IpcMessage(0x04, 0x01); Logger.logger.Log($"Sent: {message.ToString()}"); IpcMessage response = IpcClient.SendMessage(stream, message); Logger.logger.Log($"received: {response.ToString()}"); } ); fSuccess &= await subprocessTask; return(fSuccess); }