Skip to content

Commit

Permalink
Enable ProcessInfo2 in DiagnosticsClient. (#2564)
Browse files Browse the repository at this point in the history
Update tests to reflect when entrypoint is available.
  • Loading branch information
jander-msft authored Sep 10, 2021
1 parent f53b948 commit a1dcf21
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -274,16 +274,12 @@ static IEnumerable<int> GetAllPublishedProcesses()

internal ProcessInfo GetProcessInfo()
{
// RE: https://github.com/dotnet/runtime/issues/54083
// If the GetProcessInfo2 command is sent too early, it will crash the runtime instance.
// Disable the usage of the command until that issue is fixed.

// Attempt to get ProcessInfo v2
//ProcessInfo processInfo = TryGetProcessInfo2();
//if (null != processInfo)
//{
// return processInfo;
//}
ProcessInfo processInfo = TryGetProcessInfo2();
if (null != processInfo)
{
return processInfo;
}

IpcMessage request = CreateProcessInfoMessage();
using IpcResponse response = IpcClient.SendMessageGetContinuation(_endpoint, request);
Expand All @@ -292,16 +288,12 @@ internal ProcessInfo GetProcessInfo()

internal async Task<ProcessInfo> GetProcessInfoAsync(CancellationToken token)
{
// RE: https://github.com/dotnet/runtime/issues/54083
// If the GetProcessInfo2 command is sent too early, it will crash the runtime instance.
// Disable the usage of the command until that issue is fixed.

// Attempt to get ProcessInfo v2
//ProcessInfo processInfo = await TryGetProcessInfo2Async(token);
//if (null != processInfo)
//{
// return processInfo;
//}
ProcessInfo processInfo = await TryGetProcessInfo2Async(token);
if (null != processInfo)
{
return processInfo;
}

IpcMessage request = CreateProcessInfoMessage();
using IpcResponse response = await IpcClient.SendMessageGetContinuationAsync(_endpoint, request, token).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@

namespace Microsoft.Diagnostics.NETCore.Client
{
internal static class ReversedServerHelper
internal static class DiagnosticPortsHelper
{
private const string DiagnosticPortsEnvName = "DOTNET_DiagnosticPorts";
private const string DefaultDiagnosticPortSuspendEnvName = "DOTNET_DefaultDiagnosticPortSuspend";

/// <summary>
/// Creates a unique server name to avoid collisions from simultaneous running tests
/// or potentially abandoned socket files.
Expand All @@ -27,21 +30,15 @@ public static string CreateServerTransportName()
}
}

/// <summary>
/// Starts the Tracee executable while enabling connection to reverse diagnostics server.
/// </summary>
public static TestRunner StartTracee(ITestOutputHelper _outputHelper, string transportName)
public static void SetDiagnosticPort(this TestRunner runner, string transportName, bool suspend)
{
var runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(targetFramework: "net5.0"), _outputHelper);
runner.AddReversedServer(transportName);
runner.Start();
return runner;
string suspendArgument = suspend ? "suspend" : "nosuspend";
runner.AddEnvVar(DiagnosticPortsEnvName, $"{transportName},connect,{suspendArgument};");
}

public static void AddReversedServer(this TestRunner runner, string transportName)
public static void SuspendDefaultDiagnosticPort(this TestRunner runner)
{
runner.AddEnvVar("DOTNET_DiagnosticsMonitorAddress", transportName);
runner.AddEnvVar("DOTNET_DiagnosticPorts", $"{transportName},nosuspend;");
runner.AddEnvVar(DefaultDiagnosticPortSuspendEnvName, "1");
}
}
}
104 changes: 87 additions & 17 deletions src/tests/Microsoft.Diagnostics.NETCore.Client/GetProcessInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,51 +11,121 @@ namespace Microsoft.Diagnostics.NETCore.Client
{
public class GetProcessInfoTests
{
private readonly ITestOutputHelper output;
private readonly ITestOutputHelper _output;

public GetProcessInfoTests(ITestOutputHelper outputHelper)
{
output = outputHelper;
_output = outputHelper;
}

[Fact]
public Task BasicProcessInfoTest()
public Task BasicProcessInfoNoSuspendTest()
{
return BasicProcessInfoTestCore(useAsync: false);
return BasicProcessInfoTestCore(useAsync: false, suspend: false);
}

[Fact]
public Task BasicProcessInfoTestAsync()
public Task BasicProcessInfoNoSuspendTestAsync()
{
return BasicProcessInfoTestCore(useAsync: true);
return BasicProcessInfoTestCore(useAsync: true, suspend: false);
}

private async Task BasicProcessInfoTestCore(bool useAsync)
[Fact]
public Task BasicProcessInfoSuspendTest()
{
return BasicProcessInfoTestCore(useAsync: false, suspend: true);
}

[Fact]
public Task BasicProcessInfoSuspendTestAsync()
{
return BasicProcessInfoTestCore(useAsync: true, suspend: true);
}

private async Task BasicProcessInfoTestCore(bool useAsync, bool suspend)
{
using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(targetFramework: "net5.0"), output);
using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(targetFramework: "net5.0"), _output);
if (suspend)
{
runner.SuspendDefaultDiagnosticPort();
}
runner.Start();

try
{
DiagnosticsClientApiShim clientShim = new DiagnosticsClientApiShim(new DiagnosticsClient(runner.Pid), useAsync);

ProcessInfo processInfo = await clientShim.GetProcessInfo();
// While suspended, the runtime will not provide entrypoint information.
if (suspend)
{
ProcessInfo processInfoBeforeResume = await clientShim.GetProcessInfo();
ValidateProcessInfo(runner.Pid, processInfoBeforeResume);
Assert.True(string.IsNullOrEmpty(processInfoBeforeResume.ManagedEntrypointAssemblyName));

Assert.NotNull(processInfo);
Assert.Equal(runner.Pid, (int)processInfo.ProcessId);
Assert.NotNull(processInfo.CommandLine);
Assert.NotNull(processInfo.OperatingSystem);
Assert.NotNull(processInfo.ProcessArchitecture);
//Assert.Equal("Tracee", processInfo.ManagedEntrypointAssemblyName);
//Version clrVersion = ParseVersionRemoveLabel(processInfo.ClrProductVersionString);
//Assert.True(clrVersion >= new Version(6, 0, 0));
await clientShim.ResumeRuntime();
}

// The entrypoint information is available some short time after the runtime
// begins to execute. Retry getting process information until entrypoint is available.
ProcessInfo processInfo = await GetProcessInfoWithEntrypointAsync(clientShim);
ValidateProcessInfo(runner.Pid, processInfo);
Assert.Equal("Tracee", processInfo.ManagedEntrypointAssemblyName);
}
finally
{
runner.PrintStatus();
}
}

