Skip to content

Instantly share code, notes, and snippets.

@galeone
Created February 16, 2022 08:30
Show Gist options
  • Save galeone/f8bdf0fb4fafc517a4f65537b2ae2634 to your computer and use it in GitHub Desktop.
Save galeone/f8bdf0fb4fafc517a4f65537b2ae2634 to your computer and use it in GitHub Desktop.
Enable code coverage support on Unreal Built Tool Linux Toolchain
--- 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;
@CanisHelix
Copy link

@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/

@galeone
Copy link
Author

galeone commented Oct 9, 2023

@CanisHelix but these are the sanitizers, it's not the code coverage support

@tomdell13
Copy link

@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!

@galeone
Copy link
Author

galeone commented May 16, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment