Skip to content

Commit b002b82

Browse files
AlexWFMSalexwillCopilot
authored
Fix ASP.NET workloads crashing on Windows due to Linux-only process commands (#732)
* Fix ASP.NET workloads crashing on Windows due to Linux-only process commands AspNetServerExecutor and AspNetOrchardServerExecutor unconditionally invoked the Unix-only commands pkill and fuser (and, for Orchard, nohup). On Windows these fail to start (''The system cannot find the file specified''), so the workload crash-loops until VC hits its restart-on-crash cap and the experiment fails -- despite SupportedPlatforms declaring win-x64/win-arm64. Replace them with platform-aware branches: keep pkill/fuser/nohup on Unix and use taskkill (and a direct server-executable launch for Orchard) on Windows. Update the Windows unit tests accordingly; the Linux tests are unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document ASP.NET workload platform support Clarify that the Bombardier-based profile runs on Windows and Linux while the Wrk-based profiles are Linux-only (Wrk has no Windows build), which matches the now-functional cross-platform server executors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Bump version to 3.3.9 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: alexwill <alexwill@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3e420a6 commit b002b82

6 files changed

Lines changed: 62 additions & 16 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.3.8
1+
3.3.9

src/VirtualClient/VirtualClient.Actions.UnitTests/AspNetBench/AspNetOrchardServerExecutorTests.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,16 @@ public async Task AspNetOrchardServerExecutorRunsTheExpectedWorkloadCommandInWin
8686
await executor.ExecuteAsync(CancellationToken.None);
8787

8888
this.mockFixture.Tracking.AssertCommandsExecuted(true,
89-
"pkill OrchardCore",
90-
"fuser -n tcp -k 5014",
89+
"taskkill /F /IM OrchardCore\\.Cms\\.Web\\.exe",
9190
$"{Regex.Escape(this.mockDotNetPackage.Path)}\\\\dotnet\\.exe publish -c Release --sc -f net9\\.0 {Regex.Escape(this.mockOrchardCorePackage.Path)}\\\\src\\\\OrchardCore\\.Cms\\.Web\\\\OrchardCore\\.Cms\\.Web\\.csproj",
92-
$"nohup {Regex.Escape(this.mockOrchardCorePackage.Path)}\\\\src\\\\OrchardCore\\.Cms\\.Web\\\\bin\\\\Release\\\\net9\\.0\\\\win-x64\\\\publish\\\\OrchardCore\\.Cms\\.Web --urls http://\\*:5014"
91+
$"{Regex.Escape(this.mockOrchardCorePackage.Path)}\\\\src\\\\OrchardCore\\.Cms\\.Web\\\\bin\\\\Release\\\\net9\\.0\\\\win-x64\\\\publish\\\\OrchardCore\\.Cms\\.Web\\.exe --urls http://\\*:5014"
9392
);
9493

95-
this.mockFixture.Tracking.AssertCommandExecutedTimes("pkill", 1);
96-
this.mockFixture.Tracking.AssertCommandExecutedTimes("fuser", 1);
94+
// On Windows the Linux-only 'pkill'/'fuser'/'nohup' commands must NOT be used.
95+
this.mockFixture.Tracking.AssertCommandExecutedTimes("taskkill", 1);
96+
this.mockFixture.Tracking.AssertCommandExecutedTimes("pkill", 0);
97+
this.mockFixture.Tracking.AssertCommandExecutedTimes("fuser", 0);
98+
this.mockFixture.Tracking.AssertCommandExecutedTimes("nohup", 0);
9799
}
98100
}
99101

src/VirtualClient/VirtualClient.Actions.UnitTests/AspNetBench/AspNetServerExecutorTests.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,15 @@ public async Task AspNetServerExecutorRunsTheExpectedWorkloadCommandInWindows()
101101
await executor.ExecuteAsync(CancellationToken.None);
102102