/// <summary>
/// Get process information with entrypoint information with exponential backoff on retries.
/// </summary>
private async Task<ProcessInfo> GetProcessInfoWithEntrypointAsync(DiagnosticsClientApiShim shim)
{
int retryMilliseconds = 5;
int currentAttempt = 1;
const int maxAttempts = 10;

_output.WriteLine("Getting process info with entrypoint:");
while (currentAttempt <= maxAttempts)
{
_output.WriteLine("- Attempt {0} of {1}.", currentAttempt, maxAttempts);

ProcessInfo processInfo = await shim.GetProcessInfo();
Assert.NotNull(processInfo);

if (!string.IsNullOrEmpty(processInfo.ManagedEntrypointAssemblyName))
{
_output.WriteLine("Got process info with entrypoint.");
return processInfo;
}

currentAttempt++;

if (currentAttempt != maxAttempts)
{
_output.WriteLine(" Waiting {0} ms.", retryMilliseconds);

await Task.Delay(retryMilliseconds);

retryMilliseconds = Math.Min(2 * retryMilliseconds, 500);
}
}

throw new InvalidOperationException("Unable to get process info with entrypoint.");
}

private static void ValidateProcessInfo(int expectedProcessId, ProcessInfo processInfo)
{
Assert.NotNull(processInfo);
Assert.Equal(expectedProcessId, (int)processInfo.ProcessId);
Assert.NotNull(processInfo.CommandLine);
Assert.NotNull(processInfo.OperatingSystem);
Assert.NotNull(processInfo.ProcessArchitecture);
Version clrVersion = ParseVersionRemoveLabel(processInfo.ClrProductVersionString);
Assert.True(clrVersion >= new Version(6, 0, 0));
}

private static Version ParseVersionRemoveLabel(string versionString)
{
Assert.NotNull(versionString);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static RemoteTestExecution StartProcess(string commandLine, ITestOutputHe
TestRunner runner = new TestRunner(commandLine, outputHelper, redirectError: true, redirectInput: true);
if (!string.IsNullOrEmpty(reversedServerTransportName))
{
runner.AddReversedServer(reversedServerTransportName);
runner.SetDiagnosticPort(reversedServerTransportName, suspend: false);
}
runner.Start(testProcessTimeout: 60_000);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ public async Task ReversedServerNoCreateTransportAfterDispose()

private ReversedDiagnosticsServer CreateReversedServer(out string transportName)
{
transportName = ReversedServerHelper.CreateServerTransportName();
transportName = DiagnosticPortsHelper.CreateServerTransportName();
_outputHelper.WriteLine("Starting reversed server at '" + transportName + "'.");
return new ReversedDiagnosticsServer(transportName);
}
Expand All @@ -331,7 +331,10 @@ private async Task<IpcEndpointInfo> AcceptEndpointInfo(ReversedDiagnosticsServer
private TestRunner StartTracee(string transportName)
{
_outputHelper.WriteLine("Starting tracee.");
return ReversedServerHelper.StartTracee(_outputHelper, transportName);
var runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(targetFramework: "net5.0"), _outputHelper);
runner.SetDiagnosticPort(transportName, suspend: true);
runner.Start();
return runner;
}

private async Task VerifyWaitForConnection(IpcEndpointInfo info, bool useAsync, bool expectTimeout = false)
Expand Down

0 comments on commit a1dcf21

Please sign in to comment.