diff --git a/src/mono/mono.proj b/src/mono/mono.proj
index 9e90b9134fb103..7cd03731fcd232 100644
--- a/src/mono/mono.proj
+++ b/src/mono/mono.proj
@@ -40,6 +40,7 @@
true
true
true
+ true
true
true
$([MSBuild]::NormalizeDirectory('$(MonoObjDir)', 'cross'))
diff --git a/src/mono/sample/HelloWorld/HelloWorld.csproj b/src/mono/sample/HelloWorld/HelloWorld.csproj
index f727ffe343f4d1..9ae9581798b9c9 100644
--- a/src/mono/sample/HelloWorld/HelloWorld.csproj
+++ b/src/mono/sample/HelloWorld/HelloWorld.csproj
@@ -3,4 +3,34 @@
Exe
$(NetCoreAppCurrent)
+
+
+
+
+
+ <_AotOutputType>Library
+ <_AotLibraryFormat>Dylib
+ false
+
+
+
+
+
+
+
+
+
+
diff --git a/src/mono/sample/HelloWorld/Makefile b/src/mono/sample/HelloWorld/Makefile
index 28df19d738f9d6..10a1a52f5d1324 100644
--- a/src/mono/sample/HelloWorld/Makefile
+++ b/src/mono/sample/HelloWorld/Makefile
@@ -4,6 +4,11 @@ DOTNET_Q_ARGS=--nologo -v:q -consoleloggerparameters:NoSummary
MONO_CONFIG ?=Debug
MONO_ARCH=x64
+AOT?=false
+
+#NET_TRACE_PATH=
+#PGO_BINARY_PATH=
+#MIBC_PROFILE_PATH=
OS := $(shell uname -s)
ifeq ($(OS),Darwin)
@@ -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 \
diff --git a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
index fcc24b6111b807..6d639794d3289f 100644
--- a/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
+++ b/src/tasks/AotCompilerTask/MonoAOTCompiler.cs
@@ -111,6 +111,16 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
///
public bool UseDwarfDebug { get; set; }
+ ///
+ /// Path to Dotnet PGO binary (dotnet-pgo)
+ ///
+ public string? PgoBinaryPath { get; set; }
+
+ ///
+ /// NetTrace file to use when invoking dotnet-pgo for
+ ///
+ public string? NetTracePath { get; set; }
+
///
/// File to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled.
///
@@ -119,7 +129,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
///
/// Mibc file to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled.
///
- public string[]? MibcProfilePath { get; set; }
+ public string[] MibcProfilePath { get; set; } = Array.Empty();
///
/// List of profilers to use.
@@ -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)
@@ -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;
}
}
@@ -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())
@@ -417,6 +471,9 @@ private bool ExecuteInternal()
_cache = new FileCache(CacheFilePath, Log);
+ if (!string.IsNullOrEmpty(NetTracePath) && !ProcessNettrace(NetTracePath))
+ return false;
+
List argsList = new();
foreach (var assemblyItem in _assembliesToCompile)
argsList.Add(GetPrecompileArgumentsFor(assemblyItem, monoPaths));
@@ -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)
@@ -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,
@@ -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);
}
@@ -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);
@@ -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");