]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
fix: Improve handling of .gcno files
authorJoel Rosdahl <joel@rosdahl.net>
Sun, 3 Apr 2022 06:59:46 +0000 (08:59 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Sun, 3 Apr 2022 07:06:17 +0000 (09:06 +0200)
When support for caching a compilation with coverage (producing a .gcno
file), the commit[1] made sure to avoid rewriting the input path to
relative with the motivation “also make sure to use the source file
path, since this is in the notes”. However, this seems to be unnecessary
since a relative input file path will be written as is to the .gcno, not
the absolute path. This is the case for at least GCC 4.7+ and Clang
3.6+. Fix this by potentially converting the input path to relative even
when generating coverage.

When investigating the above issue, I noticed that GCC 9+ includes the
current working directory (CWD) in the .gcno file. This means that we
have include the CWD in the hash when compiling with
-ftest-coverage/--coverage in order to replicate what the compiler would
produce. Since this makes it impossible get cache hits when compiling in
different directories, a new gcno_cwd sloppiness has been added for
opting out of hashing the CWD, with the tradeoff of potentially getting
an incorrect directory in the .gcno file.

[1]: 02d3f078bd2495b8db2264ae0b2c692b4c5ba1bd

Fixes #1032.

doc/MANUAL.adoc
src/Config.cpp
src/argprocessing.cpp
src/ccache.cpp
src/core/Sloppiness.hpp
test/suites/profiling.bash
unittest/test_Config.cpp

index 0cf11e62b710394c74ddb8edf437fa42db1b0460..05bb97d78609d073e7a730d7fcc7d47470f37475 100644 (file)
@@ -913,6 +913,14 @@ Examples:
 *file_stat_matches_ctime*::
     Ignore ctimes when *file_stat_matches* is enabled. This can be useful when
     backdating files' mtimes in a controlled way.
+*gcno_cwd*::
+    By default, ccache will include the current working directory in the hash
+    when producing a `.gcno` file (when compiling with `-ftest-coverage` or
+    `--coverage`). This is because GCC 9+ includes the current working directory
+    in the `.gcno` file. The *gcno_cwd* sloppiness makes ccache not hash the
+    current working directory so that you can get cache hits when compiling in
+    different directories, with the tradeoff of potentially getting an incorrect
+    directory in the `.gcno` file.
 *include_file_ctime*::
     By default, ccache will not cache a file if it includes a header whose ctime
     is too new. This sloppiness disables that check. See also
index ffc885977814ef3de6bc517ea32ccc1a7409d9f5..b6c51db55b81c8daebe0fe29bd0611a82892b407 100644 (file)
@@ -273,6 +273,8 @@ parse_sloppiness(const std::string& value)
       result.enable(core::Sloppy::file_stat_matches);
     } else if (token == "file_stat_matches_ctime") {
       result.enable(core::Sloppy::file_stat_matches_ctime);
+    } else if (token == "gcno_cwd") {
+      result.enable(core::Sloppy::gcno_cwd);
     } else if (token == "include_file_ctime") {
       result.enable(core::Sloppy::include_file_ctime);
     } else if (token == "include_file_mtime") {
@@ -308,6 +310,9 @@ format_sloppiness(core::Sloppiness sloppiness)
   if (sloppiness.is_enabled(core::Sloppy::file_stat_matches_ctime)) {
     result += "file_stat_matches_ctime, ";
   }
+  if (sloppiness.is_enabled(core::Sloppy::gcno_cwd)) {
+    result += "gcno_cwd, ";
+  }
   if (sloppiness.is_enabled(core::Sloppy::include_file_ctime)) {
     result += "include_file_ctime, ";
   }
index ab0e1c02623f659878c0ac742139e085ea174eda..fbd92188d0dc9098cf6ed95a8fadf05437bfe926 100644 (file)
@@ -1014,12 +1014,6 @@ process_arg(const Context& ctx,
     }
   }
 
-  // The source code file path gets put into the notes.
-  if (args_info.generating_coverage) {
-    args_info.input_file = args[i];
-    return nullopt;
-  }
-
   // Rewrite to relative to increase hit rate.
   args_info.input_file = Util::make_relative_path(ctx, args[i]);
 
index 8d30e37d7831aca37f33da055c229d9a5a56fd37..0b2fbd22e856167302a5c609a418b3c596776e3d 100644 (file)
@@ -1395,6 +1395,16 @@ hash_common_info(const Context& ctx,
     hash.hash(ctx.args_info.output_obj);
   }
 
+  if (ctx.args_info.generating_coverage
+      && !(ctx.config.sloppiness().is_enabled(core::Sloppy::gcno_cwd))) {
+    // GCC 9+ includes $PWD in the .gcno file. Since we don't have knowledge
+    // about compiler version we always (unless sloppiness is wanted) include
+    // the directory in the hash for now.
+    LOG_RAW("Hashing apparent CWD due to generating a .gcno file");
+    hash.hash_delimiter("CWD in .gcno");
+    hash.hash(ctx.apparent_cwd);
+  }
+
   // Possibly hash the coverage data file path.
   if (ctx.args_info.generating_coverage && ctx.args_info.profile_arcs) {
     std::string dir;
index 917526bf40061e2b70cca8edb7c7a0fae7875f16..fc4cff5cf5ba71a0599f9cd49174fa41bcfa5386 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 Joel Rosdahl and other contributors
+// Copyright (C) 2021-2022 Joel Rosdahl and other contributors
 //
 // See doc/AUTHORS.adoc for a complete list of contributors.
 //
@@ -46,6 +46,8 @@ enum class Sloppy : uint32_t {
   modules = 1U << 9,
   // Ignore virtual file system (VFS) overlay file.
   ivfsoverlay = 1U << 10,
+  // Allow us to include incorrect working directory in .gcno files.
+  gcno_cwd = 1U << 11,
 };
 
 class Sloppiness
index c1231b0ee600fe660b0eff47679a55742f634f99..d2bb2be876619d9021ed3fae4a2faafed019f200 100644 (file)
@@ -1,3 +1,11 @@
+# Remove header, including a volatile timestamp, from a .gcno file.
+normalize_gcno_file() {
+    local from="$1"
+    local to="$2"
+    cut -b 13- "${from}" >"${to}"
+}
+
+
 SUITE_profiling_PROBE() {
     touch test.c
     if ! $COMPILER -fprofile-generate -c test.c 2>/dev/null; then
@@ -202,6 +210,67 @@ SUITE_profiling() {
     expect_different_content obj1/test.o obj2/test.o # different paths to .gcda file
     expect_stat direct_cache_hit 2
     expect_stat cache_miss 2
+
+    # -------------------------------------------------------------------------
+    TEST "-ftest-coverage, different directories"
+
+    mkdir obj1 obj2
+
+    cd obj1
+    $COMPILER -ftest-coverage -c "$(pwd)/../test.c"
+    normalize_gcno_file test.gcno test.gcno.reference
+
+    $CCACHE_COMPILE -ftest-coverage -c "$(pwd)/../test.c"
+    expect_stat direct_cache_hit 0
+    expect_stat cache_miss 1
+    normalize_gcno_file test.gcno test.gcno.ccache-miss
+    expect_equal_content test.gcno.reference test.gcno.ccache-miss
+
+    $CCACHE_COMPILE -ftest-coverage -c "$(pwd)/../test.c"
+    expect_stat direct_cache_hit 1
+    expect_stat cache_miss 1
+    normalize_gcno_file test.gcno test.gcno.ccache-hit
+    expect_equal_content test.gcno.reference test.gcno.ccache-hit
+
+    cd ../obj2
+    $COMPILER -ftest-coverage -c "$(pwd)/../test.c"
+    normalize_gcno_file test.gcno test.gcno.reference
+
+    $CCACHE_COMPILE -ftest-coverage -c "$(pwd)/../test.c"
+    expect_stat direct_cache_hit 1
+    expect_stat cache_miss 2
+    normalize_gcno_file test.gcno test.gcno.ccache-miss
+    expect_equal_content test.gcno.reference test.gcno.ccache-miss
+
+    $CCACHE_COMPILE -ftest-coverage -c "$(pwd)/../test.c"
+    expect_stat direct_cache_hit 2
+    expect_stat cache_miss 2
+    normalize_gcno_file test.gcno test.gcno.ccache-hit
+    expect_equal_content test.gcno.reference test.gcno.ccache-hit
+
+    # -------------------------------------------------------------------------
+    TEST "-ftest-coverage, different directories, basedir, sloppy gcno_cwd"
+
+    export CCACHE_SLOPPINESS="$CCACHE_SLOPPINESS gcno_cwd"
+    export CCACHE_BASEDIR="$(pwd)"
+
+    mkdir obj1 obj2
+
+    cd obj1
+
+    $CCACHE_COMPILE -ftest-coverage -c "$(pwd)/../test.c"
+    expect_stat direct_cache_hit 0
+    expect_stat cache_miss 1
+
+    $CCACHE_COMPILE -ftest-coverage -c "$(pwd)/../test.c"
+    expect_stat direct_cache_hit 1
+    expect_stat cache_miss 1
+
+    cd ../obj2
+
+    $CCACHE_COMPILE -ftest-coverage -c "$(pwd)/../test.c"
+    expect_stat direct_cache_hit 2
+    expect_stat cache_miss 1
 }
 
 merge_profiling_data() {
index c5ff92ad9a9af51ee8eeee33cf94f1b007980f05..07443ecbae305816eecaa9da3393aef92897dce1 100644 (file)
@@ -130,7 +130,7 @@ TEST_CASE("Config::update_from_file")
     "run_second_cpp = false\n"
     "sloppiness =     time_macros   ,include_file_mtime"
     "  include_file_ctime,file_stat_matches,file_stat_matches_ctime,pch_defines"
-    " ,  no_system_headers,system_headers,clang_index_store,ivfsoverlay\n"
+    " ,  no_system_headers,system_headers,clang_index_store,ivfsoverlay,gcno_cwd\n"
     "stats = false\n"
     "temporary_dir = ${USER}_foo\n"
     "umask = 777"); // Note: no newline.
@@ -172,6 +172,7 @@ TEST_CASE("Config::update_from_file")
         == (static_cast<uint32_t>(core::Sloppy::clang_index_store)
             | static_cast<uint32_t>(core::Sloppy::file_stat_matches)
             | static_cast<uint32_t>(core::Sloppy::file_stat_matches_ctime)
+            | static_cast<uint32_t>(core::Sloppy::gcno_cwd)
             | static_cast<uint32_t>(core::Sloppy::include_file_ctime)
             | static_cast<uint32_t>(core::Sloppy::include_file_mtime)
             | static_cast<uint32_t>(core::Sloppy::ivfsoverlay)
@@ -415,7 +416,7 @@ TEST_CASE("Config::visit_items")
     "secondary_storage = ss\n"
     "sloppiness = include_file_mtime, include_file_ctime, time_macros,"
     " file_stat_matches, file_stat_matches_ctime, pch_defines, system_headers,"
-    " clang_index_store, ivfsoverlay\n"
+    " clang_index_store, ivfsoverlay, gcno_cwd\n"
     "stats = false\n"
     "stats_log = sl\n"
     "temporary_dir = td\n"
@@ -474,8 +475,9 @@ TEST_CASE("Config::visit_items")
     "(test.conf) run_second_cpp = false",
     "(test.conf) secondary_storage = ss",
     "(test.conf) sloppiness = clang_index_store, file_stat_matches,"
-    " file_stat_matches_ctime, include_file_ctime, include_file_mtime,"
-    " ivfsoverlay, pch_defines, system_headers, time_macros",
+    " file_stat_matches_ctime, gcno_cwd, include_file_ctime,"
+    " include_file_mtime, ivfsoverlay, pch_defines, system_headers,"
+    " time_macros",
     "(test.conf) stats = false",
     "(test.conf) stats_log = sl",
     "(test.conf) temporary_dir = td",