103103
this.mockFixture.Tracking.AssertCommandsExecuted(true,
104-
"pkill dotnet",
105-
"fuser -n tcp -k 9876",
104+
"taskkill /F /IM dotnet\\.exe",
106105
$"{Regex.Escape(this.mockDotNetPackage.Path)}\\\\dotnet\\.exe build -c Release -p:BenchmarksTargetFramework=net8\\.0",
107106
$"{Regex.Escape(this.mockDotNetPackage.Path)}\\\\dotnet\\.exe {Regex.Escape(this.mockAspNetBenchPackage.Path)}\\\\src\\\\Benchmarks\\\\bin\\\\Release\\\\net8\\.0\\\\Benchmarks\\.dll --nonInteractive true --scenarios json --urls http://\\*:9876 --server Kestrel --kestrelTransport Sockets --protocol http --header \\\"Accept: application/json,text/html;q=0\\.9,application/xhtml\\+xml;q=0\\.9,application/xml;q=0\\.8,\\*/\\*;q=0\\.7\\\" --header \\\"Connection: keep-alive\\\""
108107
);
109108

110-
this.mockFixture.Tracking.AssertCommandExecutedTimes("pkill", 1);
111-
this.mockFixture.Tracking.AssertCommandExecutedTimes("fuser", 1);
109+
// On Windows the Linux-only 'pkill'/'fuser' commands must NOT be used; 'taskkill' is used instead.
110+
this.mockFixture.Tracking.AssertCommandExecutedTimes("taskkill", 1);
111+
this.mockFixture.Tracking.AssertCommandExecutedTimes("pkill", 0);
112+
this.mockFixture.Tracking.AssertCommandExecutedTimes("fuser", 0);
112113
}
113114
}
114115

