Skip to content

Commit a1dcf21

Browse files
authored
Enable ProcessInfo2 in DiagnosticsClient. (#2564)
Update tests to reflect when entrypoint is available.
1 parent f53b948 commit a1dcf21

File tree

5 files changed

+112
-50
lines changed

5 files changed

+112
-50
lines changed

src/Microsoft.Diagnostics.NETCore.Client/DiagnosticsClient/DiagnosticsClient.cs

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -274,16 +274,12 @@ static IEnumerable<int> GetAllPublishedProcesses()
274274

275275
internal ProcessInfo GetProcessInfo()
276276
{
277-
// RE: https://github.com/dotnet/runtime/issues/54083
278-
// If the GetProcessInfo2 command is sent too early, it will crash the runtime instance.
279-
// Disable the usage of the command until that issue is fixed.
280-
281277
// Attempt to get ProcessInfo v2
282-
//ProcessInfo processInfo = TryGetProcessInfo2();
283-
//if (null != processInfo)
284-
//{
285-
// return processInfo;
286-
//}
278+
ProcessInfo processInfo = TryGetProcessInfo2();
279+
if (null != processInfo)
280+
{
281+
return processInfo;
282+
}
287283

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

293289
internal async Task<ProcessInfo> GetProcessInfoAsync(CancellationToken token)
294290
{
295-
// RE: https://github.com/dotnet/runtime/issues/54083
296-
// If the GetProcessInfo2 command is sent too early, it will crash the runtime instance.
297-
// Disable the usage of the command until that issue is fixed.
298-
299291
// Attempt to get ProcessInfo v2
300-
//ProcessInfo processInfo = await TryGetProcessInfo2Async(token);
301-
//if (null != processInfo)
302-
//{
303-
// return processInfo;
304-
//}
292+
ProcessInfo processInfo = await TryGetProcessInfo2Async(token);
293+
if (null != processInfo)
294+
{
295+
return processInfo;
296+
}
305297

306298
IpcMessage request = CreateProcessInfoMessage();
307299
using IpcResponse response = await IpcClient.SendMessageGetContinuationAsync(_endpoint, request, token).ConfigureAwait(false);

src/tests/Microsoft.Diagnostics.NETCore.Client/ReversedServerHelper.cs renamed to src/tests/Microsoft.Diagnostics.NETCore.Client/DiagnosticPortsHelper.cs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88

99
namespace Microsoft.Diagnostics.NETCore.Client
1010
{
11-
internal static class ReversedServerHelper
11+
internal static class DiagnosticPortsHelper
1212
{
13+
private const string DiagnosticPortsEnvName = "DOTNET_DiagnosticPorts";
14+
private const string DefaultDiagnosticPortSuspendEnvName = "DOTNET_DefaultDiagnosticPortSuspend";
15+
1316
/// <summary>
1417
/// Creates a unique server name to avoid collisions from simultaneous running tests
1518
/// or potentially abandoned socket files.
@@ -27,21 +30,15 @@ public static string CreateServerTransportName()
2730
}
2831
}
2932

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

41-
public static void AddReversedServer(this TestRunner runner, string transportName)
39+
public static void SuspendDefaultDiagnosticPort(this TestRunner runner)
4240
{
43-
runner.AddEnvVar("DOTNET_DiagnosticsMonitorAddress", transportName);
44-
runner.AddEnvVar("DOTNET_DiagnosticPorts", $"{transportName},nosuspend;");
41+
runner.AddEnvVar(DefaultDiagnosticPortSuspendEnvName, "1");
4542
}
4643
}
4744
}

src/tests/Microsoft.Diagnostics.NETCore.Client/GetProcessInfoTests.cs

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,51 +11,121 @@ namespace Microsoft.Diagnostics.NETCore.Client
1111
{
1212
public class GetProcessInfoTests
1313
{
14-
private readonly ITestOutputHelper output;
14+
private readonly ITestOutputHelper _output;
1515

1616
public GetProcessInfoTests(ITestOutputHelper outputHelper)
1717
{
18-
output = outputHelper;
18+
_output = outputHelper;
1919
}
2020

2121
[Fact]
22-
public Task BasicProcessInfoTest()
22+
public Task BasicProcessInfoNoSuspendTest()
2323
{
24-
return BasicProcessInfoTestCore(useAsync: false);
24+
return BasicProcessInfoTestCore(useAsync: false, suspend: false);
2525
}
2626

2727
[Fact]
28-
public Task BasicProcessInfoTestAsync()
28+
public Task BasicProcessInfoNoSuspendTestAsync()
2929
{
30-
return BasicProcessInfoTestCore(useAsync: true);
30+
return BasicProcessInfoTestCore(useAsync: true, suspend: false);
3131
}
3232

33-
private async Task BasicProcessInfoTestCore(bool useAsync)
33+
[Fact]
34+
public Task BasicProcessInfoSuspendTest()
35+
{
36+
return BasicProcessInfoTestCore(useAsync: false, suspend: true);
37+
}
38+
39+
[Fact]
40+
public Task BasicProcessInfoSuspendTestAsync()
41+
{
42+
return BasicProcessInfoTestCore(useAsync: true, suspend: true);
43+
}
44+
45+
private async Task BasicProcessInfoTestCore(bool useAsync, bool suspend)
3446
{
35-
using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(targetFramework: "net5.0"), output);
47+
using TestRunner runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(targetFramework: "net5.0"), _output);
48+
if (suspend)
49+
{
50+
runner.SuspendDefaultDiagnosticPort();
51+
}
3652
runner.Start();
3753

3854
try
3955
{
4056
DiagnosticsClientApiShim clientShim = new DiagnosticsClientApiShim(new DiagnosticsClient(runner.Pid), useAsync);
4157

42-
ProcessInfo processInfo = await clientShim.GetProcessInfo();
58+
// While suspended, the runtime will not provide entrypoint information.
59+
if (suspend)
60+
{
61+
ProcessInfo processInfoBeforeResume = await clientShim.GetProcessInfo();
62+
ValidateProcessInfo(runner.Pid, processInfoBeforeResume);
63+
Assert.True(string.IsNullOrEmpty(processInfoBeforeResume.ManagedEntrypointAssemblyName));
4364

44-
Assert.NotNull(processInfo);
45-
Assert.Equal(runner.Pid, (int)processInfo.ProcessId);
46-
Assert.NotNull(processInfo.CommandLine);
47-
Assert.NotNull(processInfo.OperatingSystem);
48-
Assert.NotNull(processInfo.ProcessArchitecture);
49-
//Assert.Equal("Tracee", processInfo.ManagedEntrypointAssemblyName);
50-
//Version clrVersion = ParseVersionRemoveLabel(processInfo.ClrProductVersionString);
51-
//Assert.True(clrVersion >= new Version(6, 0, 0));
65+
await clientShim.ResumeRuntime();
66+
}
67+
68+
// The entrypoint information is available some short time after the runtime
69+
// begins to execute. Retry getting process information until entrypoint is available.
70+
ProcessInfo processInfo = await GetProcessInfoWithEntrypointAsync(clientShim);
71+
ValidateProcessInfo(runner.Pid, processInfo);
72+
Assert.Equal("Tracee", processInfo.ManagedEntrypointAssemblyName);
5273
}
5374
finally
5475
{
5576
runner.PrintStatus();
5677
}
5778
}
5879

80+
/// <summary>
81+
/// Get process information with entrypoint information with exponential backoff on retries.
82+
/// </summary>
83+
private async Task<ProcessInfo> GetProcessInfoWithEntrypointAsync(DiagnosticsClientApiShim shim)
84+
{
85+
int retryMilliseconds = 5;
86+
int currentAttempt = 1;
87+
const int maxAttempts = 10;
88+
89+
_output.WriteLine("Getting process info with entrypoint:");
90+
while (currentAttempt <= maxAttempts)
91+
{
92+
_output.WriteLine("- Attempt {0} of {1}.", currentAttempt, maxAttempts);
93+
94+
ProcessInfo processInfo = await shim.GetProcessInfo();
95+
Assert.NotNull(processInfo);
96+
97+
if (!string.IsNullOrEmpty(processInfo.ManagedEntrypointAssemblyName))
98+
{
99+
_output.WriteLine("Got process info with entrypoint.");
100+
return processInfo;
101+
}
102+
103+
currentAttempt++;
104+
105+
if (currentAttempt != maxAttempts)
106+
{
107+
_output.WriteLine(" Waiting {0} ms.", retryMilliseconds);
108+
109+
await Task.Delay(retryMilliseconds);
110+
111+
retryMilliseconds = Math.Min(2 * retryMilliseconds, 500);
112+
}
113+
}
114+
115+
throw new InvalidOperationException("Unable to get process info with entrypoint.");
116+
}
117+
118+
private static void ValidateProcessInfo(int expectedProcessId, ProcessInfo processInfo)
119+
{
120+
Assert.NotNull(processInfo);
121+
Assert.Equal(expectedProcessId, (int)processInfo.ProcessId);
122+
Assert.NotNull(processInfo.CommandLine);
123+
Assert.NotNull(processInfo.OperatingSystem);
124+
Assert.NotNull(processInfo.ProcessArchitecture);
125+
Version clrVersion = ParseVersionRemoveLabel(processInfo.ClrProductVersionString);
126+
Assert.True(clrVersion >= new Version(6, 0, 0));
127+
}
128+
59129
private static Version ParseVersionRemoveLabel(string versionString)
60130
{
61131
Assert.NotNull(versionString);

src/tests/Microsoft.Diagnostics.NETCore.Client/RemoteTestExecution.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public static RemoteTestExecution StartProcess(string commandLine, ITestOutputHe
5353
TestRunner runner = new TestRunner(commandLine, outputHelper, redirectError: true, redirectInput: true);
5454
if (!string.IsNullOrEmpty(reversedServerTransportName))
5555
{
56-
runner.AddReversedServer(reversedServerTransportName);
56+
runner.SetDiagnosticPort(reversedServerTransportName, suspend: false);
5757
}
5858
runner.Start(testProcessTimeout: 60_000);
5959

src/tests/Microsoft.Diagnostics.NETCore.Client/ReversedServerTests.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ public async Task ReversedServerNoCreateTransportAfterDispose()
316316

317317
private ReversedDiagnosticsServer CreateReversedServer(out string transportName)
318318
{
319-
transportName = ReversedServerHelper.CreateServerTransportName();
319+
transportName = DiagnosticPortsHelper.CreateServerTransportName();
320320
_outputHelper.WriteLine("Starting reversed server at '" + transportName + "'.");
321321
return new ReversedDiagnosticsServer(transportName);
322322
}
@@ -331,7 +331,10 @@ private async Task<IpcEndpointInfo> AcceptEndpointInfo(ReversedDiagnosticsServer
331331
private TestRunner StartTracee(string transportName)
332332
{
333333
_outputHelper.WriteLine("Starting tracee.");
334-
return ReversedServerHelper.StartTracee(_outputHelper, transportName);
334+
var runner = new TestRunner(CommonHelper.GetTraceePathWithArgs(targetFramework: "net5.0"), _outputHelper);
335+
runner.SetDiagnosticPort(transportName, suspend: true);
336+
runner.Start();
337+
return runner;
335338
}
336339

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

0 commit comments

Comments
 (0)