1
- using System . Threading ;
2
- using System . Threading . Tasks ;
3
- using VirtualClient . Common . Telemetry ;
1
+ using System . IO ;
4
2
5
3
namespace VirtualClient . Actions . Wrathmark
6
4
{
7
5
using System ;
8
6
using System . Collections . Generic ;
7
+ using System . Threading ;
8
+ using System . Threading . Tasks ;
9
+
9
10
using Microsoft . Extensions . DependencyInjection ;
10
- using Polly . Caching ;
11
11
12
12
using VirtualClient . Common ;
13
13
using VirtualClient . Common . Extensions ;
14
+ using VirtualClient . Common . Telemetry ;
14
15
using VirtualClient . Contracts ;
15
16
16
17
/// <summary>
@@ -20,6 +21,10 @@ public class WrathmarkWorkloadExecutor : VirtualClientComponent
20
21
{
21
22
private readonly ISystemManagement systemManagement ;
22
23
private readonly ProcessManager processManager ;
24
+ private readonly IPackageManager packageManager ;
25
+
26
+ private string benchmarkDirectory ;
27
+ private string dotnetExePath ;
23
28
24
29
/// <summary>
25
30
/// Initializes a new instance of the <see cref="WrathmarkWorkloadExecutor"/> class.
@@ -34,8 +39,21 @@ public WrathmarkWorkloadExecutor(
34
39
// Follows the example, but is wrong. The services should be injected to this class, not resolved here
35
40
this . systemManagement = dependencies . GetService < ISystemManagement > ( ) ;
36
41
this . processManager = this . systemManagement . ProcessManager ;
42
+ this . packageManager = this . systemManagement . PackageManager ;
37
43
}
38
44
45
+ /// <summary>
46
+ /// The name of the package where the DotNetSDK package is downloaded.
47
+ /// </summary>
48
+ public string DotNetSdkPackageName => this . Parameters . GetValue < string > ( nameof ( WrathmarkWorkloadExecutor . DotNetSdkPackageName ) , "dotnetsdk" ) ;
49
+
50
+ /// <summary>
51
+ /// The name of the package where the AspNetBench package is downloaded.
52
+ /// </summary>
53
+ public string TargetFramework =>
54
+ // Lower case to prevent build path issue.
55
+ this . Parameters . GetValue < string > ( nameof ( WrathmarkWorkloadExecutor . TargetFramework ) ) . ToLower ( ) ;
56
+
39
57
/// <summary>
40
58
/// Name of the tool.
41
59
/// </summary>
@@ -51,32 +69,125 @@ public WrathmarkWorkloadExecutor(
51
69
/// </summary>
52
70
protected string WorkloadExecutablePath { get ; }
53
71
72
+ /// <inheritdoc />
73
+ protected override async Task InitializeAsync ( EventContext telemetryContext , CancellationToken cancellationToken )
74
+ {
75
+ DependencyPath workloadPackage = await this . packageManager . GetPackageAsync (
76
+ this . PackageName ,
77
+ cancellationToken ) ;
78
+
79
+ if ( workloadPackage == null )
80
+ {
81
+ throw new DependencyException (
82
+ $ "The expected package '{ this . PackageName } ' does not exist on the system or is not registered.",
83
+ ErrorReason . WorkloadDependencyMissing ) ;
84
+ }
85
+
86
+ const string RelativeBenchmarkPath = "wrath-sharp" ;
87
+ this . benchmarkDirectory = this . Combine ( workloadPackage . Path , RelativeBenchmarkPath ) ;
88
+
89
+ DependencyPath dotnetSdkPackage = await this . packageManager . GetPackageAsync (
90
+ this . DotNetSdkPackageName ,
91
+ cancellationToken )
92
+ . ConfigureAwait ( false ) ;
93
+
94
+ if ( dotnetSdkPackage == null )
95
+ {
96
+ throw new DependencyException (
97
+ $ "The expected package '{ this . DotNetSdkPackageName } ' does not exist on the system or is not registered.",
98
+ ErrorReason . WorkloadDependencyMissing ) ;
99
+ }
100
+
101
+ this . dotnetExePath = this . Combine ( dotnetSdkPackage . Path , this . Platform == PlatformID . Unix ? "dotnet" : "dotnet.exe" ) ;
102
+
103
+ // Build the wrath sharp project
104
+ // To make native libraries that can be used, enumerate the SupportedPlatforms metadata and call publish for each
105
+ // Outputs
106
+ // bin/Release/net6.0/linux-x64/wrath-sharp.dll
107
+ // bin/Release/net6.0/linux-x64/publish/wrath-sharp.dll
108
+ string publishArgument = $ "publish -c Release -r { this . PlatformArchitectureName } -f { this . TargetFramework } /p:UseSharedCompilation=false /p:BuildInParallel=false /m:1 /p:Deterministic=true /p:Optimize=true";
109
+ await this . ExecuteCommandAsync (
110
+ this . dotnetExePath ,
111
+ publishArgument ,
112
+ this . benchmarkDirectory ,
113
+ cancellationToken )
114
+ . ConfigureAwait ( false ) ;
115
+ }
116
+
54
117
/// <summary>
55
118
/// Executes the Wrathmark component logic.
56
119
/// </summary>
57
120
/// <param name="telemetryContext">Provides context information that will be captured with telemetry events.</param>
58
121
/// <param name="cancellationToken">A token that can be used to cancel the operation.</param>
59
122
protected override async Task ExecuteAsync ( EventContext telemetryContext , CancellationToken cancellationToken )
60
123
{
124
+ // Example: ./bin/Release/net6.0/linux-x64/publish
125
+ string outputDirectory = Path . Combine (
126
+ this . benchmarkDirectory ,
127
+ "bin" ,
128
+ "Release" ,
129
+ this . TargetFramework ,
130
+ this . PlatformArchitectureName ,
131
+ "Publish" ) ;
132
+
61
133
this . Logger . LogTraceMessage (
62
134
$ "{ nameof ( WrathmarkWorkloadExecutor ) } .Starting",
63
135
telemetryContext ) ;
64
136
65
137
var startTime = DateTime . UtcNow ;
66
138
67
- var results = await this . ExecuteWorkloadAsync ( telemetryContext , cancellationToken ) ;
139
+ var results = await this . ExecuteWorkloadAsync (
140
+ this . dotnetExePath ,
141
+ $ "run { outputDirectory } --framework ${ this . TargetFramework } ",
142
+ this . benchmarkDirectory ,
143
+ telemetryContext ,
144
+ cancellationToken ) ;
68
145
69
146
var endTime = DateTime . UtcNow ;
70
147
71
148
await this . CaptureMetricsAsync ( results , startTime , endTime , telemetryContext , cancellationToken ) ;
72
149
}
73
150
74
- private Task < string > ExecuteWorkloadAsync ( EventContext telemetryContext , CancellationToken cancellationToken )
151
+ private async Task ExecuteCommandAsync (
152
+ string pathToExe ,
153
+ string commandLineArguments ,
154
+ string workingDirectory ,
155
+ CancellationToken cancellationToken )
156
+ {
157
+ if ( ! cancellationToken . IsCancellationRequested )
158
+ {
159
+ this . Logger . LogTraceMessage (
160
+ $ "Executing process '{ pathToExe } ' '{ commandLineArguments } ' at directory '{ workingDirectory } '.") ;
161
+
162
+ EventContext telemetryContext = EventContext . Persisted ( )
163
+ . AddContext ( "command" , pathToExe )
164
+ . AddContext ( "commandArguments" , commandLineArguments ) ;
165
+
166
+ using ( IProcessProxy process =
167
+ this . systemManagement . ProcessManager . CreateElevatedProcess (
168
+ this . Platform ,
169
+ pathToExe ,
170
+ commandLineArguments ,
171
+ workingDirectory ) )
172
+ {
173
+ this . CleanupTasks . Add ( ( ) => process . SafeKill ( ) ) ;
174
+ await process . StartAndWaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
175
+
176
+ if ( ! cancellationToken . IsCancellationRequested )
177
+ {
178
+ await this . LogProcessDetailsAsync ( process , telemetryContext ) ;
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ private Task < string > ExecuteWorkloadAsync ( string command , string arguments , string workingDirectory , EventContext telemetryContext , CancellationToken cancellationToken )
75
185
{
76
186
EventContext relatedContext = telemetryContext . Clone ( )
77
187
. AddContext ( "packageName" , this . PackageName )
78
- . AddContext ( "packagePath" , this . WorkloadPackage . Path )
79
- . AddContext ( "command" , this . WorkloadExecutablePath )
188
+ . AddContext ( "packagePath" , workingDirectory )
189
+ . AddContext ( "command" , command )
190
+ . AddContext ( "arguments" , arguments )
80
191
;
81
192
82
193
return this . Logger . LogMessageAsync ( $ "{ nameof ( WrathmarkWorkloadExecutor ) } .ExecuteWorkload", relatedContext , async ( ) =>
@@ -88,7 +199,7 @@ private Task<string> ExecuteWorkloadAsync(EventContext telemetryContext, Cancell
88
199
{
89
200
// We create a operating system process to host the executing workload, start it and
90
201
// wait for it to exit.
91
- using ( IProcessProxy workloadProcess = this . processManager . CreateProcess ( this . WorkloadExecutablePath , null , this . WorkloadPackage . Path ) )
202
+ using ( IProcessProxy workloadProcess = this . processManager . CreateProcess ( command , arguments , workingDirectory ) )
92
203
{
93
204
this . CleanupTasks . Add ( ( ) => workloadProcess . SafeKill ( ) ) ;
94
205
0 commit comments