@@ -111,6 +111,16 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
111111 /// </summary>
112112 public bool UseDwarfDebug { get ; set ; }
113113
114+ /// <summary>
115+ /// Path to Dotnet PGO binary (dotnet-pgo)
116+ /// </summary>
117+ public string ? PgoBinaryPath { get ; set ; }
118+
119+ /// <summary>
120+ /// NetTrace file to use when invoking dotnet-pgo for
121+ /// </summary>
122+ public string ? NetTracePath { get ; set ; }
123+
114124 /// <summary>
115125 /// File to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled.
116126 /// </summary>
@@ -119,7 +129,7 @@ public class MonoAOTCompiler : Microsoft.Build.Utilities.Task
119129 /// <summary>
120130 /// Mibc file to use for profile-guided optimization, *only* the methods described in the file will be AOT compiled.
121131 /// </summary>
122- public string [ ] ? MibcProfilePath { get ; set ; }
132+ public string [ ] MibcProfilePath { get ; set ; } = Array . Empty < string > ( ) ;
123133
124134 /// <summary>
125135 /// List of profilers to use.
@@ -271,6 +281,20 @@ private bool ProcessAndValidateArguments()
271281 if ( ! Directory . Exists ( IntermediateOutputPath ) )
272282 Directory . CreateDirectory ( IntermediateOutputPath ) ;
273283
284+ if ( ! string . IsNullOrEmpty ( NetTracePath ) )
285+ {
286+ if ( ! File . Exists ( NetTracePath ) )
287+ {
288+ Log . LogError ( $ "NetTracePath={ nameof ( NetTracePath ) } doesn't exist") ;
289+ return false ;
290+ }
291+ if ( ! File . Exists ( PgoBinaryPath ) )
292+ {
293+ Log . LogError ( $ "NetTracePath was provided, but { nameof ( PgoBinaryPath ) } ='{ PgoBinaryPath } ' doesn't exist") ;
294+ return false ;
295+ }
296+ }
297+
274298 if ( AotProfilePath != null )
275299 {
276300 foreach ( var path in AotProfilePath )
@@ -283,15 +307,12 @@ private bool ProcessAndValidateArguments()
283307 }
284308 }
285309
286- if ( MibcProfilePath != null )
310+ foreach ( var path in MibcProfilePath )
287311 {
288- foreach ( var path in MibcProfilePath )
312+ if ( ! File . Exists ( path ) )
289313 {
290- if ( ! File . Exists ( path ) )
291- {
292- Log . LogError ( $ "MibcProfilePath '{ path } ' doesn't exist.") ;
293- return false ;
294- }
314+ Log . LogError ( $ "MibcProfilePath '{ path } ' doesn't exist.") ;
315+ return false ;
295316 }
296317 }
297318
@@ -400,6 +421,39 @@ public override bool Execute()
400421 }
401422 }
402423
424+ private bool ProcessNettrace ( string netTraceFile )
425+ {
426+ var outputMibcPath = Path . Combine ( OutputDir , Path . ChangeExtension ( Path . GetFileName ( netTraceFile ) , ".mibc" ) ) ;
427+
428+ if ( _cache ! . Enabled )
429+ {
430+ string hash = Utils . ComputeHash ( netTraceFile ) ;
431+ if ( ! _cache ! . UpdateAndCheckHasFileChanged ( $ "-mibc-source-file-{ Path . GetFileName ( netTraceFile ) } ", hash ) )
432+ {
433+ Log . LogMessage ( MessageImportance . Low , $ "Skipping generating { outputMibcPath } from { netTraceFile } because source file hasn't changed") ;
434+ return true ;
435+ }
436+ else
437+ {
438+ Log . LogMessage ( MessageImportance . Low , $ "Generating { outputMibcPath } from { netTraceFile } because the source file's hash has changed.") ;
439+ }
440+ }
441+
442+ ( int exitCode , string output ) = Utils . TryRunProcess ( Log ,
443+ PgoBinaryPath ! ,
444+ $ "create-mibc --trace { netTraceFile } --output { outputMibcPath } ") ;
445+
446+ if ( exitCode != 0 )
447+ {
448+ Log . LogError ( $ "dotnet-pgo({ PgoBinaryPath } ) failed for { netTraceFile } :{ output } ") ;
449+ return false ;
450+ }
451+
452+ MibcProfilePath = MibcProfilePath . Append ( outputMibcPath ) . ToArray ( ) ;
453+ Log . LogMessage ( MessageImportance . Low , $ "Generated { outputMibcPath } from { PgoBinaryPath } ") ;
454+ return true ;
455+ }
456+
403457 private bool ExecuteInternal ( )
404458 {
405459 if ( ! ProcessAndValidateArguments ( ) )
@@ -417,6 +471,9 @@ private bool ExecuteInternal()
417471
418472 _cache = new FileCache ( CacheFilePath , Log ) ;
419473
474+ if ( ! string . IsNullOrEmpty ( NetTracePath ) && ! ProcessNettrace ( NetTracePath ) )
475+ return false ;
476+
420477 List < PrecompileArguments > argsList = new ( ) ;
421478 foreach ( var assemblyItem in _assembliesToCompile )
422479 argsList . Add ( GetPrecompileArgumentsFor ( assemblyItem , monoPaths ) ) ;
@@ -756,7 +813,7 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st
756813 }
757814 }
758815
759- if ( MibcProfilePath ? . Length > 0 )
816+ if ( MibcProfilePath . Length > 0 )
760817 {
761818 aotArgs . Add ( "profile-only" ) ;
762819 foreach ( var path in MibcProfilePath )
@@ -833,12 +890,13 @@ private PrecompileArguments GetPrecompileArgumentsFor(ITaskItem assemblyItem, st
833890 private bool PrecompileLibrary ( PrecompileArguments args )
834891 {
835892 string assembly = args . AOTAssembly . GetMetadata ( "FullPath" ) ;
893+ string output ;
836894 try
837895 {
838896 string msgPrefix = $ "[{ Path . GetFileName ( assembly ) } ] ";
839897
840898 // run the AOT compiler
841- ( int exitCode , string output ) = Utils . TryRunProcess ( Log ,
899+ ( int exitCode , output ) = Utils . TryRunProcess ( Log ,
842900 CompilerBinaryPath ,
843901 $ "--response=\" { args . ResponseFilePath } \" ",
844902 args . EnvironmentVariables ,
@@ -878,6 +936,12 @@ private bool PrecompileLibrary(PrecompileArguments args)
878936 bool copied = false ;
879937 foreach ( var proxyFile in args . ProxyFiles )
880938 {
939+ if ( ! File . Exists ( proxyFile . TempFile ) )
940+ {
941+ Log . LogError ( $ "Precompile command succeeded, but can't find the expected temporary output file - { proxyFile . TempFile } for { assembly } .{ Environment . NewLine } { output } ") ;
942+ return false ;
943+ }
944+
881945 copied |= proxyFile . CopyOutputFileIfChanged ( ) ;
882946 _fileWrites . Add ( proxyFile . TargetFile ) ;
883947 }
@@ -1092,8 +1156,20 @@ public FileCache(string? cacheFilePath, TaskLoggingHelper log)
10921156 _newCache = new ( _oldCache . FileHashes ) ;
10931157 }
10941158
1159+ public bool UpdateAndCheckHasFileChanged ( string filePath , string newHash )
1160+ {
1161+ if ( ! Enabled )
1162+ throw new InvalidOperationException ( "Cache is not enabled. Make sure the cache file path is set" ) ;
1163+
1164+ _newCache ! . FileHashes [ filePath ] = newHash ;
1165+ return ! _oldCache ! . FileHashes . TryGetValue ( filePath , out string ? oldHash ) || oldHash != newHash ;
1166+ }
1167+
10951168 public bool ShouldCopy ( ProxyFile proxyFile , [ NotNullWhen ( true ) ] out string ? cause )
10961169 {
1170+ if ( ! Enabled )
1171+ throw new InvalidOperationException ( "Cache is not enabled. Make sure the cache file path is set" ) ;
1172+
10971173 cause = null ;
10981174
10991175 string newHash = Utils . ComputeHash ( proxyFile . TempFile ) ;
@@ -1151,6 +1227,9 @@ public bool CopyOutputFileIfChanged()
11511227
11521228 try
11531229 {
1230+ if ( ! File . Exists ( TempFile ) )
1231+ throw new LogAsErrorException ( $ "Could not find the temporary file { TempFile } for target file { TargetFile } . Look for any errors/warnings generated earlier in the build.") ;
1232+
11541233 if ( ! _cache . ShouldCopy ( this , out string ? cause ) )
11551234 {
11561235 _cache . Log . LogMessage ( MessageImportance . Low , $ "Skipping copying over { TargetFile } as the contents are unchanged") ;
0 commit comments