Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.3.25
3.3.26
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ private IEnumerable<string> GetLZBenchProfileExpectedCommands(PlatformID platfor
$"sudo make",
$"wget https://sun.aei.polsl.pl//~sdeor/corpus/silesia.zip",
$"sudo unzip silesia.zip -d silesia",
$"sudo bash \"/home/user/tools/VirtualClient/scripts/lzbench/lzbenchexecutor.sh\" \"-t16,16 -eall -o4 -r /home/user/tools/VirtualClient/packages/lzbench/silesia\""
$"bash \"/home/user/tools/VirtualClient/scripts/lzbench/lzbenchexecutor.sh\" \"-t16,16 -eall -o4 -r /home/user/tools/VirtualClient/packages/lzbench/silesia\" \"/home/user/tools/VirtualClient/temp/lzbench/results-summary.csv\""
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public void SetupDefaultBehavior()
this.mockFixture.File.Reset();
this.mockFixture.File.Setup(f => f.Exists(It.IsAny<string>()))
.Returns(true);
this.mockFixture.File.Setup(f => f.ReadAllTextAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(MockFixture.ReadFile(MockFixture.ExamplesDirectory, "Lzbench", "LzbenchResultsExample.csv"));
this.mockFixture.Directory.Setup(f => f.Exists(It.IsAny<string>()))
.Returns(true);

Expand Down Expand Up @@ -164,7 +166,7 @@ public async Task LzbenchExecutorRunsTheExpectedWorkloadCommand()
};
string mockPackagePath = this.mockPackage.Path;

string expectedCommand = $"sudo bash \"{this.mockFixture.PlatformSpecifics.GetScriptPath("lzbench", "lzbenchexecutor.sh")}\" \"testOption1 testOption2 {this.mockFixture.PlatformSpecifics.Combine(mockPackagePath, "silesia")}\"";
string expectedCommand = $"bash \"{this.mockFixture.PlatformSpecifics.GetScriptPath("lzbench", "lzbenchexecutor.sh")}\" \"testOption1 testOption2 {this.mockFixture.PlatformSpecifics.Combine(mockPackagePath, "silesia")}\" \"{this.mockFixture.PlatformSpecifics.GetTempPath("lzbench", "results-summary.csv")}\"";

bool commandExecuted = false;
this.mockFixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDir) =>
Expand Down Expand Up @@ -215,7 +217,7 @@ public async Task LzbenchExecutorExecutesTheCorrectCommandsWithInstallationIfInp
$"sudo make",
$"sudo wget https://sun.aei.polsl.pl//~sdeor/corpus/silesia.zip",
$"sudo unzip silesia.zip -d silesia",
$"sudo bash \"{this.mockFixture.PlatformSpecifics.GetScriptPath("lzbench", "lzbenchexecutor.sh")}\" \"testOption1 testOption2 {this.mockFixture.PlatformSpecifics.Combine(mockPackagePath, "silesia")}\""
$"bash \"{this.mockFixture.PlatformSpecifics.GetScriptPath("lzbench", "lzbenchexecutor.sh")}\" \"testOption1 testOption2 {this.mockFixture.PlatformSpecifics.Combine(mockPackagePath, "silesia")}\" \"{this.mockFixture.PlatformSpecifics.GetTempPath("lzbench", "results-summary.csv")}\""
};

int processCount = 0;
Expand Down Expand Up @@ -260,7 +262,7 @@ public async Task LzbenchExecutorExecutesTheCorrectCommandsWithInstallationIfInp
$"sudo rm -rf \"{mockPackagePath}\"",
$"sudo git clone -b v1.8.1 https://github.com/inikep/lzbench.git",
$"sudo make",
$"sudo bash \"{this.mockFixture.PlatformSpecifics.GetScriptPath("lzbench", "lzbenchexecutor.sh")}\" \"testOption1 testOption2 Test1.zip Test2.txt\"",
$"bash \"{this.mockFixture.PlatformSpecifics.GetScriptPath("lzbench", "lzbenchexecutor.sh")}\" \"testOption1 testOption2 Test1.zip Test2.txt\" \"{this.mockFixture.PlatformSpecifics.GetTempPath("lzbench", "results-summary.csv")}\"",
};

