Skip to content

Commit

Permalink
Validate behavior of ProcessInfo command before and after suspension …
Browse files Browse the repository at this point in the history
…point (#63382)

This only applies to CoreCLR Unix processes.
  • Loading branch information
John Salem authored Feb 3, 2022
1 parent 73ddf6e commit c99b3c7
Showing 1 changed file with 128 additions and 0 deletions.
128 changes: 128 additions & 0 deletions src/tests/tracing/eventpipe/pauseonstart/pauseonstart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.Tools.RuntimeClient;
using Tracing.Tests.Common;
using System.Threading;
Expand Down Expand Up @@ -305,6 +306,133 @@ public static async Task<bool> TEST_DisabledCommandsError()
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;
}

public static async Task<int> Main(string[] args)
{
if (args.Length >= 1)
Expand Down

0 comments on commit c99b3c7

Please sign in to comment.