// Is the compiler being asked to output coverage data (.gcda) at runtime?
bool profile_arcs = false;
- // Name of the custom profile directory (default: object dirname).
- std::string profile_dir;
+ // Name of the custom profile directory or file.
+ std::string profile_path;
// Profile generation / usage information.
bool profile_use = false;
// Possibly hash the coverage data file path.
if (ctx.args_info.generating_coverage && ctx.args_info.profile_arcs) {
std::string dir;
- if (!ctx.args_info.profile_dir.empty()) {
- dir = ctx.args_info.profile_dir;
+ if (!ctx.args_info.profile_path.empty()) {
+ dir = ctx.args_info.profile_path;
} else {
dir =
Util::real_path(std::string(Util::dir_name(ctx.args_info.output_obj)));
}
}
+static bool
+hash_profile_data_file(const Context& ctx, struct hash* hash)
+{
+ const std::string& profile_path = ctx.args_info.profile_path;
+ string_view base_name = Util::remove_extension(ctx.args_info.output_obj);
+ std::string hashified_cwd = ctx.apparent_cwd;
+ std::replace(hashified_cwd.begin(), hashified_cwd.end(), '/', '#');
+
+ std::vector<std::string> paths_to_try{
+ // -fprofile-use[=dir]/-fbranch-probabilities (GCC <9)
+ fmt::format("{}/{}.gcda", profile_path, base_name),
+ // -fprofile-use[=dir]/-fbranch-probabilities (GCC >=9)
+ fmt::format("{}/{}#{}.gcda", profile_path, hashified_cwd, base_name),
+ // -fprofile(-instr)-use=file (Clang), -fauto-profile=file (GCC >=5)
+ profile_path,
+ // -fprofile(-instr)-use=dir (Clang)
+ fmt::format("{}/default.profdata", profile_path),
+ // -fauto-profile (GCC >=5)
+ "fbdata.afdo", // -fprofile-dir is not used
+ };
+
+ bool found = false;
+ for (const std::string& p : paths_to_try) {
+ cc_log("Checking for profile data file %s", p.c_str());
+ auto st = Stat::stat(p);
+ if (st && !st.is_directory()) {
+ cc_log("Adding profile data %s to the hash", p.c_str());
+ hash_delimiter(hash, "-fprofile-use");
+ if (hash_file(hash, p.c_str())) {
+ found = true;
+ }
+ }
+ }
+
+ return found;
+}
+
// Update a hash sum with information specific to the direct and preprocessor
// modes and calculate the result name. Returns the result name on success,
// otherwise NULL. Caller frees.
hash_nvcc_host_compiler(ctx, hash, nullptr, nullptr);
}
- // For profile generation (-fprofile-arcs, -fprofile-generate):
- // - hash profile directory
+ // For profile generation (-fprofile(-instr)-generate[=path])
+ // - hash profile path
//
- // For profile usage (-fprofile-use):
+ // For profile usage (-fprofile(-instr)-use, -fbranch-probabilities):
// - hash profile data
//
// -fbranch-probabilities and -fvpt usage is covered by
// -fprofile-generate/-fprofile-use.
//
// The profile directory can be specified as an argument to
- // -fprofile-generate=, -fprofile-use= or -fprofile-dir=.
+ // -fprofile(-instr)-generate=, -fprofile(-instr)-use= or -fprofile-dir=.
+
if (ctx.args_info.profile_generate) {
+ assert(!ctx.args_info.profile_path.empty());
cc_log("Adding profile directory %s to our hash",
- ctx.args_info.profile_dir.c_str());
+ ctx.args_info.profile_path.c_str());
hash_delimiter(hash, "-fprofile-dir");
- hash_string(hash, ctx.args_info.profile_dir);
+ hash_string(hash, ctx.args_info.profile_path);
}
- if (ctx.args_info.profile_use) {
- // Calculate gcda name.
- string_view base_name = Util::remove_extension(ctx.args_info.output_obj);
- std::string gcda_name =
- fmt::format("{}/{}.gcda", ctx.args_info.profile_dir, base_name);
- cc_log("Adding profile data %s to our hash", gcda_name.c_str());
- // Add the gcda to our hash.
- hash_delimiter(hash, "-fprofile-use");
- hash_file(hash, gcda_name.c_str());
+ if (ctx.args_info.profile_use && !hash_profile_data_file(ctx, hash)) {
+ cc_log("No profile data file found");
+ failed(STATS_NOINPUT);
}
// Adding -arch to hash since cpp output is affected.
return true;
}
+static bool
+process_profiling_option(Context& ctx, const std::string& arg)
+{
+ std::string new_profile_path;
+ bool new_profile_use = false;
+
+ if (Util::starts_with(arg, "-fprofile-dir=")) {
+ new_profile_path = arg.substr(arg.find('=') + 1);
+ } else if (arg == "-fprofile-generate" || arg == "-fprofile-instr-generate") {
+ ctx.args_info.profile_generate = true;
+ if (ctx.guessed_compiler == GuessedCompiler::clang) {
+ new_profile_path = ".";
+ } else {
+ // GCC uses $PWD/$(basename $obj).
+ new_profile_path = ctx.apparent_cwd;
+ }
+ } else if (Util::starts_with(arg, "-fprofile-generate=")
+ || Util::starts_with(arg, "-fprofile-instr-generate=")) {
+ ctx.args_info.profile_generate = true;
+ new_profile_path = arg.substr(arg.find('=') + 1);
+ } else if (arg == "-fprofile-use" || arg == "-fprofile-instr-use"
+ || arg == "-fbranch-probabilities" || arg == "-fauto-profile") {
+ new_profile_use = true;
+ if (ctx.args_info.profile_path.empty()) {
+ new_profile_path = ".";
+ }
+ } else if (Util::starts_with(arg, "-fprofile-use=")
+ || Util::starts_with(arg, "-fprofile-instr-use=")
+ || Util::starts_with(arg, "-fauto-profile=")) {
+ new_profile_use = true;
+ new_profile_path = arg.substr(arg.find('=') + 1);
+ } else {
+ cc_log("Unknown profiling option: %s", arg.c_str());
+ return false;
+ }
+
+ if (new_profile_use) {
+ if (ctx.args_info.profile_use) {
+ cc_log("Multiple profiling options not supported");
+ return false;
+ }
+ ctx.args_info.profile_use = true;
+ }
+
+ if (!new_profile_path.empty()) {
+ ctx.args_info.profile_path = new_profile_path;
+ cc_log("Set profile directory to %s", ctx.args_info.profile_path.c_str());
+ }
+
+ if (ctx.args_info.profile_generate && ctx.args_info.profile_use) {
+ // Too hard to figure out what the compiler will do.
+ cc_log("Both generating and using profile info, giving up");
+ return false;
+ }
+
+ return true;
+}
+
// Process the compiler options into options suitable for passing to the
// preprocessor and the real compiler. preprocessor_args doesn't include -E;
// this is added later. extra_args_to_hash are the arguments that are not
for (size_t i = 1; i < expanded_args.size(); i++) {
size_t argc = expanded_args.size();
+ const std::string& arg = expanded_args[i];
// The user knows best: just swallow the next arg.
if (str_eq(argv[i], "--ccache-skip")) {
// -Xarch_* options are too hard.
if (str_startswith(argv[i], "-Xarch_")) {
- cc_log("Unsupported compiler option :%s", argv[i]);
+ cc_log("Unsupported compiler option: %s", argv[i]);
return STATS_UNSUPPORTED_OPTION;
}
args_add(common_args, argv[i]);
continue;
}
- if (str_startswith(argv[i], "-fprofile-dir=")) {
- args_info.profile_dir.assign(argv[i] + 14);
+ if (Util::starts_with(arg, "-fprofile-")
+ || Util::starts_with(arg, "-fauto-profile")
+ || arg == "-fbranch-probabilities") {
+ if (!process_profiling_option(ctx, argv[i])) {
+ // The failure is logged by process_profiling_option.
+ return STATS_UNSUPPORTED_OPTION;
+ }
args_add(common_args, argv[i]);
continue;
}
continue;
}
- if (str_startswith(argv[i], "-fprofile-")) {
- char* arg = x_strdup(argv[i]);
- const char* arg_profile_dir = strchr(argv[i], '=');
- if (arg_profile_dir) {
- // Convert to absolute path.
- std::string dir = Util::real_path(arg_profile_dir + 1);
-
- // We can get a better hit rate by using the real path here.
- free(arg);
- char* option = x_strndup(argv[i], arg_profile_dir - argv[i]);
- arg = format("%s=%s", option, dir.c_str());
- cc_log("Rewriting %s to %s", argv[i], arg);
- free(option);
- }
-
- bool supported_profile_option = false;
- if (str_startswith(argv[i], "-fprofile-generate")
- || str_eq(argv[i], "-fprofile-arcs")) {
- args_info.profile_generate = true;
- supported_profile_option = true;
- } else if (str_startswith(argv[i], "-fprofile-use")
- || str_eq(argv[i], "-fbranch-probabilities")) {
- args_info.profile_use = true;
- supported_profile_option = true;
- } else if (str_eq(argv[i], "-fprofile-dir")) {
- supported_profile_option = true;
- }
-
- if (supported_profile_option) {
- args_add(common_args, arg);
- free(arg);
-
- // If the profile directory has already been set, give up... Hard to
- // know what the user means, and what the compiler will do.
- if (arg_profile_dir && !args_info.profile_dir.empty()) {
- cc_log("Profile directory already set; giving up");
- return STATS_UNSUPPORTED_OPTION;
- } else if (arg_profile_dir) {
- cc_log("Setting profile directory to %s", arg_profile_dir);
- args_info.profile_dir = from_cstr(arg_profile_dir);
- }
- continue;
- }
- cc_log("Unknown profile option: %s", argv[i]);
- free(arg);
- }
-
if (str_eq(argv[i], "-fcolor-diagnostics")
|| str_eq(argv[i], "-fno-color-diagnostics")
|| str_eq(argv[i], "-fdiagnostics-color")
}
}
- if (args_info.profile_dir.empty()) {
- args_info.profile_dir = ctx.apparent_cwd;
+ if (args_info.profile_path.empty()) {
+ args_info.profile_path = ctx.apparent_cwd;
}
if (explicit_language && str_eq(explicit_language, "none")) {
;;
*clang*)
COMPILER_TYPE_CLANG=true
+ CLANG_VERSION_SUFFIX=$(echo $COMPILER | sed -r 's/.*clang//')
;;
*)
echo "WARNING: Compiler $COMPILER not supported (version: $compiler_version) -- not running tests" >&2
serialize_diagnostics
sanitize_blacklist
debug_prefix_map
+profiling
+profiling_gcc
+profiling_clang
split_dwarf
masquerading
hardlink
--- /dev/null
+SUITE_profiling_PROBE() {
+ touch test.c
+ if ! $COMPILER -fprofile-generate -c test.c 2>/dev/null; then
+ echo "compiler does not support profiling"
+ fi
+ if $COMPILER_TYPE_CLANG && ! which llvm-profdata$CLANG_VERSION_SUFFIX >/dev/null 2>/dev/null; then
+ echo "llvm-profdata$CLANG_VERSION_SUFFIX tool not found"
+ fi
+}
+
+SUITE_profiling_SETUP() {
+ echo 'int main(void) { return 0; }' >test.c
+ unset CCACHE_NODIRECT
+}
+
+SUITE_profiling() {
+ # -------------------------------------------------------------------------
+ TEST "-fprofile-use, missing file"
+
+ $CCACHE_COMPILE -fprofile-use -c test.c 2>/dev/null
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 0
+ expect_stat 'no input file' 1
+
+ # -------------------------------------------------------------------------
+ TEST "-fbranch-probabilities, missing file"
+
+ $CCACHE_COMPILE -fbranch-probabilities -c test.c 2>/dev/null
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 0
+ expect_stat 'no input file' 1
+
+ # -------------------------------------------------------------------------
+ TEST "-fprofile-use=file, missing file"
+
+ $CCACHE_COMPILE -fprofile-use=data.gcda -c test.c 2>/dev/null
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 0
+ expect_stat 'no input file' 1
+
+ # -------------------------------------------------------------------------
+ TEST "-fprofile-use"
+
+ $CCACHE_COMPILE -fprofile-generate -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ $COMPILER -fprofile-generate test.o -o test
+
+ ./test
+ merge_profiling_data .
+
+ $CCACHE_COMPILE -fprofile-use -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 2
+
+ $CCACHE_COMPILE -fprofile-use -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+
+ ./test
+ merge_profiling_data .
+
+ $CCACHE_COMPILE -fprofile-use -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 3
+
+ # -------------------------------------------------------------------------
+ TEST "-fprofile-use=dir"
+
+ mkdir data
+
+ $CCACHE_COMPILE -fprofile-generate=data -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ $COMPILER -fprofile-generate=data test.o -o test
+
+ ./test
+ merge_profiling_data data
+
+ $CCACHE_COMPILE -fprofile-use=data -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 2
+
+ $CCACHE_COMPILE -fprofile-use=data -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+
+ ./test
+ merge_profiling_data data
+
+ $CCACHE_COMPILE -fprofile-use=data -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 3
+}
+
+merge_profiling_data() {
+ local dir=$1
+ if $COMPILER_TYPE_CLANG; then
+ llvm-profdata$CLANG_VERSION_SUFFIX merge -output $dir/default.profdata $dir/*.profraw
+ fi
+}
--- /dev/null
+SUITE_profiling_clang_PROBE() {
+ if ! $COMPILER_TYPE_CLANG; then
+ echo "compiler is not Clang"
+ fi
+ if ! which llvm-profdata$CLANG_VERSION_SUFFIX >/dev/null 2>/dev/null; then
+ echo "llvm-profdata$CLANG_VERSION_SUFFIX tool not found"
+ fi
+}
+
+SUITE_profiling_clang_SETUP() {
+ echo 'int main(void) { return 0; }' >test.c
+ unset CCACHE_NODIRECT
+}
+
+SUITE_profiling_clang() {
+ # -------------------------------------------------------------------------
+ TEST "-fprofile-use=file"
+
+ mkdir data
+
+ $CCACHE_COMPILE -fprofile-generate=data -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ $COMPILER -fprofile-generate=data test.o -o test
+
+ ./test
+ llvm-profdata$CLANG_VERSION_SUFFIX merge -output foo.profdata data/default_*.profraw
+
+ $CCACHE_COMPILE -fprofile-use=foo.profdata -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 2
+
+ $CCACHE_COMPILE -fprofile-use=foo.profdata -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+
+ ./test
+ llvm-profdata$CLANG_VERSION_SUFFIX merge -output foo.profdata data/default_*.profraw
+
+ $CCACHE_COMPILE -fprofile-use=foo.profdata -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 3
+
+ # -------------------------------------------------------------------------
+ TEST "-fprofile-instr-use"
+
+ mkdir data
+
+ $CCACHE_COMPILE -fprofile-instr-generate -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ $COMPILER -fprofile-instr-generate test.o -o test
+
+ ./test
+ llvm-profdata$CLANG_VERSION_SUFFIX merge -output default.profdata default.profraw
+
+ $CCACHE_COMPILE -fprofile-instr-use -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 2
+
+ $CCACHE_COMPILE -fprofile-instr-use -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+
+ echo >>default.profdata # Dummy change to trigger modification
+
+ $CCACHE_COMPILE -fprofile-instr-use -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 3
+
+ # -------------------------------------------------------------------------
+ TEST "-fprofile-instr-use=file"
+
+ $CCACHE_COMPILE -fprofile-instr-generate=foo.profraw -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ $COMPILER -fprofile-instr-generate=data=foo.profraw test.o -o test
+
+ ./test
+ llvm-profdata$CLANG_VERSION_SUFFIX merge -output foo.profdata foo.profraw
+
+ $CCACHE_COMPILE -fprofile-instr-use=foo.profdata -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 2
+
+ $CCACHE_COMPILE -fprofile-instr-use=foo.profdata -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+
+ echo >>foo.profdata # Dummy change to trigger modification
+
+ $CCACHE_COMPILE -fprofile-instr-use=foo.profdata -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 3
+}
--- /dev/null
+SUITE_profiling_gcc_PROBE() {
+ if ! $COMPILER_TYPE_GCC; then
+ echo "compiler is not GCC"
+ fi
+}
+
+SUITE_profiling_gcc_SETUP() {
+ echo 'int main(void) { return 0; }' >test.c
+ unset CCACHE_NODIRECT
+}
+
+SUITE_profiling_gcc() {
+ # -------------------------------------------------------------------------
+ TEST "-fbranch-probabilities"
+
+ $CCACHE_COMPILE -fprofile-generate -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ $COMPILER -fprofile-generate test.o -o test
+
+ ./test
+
+ $CCACHE_COMPILE -fbranch-probabilities -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 2
+
+ $CCACHE_COMPILE -fbranch-probabilities -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+
+ ./test
+
+ $CCACHE_COMPILE -fbranch-probabilities -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 3
+
+ # -------------------------------------------------------------------------
+ TEST "-fprofile-dir=dir + -fprofile-use"
+
+ mkdir data
+
+ $CCACHE_COMPILE -fprofile-dir=data -fprofile-generate -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ $COMPILER -fprofile-dir=data -fprofile-generate test.o -o test
+
+ ./test
+
+ $CCACHE_COMPILE -fprofile-dir=data -fprofile-use -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 2
+
+ $CCACHE_COMPILE -fprofile-dir=data -fprofile-use -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+
+ ./test
+
+ $CCACHE_COMPILE -fprofile-dir=data -fprofile-use -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 3
+
+ # -------------------------------------------------------------------------
+ TEST "-fprofile-use + -fprofile-dir=dir"
+
+ mkdir data
+
+ $CCACHE_COMPILE -fprofile-generate -fprofile-dir=data -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ $COMPILER -fprofile-generate -fprofile-dir=data test.o -o test
+
+ ./test
+
+ $CCACHE_COMPILE -fprofile-use -fprofile-dir=data -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 2
+
+ $CCACHE_COMPILE -fprofile-use -fprofile-dir=data -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+
+ ./test
+
+ $CCACHE_COMPILE -fprofile-use -fprofile-dir=data -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 3
+
+ # -------------------------------------------------------------------------
+ TEST "-fprofile-dir=path1 + -fprofile-use=path2"
+
+ mkdir data
+
+ $CCACHE_COMPILE -fprofile-dir=data2 -fprofile-generate=data -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 1
+
+ $COMPILER -fprofile-dir=data2 -fprofile-generate=data test.o -o test
+
+ ./test
+
+ $CCACHE_COMPILE -fprofile-dir=data2 -fprofile-use=data -c test.c
+ expect_stat 'cache hit (direct)' 0
+ expect_stat 'cache miss' 2
+
+ $CCACHE_COMPILE -fprofile-dir=data2 -fprofile-use=data -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 2
+
+ ./test
+
+ $CCACHE_COMPILE -fprofile-dir=data2 -fprofile-use=data -c test.c
+ expect_stat 'cache hit (direct)' 1
+ expect_stat 'cache miss' 3
+}
CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
}
-TEST(fprofile_flag_with_existing_dir_should_be_rewritten_to_real_path)
-{
- Context ctx;
-
- Args orig = args_init_from_string("gcc -c -fprofile-generate=some/dir foo.c");
- Args exp_cpp = args_init_from_string("gcc");
- Args exp_extra = args_init(0, NULL);
- Args exp_cc = args_init_from_string("gcc");
- Args act_cpp;
- Args act_extra;
- Args act_cc;
-
- char* s;
-
- create_file("foo.c", "");
- mkdir("some", 0777);
- mkdir("some/dir", 0777);
- std::string path = Util::real_path("some/dir");
- s = format("-fprofile-generate=%s", path.c_str());
- args_add(exp_cpp, s);
- args_add(exp_cc, s);
- args_add(exp_cc, "-c");
- free(s);
-
- CHECK(!process_args(ctx, orig, act_cpp, act_extra, act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_extra, act_extra);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-}
-
-TEST(fprofile_flag_with_nonexistent_dir_should_not_be_rewritten)
-{
- Context ctx;
-
- Args orig = args_init_from_string("gcc -c -fprofile-generate=some/dir foo.c");
- Args exp_cpp = args_init_from_string("gcc -fprofile-generate=some/dir");
- Args exp_extra = args_init(0, NULL);
- Args exp_cc = args_init_from_string("gcc -fprofile-generate=some/dir -c");
- Args act_cpp;
- Args act_extra;
- Args act_cc;
-
- create_file("foo.c", "");
-
- CHECK(!process_args(ctx, orig, act_cpp, act_extra, act_cc));
- CHECK_ARGS_EQ_FREE12(exp_cpp, act_cpp);
- CHECK_ARGS_EQ_FREE12(exp_extra, act_extra);
- CHECK_ARGS_EQ_FREE12(exp_cc, act_cc);
-}
-
TEST(isystem_flag_with_separate_arg_should_be_rewritten_if_basedir_is_used)
{
Context ctx;