Skip to content

Commit

Permalink
[mono] Extend MonoAOTCompiler Task (#70851)
Browse files Browse the repository at this point in the history
* [mono] Extend HelloWorld sample to use MonoAotCompiler Task

* [mono] Extend MonoAotCompiler Task to invoke dotnet-pgo

* Use cache for incremental builds
  • Loading branch information
mdh1418 committed Jun 22, 2022
1 parent 058f83b commit 46f53e3
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 12 deletions.
1 change: 1 addition & 0 deletions src/mono/mono.proj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<BuildMonoAOTCrossCompiler Condition="'$(TargetsiOS)' == 'true'">true</BuildMonoAOTCrossCompiler>
<BuildMonoAOTCrossCompiler Condition="'$(TargetstvOS)' == 'true'">true</BuildMonoAOTCrossCompiler>
<BuildMonoAOTCrossCompiler Condition="'$(TargetsMacCatalyst)' == 'true'">true</BuildMonoAOTCrossCompiler>
<BuildMonoAOTCrossCompiler Condition="'$(TargetsOSX)' == 'true'">true</BuildMonoAOTCrossCompiler>
<BuildMonoAOTCrossCompiler Condition="'$(TargetsBrowser)' == 'true'">true</BuildMonoAOTCrossCompiler>
<BuildMonoAOTCrossCompiler Condition="'$(TargetsAndroid)' == 'true'">true</BuildMonoAOTCrossCompiler>
<MonoObjCrossDir>$([MSBuild]::NormalizeDirectory('$(MonoObjDir)', 'cross'))</MonoObjCrossDir>
Expand Down
30 changes: 30 additions & 0 deletions src/mono/sample/HelloWorld/HelloWorld.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,34 @@
<OutputType>Exe</OutputType>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
</PropertyGroup>

<UsingTask TaskName="MonoAOTCompiler"
AssemblyFile="$(MonoAOTCompilerTasksAssemblyPath)" />

<Target Name="AOTCompileApp" Condition="'$(RunAOTCompilation)' == 'true'" AfterTargets="CopyFilesToPublishDirectory">
<PropertyGroup>
<_AotOutputType>Library</_AotOutputType>
<_AotLibraryFormat>Dylib</_AotLibraryFormat>
<UseAotDataFile>false</UseAotDataFile>
</PropertyGroup>

<ItemGroup>
<AotInputAssemblies Include="$(PublishDir)\*.dll" />
</ItemGroup>

<MonoAOTCompiler
CompilerBinaryPath="@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','$(TargetOS.ToLowerInvariant())-$(TargetArchitecture.ToLowerInvariant())'))"
OutputType="$(_AotOutputType)"
LibraryFormat="$(_AotLibraryFormat)"
Assemblies="@(AotInputAssemblies)"
OutputDir="$(PublishDir)"
IntermediateOutputPath="$(IntermediateOutputPath)"
UseAotDataFile="$(UseAotDataFile)"
CacheFilePath="$(IntermediateOutputPath)aot_compiler_cache.json"
NetTracePath="$(NetTracePath)"
PgoBinaryPath="$(PgoBinaryPath)"
MibcProfilePath="$(MibcProfilePath)">
<Output TaskParameter="CompiledAssemblies" ItemName="BundleAssemblies" />
</MonoAOTCompiler>
</Target>
</Project>
15 changes: 13 additions & 2 deletions src/mono/sample/HelloWorld/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary

MONO_CONFIG ?=Debug
MONO_ARCH=x64
AOT?=false

#NET_TRACE_PATH=<path-to-trace-of-sample>
#PGO_BINARY_PATH=<path-to-dotnet-pgo-executable>
#MIBC_PROFILE_PATH=<path-to-mibc-for-sample>

OS := $(shell uname -s)
ifeq ($(OS),Darwin)
Expand All @@ -12,10 +17,16 @@ else
TARGET_OS=linux
endif

MONO_ENV_OPTIONS ?=--llvm
MONO_ENV_OPTIONS ?=

publish:
$(DOTNET) publish -c $(MONO_CONFIG) -r $(TARGET_OS)-$(MONO_ARCH)
$(DOTNET) publish \
-c $(MONO_CONFIG) \
-r $(TARGET_OS)-$(MONO_ARCH) \
/p:RunAOTCompilation=$(AOT) \
'/p:NetTracePath="$(NET_TRACE_PATH)"' \
'/p:PgoBinaryPath="$(PGO_BINARY_PATH)"' \
'/p:MibcProfilePath="$(MIBC_PROFILE_PATH)"'

run: publish
COMPlus_DebugWriteToStdErr=1 \
Expand Down
99 changes: 89 additions & 10 deletions src/tasks/AotCompilerTask/MonoAOTCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,16 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
/// </summary>
public bool UseDwarfDebug { get; set; }

/// <summary>
/// Path to Dotnet PGO binary (dotnet-pgo)
/// </summary>
public string? PgoBinaryPath { get; set; }

/// <summary>
/// NetTrace file to use when invoking dotnet-pgo for
/// </summary>
public string? NetTracePath { get; set; }

/// <summary>
/// File to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled.
/// </summary>
Expand All @@ -119,7 +129,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
/// <summary>
/// Mibc file to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled.
/// </summary>
public string[]? MibcProfilePath { get; set; }
public string[] MibcProfilePath { get; set; } = Array.Empty<string>();

/// <summary>
/// List of profilers to use.
Expand Down Expand Up @@ -271,6 +281,20 @@ private bool ProcessAndValidateArguments()
if (!Directory.Exists(IntermediateOutputPath))
Directory.CreateDirectory(IntermediateOutputPath);

if (!string.IsNullOrEmpty(NetTracePath))
{
if (!File.Exists(NetTracePath))
{
Log.LogError($"NetTracePath={nameof(NetTracePath)} doesn't exist");
return false;
}
if (!File.Exists(PgoBinaryPath))
{
Log.LogError($"NetTracePath was provided, but {nameof(PgoBinaryPath)}='{PgoBinaryPath}' doesn't exist");
return false;
}
}

if (AotProfilePath != null)
{
foreach (var path in AotProfilePath)
Expand All @@ -283,15 +307,12 @@ private bool ProcessAndValidateArguments()
}
}

