Skip to content

Commit 3ffe2e9

Browse files
AlexW-FAlex Williams-Ferreiraericavella
authored
Add tuning parameters to stressapttest to replace blank commandline in profile (#673)
Co-authored-by: Alex Williams-Ferreira <alexwill@microsoft.com> Co-authored-by: erica vellanoweth <37354999+ericavella@users.noreply.github.com>
1 parent dd0fbc4 commit 3ffe2e9

4 files changed

Lines changed: 279 additions & 28 deletions

File tree

src/VirtualClient/VirtualClient.Actions.UnitTests/StressAppTest/StressAppTestExecutorTests.cs

Lines changed: 180 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ public void SetupTest(PlatformID platform)
4747
this.mockFixture.Parameters = new Dictionary<string, IConvertible>()
4848
{
4949
{ nameof(StressAppTestExecutor.PackageName), "stressapptest" },
50-
{ nameof(StressAppTestExecutor.CommandLine), "" },
5150
{ nameof(StressAppTestExecutor.Scenario), "ApplyStress" },
5251
{ nameof(StressAppTestExecutor.Duration), "00:01:00" },
5352
{ nameof(StressAppTestExecutor.UseCpuStressfulMemoryCopy), false }
@@ -161,14 +160,52 @@ public void StressAppTestExecutorThrowsOnInvalidProfileDefinition(PlatformID pla
161160
}
162161

163162
this.mockFixture.Parameters[nameof(StressAppTestExecutor.Scenario)] = "ApplyStress";
164-
this.mockFixture.Parameters[nameof(StressAppTestExecutor.CommandLine)] = "-l logfile.txt";
163+
this.mockFixture.Parameters[nameof(StressAppTestExecutor.Duration)] = "00:00:00";
165164
using (TestStressAppTestExecutor executor = new TestStressAppTestExecutor(this.mockFixture))
166165
{
167166
Assert.Throws<WorkloadException>(() => executor.Validate());
168167
}
168+
}
169169

170-
this.mockFixture.Parameters[nameof(StressAppTestExecutor.CommandLine)] = "";
171-
this.mockFixture.Parameters[nameof(StressAppTestExecutor.Duration)] = "00:00:00";
170+
[Test]
171+
[TestCase(PlatformID.Unix)]
172+
public void StressAppTestExecutorThrowsWhenMemoryInMBIsInvalid(PlatformID platform)
173+
{
174+
this.SetupTest(platform);
175+
176+
this.mockFixture.Parameters[nameof(StressAppTestExecutor.MemoryInMB)] = 0;
177+
using (TestStressAppTestExecutor executor = new TestStressAppTestExecutor(this.mockFixture))
178+
{
179+
Assert.Throws<WorkloadException>(() => executor.Validate());
180+
}
181+
182+
this.mockFixture.Parameters[nameof(StressAppTestExecutor.MemoryInMB)] = -1;
183+
using (TestStressAppTestExecutor executor = new TestStressAppTestExecutor(this.mockFixture))
184+
{
185+
Assert.Throws<WorkloadException>(() => executor.Validate());
186+
}
187+
}
188+
189+
[Test]
190+
[TestCase(PlatformID.Unix)]
191+
public void StressAppTestExecutorThrowsWhenThreadCountIsInvalid(PlatformID platform)
192+
{
193+
this.SetupTest(platform);
194+
195+
this.mockFixture.Parameters[nameof(StressAppTestExecutor.ThreadCount)] = 0;
196+
using (TestStressAppTestExecutor executor = new TestStressAppTestExecutor(this.mockFixture))
197+
{
198+
Assert.Throws<WorkloadException>(() => executor.Validate());
199+
}
200+
}
201+
202+
[Test]
203+
[TestCase(PlatformID.Unix)]
204+
public void StressAppTestExecutorThrowsWhenCpuStressThreadCountIsInvalid(PlatformID platform)
205+
{
206+
this.SetupTest(platform);
207+
208+
this.mockFixture.Parameters[nameof(StressAppTestExecutor.CpuStressThreadCount)] = -1;
172209
using (TestStressAppTestExecutor executor = new TestStressAppTestExecutor(this.mockFixture))
173210
{
174211
Assert.Throws<WorkloadException>(() => executor.Validate());
@@ -241,6 +278,145 @@ public void StressAppTestExecutorSupportsIntegerAndTimeSpanDurationFormats(Platf
241278
Assert.AreEqual(integerBasedDuration, timespanBasedDuration);
242279
}
243280

281+
[Test]
282+
[TestCase(PlatformID.Unix, @"/linux-x64/stressapptest")]
283+
public async Task StressAppTestExecutorIncludesMemoryParameterInCommandLine(PlatformID platform, string command)
284+
{
285+
this.SetupTest(platform);
286+
this.mockFixture.Parameters[nameof(StressAppTestExecutor.MemoryInMB)] = 1024;
287+
288+
using (TestStressAppTestExecutor executor = new TestStressAppTestExecutor(this.mockFixture))
289+
{
290+
bool commandExecuted = false;
291+
this.mockFixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDirectory) =>
292+
{
293+
if (arguments.Contains("-M 1024"))
294+
{
295+
commandExecuted = true;
296+
}
297+
298+
return new InMemoryProcess
299+
{
300+
StartInfo = new ProcessStartInfo
301+
{
302+
FileName = exe,
303+
Arguments = arguments
304+
},
305+
ExitCode = 0,
306+
OnStart = () => true,
307+
OnHasExited = () => true
308+
};
309+
};
310+
311+
await executor.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
312+
Assert.IsTrue(commandExecuted);
313+
}
314+
}
315+
316+
[Test]
317+
[TestCase(PlatformID.Unix, @"/linux-x64/stressapptest")]
318+
public async Task StressAppTestExecutorIncludesThreadCountParameterInCommandLine(PlatformID platform, string command)
319+
{
320+
this.SetupTest(platform);
321+
this.mockFixture.Parameters[nameof(StressAppTestExecutor.ThreadCount)] = 8;
322+
323+
using (TestStressAppTestExecutor executor = new TestStressAppTestExecutor(this.mockFixture))
324+
{
325+
bool commandExecuted = false;
326+
this.mockFixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDirectory) =>
327+
{
328+
if (arguments.Contains("-m 8"))
329+
{
330+
commandExecuted = true;
331+
}
332+
333+
return new InMemoryProcess
334+
{
335+
StartInfo = new ProcessStartInfo
336+
{
337+
FileName = exe,
338+
Arguments = arguments
339+
},
340+
ExitCode = 0,
341+
OnStart = () => true,
342+
OnHasExited = () => true
343+
};
344+
};
345+
346+
await executor.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
347+
Assert.IsTrue(commandExecuted);
348+
}
349+
}
350+
351+
[Test]
352+
[TestCase(PlatformID.Unix, @"/linux-x64/stressapptest")]
353+
public async Task StressAppTestExecutorIncludesCpuStressThreadCountParameterInCommandLine(PlatformID platform, string command)
354+
{
355+
this.SetupTest(platform);
356+
this.mockFixture.Parameters[nameof(StressAppTestExecutor.CpuStressThreadCount)] = 4;
357+
358+
using (TestStressAppTestExecutor executor = new TestStressAppTestExecutor(this.mockFixture))
359+
{
360+
bool commandExecuted = false;
361+
this.mockFixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDirectory) =>
362+
{
363+
if (arguments.Contains("-C 4"))
364+
{
365+
commandExecuted = true;
366+
}
367+
368+
return new InMemoryProcess
369+
{
370+
StartInfo = new ProcessStartInfo
371+
{
372+
FileName = exe,
373+
Arguments = arguments
374+
},
375+
ExitCode = 0,
376+
OnStart = () => true,
377+
OnHasExited = () => true
378+
};
379+
};
380+
381+
await executor.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
382+
Assert.IsTrue(commandExecuted);
383+
}
384+
}
385+
386+
[Test]
387+
[TestCase(PlatformID.Unix, @"/linux-x64/stressapptest")]
388+
public async Task StressAppTestExecutorOmitsOptionalParametersWhenNotSpecified(PlatformID platform, string command)
389+
{
390+
this.SetupTest(platform);
391+
392+
using (TestStressAppTestExecutor executor = new TestStressAppTestExecutor(this.mockFixture))
393+
{
394+
bool commandCorrect = false;
395+
this.mockFixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDirectory) =>
396+
{
397+
if (!arguments.Contains("-M") && !arguments.Contains("-m ") && !arguments.Contains("-C"))
398+
{
399+
commandCorrect = true;
400+
}
401+
402+
return new InMemoryProcess
403+
{
404+
StartInfo = new ProcessStartInfo
405+
{
406+
FileName = exe,
407+
Arguments = arguments
408+
},
409+
ExitCode = 0,
410+
OnStart = () => true,
411+
OnHasExited = () => true
412+
};
413+
};
414+
415+
await executor.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
416+
Assert.IsTrue(commandCorrect);
417+
}
418+
}
419+
244420
private class TestStressAppTestExecutor : StressAppTestExecutor
245421
{
246422
public TestStressAppTestExecutor(MockFixture fixture)

src/VirtualClient/VirtualClient.Actions/StressAppTest/StressAppTestExecutor.cs

Lines changed: 82 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,35 +40,72 @@ public StressAppTestExecutor(IServiceCollection dependencies, IDictionary<string
4040
}
4141

4242
/// <summary>
43-
/// The command line argument defined in the profile.
43+
/// The duration of the StressAppTest workload.
4444
/// </summary>
45-
public string CommandLine
45+
public TimeSpan Duration
4646
{
4747
get
4848
{
49-
return this.Parameters.GetValue<string>(nameof(StressAppTestExecutor.CommandLine));
49+
return this.Parameters.GetTimeSpanValue(nameof(StressAppTestExecutor.Duration), TimeSpan.FromSeconds(60));
5050
}
5151
}
5252

5353
/// <summary>
54-
/// The duration of the StressAppTest workload.
54+
/// Switch to toggle StressAppTest built-in option to use more CPU-Stressful memory copy (-W flag).
5555
/// </summary>
56-
public TimeSpan Duration
56+
public bool UseCpuStressfulMemoryCopy
5757
{
5858
get
5959
{
60-
return this.Parameters.GetTimeSpanValue(nameof(StressAppTestExecutor.Duration), TimeSpan.FromSeconds(60));
60+
return this.Parameters.GetValue<bool>(nameof(StressAppTestExecutor.UseCpuStressfulMemoryCopy));
6161
}
6262
}
6363

6464
/// <summary>
65-
/// The UseCpuStressfulMemoryCopy argument defined in the profile, Switch to toggle StressAppTest built-in option to use more CPU-Stressful memory copy
65+
/// Optional. Megabytes of RAM to test (-M flag). When not specified, stressapptest auto-detects (~94% of system memory).
6666
/// </summary>
67-
public bool UseCpuStressfulMemoryCopy
67+
public int? MemoryInMB
6868
{
6969
get
7070
{
71-
return this.Parameters.GetValue<bool>(nameof(StressAppTestExecutor.UseCpuStressfulMemoryCopy));
71+
if (this.Parameters.TryGetValue(nameof(StressAppTestExecutor.MemoryInMB), out IConvertible value) && value != null)
72+
{
73+
return value.ToInt32(null);
74+
}
75+
76+
return null;
77+
}
78+
}
79+
80+
/// <summary>
81+
/// Optional. Number of memory copy threads to run (-m flag). When not specified, stressapptest defaults to 2 threads per CPU.
82+
/// </summary>
83+
public int? ThreadCount
84+
{
85+
get
86+
{
87+
if (this.Parameters.TryGetValue(nameof(StressAppTestExecutor.ThreadCount), out IConvertible value) && value != null)
88+
{
89+
return value.ToInt32(null);
90+
}
91+
92+
return null;
93+
}
94+
}
95+
96+
/// <summary>
97+
/// Optional. Number of CPU stress threads to run (-C flag). When not specified, no CPU stress threads are added.
98+
/// </summary>
99+
public int? CpuStressThreadCount
100+
{
101+
get
102+
{
103+
if (this.Parameters.TryGetValue(nameof(StressAppTestExecutor.CpuStressThreadCount), out IConvertible value) && value != null)
104+
{
105+
return value.ToInt32(null);
106+
}
107+
108+
return null;
72109
}
73110
}
74111

@@ -139,17 +176,29 @@ protected override void Validate()
139176
if (this.Duration <= TimeSpan.Zero)
140177
{
141178
throw new WorkloadException(
142-
$"Unexpected profile definition.The action in the profile does not contain the " +
143-
$"required value for'{nameof(this.Duration)}' arguments defined. {nameof(this.Duration)} should be greater than 0",
179+
$"Unexpected profile definition. The action in the profile does not contain the " +
180+
$"required value for '{nameof(this.Duration)}' arguments defined. {nameof(this.Duration)} should be greater than 0.",
181+
ErrorReason.InvalidProfileDefinition);
182+
}
183+
184+
if (this.MemoryInMB.HasValue && this.MemoryInMB.Value <= 0)
185+
{
186+
throw new WorkloadException(
187+
$"Unexpected profile definition. The '{nameof(this.MemoryInMB)}' parameter must be greater than 0.",
188+
ErrorReason.InvalidProfileDefinition);
189+
}
190+
191+
if (this.ThreadCount.HasValue && this.ThreadCount.Value <= 0)
192+
{
193+
throw new WorkloadException(
194+
$"Unexpected profile definition. The '{nameof(this.ThreadCount)}' parameter must be greater than 0.",
144195
ErrorReason.InvalidProfileDefinition);
145196
}
146197

147-
if (this.CommandLine.Contains("-l"))
198+
if (this.CpuStressThreadCount.HasValue && this.CpuStressThreadCount.Value < 0)
148199
{
149200
throw new WorkloadException(
150-
$"Unexpected profile definition.The action in the profile does not contain the " +
151-
$"required value for'{nameof(this.CommandLine)}' arguments defined. {nameof(this.CommandLine)} should not contain a custom log file, with " +
152-
$"-l parameter. That is being appended programatically",
201+
$"Unexpected profile definition. The '{nameof(this.CpuStressThreadCount)}' parameter must be greater than or equal to 0.",
153202
ErrorReason.InvalidProfileDefinition);
154203
}
155204
}
@@ -163,13 +212,28 @@ private async Task ExecuteWorkloadAsync(EventContext telemetryContext, Cancellat
163212
{
164213
using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken))
165214
{
166-
string commandLineArguments = this.CommandLine;
167-
commandLineArguments += " -s " + this.Duration.TotalSeconds;
168-
if (this.UseCpuStressfulMemoryCopy && !commandLineArguments.Contains("-W"))
215+
string commandLineArguments = "-s " + this.Duration.TotalSeconds;
216+
217+
if (this.UseCpuStressfulMemoryCopy)
169218
{
170219
commandLineArguments += " -W";
171220
}
172221

222+
if (this.MemoryInMB.HasValue)
223+
{
224+
commandLineArguments += " -M " + this.MemoryInMB.Value;
225+
}
226+
227+
if (this.ThreadCount.HasValue)
228+
{
229+
commandLineArguments += " -m " + this.ThreadCount.Value;
230+
}
231+
232+
if (this.CpuStressThreadCount.HasValue)
233+
{
234+
commandLineArguments += " -C " + this.CpuStressThreadCount.Value;
235+
}
236+
173237
// Example command with arguments: ./stressapptest -s 60 -l stressapptestLogs_202301131037407031.txt
174238
string resultsFileName = $"stressapptestLogs_{DateTime.UtcNow.ToString("yyyyMMddHHmmssffff")}.txt";
175239
commandLineArguments += " -l " + resultsFileName;

src/VirtualClient/VirtualClient.Main/profiles/PERF-MEM-STRESSAPPTEST.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@
77
},
88
"Parameters": {
99
"Duration": "00:01:00",
10-
"UseCpuStressfulMemoryCopy": false
10+
"UseCpuStressfulMemoryCopy": false,
11+
"MemoryInMB": null,
12+
"ThreadCount": null,
13+
"CpuStressThreadCount": null
1114
},
1215
"Actions": [
1316
{
1417
"Type": "StressAppTestExecutor",
1518
"Parameters": {
1619
"Scenario": "ApplySystemStress",
17-
"CommandLine": "",
1820
"Duration": "$.Parameters.Duration",
1921
"UseCpuStressfulMemoryCopy": "$.Parameters.UseCpuStressfulMemoryCopy",
22+
"MemoryInMB": "$.Parameters.MemoryInMB",
23+
"ThreadCount": "$.Parameters.ThreadCount",
24+
"CpuStressThreadCount": "$.Parameters.CpuStressThreadCount",
2025
"PackageName": "stressapptest"
2126
}
2227
}

0 commit comments

Comments
 (0)