https://github.com/nicolas-guichard created https://github.com/llvm/llvm-project/pull/230077
GCC has a -fprofile-abs-path option to use absolute paths to source files in GCNO files. Citing GCC's documentation: > Automatically convert relative source file names to absolute path names in > the .gcno files. This allows gcov to find the correct sources in projects > where compilations occur with different working directories. This is particularly useful when doing unity builds where a generated source file includes many original source files, as otherwise the GCNO refers to the original source files by relative paths. When the -fprofile-abs-path flag is passed to clang/clang++, the new -coverage-notes-abs-paths flag is passed to cc1, which calls vfs::FileSystem::makeAbsolute to get the absolute path. getFilename is moved to a member function in GCOVProfiler so that it can access the GCOVOptions. This also removes the need for its VFS argument and the getVirtualFileSystem member function. >From aa77838ad96ae0e8e9edac5c4ba007d7a4fca889 Mon Sep 17 00:00:00 2001 From: Nicolas Guichard <[email protected]> Date: Wed, 7 Oct 2026 09:33:10 +0200 Subject: [PATCH] [gcov] Add support for -fprofile-abs-path GCC has a -fprofile-abs-path option to use absolute paths to source files in GCNO files. Citing GCC's documentation: > Automatically convert relative source file names to absolute path > names in the .gcno files. This allows gcov to find the correct > sources in projects where compilations occur with different working > directories. This is particularly useful when doing unity builds where a generated source file includes many original source files, as otherwise the GCNO refers to the original source files by relative paths. When the -fprofile-abs-path flag is passed to clang/clang++, the new -coverage-notes-abs-paths flag is passed to cc1, which calls vfs::FileSystem::makeAbsolute to get the absolute path. getFilename is moved to a member function in GCOVProfiler so that it can access the GCOVOptions. This also removes the need for its VFS argument and the getVirtualFileSystem member function. --- clang/include/clang/Basic/CodeGenOptions.h | 3 ++ clang/include/clang/Options/Options.td | 6 ++++ clang/lib/CodeGen/BackendUtil.cpp | 1 + clang/lib/Driver/ToolChains/Clang.cpp | 6 ++++ .../CodeGen/code-coverage-absolute-paths.c | 13 +++++++++ clang/test/Driver/coverage.c | 8 +++++ .../llvm/Transforms/Utils/Instrumentation.h | 3 ++ .../Instrumentation/GCOVProfiling.cpp | 29 ++++++++++++------- 8 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 clang/test/CodeGen/code-coverage-absolute-paths.c diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index c3a23415c922a..99a76b4edbf0e 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -292,6 +292,9 @@ class CodeGenOptions : public CodeGenOptionsBase { /// The filename with path we use for coverage notes files. std::string CoverageNotesFile; + /// Whether to force absolute paths in coverage notes files. + bool UseAbsolutePathsInNotesFile; + /// Regexes separated by a semi-colon to filter the files to instrument. std::string ProfileFilterFiles; diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 88fbd72f4eae5..82aba4d4da4ff 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -2020,6 +2020,10 @@ def fno_profile_use : Flag<["-"], "fno-profile-use">, def ftest_coverage : Flag<["-"], "ftest-coverage">, Group<f_Group>, HelpText<"Produce gcov notes files (*.gcno)">; def fno_test_coverage : Flag<["-"], "fno-test-coverage">, Group<f_Group>; +def fprofile_abs_path : Flag<["-"], "fprofile-abs-path">, + Group<f_Group>, Visibility<[ClangOption, CLOption]>, + HelpText<"Use absolute paths in gcov notes files (*.gcno)">; +def fno_profile_abs_path : Flag<["-"], "fno-profile-abs-path">, Group<f_Group>; def fprofile_arcs : Flag<["-"], "fprofile-arcs">, Group<f_Group>, HelpText<"Instrument code to produce gcov data files (*.gcda)">; def fno_profile_arcs : Flag<["-"], "fno-profile-arcs">, Group<f_Group>; @@ -8246,6 +8250,8 @@ def : Joined<["-"], "coverage-data-file=">, MarshallingInfoString<CodeGenOpts<"CoverageDataFile">>; def : Joined<["-"], "coverage-notes-file=">, MarshallingInfoString<CodeGenOpts<"CoverageNotesFile">>; +def : Joined<["-"], "coverage-notes-abs-paths">, + MarshallingInfoFlag<CodeGenOpts<"UseAbsolutePathsInNotesFile">>; def coverage_version_EQ : Joined<["-"], "coverage-version=">, HelpText<"Four-byte version string for gcov files.">; def dump_coverage_mapping : Flag<["-"], "dump-coverage-mapping">, diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index c754c75aa59de..6ba8ebd2bc4d8 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -527,6 +527,7 @@ getGCOVOptions(const CodeGenOptions &CodeGenOpts, const LangOptions &LangOpts) { // LLVM's -default-gcov-version flag is set to something invalid. GCOVOptions Options; Options.EmitNotes = !CodeGenOpts.CoverageNotesFile.empty(); + Options.UseAbsolutePathsInNotes = CodeGenOpts.UseAbsolutePathsInNotesFile; Options.EmitData = !CodeGenOpts.CoverageDataFile.empty(); llvm::copy(CodeGenOpts.CoverageVersion, std::begin(Options.Version)); Options.NoRedZone = CodeGenOpts.DisableRedZone; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 63e7c42549d3f..df29f8d9c98f0 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -688,6 +688,12 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C, if (EmitCovNotes) { CmdArgs.push_back( Args.MakeArgString("-coverage-notes-file=" + CoverageFilename)); + + bool UseAbsolutePathsInNotes = + Args.hasFlag(options::OPT_fprofile_abs_path, + options::OPT_fno_profile_abs_path, false); + if (UseAbsolutePathsInNotes) + CmdArgs.push_back(Args.MakeArgString("-coverage-notes-abs-paths")); } if (EmitCovData) { diff --git a/clang/test/CodeGen/code-coverage-absolute-paths.c b/clang/test/CodeGen/code-coverage-absolute-paths.c new file mode 100644 index 0000000000000..839fcda8774d5 --- /dev/null +++ b/clang/test/CodeGen/code-coverage-absolute-paths.c @@ -0,0 +1,13 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: cd %S + +// RUN: %clang_cc1 -emit-llvm-bc -o /dev/null -coverage-notes-file=%t/notes-relative.gcno %{s:basename} +// RUN: FileCheck --check-prefix=RELATIVE %s --input-file %t/notes-relative.gcno -DABSOLUTE_PATH=%s + +// RUN: %clang_cc1 -emit-llvm-bc -o /dev/null -coverage-notes-file=%t/notes-absolute.gcno -coverage-notes-abs-paths %{s:basename} +// RUN: FileCheck --check-prefix=ABSOLUTE %s --input-file %t/notes-absolute.gcno -DABSOLUTE_PATH=%s + +void test() {} + +// RELATIVE-NOT: [[ABSOLUTE_PATH]] +// ABSOLUTE: [[ABSOLUTE_PATH]] diff --git a/clang/test/Driver/coverage.c b/clang/test/Driver/coverage.c index ab791ada2d351..4aee8720470d4 100644 --- a/clang/test/Driver/coverage.c +++ b/clang/test/Driver/coverage.c @@ -32,6 +32,14 @@ // PROFILE-DIR: "-coverage-data-file=abc +/// Test -fprofile-abs-path +// RUN: %clang -### -S -ftest-coverage %s 2>&1 | FileCheck --check-prefix=NO-PROFILE-ABS-PATH %s +// RUN: %clang -### -S -fprofile-abs-path %s 2>&1 | FileCheck --check-prefix=NO-PROFILE-ABS-PATH %s +// RUN: %clang -### -S -ftest-coverage -fprofile-abs-path %s 2>&1 | FileCheck --check-prefix=PROFILE-ABS-PATH %s + +// NO-PROFILE-ABS-PATH-NOT: "-coverage-notes-abs-paths" +// PROFILE-ABS-PATH: "-coverage-notes-abs-paths" + /// These should only get passed if any of --coverage, -ftest-coverage, or /// -fprofile-arcs is passed. // RUN: %clang -### -c %s 2>&1 | FileCheck --check-prefix=NO-COV %s diff --git a/llvm/include/llvm/Transforms/Utils/Instrumentation.h b/llvm/include/llvm/Transforms/Utils/Instrumentation.h index 8405245b2bc0a..e8279ae58e2cb 100644 --- a/llvm/include/llvm/Transforms/Utils/Instrumentation.h +++ b/llvm/include/llvm/Transforms/Utils/Instrumentation.h @@ -67,6 +67,9 @@ struct GCOVOptions { // Specify whether to emit .gcno files. bool EmitNotes; + // Specify whether to force absolute paths in coverage notes files. + bool UseAbsolutePathsInNotes; + // Specify whether to modify the program to emit .gcda files when run. bool EmitData; diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp index 78b5dcf21cd03..0c96adddc72cd 100644 --- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp +++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp @@ -76,6 +76,7 @@ static unsigned wordsOfString(StringRef s) { GCOVOptions GCOVOptions::getDefault() { GCOVOptions Options; Options.EmitNotes = true; + Options.UseAbsolutePathsInNotes = false; Options.EmitData = true; Options.NoRedZone = false; Options.Atomic = AtomicCounter; @@ -113,7 +114,7 @@ class GCOVProfiler { os->write_zeros(4 - s.size() % 4); } void writeBytes(const char *Bytes, int Size) { os->write(Bytes, Size); } - vfs::FileSystem &getVirtualFileSystem() const { return VFS; } + SmallString<128> getFilename(const DIScope *SP); private: // Create the .gcno files for the Module based on DebugInfo. @@ -216,16 +217,22 @@ static StringRef getFunctionName(const DISubprogram *SP) { /// Extract a filename for a DIScope. /// -/// Prefer relative paths in the coverage notes. Clang also may split -/// up absolute paths into a directory and filename component. When -/// the relative path doesn't exist, reconstruct the absolute path. -static SmallString<128> getFilename(const DIScope *SP, vfs::FileSystem &VFS) { +/// Unless -fprofile-abs-path has been passed, prefer relative paths in the +/// coverage notes. Clang also may split up absolute paths into a directory and +/// filename component. When the relative path doesn't exist, reconstruct the +/// absolute path. +SmallString<128> GCOVProfiler::getFilename(const DIScope *SP) { SmallString<128> Path; StringRef RelPath = SP->getFilename(); - if (VFS.exists(RelPath)) + if (VFS.exists(RelPath)) { Path = RelPath; - else + if (Options.UseAbsolutePathsInNotes) { + VFS.makeAbsolute(Path); + sys::path::remove_dots(Path, true); + } + } else { sys::path::append(Path, SP->getDirectory(), SP->getFilename()); + } return Path; } @@ -362,7 +369,7 @@ namespace { void writeOut(uint32_t CfgChecksum) { write(GCOV_TAG_FUNCTION); - SmallString<128> Filename = getFilename(SP, P->getVirtualFileSystem()); + SmallString<128> Filename = P->getFilename(SP); uint32_t BlockLen = 3 + wordsOfString(getFunctionName(SP)); BlockLen += 1 + wordsOfString(Filename) + 4; @@ -460,7 +467,7 @@ bool GCOVProfiler::isFunctionInstrumented(const Function &F) { if (FilterRe.empty() && ExcludeRe.empty()) { return true; } - SmallString<128> Filename = getFilename(F.getSubprogram(), VFS); + SmallString<128> Filename = getFilename(F.getSubprogram()); auto It = InstrumentedFiles.find(Filename); if (It != InstrumentedFiles.end()) { return It->second; @@ -795,7 +802,7 @@ bool GCOVProfiler::emitProfileNotes( // Add the function line number to the lines of the entry block // to have a counter for the function definition. uint32_t Line = SP->getLine(); - auto Filename = getFilename(SP, VFS); + auto Filename = getFilename(SP); BranchProbabilityInfo *BPI = GetBPI(F); BlockFrequencyInfo *BFI = GetBFI(F); @@ -887,7 +894,7 @@ bool GCOVProfiler::emitProfileNotes( if (SP != getDISubprogram(Scope)) continue; - GCOVLines &Lines = Block.getFile(getFilename(Loc->getScope(), VFS)); + GCOVLines &Lines = Block.getFile(getFilename(Loc->getScope())); Lines.addLine(Loc.getLine()); } Line = 0; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
