-
-
Save galeone/f8bdf0fb4fafc517a4f65537b2ae2634 to your computer and use it in GitHub Desktop.
--- a/Engine/Source/Programs/UnrealBuildTool/Configuration/ModuleRules.cs | |
+++ b/Engine/Source/Programs/UnrealBuildTool/Configuration/ModuleRules.cs | |
@@ -606,6 +606,10 @@ namespace UnrealBuildTool | |
{ | |
get | |
{ | |
+ if (bCodeCoverage) { | |
+ return CodeOptimization.Never; | |
+ } | |
+ | |
if (OptimizeCodeOverride.HasValue) | |
return OptimizeCodeOverride.Value; | |
@@ -703,6 +707,11 @@ namespace UnrealBuildTool | |
/// </summary> | |
public bool bUseRTTI = false; | |
+ /// <summary> | |
+ /// Enable code coverage compilation/linking support. | |
+ /// </summary> | |
+ public bool bCodeCoverage = false; | |
+ | |
/// <summary> | |
/// Direct the compiler to generate AVX instructions wherever SSE or AVX intrinsics are used, on the platforms that support it. | |
/// Note that by enabling this you are changing the minspec for the PC platform, and the resultant executable will crash on machines without AVX support. | |
diff --git a/Engine/Source/Programs/UnrealBuildTool/Configuration/TargetRules.cs b/Engine/Source/Programs/UnrealBuildTool/Configuration/TargetRules.cs | |
index b3dac4efa6c..e0b6e130e9c 100644 | |
--- a/Engine/Source/Programs/UnrealBuildTool/Configuration/TargetRules.cs | |
+++ b/Engine/Source/Programs/UnrealBuildTool/Configuration/TargetRules.cs | |
@@ -1083,6 +1083,13 @@ namespace UnrealBuildTool | |
[XmlConfigFile(Category = "BuildConfiguration")] | |
public bool bPGOOptimize = false; | |
+ /// <summary> | |
+ /// Whether the target requires code coverage compilation and linking. | |
+ /// </summary> | |
+ [CommandLine("-CodeCoverage", Value = "true")] | |
+ [XmlConfigFile(Category = "BuildConfiguration")] | |
+ public bool bCodeCoverage; | |
+ | |
/// <summary> | |
/// Whether to support edit and continue. Only works on Microsoft compilers. | |
/// </summary> | |
@@ -2493,6 +2500,11 @@ namespace UnrealBuildTool | |
get { return Inner.bPGOOptimize; } | |
} | |
+ public bool bCodeCoverage | |
+ { | |
+ get {return Inner.bCodeCoverage; } | |
+ } | |
+ | |
public bool bSupportEditAndContinue | |
{ | |
get { return Inner.bSupportEditAndContinue; } | |
diff --git a/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildBinary.cs b/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildBinary.cs | |
index 4809ab00135..ccdaa77f718 100644 | |
--- a/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildBinary.cs | |
+++ b/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildBinary.cs | |
@@ -184,6 +184,7 @@ namespace UnrealBuildTool | |
// Setup linking environment. | |
LinkEnvironment BinaryLinkEnvironment = SetupBinaryLinkEnvironment(Target, ToolChain, LinkEnvironment, CompileEnvironment, SpecificFilesToCompile, WorkingSet, ExeDir, Graph); | |
+ BinaryLinkEnvironment.bCodeCoverage = CompileEnvironment.bCodeCoverage; | |
// If we're generating projects, we only need include paths and definitions, there is no need to run the linking logic. | |
if (ProjectFileGenerator.bGenerateProjectFiles) | |
@@ -231,6 +232,7 @@ namespace UnrealBuildTool | |
ConsoleAppLinkEvironment.bIsBuildingConsoleApplication = true; | |
ConsoleAppLinkEvironment.WindowsEntryPointOverride = "WinMainCRTStartup"; // For WinMain() instead of "main()" for Launch module | |
ConsoleAppLinkEvironment.OutputFilePaths = ConsoleAppLinkEvironment.OutputFilePaths.Select(Path => GetAdditionalConsoleAppPath(Path)).ToList(); | |
+ ConsoleAppLinkEvironment.bCodeCoverage = CompileEnvironment.bCodeCoverage; | |
// Link the console app executable | |
FileItem[] ConsoleAppOutputFiles = ToolChain.LinkAllFiles(ConsoleAppLinkEvironment, false, Graph); | |
@@ -700,6 +702,7 @@ namespace UnrealBuildTool | |
private LinkEnvironment SetupBinaryLinkEnvironment(ReadOnlyTargetRules Target, UEToolChain ToolChain, LinkEnvironment LinkEnvironment, CppCompileEnvironment CompileEnvironment, List<FileReference> SpecificFilesToCompile, ISourceFileWorkingSet WorkingSet, DirectoryReference ExeDir, IActionGraphBuilder Graph) | |
{ | |
LinkEnvironment BinaryLinkEnvironment = new LinkEnvironment(LinkEnvironment); | |
+ BinaryLinkEnvironment.bCodeCoverage = CompileEnvironment.bCodeCoverage; | |
HashSet<UEBuildModule> LinkEnvironmentVisitedModules = new HashSet<UEBuildModule>(); | |
List<UEBuildBinary> BinaryDependencies = new List<UEBuildBinary>(); | |
diff --git a/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildModuleCPP.cs b/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildModuleCPP.cs | |
index 481bb6db1f2..a1caeec2253 100644 | |
--- a/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildModuleCPP.cs | |
+++ b/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildModuleCPP.cs | |
@@ -624,6 +624,7 @@ namespace UnrealBuildTool | |
CompileEnvironment.PrecompiledHeaderAction = PrecompiledHeaderAction.Create; | |
CompileEnvironment.PrecompiledHeaderIncludeFilename = WrapperFile.Location; | |
CompileEnvironment.bOptimizeCode = ModuleCompileEnvironment.bOptimizeCode; | |
+ CompileEnvironment.bCodeCoverage = ModuleCompileEnvironment.bCodeCoverage; | |
// Create the action to compile the PCH file. | |
CPPOutput Output; | |
@@ -822,6 +823,7 @@ namespace UnrealBuildTool | |
private void CopySettingsForSharedPCH(CppCompileEnvironment ModuleCompileEnvironment, CppCompileEnvironment CompileEnvironment) | |
{ | |
CompileEnvironment.bOptimizeCode = ModuleCompileEnvironment.bOptimizeCode; | |
+ CompileEnvironment.bCodeCoverage = ModuleCompileEnvironment.bCodeCoverage; | |
CompileEnvironment.bUseRTTI = ModuleCompileEnvironment.bUseRTTI; | |
CompileEnvironment.bEnableExceptions = ModuleCompileEnvironment.bEnableExceptions; | |
CompileEnvironment.ShadowVariableWarningLevel = ModuleCompileEnvironment.ShadowVariableWarningLevel; | |
@@ -1238,9 +1240,13 @@ namespace UnrealBuildTool | |
/// <param name="Setting">The optimization setting from the rules file</param> | |
/// <param name="Configuration">The active target configuration</param> | |
/// <param name="bIsEngineModule">Whether the current module is an engine module</param> | |
+ /// <param name="bCodeCoverage">Whether the current module should be compiled with code coverage support</param> | |
/// <returns>True if optimization should be enabled</returns> | |
- public static bool ShouldEnableOptimization(ModuleRules.CodeOptimization Setting, UnrealTargetConfiguration Configuration, bool bIsEngineModule) | |
+ public static bool ShouldEnableOptimization(ModuleRules.CodeOptimization Setting, UnrealTargetConfiguration Configuration, bool bIsEngineModule, bool bCodeCoverage) | |
{ | |
+ if (bCodeCoverage) { | |
+ return false; | |
+ } | |
switch(Setting) | |
{ | |
case ModuleRules.CodeOptimization.Never: | |
@@ -1275,7 +1281,8 @@ namespace UnrealBuildTool | |
// Override compile environment | |
Result.bUseUnity = Rules.bUseUnity; | |
- Result.bOptimizeCode = ShouldEnableOptimization(Rules.OptimizeCode, Target.Configuration, Rules.bTreatAsEngineModule); | |
+ Result.bCodeCoverage = Target.bCodeCoverage; // From Target! | |
+ Result.bOptimizeCode = ShouldEnableOptimization(Rules.OptimizeCode, Target.Configuration, Rules.bTreatAsEngineModule, Result.bCodeCoverage); | |
Result.bUseRTTI |= Rules.bUseRTTI; | |
Result.bUseAVX = Rules.bUseAVX; | |
Result.bEnableBufferSecurityChecks = Rules.bEnableBufferSecurityChecks; | |
@@ -1348,8 +1355,9 @@ namespace UnrealBuildTool | |
{ | |
CppCompileEnvironment CompileEnvironment = new CppCompileEnvironment(BaseCompileEnvironment); | |
- // Use the default optimization setting for | |
- CompileEnvironment.bOptimizeCode = ShouldEnableOptimization(ModuleRules.CodeOptimization.Default, Target.Configuration, Rules.bTreatAsEngineModule); | |
+ // Use the default optimization setting for | |
+ CompileEnvironment.bOptimizeCode = ShouldEnableOptimization(ModuleRules.CodeOptimization.Default, Target.Configuration, Rules.bTreatAsEngineModule, Rules.bCodeCoverage); | |
+ CompileEnvironment.bCodeCoverage = Rules.bCodeCoverage; | |
// Override compile environment | |
CompileEnvironment.bIsBuildingDLL = !Target.ShouldCompileMonolithic(); | |
diff --git a/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildTarget.cs b/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildTarget.cs | |
index 97a937d41af..2b401a4f326 100644 | |
--- a/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildTarget.cs | |
+++ b/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildTarget.cs | |
@@ -3439,6 +3439,7 @@ namespace UnrealBuildTool | |
GlobalCompileEnvironment.bHideSymbolsByDefault = !Rules.bPublicSymbolsByDefault; | |
GlobalCompileEnvironment.CppStandard = Rules.CppStandard; | |
GlobalCompileEnvironment.AdditionalArguments = Rules.AdditionalCompilerArguments; | |
+ GlobalCompileEnvironment.bCodeCoverage = Rules.bCodeCoverage; | |
GlobalLinkEnvironment.bIsBuildingConsoleApplication = Rules.bIsBuildingConsoleApplication; | |
GlobalLinkEnvironment.bOptimizeForSize = Rules.bCompileForSize; | |
@@ -3456,6 +3457,7 @@ namespace UnrealBuildTool | |
GlobalLinkEnvironment.bUseFastPDBLinking = Rules.bUseFastPDBLinking ?? false; | |
GlobalLinkEnvironment.bPrintTimingInfo = Rules.bPrintToolChainTimingInfo; | |
GlobalLinkEnvironment.AdditionalArguments = Rules.AdditionalLinkerArguments; | |
+ GlobalLinkEnvironment.bCodeCoverage = Rules.bCodeCoverage; | |
if (Rules.bPGOOptimize && Rules.bPGOProfile) | |
{ | |
diff --git a/Engine/Source/Programs/UnrealBuildTool/Platform/Linux/LinuxToolChain.cs b/Engine/Source/Programs/UnrealBuildTool/Platform/Linux/LinuxToolChain.cs | |
index fb38ffe34fe..ba6b28f48d0 100644 | |
--- a/Engine/Source/Programs/UnrealBuildTool/Platform/Linux/LinuxToolChain.cs | |
+++ b/Engine/Source/Programs/UnrealBuildTool/Platform/Linux/LinuxToolChain.cs | |
@@ -170,6 +170,7 @@ namespace UnrealBuildTool | |
bIsCrossCompiling = true; | |
bHasValidCompiler = DetermineCompilerVersion(); | |
+ CompilerRTPath = Path.Combine(Path.Combine(BaseLinuxPath, String.Format("lib/clang/{0}/lib/linux/", CompilerVersionString))); | |
} | |
if (!bHasValidCompiler) | |
@@ -767,8 +768,13 @@ namespace UnrealBuildTool | |
} | |
} | |
- // optimization level | |
- if (!CompileEnvironment.bOptimizeCode) | |
+ if (CompileEnvironment.bCodeCoverage) | |
+ { | |
+ Result += " -O0"; | |
+ Result += " -fprofile-arcs -ftest-coverage"; // gcov | |
+ //Result += " -fprofile-instr-generate -fcoverage-mapping"; // llvm-cov | |
+ } | |
+ else if (!CompileEnvironment.bOptimizeCode) // optimization level | |
{ | |
Result += " -O0"; | |
} | |
@@ -1019,6 +1025,15 @@ namespace UnrealBuildTool | |
Result += " -Wl,--gdb-index"; | |
} | |
+ if (LinkEnvironment.bCodeCoverage) | |
+ { | |
+ // Unreal Separates the linking phase and the compilation phase. | |
+ // We pass to clang the flag `--coverage` during the compile time | |
+ // And we link the correct compiler-rt library (shipped by UE, and part of the LLVM toolchain) | |
+ // to every binary produced. | |
+ Result += string.Format(" -L{0} -l{1}", CompilerRTPath, "clang_rt.profile-x86_64"); // gcov | |
+ // Result += " -fprofile-instr-generate"; // llvm-cov | |
+ } | |
// RPATH for third party libs | |
Result += " -Wl,-rpath=${ORIGIN}"; | |
Result += " -Wl,-rpath-link=${ORIGIN}"; | |
@@ -1142,6 +1157,7 @@ namespace UnrealBuildTool | |
protected string BaseLinuxPath; | |
protected string ClangPath; | |
protected string GCCPath; | |
+ protected string CompilerRTPath; | |
protected string ArPath; | |
protected string LlvmArPath; | |
protected string RanlibPath; | |
@@ -1270,6 +1286,11 @@ namespace UnrealBuildTool | |
Log.TraceInformation(" Prefix for PGO data files='{0}'", CompileEnvironment.PGOFilenamePrefix); | |
} | |
+ if (CompileEnvironment.bCodeCoverage) | |
+ { | |
+ Log.TraceInformation("Using --coverage build flag"); | |
+ } | |
+ | |
if (CompileEnvironment.bPGOProfile) | |
{ | |
Log.TraceInformation("Using PGI (profile guided instrumentation)."); | |
diff --git a/Engine/Source/Programs/UnrealBuildTool/Platform/Linux/UEBuildLinux.cs b/Engine/Source/Programs/UnrealBuildTool/Platform/Linux/UEBuildLinux.cs | |
index d8b35ac02ab..de3fc2d944e 100644 | |
--- a/Engine/Source/Programs/UnrealBuildTool/Platform/Linux/UEBuildLinux.cs | |
+++ b/Engine/Source/Programs/UnrealBuildTool/Platform/Linux/UEBuildLinux.cs | |
@@ -526,6 +526,8 @@ namespace UnrealBuildTool | |
LinkEnvironment.PGOFilenamePrefix = CompileEnvironment.PGOFilenamePrefix; | |
} | |
+ LinkEnvironment.bCodeCoverage = CompileEnvironment.bCodeCoverage; | |
+ | |
// For consistency with other platforms, also enable LTO whenever doing profile-guided optimizations. | |
// Obviously both PGI (instrumented) and PGO (optimized) binaries need to have that | |
if (CompileEnvironment.bPGOProfile || CompileEnvironment.bPGOOptimize) | |
diff --git a/Engine/Source/Programs/UnrealBuildTool/System/CppCompileEnvironment.cs b/Engine/Source/Programs/UnrealBuildTool/System/CppCompileEnvironment.cs | |
index 189954552a3..f56830c64d2 100644 | |
--- a/Engine/Source/Programs/UnrealBuildTool/System/CppCompileEnvironment.cs | |
+++ b/Engine/Source/Programs/UnrealBuildTool/System/CppCompileEnvironment.cs | |
@@ -221,6 +221,11 @@ namespace UnrealBuildTool | |
/// </summary> | |
public bool bOptimizeCode = false; | |
+ /// <summary> | |
+ /// True if the compilation should produce tracing output for code coverage. | |
+ /// </summary> | |
+ public bool bCodeCoverage = false; | |
+ | |
/// <summary> | |
/// Whether to optimize for minimal code size | |
/// </summary> | |
@@ -428,6 +433,7 @@ namespace UnrealBuildTool | |
bUndefinedIdentifierWarningsAsErrors = Other.bUndefinedIdentifierWarningsAsErrors; | |
bEnableUndefinedIdentifierWarnings = Other.bEnableUndefinedIdentifierWarnings; | |
bOptimizeCode = Other.bOptimizeCode; | |
+ bCodeCoverage = Other.bCodeCoverage; | |
bOptimizeForSize = Other.bOptimizeForSize; | |
bCreateDebugInfo = Other.bCreateDebugInfo; | |
bIsBuildingLibrary = Other.bIsBuildingLibrary; | |
diff --git a/Engine/Source/Programs/UnrealBuildTool/System/LinkEnvironment.cs b/Engine/Source/Programs/UnrealBuildTool/System/LinkEnvironment.cs | |
index 610e4b3db4d..9a94a6b4388 100644 | |
--- a/Engine/Source/Programs/UnrealBuildTool/System/LinkEnvironment.cs | |
+++ b/Engine/Source/Programs/UnrealBuildTool/System/LinkEnvironment.cs | |
@@ -196,6 +196,11 @@ namespace UnrealBuildTool | |
/// </summary> | |
public bool bOptimizeForSize = false; | |
+ /// <summary> | |
+ /// Wether to link code coverage / tracing libs | |
+ /// </summary> | |
+ public bool bCodeCoverage = false; | |
+ | |
/// <summary> | |
/// Whether to omit frame pointers or not. Disabling is useful for e.g. memory profiling on the PC | |
/// </summary> | |
@@ -349,6 +354,7 @@ namespace UnrealBuildTool | |
DefaultStackSize = Other.DefaultStackSize; | |
DefaultStackSizeCommit = Other.DefaultStackSizeCommit; | |
bOptimizeForSize = Other.bOptimizeForSize; | |
+ bCodeCoverage = Other.bCodeCoverage; | |
bOmitFramePointers = Other.bOmitFramePointers; | |
bSupportEditAndContinue = Other.bSupportEditAndContinue; | |
bUseIncrementalLinking = Other.bUseIncrementalLinking; |
That's strange! The symbols missing are in the library we link here: https://gist.github.com/galeone/f8bdf0fb4fafc517a4f65537b2ae2634#file-engine-coverage-patch-L199
Maybe you can try to pass the -coverage
flag, instead of directly passing the library as I did, and thus let the compiler use the right library. Perhaps something changed in the toolchain, I haven't checked ue5 yet
It's entirely possible the toolchain changed, I just re-compiled 4.27 to check and that worked fine. Re-compiling 5.0 again, and I'll try -coverage
and let you know how it works.
So Result += string.Format(" -L{0} -l{1} -coverage", CompilerRTPath, "clang_rt.profile-x86_64"); // gcov
might be a better try?
Nope, I mean only passing the --coverage
flag without explicitly link the library. Thus
Result += " --coverage"; // gcov
That works, albeit a strange issue where I need to run it twice.
[136/2101] Compile Module.Chaos.1_of_7.cpp
[136/2101]Compile Module.Chaos.1_of_7.cpp - Error but no output
[136/2101]Compile Module.Chaos.1_of_7.cpp - 137 /home/administrator/Applications/Epic/UE_5.0_Source/Engine/Source /home/administrator/Applications/Epic/UE_5.0_Source/Engine/Extras/ThirdPartyNotUE/SDKs/HostLinux/Linux_x64/v19_clang-11.0.1-centos7/x86_64-unknown-linux-gnu/bin/clang++ @"/home/administrator/Applications/Epic/UE_5.0_Source/Engine/Intermediate/Build/Linux/B4D820EA/UnrealEditor/Development/Chaos/Module.Chaos.1_of_7.cpp.o.rsp"
Appears the first time, but compiles okay on the second run.
Cool! Anyway, you shouldn't compile the whole engine with the -CodeCoverage
flag enabled, but it's better to have an installed version of the engine (with the UBT modified) and compile the project/module you want to measure the coverage only - otherwise, compiling the whole engine with optimization disabled will slow down the development a lot
I've only been passing in -CodeCoverage
when compiling the project/module and not the engine. UE5 is doing half the engine on the first project compile using the engine build, 2nd/3rd projects are actually fine too, just seems like a first project only issue. Next fun part is trying to see if I can get this into ue4-docker to automate more. The flag works with ue4cli no problem.
Thanks for all the help so far. I'll write it up if I get the docker images working too.
awesome, keep me posted!
Awesome work @galeone! Have you considered submitting this as a PR?
@galeone Really awesome to see this implemented in 5.3.0 as part of CL 25794147 and documented here: https://docs.unrealengine.com/5.3/en-US/static-code-analysis-in-unreal-engine/
@CanisHelix but these are the sanitizers, it's not the code coverage support
@galeone I will be testing shortly, but trying to apply the patch to 5.3.1, it looks like your changes are finally included in the Unreal release!
Verified. The merged it in 5.4.1 for sure (just tested). They modified this a little bit (for no reason). So instead of ForceQuit, you must use Quit. And instead of Quit, you must use SoftQuit
All the rest is the same
This is really awesome, and appreciate the efforts put into this.
I have errors when compiling a project with a UE5 Source with the above changes. The engine itself builds fine with the changes but not the project. I believe Clang has not changed between UE4.27 and UE5.0, do you have any idea what the errors could be? This only occurs when using the -CodeCoverage flag.