int processCount = 0;
Expand Down Expand Up @@ -302,7 +304,7 @@ public async Task LzbenchExecutorSkipsInitializationOfTheWorkloadForExecutionAft
string mockPackagePath = this.mockPackage.Path;
List<string> expectedCommands = new List<string>
{
$"sudo bash \"{this.mockFixture.PlatformSpecifics.GetScriptPath("lzbench", "lzbenchexecutor.sh")}\" \"testOption1 testOption2 Test1.zip Test2.txt\""
$"bash \"{this.mockFixture.PlatformSpecifics.GetScriptPath("lzbench", "lzbenchexecutor.sh")}\" \"testOption1 testOption2 Test1.zip Test2.txt\" \"{this.mockFixture.PlatformSpecifics.GetTempPath("lzbench", "results-summary.csv")}\""
};

int processCount = 0;
Expand Down Expand Up @@ -337,6 +339,69 @@ public async Task LzbenchExecutorSkipsInitializationOfTheWorkloadForExecutionAft
Assert.IsTrue(processCount == 1);
}

[Test]
public async Task LzbenchExecutorCreatesAndCleansUpResultsUsingTheCurrentUser()
{
string resultsDirectory = this.mockFixture.PlatformSpecifics.GetTempPath("lzbench");
string resultsFile = this.mockFixture.PlatformSpecifics.Combine(resultsDirectory, "results-summary.csv");
this.mockFixture.Directory.Setup(directory => directory.Exists(resultsDirectory)).Returns(false);
this.mockFixture.StateManager.OnGetState().ReturnsAsync(JObject.FromObject(new LzbenchExecutor.LzbenchState()
{
LzbenchInitialized = true
}));

using (TestLzbenchExecutor executor = new TestLzbenchExecutor(this.mockFixture.Dependencies, this.mockFixture.Parameters))
{
await executor.ExecuteAsync(CancellationToken.None).ConfigureAwait(false);
}

this.mockFixture.Directory.Verify(directory => directory.CreateDirectory(resultsDirectory), Times.Once);
this.mockFixture.File.Verify(file => file.Delete(resultsFile), Times.Once);
}

[Test]
public async Task LzbenchExecutorReportsCancellationAsAWorkloadFailure()
{
using (CancellationTokenSource cancellationSource = new CancellationTokenSource())
{
this.mockFixture.StateManager.OnGetState().ReturnsAsync(JObject.FromObject(new LzbenchExecutor.LzbenchState()
{
LzbenchInitialized = true
}));
this.mockFixture.ProcessManager.OnCreateProcess = (exe, arguments, workingDir) =>
{
return new InMemoryProcess
{
StartInfo = new ProcessStartInfo
{
FileName = exe,
Arguments = arguments
},
ExitCode = 0,
OnStart = () =>
{
cancellationSource.Cancel();
return true;
},
OnHasExited = () => true
};
};

using (TestLzbenchExecutor executor = new TestLzbenchExecutor(this.mockFixture.Dependencies, this.mockFixture.Parameters))
{
await executor.ExecuteAsync(cancellationSource.Token).ConfigureAwait(false);
}

IEnumerable<string> statusMetrics = this.mockFixture.Logger
.Where(entry => entry.Item3 is EventContext context
&& context.Properties.ContainsKey("metricName"))
.Select(entry => ((EventContext)entry.Item3).Properties["metricName"].ToString());

CollectionAssert.Contains(statusMetrics, "Failed");
CollectionAssert.DoesNotContain(statusMetrics, "Succeeded");
}
}

private class TestLzbenchExecutor : LzbenchExecutor
{
public TestLzbenchExecutor(IServiceCollection dependencies, IDictionary<string, IConvertible> parameters)
Expand Down
99 changes: 65 additions & 34 deletions src/VirtualClient/VirtualClient.Actions/Lzbench/LzbenchExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ namespace VirtualClient.Actions
{
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -101,32 +99,71 @@ private string LzbenchScriptPath
}
}

private string ResultsDirectory
{
get
{
return this.PlatformSpecifics.GetTempPath("lzbench");
}
}

private string ResultsFilePath
{
get
{
return this.Combine(this.ResultsDirectory, "results-summary.csv");
}
}