if (MibcProfilePath != null)
foreach (var path in MibcProfilePath)
{
foreach (var path in MibcProfilePath)
if (!File.Exists(path))
{
if (!File.Exists(path))
{
Log.LogError($"MibcProfilePath '{path}' doesn't exist.");
return false;
}
Log.LogError($"MibcProfilePath '{path}' doesn't exist.");
return false;
}
}

Expand Down Expand Up @@ -400,6 +421,39 @@ public override bool Execute()
}
}

private bool ProcessNettrace(string netTraceFile)
{
var outputMibcPath = Path.Combine(OutputDir, Path.ChangeExtension(Path.GetFileName(netTraceFile), ".mibc"));

if (_cache!.Enabled)
{
string hash = Utils.ComputeHash(netTraceFile);
if (!_cache!.UpdateAndCheckHasFileChanged($"-mibc-source-file-{Path.GetFileName(netTraceFile)}", hash))
{
Log.LogMessage(MessageImportance.Low, $"Skipping generating {outputMibcPath} from {netTraceFile} because source file hasn't changed");
return true;
}
else
{
Log.LogMessage(MessageImportance.Low, $"Generating {outputMibcPath} from {netTraceFile} because the source file's hash has changed.");
}
}

(int exitCode, string output) = Utils.TryRunProcess(Log,
PgoBinaryPath!,
$"create-mibc --trace {netTraceFile} --output {outputMibcPath}");

if (exitCode != 0)
{
Log.LogError($"dotnet-pgo({PgoBinaryPath}) failed for {netTraceFile}:{output}");
return false;
}

MibcProfilePath = MibcProfilePath.Append(outputMibcPath).ToArray();
Log.LogMessage(MessageImportance.Low, $"Generated {outputMibcPath} from {PgoBinaryPath}");
return true;
}

private bool ExecuteInternal()
{
if (!ProcessAndValidateArguments())
Expand All @@ -417,6 +471,9 @@ private bool ExecuteInternal()

_cache = new FileCache(CacheFilePath, Log);

if (!string.IsNullOrEmpty(NetTracePath) && !ProcessNettrace(NetTracePath))
return false;

List<PrecompileArguments> argsList = new();
foreach (var assemblyItem in _assembliesToCompile)
argsList.Add(GetPrecompileArgumentsFor(assemblyItem, monoPaths));
Expand Down Expand Up @@ -756,7 +813,7 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st
}
}

if (MibcProfilePath?.Length > 0)
if (MibcProfilePath.Length > 0)
{
aotArgs.Add("profile-only");
foreach (var path in MibcProfilePath)
Expand Down Expand Up @@ -833,12 +890,13 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st
private bool PrecompileLibrary(PrecompileArguments args)
{
string assembly = args.AOTAssembly.GetMetadata("FullPath");
string output;
try
{
string msgPrefix = $"[{Path.GetFileName(assembly)}] ";

// run the AOT compiler
(int exitCode, string output) = Utils.TryRunProcess(Log,
(int exitCode, output) = Utils.TryRunProcess(Log,
CompilerBinaryPath,
$"--response=\"{args.ResponseFilePath}\"",
args.EnvironmentVariables,
Expand Down Expand Up @@ -878,6 +936,12 @@ private bool PrecompileLibrary(PrecompileArguments args)
bool copied = false;
foreach (var proxyFile in args.ProxyFiles)
{
if (!File.Exists(proxyFile.TempFile))
{
Log.LogError($"Precompile command succeeded, but can't find the expected temporary output file - {proxyFile.TempFile} for {assembly}.{Environment.NewLine}{output}");
return false;
}

copied |= proxyFile.CopyOutputFileIfChanged();
_fileWrites.Add(proxyFile.TargetFile);
}
Expand Down Expand Up @@ -1092,8 +1156,20 @@ public FileCache(string? cacheFilePath, TaskLoggingHelper log)
_newCache = new(_oldCache.FileHashes);
}

public bool UpdateAndCheckHasFileChanged(string filePath, string newHash)
{
if (!Enabled)
throw new InvalidOperationException("Cache is not enabled. Make sure the cache file path is set");

_newCache!.FileHashes[filePath] = newHash;
return !_oldCache!.FileHashes.TryGetValue(filePath, out string? oldHash) || oldHash != newHash;
}

public bool ShouldCopy(ProxyFile proxyFile, [NotNullWhen(true)] out string? cause)
{
if (!Enabled)
throw new InvalidOperationException("Cache is not enabled. Make sure the cache file path is set");

cause = null;

string newHash = Utils.ComputeHash(proxyFile.TempFile);
Expand Down Expand Up @@ -1151,6 +1227,9 @@ public bool CopyOutputFileIfChanged()

try
{
if (!File.Exists(TempFile))
throw new LogAsErrorException($"Could not find the temporary file {TempFile} for target file {TargetFile}. Look for any errors/warnings generated earlier in the build.");

if (!_cache.ShouldCopy(this, out string? cause))
{
_cache.Log.LogMessage(MessageImportance.Low, $"Skipping copying over {TargetFile} as the contents are unchanged");
Expand Down

0 comments on commit 46f53e3

Please sign in to comment.