From d515b8125364ef85fb100f1430b2174ed816cc18 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 17 May 2023 00:43:00 -0700 Subject: [PATCH] [Driver][gcov] Derive .gcno .gcda file names from -o and -dumpdir Resolve a FIXME. When -ftest-profile, -fprofile-arcs, or --coverage is specified and the driver performs both compilation and linking phases, derive the .gcno & .gcda file names from -o and -dumpdir. `clang --coverage d/a.c d/b.c -o e/x && e/x` will now emit `e/x-[ab].gc{no,da}` like GCC. For -fprofile-dir=, we make the deliberate decision to not mangle the input filename if relative. The following script demonstrates the .gcno and .gcda filenames. ``` PATH=/tmp/Rel/bin:$PATH # adapt according to your build directory mkdir -p d e f echo 'int main() {}' > d/a.c echo > d/b.c a() { rm $@ || exit 1; } clang --coverage d/a.c d/b.c && ./a.out a a-[ab].gc{no,da} clang --coverage d/a.c d/b.c -o e/x && e/x a e/x-[ab].gc{no,da} clang --coverage d/a.c d/b.c -o e/x -dumpdir f/ && e/x a f/[ab].gc{no,da} clang --coverage -fprofile-dir=f d/a.c d/b.c -o e/x && e/x a e/x-[ab].gcno f/e/x-[ab].gcda clang -c --coverage d/a.c d/b.c && clang --coverage a.o b.o && ./a.out a [ab].gc{no,da} clang -c --coverage -fprofile-dir=f d/a.c d/b.c && clang --coverage a.o b.o && ./a.out a [ab].gcno f/[ab].gcda clang -c --coverage d/a.c -o e/xa.o && clang --coverage e/xa.o && ./a.out a e/xa.gc{no,da} clang -c --coverage d/a.c -o e/xa.o -dumpdir f/g && clang --coverage e/xa.o && ./a.out a f/ga.gc{no,da} ``` The gcov code accidentally claims -c and -S, so -fsyntax-only -c/-S and -E -c/-S don't leave to a -Wunused-command-line-argument warning. Keep the unintended code for now. --- clang/lib/Driver/ToolChains/Clang.cpp | 50 ++++++++++++++++++++--------------- clang/test/Driver/coverage.c | 27 ++++++++++++++----- clang/test/Driver/working-directory.c | 2 +- 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index e2c4012..108be34 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -705,10 +705,10 @@ static void addDashXForInput(const ArgList &Args, const InputInfo &Input, } static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C, - const Driver &D, const InputInfo &Output, + const JobAction &JA, const InputInfo &Output, const ArgList &Args, SanitizerArgs &SanArgs, ArgStringList &CmdArgs) { - + const Driver &D = TC.getDriver(); auto *PGOGenerateArg = Args.getLastArg(options::OPT_fprofile_generate, options::OPT_fprofile_generate_EQ, options::OPT_fno_profile_generate); @@ -911,32 +911,38 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C, Args.hasArg(options::OPT_coverage)) FProfileDir = Args.getLastArg(options::OPT_fprofile_dir); + // TODO: Don't claim -c/-S to warn about -fsyntax-only -c/-S, -E -c/-S, + // like we warn about -fsyntax-only -E. + (void)(Args.hasArg(options::OPT_c) || Args.hasArg(options::OPT_S)); + // Put the .gcno and .gcda files (if needed) next to the object file or // bitcode file in the case of LTO. - // FIXME: There should be a simpler way to find the object file for this - // input, and this code probably does the wrong thing for commands that - // compile and link all at once. - if ((Args.hasArg(options::OPT_c) || Args.hasArg(options::OPT_S)) && - (EmitCovNotes || EmitCovData) && Output.isFilename()) { - SmallString<128> OutputFilename; - if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT__SLASH_Fo)) - OutputFilename = FinalOutput->getValue(); - else if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) - OutputFilename = FinalOutput->getValue(); - else - OutputFilename = llvm::sys::path::filename(Output.getBaseInput()); - SmallString<128> CoverageFilename = OutputFilename; - if (llvm::sys::path::is_relative(CoverageFilename)) - (void)D.getVFS().makeAbsolute(CoverageFilename); + if (EmitCovNotes || EmitCovData) { + SmallString<128> CoverageFilename; + if (Arg *DumpDir = Args.getLastArgNoClaim(options::OPT_dumpdir)) { + // Form ${dumpdir}${basename}.gcno. Note that dumpdir may not end with a + // path separator. + CoverageFilename = DumpDir->getValue(); + CoverageFilename += llvm::sys::path::filename(Output.getBaseInput()); + } else if (Arg *FinalOutput = + C.getArgs().getLastArg(options::OPT__SLASH_Fo)) { + CoverageFilename = FinalOutput->getValue(); + } else if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) { + CoverageFilename = FinalOutput->getValue(); + } else { + CoverageFilename = llvm::sys::path::filename(Output.getBaseInput()); + } llvm::sys::path::replace_extension(CoverageFilename, "gcno"); - - CmdArgs.push_back("-coverage-notes-file"); - CmdArgs.push_back(Args.MakeArgString(CoverageFilename)); + if (EmitCovNotes) { + CmdArgs.push_back("-coverage-notes-file"); + CmdArgs.push_back(Args.MakeArgString(CoverageFilename)); + } if (EmitCovData) { if (FProfileDir) { + SmallString<128> Gcno = CoverageFilename; CoverageFilename = FProfileDir->getValue(); - llvm::sys::path::append(CoverageFilename, OutputFilename); + llvm::sys::path::append(CoverageFilename, Gcno); } llvm::sys::path::replace_extension(CoverageFilename, "gcda"); CmdArgs.push_back("-coverage-data-file"); @@ -5760,7 +5766,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // for sampling, overhead of call arc collection is way too high and there's // no way to collect the output. if (!Triple.isNVPTX() && !Triple.isAMDGCN()) - addPGOAndCoverageFlags(TC, C, D, Output, Args, SanitizeArgs, CmdArgs); + addPGOAndCoverageFlags(TC, C, JA, Output, Args, SanitizeArgs, CmdArgs); Args.AddLastArg(CmdArgs, options::OPT_fclang_abi_compat_EQ); diff --git a/clang/test/Driver/coverage.c b/clang/test/Driver/coverage.c index b1a0456..bb0bf45 100644 --- a/clang/test/Driver/coverage.c +++ b/clang/test/Driver/coverage.c @@ -2,22 +2,22 @@ // RUN: %clang -### -S -ftest-coverage -fno-test-coverage %s 2>&1 | FileCheck --check-prefix=NO-TEST-COVERAGE %s // TEST-COVERAGE: "-ftest-coverage" -// TEST-COVERAGE: "-coverage-notes-file" "{{.*}}{{/|\\\\}}coverage.gcno" +// TEST-COVERAGE: "-coverage-notes-file" "coverage.gcno" // NO-TEST-COVERAGE-NOT: "-coverage-notes-file" // RUN: %clang -### -S -fprofile-arcs %s 2>&1 | FileCheck --check-prefix=PROFILE-ARCS %s // RUN: %clang -### -S -fprofile-arcs -fno-profile-arcs %s 2>&1 | FileCheck --check-prefix=NO-PROFILE-ARCS %s +// NO-PROFILE-ARCS-NOT: "-coverage-notes-file" // PROFILE-ARCS: "-fprofile-arcs" -// PROFILE-ARCS: "-coverage-notes-file" "{{.*}}{{/|\\\\}}coverage.c" -// NO-PROFILE-ARCS-NOT: "-ftest-coverage" +// PROFILE-ARCS: "-coverage-data-file" "coverage.gcda" -// RUN: %clang -### -S -fprofile-arcs %s -o /foo/bar.o 2>&1 | FileCheck --check-prefix=GCNO-LOCATION %s +// RUN: %clang -### -S -ftest-coverage %s -o /foo/bar.o 2>&1 | FileCheck --check-prefix=GCNO-LOCATION %s // RUN: %clang_cl -### /c --coverage /Fo/foo/bar.obj -- %s 2>&1 | FileCheck --check-prefix=GCNO-LOCATION %s -// RUN: %clang -### -c -fprofile-arcs %s -o foo/bar.o 2>&1 | FileCheck --check-prefix=GCNO-LOCATION-REL %s +// RUN: %clang -### -c -ftest-coverage %s -o foo/bar.o 2>&1 | FileCheck --check-prefix=GCNO-LOCATION-REL %s // GCNO-LOCATION: "-coverage-notes-file" "{{.*}}/foo/bar.gcno" -// GCNO-LOCATION-REL: "-coverage-notes-file" "{{.*}}{{/|\\\\}}foo/bar.gcno" +// GCNO-LOCATION-REL: "-coverage-notes-file" "foo/bar.gcno" /// Don't warn -Wunused-command-line-argument. // RUN: %clang -E -Werror --coverage -ftest-coverage -fprofile-arcs %s @@ -35,3 +35,18 @@ // RUN: %clang -### -c %s 2>&1 | FileCheck --check-prefix=NO-COV %s // NO-COV-NOT: "-coverage-notes-file" // NO-COV-NOT: "-coverage-data-file" + +// RUN: rm -rf %t && mkdir %t && cd %t +// RUN: mkdir d e f && cp %s d/a.c && touch d/b.c + +// RUN: %clang -### --coverage d/a.c d/b.c -o e/x 2>&1 | FileCheck %s --check-prefix=LINK1 +// LINK1: -cc1{{.*}} "-coverage-notes-file" "e/x-a.gcno" "-coverage-data-file" "e/x-a.gcda" +// LINK1: -cc1{{.*}} "-coverage-notes-file" "e/x-b.gcno" "-coverage-data-file" "e/x-b.gcda" + +// RUN: %clang -### --coverage d/a.c d/b.c -o e/x -dumpdir f/g 2>&1 | FileCheck %s --check-prefix=LINK2 +// LINK2: -cc1{{.*}} "-coverage-notes-file" "f/ga.gcno" "-coverage-data-file" "f/ga.gcda" +// LINK2: -cc1{{.*}} "-coverage-notes-file" "f/gb.gcno" "-coverage-data-file" "f/gb.gcda" + +// RUN: %clang -### --coverage d/a.c d/b.c -o e/x -fprofile-dir=f 2>&1 | FileCheck %s --check-prefix=LINK3 +// LINK3: -cc1{{.*}} "-coverage-notes-file" "e/x-a.gcno" "-coverage-data-file" "f{{/|\\\\}}e/x-a.gcda" +// LINK3: -cc1{{.*}} "-coverage-notes-file" "e/x-b.gcno" "-coverage-data-file" "f{{/|\\\\}}e/x-b.gcda" diff --git a/clang/test/Driver/working-directory.c b/clang/test/Driver/working-directory.c index 75c6dc4..8dd6be6 100644 --- a/clang/test/Driver/working-directory.c +++ b/clang/test/Driver/working-directory.c @@ -6,6 +6,6 @@ // CHECK_NO_FILE: no such file or directory: 'no_such_file.cpp' -// CHECK_WORKS: "-coverage-notes-file" "{{[^"]+}}test{{/|\\\\}}Driver{{/|\\\\}}Inputs{{/|\\\\}}pchfile.gcno" +// CHECK_WORKS: "-coverage-notes-file" "pchfile.gcno" // CHECK_WORKS: "-working-directory" "{{[^"]+}}test{{/|\\\\}}Driver{{/|\\\\}}Inputs" // CHECK_WORKS: "-fdebug-compilation-dir={{[^"]+}}test{{/|\\\\}}Driver{{/|\\\\}}Inputs" -- 2.7.4