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_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); }