/// <summary>
/// Executes the Lzbench workload.
/// </summary>
protected override async Task ExecuteAsync(EventContext telemetryContext, CancellationToken cancellationToken)
{
using (BackgroundOperations profiling = BackgroundOperations.BeginProfiling(this, cancellationToken))
{
if (!this.fileSystem.Directory.Exists(this.ResultsDirectory))
{
this.fileSystem.Directory.CreateDirectory(this.ResultsDirectory);
}

string commandLineArguments = this.GetCommandLineArguments();

// Note:
// We are attempting to add in a common method for execution of commands as we have seen throughout many executors.
// In the near term, we are going to add this in slowly and incrementally in order to avoid raising the likelihood
// of regressions.
using (IProcessProxy process = await this.ExecuteCommandAsync("bash", $"\"{this.LzbenchScriptPath}\" \"{commandLineArguments}\"", this.LzbenchDirectory, telemetryContext, cancellationToken, runElevated: true))
try
{
if (!cancellationToken.IsCancellationRequested)
using (IProcessProxy process = await this.ExecuteCommandAsync(
"bash",
$"\"{this.LzbenchScriptPath}\" \"{commandLineArguments}\" \"{this.ResultsFilePath}\"",
this.LzbenchDirectory,
telemetryContext,
cancellationToken,
runElevated: false))
{
cancellationToken.ThrowIfCancellationRequested();

if (process.IsErrored())
{
await this.LogProcessDetailsAsync(process, telemetryContext, "LZbench");
process.ThrowIfWorkloadFailed();
}

await this.CaptureMetricsAsync(process, commandLineArguments, telemetryContext, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
}
}
catch (OperationCanceledException exc) when (cancellationToken.IsCancellationRequested)
{
// A normal cancellation is success-shaped in component status telemetry. Lzbench must
// instead report failure when it is cancelled before producing complete results.
throw new WorkloadException(
"The Lzbench workload was cancelled before it completed and produced results.",
exc,
ErrorReason.WorkloadFailed);
}
}
}

Expand Down Expand Up @@ -166,39 +203,33 @@ protected override async Task InitializeAsync(EventContext telemetryContext, Can

private async Task CaptureMetricsAsync(IProcessProxy process, string commandArguments, EventContext telemetryContext, CancellationToken cancellationToken)
{
if (!cancellationToken.IsCancellationRequested)
{
this.MetadataContract.AddForScenario(
"Lzbench",
process.FullCommand(),
toolVersion: null);
cancellationToken.ThrowIfCancellationRequested();

this.MetadataContract.Apply(telemetryContext);
this.MetadataContract.AddForScenario(
"Lzbench",
process.FullCommand(),
toolVersion: null);

string[] resultsFiles = this.fileSystem.Directory.GetFiles(this.LzbenchDirectory, "results-summary.csv", SearchOption.AllDirectories);
this.MetadataContract.Apply(telemetryContext);

foreach (string file in resultsFiles)
{
KeyValuePair<string, string> results = await this.LoadResultsAsync(file, cancellationToken);
await this.LogProcessDetailsAsync(process, telemetryContext, "LZbench", logToFile: true, results: results);

LzbenchMetricsParser parser = new LzbenchMetricsParser(results.Value);
IList<Metric> metrics = parser.Parse();

this.Logger.LogMetrics(
"Lzbench",
"Lzbench",
process.StartTime,
process.ExitTime,
metrics,
null,
commandArguments,
this.Tags,
telemetryContext);

await this.fileSystem.File.DeleteAsync(file);
}
}
KeyValuePair<string, string> results = await this.LoadResultsAsync(this.ResultsFilePath, cancellationToken);
await this.LogProcessDetailsAsync(process, telemetryContext, "LZbench", logToFile: true, results: results);

LzbenchMetricsParser parser = new LzbenchMetricsParser(results.Value);
IList<Metric> metrics = parser.Parse();

this.Logger.LogMetrics(
"Lzbench",
"Lzbench",
process.StartTime,
process.ExitTime,
metrics,
null,
commandArguments,
this.Tags,
telemetryContext);

await this.fileSystem.File.DeleteAsync(this.ResultsFilePath);
}

private async Task ExecuteCommandAsync(string pathToExe, string commandLineArguments, string workingDirectory, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/bin/bash

echo $1
./lzbench $1 > results-summary.csv

echo "$1"
./lzbench $1 > "$2"
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"Description": "LZbench Workload",
"Metadata": {
"RecommendedMinimumExecutionTime": "18:00:00",
"RecommendedMinimumExecutionTime": "24:00:00",
"SupportedPlatforms": "linux-x64,linux-arm64",
"SupportedOperatingSystems": "CBL-Mariner,CentOS,Debian,RedHat,Suse,Ubuntu"
},
Expand Down
Loading