src/VirtualClient/VirtualClient.Actions/ASPNET/AspNetOrchardServerExecutor.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,20 @@ private Task DeleteStateAsync(EventContext telemetryContext, CancellationToken c
354354
private async Task KillServerInstancesAsync(EventContext telemetryContext, CancellationToken cancellationToken)
355355
{
356356
this.Logger.LogTraceMessage($"{this.TypeName}.KillServerInstances");
357-
await this.ExecuteCommandAsync("pkill", "OrchardCore", this.aspnetOrchardDirectory, telemetryContext, cancellationToken);
358-
await this.ExecuteCommandAsync("fuser", $"-n tcp -k {this.ServerPort}", this.aspnetOrchardDirectory, telemetryContext, cancellationToken);
357+
358+
// 'pkill'/'fuser' exist only on Unix; on Windows they fail to start ("The system cannot
359+
// find the file specified"), which crashes the workload. On Windows the equivalent
360+
// 'taskkill' is used to terminate the self-contained OrchardCore server (which also
361+
// releases the TCP port it was holding).
362+
if (this.Platform == PlatformID.Unix)
363+
{
364+
await this.ExecuteCommandAsync("pkill", "OrchardCore", this.aspnetOrchardDirectory, telemetryContext, cancellationToken);
365+
await this.ExecuteCommandAsync("fuser", $"-n tcp -k {this.ServerPort}", this.aspnetOrchardDirectory, telemetryContext, cancellationToken);
366+
}
367+
else
368+
{
369+
await this.ExecuteCommandAsync("taskkill", "/F /IM OrchardCore.Cms.Web.exe", this.aspnetOrchardDirectory, telemetryContext, cancellationToken);
370+
}
359371

360372
await this.WaitAsync(TimeSpan.FromSeconds(3), cancellationToken);
361373
}
@@ -368,9 +380,19 @@ private void StartServerInstances(EventContext telemetryContext, CancellationTok
368380
{
369381
try
370382
{
371-
string command = "nohup";
372383
string workingDirectory = this.aspnetOrchardDirectory;
373-
string commandArguments = $"{this.Combine(this.aspnetOrchardPublishPath, "OrchardCore.Cms.Web")} --urls http://*:{this.ServerPort}";
384+
385+
// 'nohup' (run a process immune to hangups, detached) exists only on Unix. On
386+
// Windows it fails to start ("The system cannot find the file specified"), so the
387+
// self-contained OrchardCore server executable is launched directly instead.
388+
string serverExecutable = this.Combine(
389+
this.aspnetOrchardPublishPath,
390+
this.Platform == PlatformID.Unix ? "OrchardCore.Cms.Web" : "OrchardCore.Cms.Web.exe");
391+
392+
string command = this.Platform == PlatformID.Unix ? "nohup" : serverExecutable;
393+
string commandArguments = this.Platform == PlatformID.Unix
394+
? $"{serverExecutable} --urls http://*:{this.ServerPort}"
395+
: $"--urls http://*:{this.ServerPort}";
374396

375397
relatedContext.AddContext("command", command);
376398
relatedContext.AddContext("commandArguments", commandArguments);

src/VirtualClient/VirtualClient.Actions/ASPNET/AspNetServerExecutor.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,21 @@ private async Task KillServerInstancesAsync(EventContext telemetryContext, Cance
366366
{
367367
this.Logger.LogTraceMessage($"{this.TypeName}.KillServerInstances");
368368

369-
await this.ExecuteCommandAsync("pkill", "dotnet", this.aspnetBenchDirectory, telemetryContext, cancellationToken);
369+
// The ASP.NET (Kestrel) benchmark server is hosted by the .NET runtime (dotnet). Both the
370+
// server process(es) and the TCP port they hold must be released using platform-native
371+
// commands. 'pkill'/'fuser' exist only on Unix; on Windows they fail to start
372+
// ("The system cannot find the file specified"), which crashes the workload. On Windows the
373+
// equivalent 'taskkill' is used instead (killing the process also releases its port).
374+
if (this.Platform == PlatformID.Unix)
375+
{
376+
await this.ExecuteCommandAsync("pkill", "dotnet", this.aspnetBenchDirectory, telemetryContext, cancellationToken);
370377

371-
await this.ExecuteCommandAsync("fuser", $"-n tcp -k {this.ServerPort}", this.aspnetBenchDirectory, telemetryContext, cancellationToken);
378+
await this.ExecuteCommandAsync("fuser", $"-n tcp -k {this.ServerPort}", this.aspnetBenchDirectory, telemetryContext, cancellationToken);
379+
}
380+
else
381+
{
382+
await this.ExecuteCommandAsync("taskkill", "/F /IM dotnet.exe", this.aspnetBenchDirectory, telemetryContext, cancellationToken);
383+
}
372384

373385
await this.WaitAsync(TimeSpan.FromSeconds(3), cancellationToken);
374386
}

website/docs/workloads/aspnetbench/aspnetbench.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ server process to be pinned to specific CPU cores for controlled performance mea
2222
* [Bombardier Documentation](../bombardier/bombardier.md)
2323
* [Wrk/Wrk2 Documentation](../wrk/wrk.md)
2424

25+
## Platform Support
26+
The ASP.NET Kestrel and OrchardCore server components run on all supported platforms (`linux-x64`, `linux-arm64`,
27+
`win-x64`, `win-arm64`). Each profile's overall platform support is determined by the HTTP client (load generator) it uses:
28+
29+
* **Bombardier** is cross-platform, so `PERF-WEB-ASPNET-BOMBARDIER` runs on both Linux and Windows
30+
(`linux-x64`, `linux-arm64`, `win-x64`, `win-arm64`).
31+
* **Wrk** is Linux-only, so the Wrk-based profiles (`PERF-WEB-ASPNET-WRK`, `PERF-WEB-ASPNET-WRK-AFFINITY`,
32+
`PERF-WEB-ASPNET-ORCHARD-WRK`) run on `linux-x64` and `linux-arm64` only.
33+
2534
## What is Being Measured?
2635
The client tools (Bombardier, Wrk or Wrk2) generate concurrent HTTP requests against the ASP.NET server and capture latency
2736
percentile distributions and throughput statistics.

0 commit comments

Comments
 (0)