summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVictor "Nate" Graf <nategraf1@gmail.com>2017-08-08 11:14:42 -0700
committerGitHub <noreply@github.com>2017-08-08 11:14:42 -0700
commit488d562052e61dde32ff0593835acc2713e9a0d1 (patch)
treea7214b175fcadc9826bfb7bb672d47dbe5205d73
parentd0e76ed19b8578caf746b3a4faa324c521d6420e (diff)
downloadcoreclr-488d562052e61dde32ff0593835acc2713e9a0d1.tar.gz
coreclr-488d562052e61dde32ff0593835acc2713e9a0d1.tar.bz2
coreclr-488d562052e61dde32ff0593835acc2713e9a0d1.zip
Add build-time check to enforce PGO compilation for applicable DLL files (#13258)
* Added PGO execution verification script Added a script which can help verify whether or not PGO was used to compile PE files passed to it * Added target to enforce PGO compilation * Remove OS checking for enforcepgo
-rw-r--r--build.cmd15
-rw-r--r--src/build.proj14
-rw-r--r--src/scripts/pgocheck.py77
3 files changed, 106 insertions, 0 deletions
diff --git a/build.cmd b/build.cmd
index 840df6dfff..d48042a05f 100644
--- a/build.cmd
+++ b/build.cmd
@@ -80,6 +80,7 @@ set __BuildStandaloneGCOnly="-DFEATURE_STANDALONE_GC_ONLY=0"
set __PgoInstrument=0
set __PgoOptimize=1
+set __EnforcePgo=0
set __IbcTuning=
REM __PassThroughArgs is a set of things that will be passed through to nested calls to build.cmd
@@ -140,6 +141,7 @@ if /i "%1" == "skiprestoreoptdata" (set __RestoreOptData=0&set processedArgs=!p
if /i "%1" == "usenmakemakefiles" (set __NMakeMakefiles=1&set __ConfigureOnly=1&set __BuildNative=1&set __BuildNativeCoreLib=0&set __BuildCoreLib=0&set __BuildTests=0&set __BuildPackages=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%1" == "pgoinstrument" (set __PgoInstrument=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%1" == "nopgooptimize" (set __PgoOptimize=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
+if /i "%1" == "enforcepgo" (set __EnforcePgo=1&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%1" == "ibcinstrument" (set __IbcTuning=/Tuning&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop)
if /i "%1" == "toolset_dir" (set __ToolsetDir=%2&set __PassThroughArgs=%__PassThroughArgs% %2&set processedArgs=!processedArgs! %1 %2&shift&shift&goto Arg_Loop)
if /i "%1" == "buildstandalonegc" (
@@ -193,6 +195,17 @@ if %__BuildTypeRelease%==1 set __BuildType=Release
set __RunArgs=-BuildOS=%__BuildOS% -BuildType=%__BuildType% -BuildArch=%__BuildArch%
+if %__EnforcePgo%==1 (
+ if %__BuildArchArm%==1 (
+ echo Error: enforcepgo cannot be used with arm architecture
+ goto Usage
+ )
+ if %__BuildArchArm64%==1 (
+ echo Error: enforcepgo cannot be used with arm64 architecture
+ goto Usage
+ )
+)
+
:: Set the remaining variables based upon the determined build configuration
set "__BinDir=%__RootBinDir%\Product\%__BuildOS%.%__BuildArch%.%__BuildType%"
set "__IntermediatesDir=%__RootBinDir%\obj\%__BuildOS%.%__BuildArch%.%__BuildType%"
@@ -204,6 +217,7 @@ set "__TestIntermediatesDir=%__RootBinDir%\tests\obj\%__BuildOS%.%__BuildArch%.%
set "__CrossComponentBinDir=%__BinDir%"
set "__CrossCompIntermediatesDir=%__IntermediatesDir%\crossgen"
+
if NOT "%__CrossArch%" == "" set __CrossComponentBinDir=%__CrossComponentBinDir%\%__CrossArch%
set "__CrossGenCoreLibLog=%__LogsDir%\CrossgenCoreLib_%__BuildOS%__%__BuildArch%__%__BuildType%.log"
set "__CrossgenExe=%__CrossComponentBinDir%\crossgen.exe"
@@ -626,6 +640,7 @@ echo respectively^).
echo add nativemscorlib to go further and build the native image for designated mscorlib.
echo toolset_dir ^<dir^> : set the toolset directory -- Arm64 use only. Required for Arm64 builds.
echo nopgooptimize: do not use profile guided optimizations.
+echo enforcepgo: verify after the build that PGO was used for key DLLs, and fail the build if not
echo pgoinstrument: generate instrumented code for profile guided optimization enabled binaries.
echo ibcinstrument: generate IBC-tuning-enabled native images when invoking crossgen.
echo configureonly: skip all builds; only run CMake ^(default: CMake and builds are run^)
diff --git a/src/build.proj b/src/build.proj
index 7962d27e99..b59b00c00f 100644
--- a/src/build.proj
+++ b/src/build.proj
@@ -38,4 +38,18 @@
DestinationFolder="$(BinDir)PDB" />
</Target>
+ <PropertyGroup>
+ <RunEnforcePGO Condition="$(__EnforcePgo) == '1'">true</RunEnforcePGO>
+ <RunEnforcePGO Condition="$(__BuildArch) == 'arm' OR $(__BuildArch) == 'arm64'">false</RunEnforcePGO>
+ </PropertyGroup>
+
+ <Target Name="EnforcePGO" Condition="$(RunEnforcePGO) == 'true'" AfterTargets="Build">
+ <ItemGroup>
+ <PGOEnforcedFiles Include="$(BinDir)coreclr.dll" />
+ <PGOEnforcedFiles Include="$(BinDir)clrjit.dll" />
+ </ItemGroup>
+
+ <Message Text="Checking if the following DLLs are properly compiled with PGO" Importance="High" />
+ <Exec Command="python $(MSBuildThisFileDirectory)scripts\pgocheck.py @(PGOEnforcedFiles)" />
+ </Target>
</Project>
diff --git a/src/scripts/pgocheck.py b/src/scripts/pgocheck.py
new file mode 100644
index 0000000000..d408e6eaba
--- /dev/null
+++ b/src/scripts/pgocheck.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+## Licensed to the .NET Foundation under one or more agreements.
+## The .NET Foundation licenses this file to you under the MIT license.
+## See the LICENSE file in the project root for more information.
+#
+##
+# Title :pgocheck.py
+#
+# A script to check whether or not a particular portable executable
+# (e.g. EXE, DLL) was compiled using PGO technology
+#
+################################################################################
+
+from glob import glob
+import sys
+import re
+import subprocess
+import argparse
+
+# This pattern matches the line which specifies if PGO, LTCG, or similar techologies were used for compilation
+# coffgrp matches the literal string. It uniquely identifies within the field in question
+# (?:\s+[0-9A-F]+){4} matches 4 hex valued fields without capturing them
+# \((\S*)\) captures the text identifier from the dump output, letting us know the technology
+pgo_pattern_str = r'coffgrp(?:\s+[0-9A-F]+){4}\s+\((\S*)\)'
+pgo_pattern = re.compile(pgo_pattern_str)
+
+def was_compiled_with_pgo(filename):
+ headers = subprocess.check_output(["link", "/dump", "/headers", filename])
+
+ match = pgo_pattern.search(headers)
+
+ result = False
+ tech = "UNKNOWN"
+ if match:
+ result = match.group(1) == 'PGU'
+ tech = match.group(1)
+
+ return result, tech
+
+if __name__ == "__main__":
+ from sys import stdout, stderr
+
+ parser = argparse.ArgumentParser(description="Check if the given PE files were compiled with PGO. Fails if the files were not.")
+ parser.add_argument('files', metavar='file', nargs='+', help="the files to check for PGO flags")
+ parser.add_argument('--negative', action='store_true', help="fail on PGO flags found")
+ parser.add_argument('--quiet', action='store_true', help="don't output; just return a code")
+
+ args = parser.parse_args()
+ # Divide up filenames which are separated by semicolons as well as the ones by spaces. Avoid duplicates
+ filenames = set()
+ for token in args.files:
+ unexpanded_filenames = token.split(';')
+ # Provide support for Unix-style filename expansion (i.e. with * and ?)
+ for unexpanded_filename in unexpanded_filenames:
+ expanded_filenames = glob(unexpanded_filename)
+ if unexpanded_filename and not expanded_filenames:
+ stderr.write("ERROR: Could not find file(s) {0}\n".format(unexpanded_filename))
+ exit(2)
+ filenames.update(expanded_filenames)
+
+ success = True
+ for filename in filenames:
+ result, tech = was_compiled_with_pgo(filename)
+ success = success and result
+
+ if not args.quiet:
+ status = "compiled with PGO" if result else "NOT compiled with PGO"
+ sys.stdout.write("{0}: {1} ({2})\n".format(filename, status, tech))
+
+ if not success:
+ if not args.quiet:
+ if not args.negative:
+ stderr.write("ERROR: The files listed above must be compiled with PGO\n")
+ else:
+ stderr.write("ERROR: The files listed above must NOT be compiled with PGO\n")
+ exit(1) \ No newline at end of file