diff --git a/ccache.c b/ccache.c index c407760..434a3f9 100644 --- a/ccache.c +++ b/ccache.c @@ -95,9 +95,15 @@ static char *output_obj; /* The path to the dependency file (implicit or specified with -MF). */ static char *output_dep; -/* Diagnostic generation information (clang). */ +/* Diagnostic generation information (clang). Contains pathname if not NULL. */ static char *output_dia = NULL; +/* -gsplit-dwarf support: Split dwarf information (GCC 4.8 and up). Contains pathname if not NULL. */ +static char *output_dwo = NULL; + +/* in conjunction with -gsplit-dwarf, try to reduce copying */ +static int called_compiler = 0; + /* * Name (represented as a struct file_hash) of the file containing the cached * object code. @@ -129,6 +135,21 @@ static char *cached_dep; static char *cached_dia; /* + * -gsplit-dwarf support: + * Full path to the file containing the split dwarf (for GCC 4.8 and above) + * (cachedir/a/b/cdef[...]-size.dwo). + * + * contains NULL if -gsplit-dwarf is not given. + */ +static char *cached_dwo; + +/* + * -gsplit-dwarf support: + * split_dwarf_p is true if "-gsplit-dwarf" is given to the compiler (GCC 4.8 and up). + */ +bool split_dwarf_p = false; + +/* * Full path to the file containing the manifest * (cachedir/a/b/cdef[...]-size.manifest). */ @@ -167,6 +188,24 @@ char *stats_file = NULL; /* Whether the output is a precompiled header */ static bool output_is_precompiled_header = false; +/* + * -gsplit-dwarf support: + * Also used for profile support (?) + * + * Whether we should output to the real object first before saving + * into cache. + * This direct output to the real object first + * becomes necessary when a produced object file needs + * to contain a valid source pathname (or part of it) such as in the + * case of .o file containing the pathname of .dwo file when + * -gsplit-dwarf is specified. + * Unless output_to_real_object_first is set to true, the recorded .dwo + * filepath in .o file ends up as that of a temporary file which is + * removed after compilation. + */ + +static bool output_to_real_object_first = false; + /* Profile generation / usage information */ static char *profile_dir = NULL; static bool profile_use = false; @@ -261,6 +300,7 @@ failed(void) fatal("execv of %s failed: %s", orig_args->argv[0], strerror(errno)); } + static const char * temp_dir() { @@ -735,19 +775,89 @@ static void to_cache(struct args *args) { char *tmp_stdout, *tmp_stderr, *tmp_dia; + char *tmp_obj; /* for supporting -gsplit-dwarf */ + char *tmp_dwo; /* for supporting -gsplit-dwarf */ + char *cp; /* for supporting -gsplit-dwarf */ struct stat st; int status, tmp_stdout_fd, tmp_stderr_fd; + /* Here cached_obj contains a file name for object */ + tmp_stdout = format("%s.tmp.stdout", cached_obj); tmp_stdout_fd = create_tmp_fd(&tmp_stdout); tmp_stderr = format("%s.tmp.stderr", cached_obj); tmp_stderr_fd = create_tmp_fd(&tmp_stderr); + /* -gsplit-dwarf support: + * we ought to create tmp_dwo path name, too + * if -gsplit-dwarf is used. + * tmp_obj will contain a file path name. + */ + if (output_to_real_object_first) { + tmp_obj = x_strdup(output_obj); + cc_log("(tmp_obj) Outputting to final destination: %s", tmp_obj); + } else { + tmp_obj = format("%s.tmp.%s", cached_obj, tmp_string()); + cc_log("(tmp_obj) Outputting to temporary destination: %s", tmp_obj); + } + + /* -gsplit-dwarf support: creating .two file name + * + * What is the reliable way of creating the .two file name + * in the case of using |-o tmp_obj| to the compiler (?) + * Heuristics based on one run of GCC 4.8 is placed below, + * and it works with gcc 4.9 as well. + * + * no matter whether -gsplit-warning is placed on the + * command line, or if so, whether it has been seen or not, + * we need to set tmp_dwo here to reflect the + * value of the current tmp_obj above to tmp_dwo. + * If -gsplit-dwarf is specified on the command line, this tmp_dwo + * will be used. + */ + + // In the extreme simple case, + //if (output_to_real_object_first) { + // tmp_obj = x_strdup(output_obj); + // cc_log("Outputting to final destination: %s", tmp_obj); + // Try replacing .o with .dwo. + // + // Not sure what .dwo file path name which GCC produces in + // the following case. + // tmp_dwo concatenate ".dwo" to tmp_obj ; ??? + // tmp_obj = format("%s.tmp.%s", cached_obj, tmp_string()); + // + // Fact: gcc-4.8 produced the following .two file for + // the command below. + // t-test.o.tmp.gazonk + // t-test.o.tmp.dwo + // /usr/bin/gcc-4.8 -gsplit-dwarf -c t-test.c -o t-test.o.tmp.gazonk + // So here is the huristics. + // Strip anything after the final '.' and add "dwo" instead. + + cp = strrchr(tmp_obj, '.'); + assert(cp != NULL); + { + /* should be ATOMIC, but since we already created cp using + * x_strdup, and format, we should be relatively safe. + */ + *cp = '\0'; + tmp_dwo = format("%s.dwo", tmp_obj); + *cp = '.'; + } + cc_log("Setting tmp_dwo to %s", tmp_dwo); + args_add(args, "-o"); - args_add(args, output_obj); + + /* change mandated by -gsplit-dwarf support above: + Let's make the compiler output to |tmp_obj| instead of output_obj*/ + args_add(args, tmp_obj); if (output_dia) { - tmp_dia = x_strdup(output_dia); + tmp_dia = output_to_real_object_first ? + x_strdup(output_dia) : + format("%s.tmp.dia.%s", cached_obj, tmp_string()); + cc_log("(tmp_dia) Outputting to final destination: %s", tmp_dia); args_add(args, "--serialize-diagnostics"); args_add(args, tmp_dia); } else { @@ -777,6 +887,13 @@ to_cache(struct args *args) stats_update(STATS_MISSING); tmp_unlink(tmp_stdout); tmp_unlink(tmp_stderr); + /* -gsplit-dwarf support */ + tmp_unlink(tmp_obj); + if (tmp_dia) { /* CI ??? TODO/FIXME: dont we need this? */ + tmp_unlink(tmp_dia); + } + if(tmp_dwo) + tmp_unlink(tmp_dwo); failed(); } if (st.st_size != 0) { @@ -784,14 +901,23 @@ to_cache(struct args *args) stats_update(STATS_STDOUT); tmp_unlink(tmp_stdout); tmp_unlink(tmp_stderr); + tmp_unlink(tmp_obj); /* -gsplit-dwarf support */ if (tmp_dia) { tmp_unlink(tmp_dia); } + if (tmp_dwo) { /* -gsplit-dwarf support */ + tmp_unlink(tmp_dwo); + } failed(); } tmp_unlink(tmp_stdout); /* + * OBSERVATION: processing stderr does not have to be + * changed in the presence of "-gsplit-dwarf". + */ + + /* * Merge stderr from the preprocessor (if any) and stderr from the real * compiler into tmp_stderr. */ @@ -838,12 +964,22 @@ to_cache(struct args *args) fd = open(tmp_stderr, O_RDONLY | O_BINARY); if (fd != -1) { - if (str_eq(output_obj, "/dev/null") || errno == ENOENT) { + /* TODO/FIXME: the order of OR clauses + * may have some issue. + */ + if ( str_eq(output_obj, "/dev/null") + || errno == ENOENT + ) { /* we can use a quick method of getting the failed output */ copy_fd(fd, 2); close(fd); tmp_unlink(tmp_stderr); + /* Copying of _dia file should be done + * here since when an error occurs, we + * may want to know the diagnostics. + */ + if (output_dia) { int ret; x_unlink(output_dia); @@ -867,30 +1003,63 @@ to_cache(struct args *args) } } + + exit(status); } } + cc_log("verbose: (tmp_stderr) %s being removed", tmp_stderr); tmp_unlink(tmp_stderr); + + cc_log("verbose: (tmp_obj) %s being removed", tmp_obj); + tmp_unlink(tmp_obj); + if (tmp_dia) { tmp_unlink(tmp_dia); } + if (tmp_dwo) { + tmp_unlink(tmp_dwo); + } failed(); } - if (stat(output_obj, &st) != 0) { - cc_log("Compiler didn't produce an object file"); + if (stat(tmp_obj, &st) != 0) { + cc_log("(tmp_obj) Compiler didn't produce an object file"); stats_update(STATS_NOOUTPUT); failed(); } if (st.st_size == 0) { - cc_log("Compiler produced an empty object file"); + cc_log("(tmp_obj) Compiler produced an empty object file"); stats_update(STATS_EMPTYOUTPUT); failed(); } + + /* -gsplit-dwarf support: + * We should also repeat the check for .dwo file + * in addition to tmp_obj when "--gsplit-dwarf is given. + * Because of the way tmp_dwo is produced when command line + * is scanned, + * we can have non-null tmp_dwo even when split_swarf_p is false. + * Check only when split_dwarf_p is true. + */ + if(tmp_dwo && split_dwarf_p) { + /* cc_log("tmp_dwo = %s", tmp_dwo); */ + if (stat(tmp_dwo, &st) != 0) { + cc_log("(tmp_dwo) Compiler didn't produce a split dwarf file"); + stats_update(STATS_NOOUTPUT); + failed(); + } + if (st.st_size == 0) { + cc_log("(tmp_dwo) Compiler produced an empty split dwarf file"); + stats_update(STATS_EMPTYOUTPUT); + failed(); + } + } + if (stat(tmp_stderr, &st) != 0) { - cc_log("Failed to stat %s: %s", tmp_stderr, strerror(errno)); + cc_log("(tmp_stderr) Failed to stat %s: %s", tmp_stderr, strerror(errno)); stats_update(STATS_ERROR); failed(); } @@ -903,11 +1072,25 @@ to_cache(struct args *args) stats_update(STATS_ERROR); failed(); } - cc_log("Stored in cache: %s", cached_stderr); + cc_log("(cached_stderr) Stored in cache: %s", cached_stderr); + /* + * TODO/FIXME: check return value of stat() and act + * accordingly. + */ if (conf->compression) { - stat(cached_stderr, &st); + /* + * The file was compressed, + * so obtain the compressed size again. + */ + if ( stat(cached_stderr, &st) == 0) { + stats_update_size(file_size(&st), 1); + } else { + cc_log("stat on cached_stderr=%s failed (%s)", cached_stderr, strerror(errno)); } + } else { stats_update_size(file_size(&st), 1); + } + } else { tmp_unlink(tmp_stderr); if (conf->recache) { @@ -918,16 +1101,69 @@ to_cache(struct args *args) if (tmp_dia) { if (stat(tmp_dia, &st) != 0) { - cc_log("Failed to stat %s: %s", tmp_dia, strerror(errno)); + cc_log("(tmp_dia) Failed to stat %s: %s", tmp_dia, strerror(errno)); stats_update(STATS_ERROR); failed(); } if (st.st_size > 0) { put_file_in_cache(tmp_dia, cached_dia); + cc_log("(cached_dia) Stored in cache: %s", cached_dia); } } - put_file_in_cache(output_obj, cached_obj); + /* TODO/DONE : + if(output_to_real_object_first) { + Perform the extra copy of .dwo file if --gsplit-dwarf is used. + } else if(move_uncompressed_file(...) != 0) { + The above line of move_uncompressed_file() + ought to be repeated for .dwo file if --gsplit-dwarf is used. + } + */ + + /* use tmp_obj instead of output_obj */ + put_file_in_cache(tmp_obj, cached_obj); + + cc_log("(cached_obj) Stored in cache: %s", cached_obj); + + /* stats_update(STATS_TOCACHE) is called AFTER the processing of + * .dwo file below. + */ + + /* TODO/FIXME: if we output to real object file first, then + tmp_obj *IS* the real object, and so we should not remove it! + and instead, we should not copy back the cached file to the final + destination object. This is a waste of time. + Same can be said of .dwo file: real and cached one below. + Somehow dependency file was not copied this way. + */ + if (!output_to_real_object_first) + tmp_unlink(tmp_obj); + + /* -gsplit-dwarf support: + * if -gsplit-dwarf is given on the compiler command line, + * .dwo file ought to be put into cache as well. + */ + if(split_dwarf_p) + { + assert(tmp_dwo); + assert(cached_dwo); + put_file_in_cache(tmp_dwo, cached_dwo); + cc_log("(cached_dwo) Stored in cache: %s", cached_dwo); + + /* internal sanity check */ + if(!generating_dependencies) + assert(tmp_dwo && cached_dwo); + + /* TODO/FIXME: if we output to real object file first, then + tmp_dwo *IS* the real .dwo file, and so we should not remove it! + and instead, we should not copy back the cached file to the final + destination .dwo. This is a waste of time. + Same can be said of .o file above: real and cached one. + Somehow dependency file was not copied this way. + */ + /* tmp_unlink(tmp_dwo); */ + } + stats_update(STATS_TOCACHE); /* Make sure we have a CACHEDIR.TAG in the cache part of cache_dir. This can @@ -953,9 +1189,12 @@ to_cache(struct args *args) } } + free(tmp_obj); free(tmp_stderr); free(tmp_stdout); free(tmp_dia); + if(tmp_dwo) + free(tmp_dwo); } /* @@ -977,7 +1216,7 @@ get_object_name_from_cpp(struct args *args, struct mdfour *hash) maximum filename length limits */ input_base = basename(input_file); tmp = strchr(input_base, '.'); - if (tmp) { + if (tmp != NULL) { *tmp = 0; } if (strlen(input_base) > 10) { @@ -986,13 +1225,18 @@ get_object_name_from_cpp(struct args *args, struct mdfour *hash) path_stderr = format("%s/tmp.cpp_stderr", temp_dir()); path_stderr_fd = create_tmp_fd(&path_stderr); + add_pending_tmp_file(path_stderr); time_of_compilation = time(NULL); if (direct_i_file) { - /* We are compiling a .i or .ii file - that means we can skip the cpp stage - * and directly form the correct i_tmpfile. */ + /* + * Fixed typo: form -> from + * we are compiling a .i or .ii file - that means we + * can skip the cpp stage and directly from the + * correct i_tmpfile + */ path_stdout = input_file; status = 0; } else { @@ -1016,8 +1260,12 @@ get_object_name_from_cpp(struct args *args, struct mdfour *hash) } if (conf->unify) { - /* When we are doing the unifying tricks we need to include the input file - * name in the hash to get the warnings right. */ + /* + * reindented: + * When we are doing the unifying tricks we need to + * include the input file name in the hash to get the + * warnings right. + */ hash_delimiter(hash, "unifyfilename"); hash_string(hash, input_file); @@ -1077,6 +1325,28 @@ update_cached_result_globals(struct file_hash *hash) cached_stderr = get_path_in_cache(object_name, ".stderr"); cached_dep = get_path_in_cache(object_name, ".d"); cached_dia = get_path_in_cache(object_name, ".dia"); + + /* --gsplit-dwarf support. + * We set cached_dwo to NULL + * to signify --gsplit-dwarf is not used. + */ + if(split_dwarf_p) + cached_dwo = get_path_in_cache(object_name, ".dwo"); + else + cached_dwo = NULL; + + /* TODO: if split_dwarf_p is not true and we get non-null + * get_path_in_cache(object_name, ".dwo"), we probably should purge + * that file! + */ + if(!split_dwarf_p) { + char *cp; + cp = get_path_in_cache(object_name, ".dwo"); + if(!cp) { + cc_log("We are not using split dwarf but, we have .dwo. Maybe we should purge it.:%s", cp); + } + } + stats_file = format("%s/%c/stats", conf->cache_dir, object_name[0]); free(object_name); } @@ -1193,7 +1463,6 @@ calculate_common_hash(struct args *args, struct mdfour *hash) } free(p); } - /* Possibly hash GCC_COLORS (for color diagnostics). */ if (compiler_is_gcc(args)) { const char *gcc_colors = getenv("GCC_COLORS"); @@ -1328,6 +1597,10 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode) hash_string(hash, args->argv[i]); } + if(split_dwarf_p) { + cc_log("Setting output_to_real_object_first to true since split_dwarf_p is true."); + output_to_real_object_first = true; + } /* * For profile generation (-fprofile-arcs, -fprofile-generate): * - hash profile directory @@ -1348,6 +1621,7 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode) * artifacts will be produced in the wrong place. */ if (profile_generate) { + output_to_real_object_first = true; if (!profile_dir) { profile_dir = get_cwd(); } @@ -1355,10 +1629,18 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode) hash_delimiter(hash, "-fprofile-dir"); hash_string(hash, profile_dir); } + /* OBSERVATION: processing for profile_use is a good example + * which we can mimic to implement the support for + * --gsplit-dwarf. + * + * DONE: check the reference to profile_use and mimic its + * processing. + */ if (profile_use) { /* Calculate gcda name */ char *gcda_name; char *base_name; + output_to_real_object_first = true; base_name = remove_extension(output_obj); if (!profile_dir) { profile_dir = get_cwd(); @@ -1383,7 +1665,7 @@ calculate_object_hash(struct args *args, struct mdfour *hash, int direct_mode) "OBJCPLUS_INCLUDE_PATH", /* clang */ NULL }; - for (p = envvars; *p; ++p) { + for (p = envvars; *p != NULL; ++p) { char *v = getenv(*p); if (v) { hash_delimiter(hash, *p); @@ -1440,6 +1722,7 @@ static void from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest) { int fd_stderr; + int ret; struct stat st; bool produce_dep_file; @@ -1448,28 +1731,61 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest) return; } - /* Check if the object file is there. */ + /* + * Check if the object file is there. + * For gsplit-dwarf support, + * check for .dwo as well. See below + * cached_obj contains the path for cached object file. + */ + if (stat(cached_obj, &st) != 0) { - cc_log("Object file %s not in cache", cached_obj); + cc_log("(cached_obj) Object file %s not in cache", cached_obj); return; } - /* Check if the diagnostic file is there. */ + /* Check if the diagnostic file is there. + * Notice the logic. We only check when output_dia is + * not NULL. + */ if (output_dia && stat(cached_dia, &st) != 0) { - cc_log("Diagnostic file %s not in cache", cached_dia); + cc_log("(cached_dia) Diagnostic file %s not in cache", cached_dia); return; } /* * Occasionally, e.g. on hard reset, our cache ends up as just filesystem - * meta-data with no content catch an easy case of this. + * meta-data with no content. Catch an easy case of this. */ if (st.st_size == 0) { - cc_log("Invalid (empty) object file %s in cache", cached_obj); + cc_log(" (cached_obj) Invalid (empty) object file %s in cache", cached_obj); x_unlink(cached_obj); return; } + /* -gsplit-dwarf support + * We should repeat the above check for cached_dwo + */ + if(split_dwarf_p && !generating_dependencies) + assert(output_dwo); + + if(output_dwo) + assert(cached_dwo); + + if (output_dwo) { + + if( stat(cached_dwo, &st) != 0) { + cc_log("(cached_dwo) Split dwarf file %s not in cache", cached_dwo); + return; + } + + if (st.st_size == 0) { + cc_log(" (cached_dwo) Invalid (empty) dwo file %s in cache", cached_dwo); + x_unlink(cached_dwo); + x_unlink(cached_obj); /* to really invalidate */ + return; + } + } + /* * (If mode != FROMCACHE_DIRECT_MODE, the dependency file is created by * gcc.) @@ -1482,20 +1798,133 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest) return; } - if (!str_eq(output_obj, "/dev/null")) { + /* TODO/DONE: In the else clause, do a similar processing for + split dwarf file. cached_dwo. in case -gsplit-dwarf is specified. */ + + if (str_eq(output_obj, "/dev/null")) { + ret = 0; + } else { /* not /dev/null */ + /* when generating object in direct mode */ + if( output_to_real_object_first && called_compiler) { + assert(mode == FROMCACHE_COMPILED_MODE); + /* we don't need to since we have called the compiler and + left the object in the final destination.*/ + cc_log("[direct object generation]: skipped copy: (output_obj) %s from (cached_obj) %s", output_obj, cached_obj); + } else { + assert( (output_to_real_object_first && (mode == FROMCACHE_CPP_MODE || mode == FROMCACHE_DIRECT_MODE)) + || (!output_to_real_object_first) ); get_file_from_cache(cached_obj, output_obj); + cc_log("Created (output_obj) %s from (cached_obj) %s", output_obj, cached_obj); +#if 0 + if( strcmp (output_obj, tmp_obj) != 0) + cc_log("PROBLEM: ooutput_obj=%s != tmp_obj=%s", output_obj, tmp_obj); +#endif + } + + /* + * if something failed internal to get_file_from_cache + * we may want to remove these files: + * if(output_dwo) x_unlink(output_dwo); + * if(cached_dwo) x_unlink(cached_dwo); + * x_unlink(output_obj); + * x_unlink(cached_stderr); + * x_unlink(cached_obj); + * x_unlink(cached_dep); + *x_unlink(cached_dia); + */ + ret = 0; } + + assert(ret == 0); + if( split_dwarf_p ) { + assert(output_dwo); + assert(output_to_real_object_first); + // we don't need to get file from cache + // when we called compiler in this run of ccache since + // the output_dwo would have been created by the compiler. + if (!called_compiler) { + assert(mode == FROMCACHE_CPP_MODE || mode == FROMCACHE_DIRECT_MODE); + get_file_from_cache(cached_dwo, output_dwo); + + } else { + assert(mode == FROMCACHE_COMPILED_MODE); + cc_log("[direct object generation]: skipped the copy from cached_dwo=%s to output_dwo=%s", + cached_dwo, output_dwo); + } + /* TODO/FIXME: + * if something failed internal to get_file_from_cache + * we may want to remove files. + * + * maybe failed() called when error exit is taken does this? + * if(output_dwo) x_unlink(output_dwo); + * if(cached_dwo) x_unlink(cached_dwo); + * x_unlink(output_obj); + * x_unlink(cached_stderr); + * x_unlink(cached_obj); + * x_unlink(cached_dep); + * x_unlink(cached_dia); + * x_unlink(output_dwo); + */ + } + + /* NOTE: The error processing above assumed we only + * do a copy/hardlink for cached_obj. We may need to repeat + * the copy and error processing for cached_dwo in a similar + * manner instead of doing the copy/link only. + * + * But do note that the unlinking of related files + * MUST BE REPEATED in the both cases of the failures of copy/link + * of cached_obj -> output_obj + * and cached_dwo -> output_dwo if(split_dwarf_p). + */ + if (produce_dep_file) { get_file_from_cache(cached_dep, output_dep); + cc_log("Created (output_dep) %s from (cached_dep) %s", output_dep, cached_dep); + /* + * if something failed internal to get_file_from_cache + * we may want to remove these files: + * if(output_dwo) x_unlink(output_dwo); + * if(cached_dwo) x_unlink(cached_dwo); + * + * x_unlink(output_obj); + * x_unlink(output_dep); + * x_unlink(cached_stderr); + * x_unlink(cached_obj); + * x_unlink(cached_dep); + * x_unlink(cached_dia); + */ } + if (output_dia) { get_file_from_cache(cached_dia, output_dia); + cc_log("Created (output_dia) %s from (cached_dia) %s", output_dia, cached_dia); + + /* + * if something failed internal to get_file_from_cache + * we may want to remove these files: + * if(output_dwo) x_unlink(output_dwo); + * if(cached_dwo) x_unlink(cached_dwo); + * x_unlink(output_obj); + * x_unlink(output_dep); + * x_unlink(output_dia); + * x_unlink(cached_stderr); + * x_unlink(cached_obj); + * x_unlink(cached_dep); + * x_unlink(cached_dia); + */ } + + /* OBSERVATION: The above seems to be the best place where we + * can copy object file and dwo file safely. + */ + /* Update modification timestamps to save files from LRU cleanup. Also gives files a sensible mtime when hard-linking. */ update_mtime(cached_obj); update_mtime(cached_stderr); + if (produce_dep_file) { update_mtime(cached_dep); } @@ -1507,13 +1936,24 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest) put_file_in_cache(output_dep, cached_dep); } + /* -gsplit-dwarf support */ + if(cached_dwo) + update_mtime(cached_dwo); + /* Send the stderr, if any. */ fd_stderr = open(cached_stderr, O_RDONLY | O_BINARY); if (fd_stderr != -1) { + cc_log("Using stderr cache (cached_stderr): %s\n", cached_stderr); copy_fd(fd_stderr, 2); close(fd_stderr); } + /* TODO/FIXME: not sure if we should put .dwo file hash in manifest.. + * Is it possible that the content of manifest is used to clear/remove + * files when "ccache -C" is issued? If so, then we need to put + * .dwo file entry in manifest. + */ + /* Create or update the manifest file. */ if (conf->direct_mode && put_object_in_manifest @@ -1528,8 +1968,17 @@ from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest) if (manifest_put(manifest_path, cached_obj_hash, included_files)) { cc_log("Added object file hash to %s", manifest_path); update_mtime(manifest_path); - stat(manifest_path, &st); - stats_update_size(file_size(&st) - old_size, old_size == 0 ? 1 : 0); + + /* + * TODO/FIXME: Check the return value of stat and act accordingly. + */ + if (stat(manifest_path, &st) == 0) { + stats_update_size((file_size(&st) - old_size), + old_size == 0 ? 1 : 0); + } else { + cc_log("stat on manifest (manifest_path) %s failed (%s), so size statistics is incorrect.", + manifest_path, strerror(errno)); + } } else { cc_log("Failed to add object file hash to %s", manifest_path); } @@ -1641,6 +2090,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args, bool result = true; bool found_color_diagnostics = false; + expanded_args = args_copy(args); stripped_args = args_init(0, NULL); dep_args = args_init(0, NULL); @@ -1679,6 +2129,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args, if (argpath[-1] == '-') { ++argpath; } + file_args = args_init_from_gcc_atfile(argpath); if (!file_args) { cc_log("Couldn't read arg file %s", argpath); @@ -1775,6 +2226,12 @@ cc_process_args(struct args *args, struct args **preprocessor_args, goto out; } output_obj = make_relative_path(x_strdup(argv[i+1])); + + /* OBSERVATION: We need to create output_dwo + if -gsplit-dwarf is given. But we should + not do here YET since --gsplit-dwarf may be + given later on the command line !*/ + i++; continue; } @@ -1785,6 +2242,20 @@ cc_process_args(struct args *args, struct args **preprocessor_args, continue; } + /* -gsplit-dwarf support + * This place is chosen as the place to handle -gsplit-dwarf. + */ + + if (str_eq(argv[i], "-gsplit-dwarf")) { + cc_log("split_dwarf_p is set to true due to -gsplit-dwarf."); + split_dwarf_p = true; + output_to_real_object_first = true; + /* we should add this option to the compiler option + when the real compiler is invoked. */ + args_add(stripped_args, argv[i]); + continue; + } + /* debugging is handled specially, so that we know if we can strip line number info */ @@ -2174,6 +2645,19 @@ cc_process_args(struct args *args, struct args **preprocessor_args, /* Rewrite to relative to increase hit rate. */ input_file = make_relative_path(x_strdup(argv[i])); + } /* for */ + + /* found_S_opt needs to be handled with caution + * with split_dwarf_p + */ + if(found_S_opt /* || generating_dependencies*/ ) { + /* even if -gsplit-dwarf is given, .dwo file is not generated. + * So split_dwarf_p is set to false. + */ + split_dwarf_p = false; + output_to_real_object_first = false; + cc_log("split_dwarf_p is reset to false due to found_S_opt"); + cc_log("output_to_real_object_first is set to false, too"); } if (!input_file) { @@ -2212,7 +2696,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args, } output_is_precompiled_header = - actual_language && strstr(actual_language, "-header"); + (actual_language && strstr(actual_language, "-header") != NULL); if (output_is_precompiled_header && !(conf->sloppiness & SLOPPY_PCH_DEFINES)) { @@ -2235,6 +2719,8 @@ cc_process_args(struct args *args, struct args **preprocessor_args, } else { stats_update(STATS_LINK); } + // git dev does not have the following line. Oct 10, 2014 + // stats_update(STATS_CANTUSEPCH); result = false; goto out; } @@ -2261,6 +2747,9 @@ cc_process_args(struct args *args, struct args **preprocessor_args, conf->cpp_extension = x_strdup(extension_for_language(p_language) + 1); } + /* TODO/FIXME: Is there an angle in which split_dwarf_p processing + has anything to do with "-" output ? */ + /* don't try to second guess the compilers heuristics for stdout handling */ if (output_obj && str_eq(output_obj, "-")) { stats_update(STATS_OUTSTDOUT); @@ -2269,6 +2758,54 @@ cc_process_args(struct args *args, struct args **preprocessor_args, goto out; } + /* -gsplit-dwarf support. + * we have probably already seen and check for + * -gsplit-dwarf by the time we reach here. (Do it somewhere above.) + * Here is the chance to create output_dwo based on the + * value of output_obj. + * The basic outline would be + * output_obj = basename(input_file) + * add ".dwo" to it? + */ + + /* + * Question: What do we do if we need to output .s file + * for output and -gsplit-dwarf is specified? + * What file(s) does GCC 4.8 produce exactly? + * Answer: + * /usr/bin/gcc-4.8 -gsplit-dwarf -c t-test.c -S + * generated + * t-test.s + *that contains assembler directives to generate data (dwarf data). + * This means that if we are only producing assembler source + * we do not need to handle ".dwo" file at all. + * + * /usr/bin/gcc-4.8 -gsplit-dwarf -c t-test.c -S -o t-test.s.gazonk + * produced + * t-test.s.gazonk + * + * DONE: + * The above condition needs to be handled. + * My modification more or less assumed that .dwo file is produced + * always (together with the paired .o file) unless -S is specified. + * + * file-scope global variable |found_S_opt| is set to true + * when -S is specified. + * + * /usr/bin/gcc-4.8 -gsplit-dwarf -c t-test.s + * produced + * t-test.o + * t-test.o.dwo. + * + * So let us disable split_dwarf_p to false if + * found_S_opt is true. + * This is done at the end of a loop in argument processing. + */ + + /* I think it is OK here, but just comment it here. We use + output_obj instead of tmp_obj even in the case of + "-gsplit-dwarf" in the code below. */ + if (!output_obj) { if (output_is_precompiled_header) { output_obj = format("%s.gch", input_file); @@ -2287,6 +2824,27 @@ cc_process_args(struct args *args, struct args **preprocessor_args, } } + /* --gsplit-dwarf: create output_dwo */ + if(split_dwarf_p && !found_S_opt) { + char *p; + p = strrchr(output_obj, '.'); + if (!p || !p[1]) { + cc_log("Badly formed object filename"); + stats_update(STATS_ARGS); + result = false; + goto out; + } + *p = '\0'; + output_dwo = format("%s.dwo", output_obj); + *p = '.'; + + cc_log("setting output_dwo to %s", output_dwo); + } + + + /* TODO/DONE: Do we need to cope with strange + output_obj file name in case split_dwarf_p ? */ + /* cope with -o /dev/null */ if (!str_eq(output_obj,"/dev/null") && stat(output_obj, &st) == 0 @@ -2315,6 +2873,7 @@ cc_process_args(struct args *args, struct args **preprocessor_args, args_add(cpp_args, explicit_language); } + /* * Since output is redirected, compilers will not color their output by * default, so force it explicitly if it would be otherwise done. @@ -2337,6 +2896,18 @@ cc_process_args(struct args *args, struct args **preprocessor_args, } } + /* -gsplit-dwarf support + * + * -gsplit-dwarf processing should not affect dependency generation. + * BUT, given the command line below, + * dependency file is generated first and then + * ccache.o is generated. + * Note the ordering and take care of that. + * + * ccache /usr/bin/gcc-4.8 -gsplit-dwarf -DHAVE_CONFIG_H -DSYSCONFDIR=/usr/local/etc -I. -I. -MD -MP -MF .deps/ccache.c.d -g -O2 -Wall -W -Werror -c -o ccache.o ccache.c + * + */ + /* * Add flags for dependency generation only to the preprocessor command line. */ @@ -2469,10 +3040,12 @@ initialize(void) if (str_eq(conf->cache_dir, "")) { fatal("configuration setting \"cache_dir\" must not be the empty string"); } + if ((p = getenv("CCACHE_DIR"))) { free(conf->cache_dir); conf->cache_dir = strdup(p); } + if (str_eq(conf->cache_dir, "")) { fatal("CCACHE_DIR must not be the empty string"); } @@ -2509,6 +3082,7 @@ initialize(void) if (conf->umask != UINT_MAX) { umask(conf->umask); } + } /* Reset the global state. Used by the test suite. */ @@ -2524,10 +3098,12 @@ cc_reset(void) args_free(orig_args); orig_args = NULL; free(input_file); input_file = NULL; free(output_obj); output_obj = NULL; + if(output_dwo) free(output_dwo); output_dwo = NULL; free(output_dep); output_dep = NULL; free(output_dia); output_dia = NULL; free(cached_obj_hash); cached_obj_hash = NULL; free(cached_obj); cached_obj = NULL; + if(cached_dwo) free(cached_dwo); cached_dwo = NULL; free(cached_stderr); cached_stderr = NULL; free(cached_dep); cached_dep = NULL; free(cached_dia); cached_dia = NULL; @@ -2544,6 +3120,12 @@ cc_reset(void) output_is_precompiled_header = false; conf = conf_create(); + split_dwarf_p = false; + +#if 0 + /* Not any more, Dec 14 ?*/ + initialize(); +#endif } /* Make a copy of stderr that will not be cached, so things like @@ -2604,6 +3186,8 @@ ccache(int argc, char *argv[]) signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); + + /* this one added Oct 2014 */ if (str_eq(conf->temporary_dir, "")) { clean_up_internal_tempdir(); } @@ -2639,7 +3223,33 @@ ccache(int argc, char *argv[]) if (output_dia) { cc_log("Diagnostic file: %s", output_dia); } - cc_log("Object file: %s", output_obj); + + /* TODO/DONE: cc_process_args ought to have generated + * output_dwo. But we may not want to do so yet before + * generating_dependencies ? + */ + if (split_dwarf_p ) { +#if 0 + if(output_obj) + cc_log("output_obj = %s", output_obj); + if(output_dep) + cc_log("output_dep = %s", output_dep); +#endif + if(output_dia) + cc_log("output_dia = %s", output_dia); + if(output_dwo) + cc_log("output_dwo = %s", output_dwo); + fflush(stdout); + if(!generating_dependencies) + assert (output_dwo); + } else { + assert (! output_dwo); + } + + if (!generating_dependencies && output_dwo) { + cc_log("Split dwarf file: %s", output_dwo); + } + cc_log("(output_obj) Object file: %s", output_obj); hash_start(&common_hash); calculate_common_hash(preprocessor_args, &common_hash); @@ -2713,6 +3323,8 @@ ccache(int argc, char *argv[]) put_object_in_manifest = true; } + called_compiler = 0; + /* if we can return from cache at this point then do */ from_cache(FROMCACHE_CPP_MODE, put_object_in_manifest); @@ -2726,6 +3338,8 @@ ccache(int argc, char *argv[]) /* run real compiler, sending output to cache */ to_cache(compiler_args); + called_compiler = 1; + /* return from cache */ from_cache(FROMCACHE_COMPILED_MODE, put_object_in_manifest); diff --git a/cleanup.c b/cleanup.c index ee872cc..d810685 100644 --- a/cleanup.c +++ b/cleanup.c @@ -72,7 +72,7 @@ traverse_fn(const char *fname, struct stat *st) goto out; } - if (strstr(p, ".tmp.")) { + if (strstr(p, ".tmp.") != NULL) { /* delete any tmp files older than 1 hour */ if (st->st_mtime + 3600 < time(NULL)) { x_unlink(fname); diff --git a/compopt.c b/compopt.c index 1996fdf..c1802e0 100644 --- a/compopt.c +++ b/compopt.c @@ -60,7 +60,6 @@ static const struct compopt compopts[] = { {"-frepo", TOO_HARD}, {"-ftest-coverage", TOO_HARD}, /* generates a .gcno file at the same time */ {"-fworking-directory", AFFECTS_CPP}, - {"-gsplit-dwarf", TOO_HARD}, /* generates a .dwo file at the same time */ {"-idirafter", AFFECTS_CPP | TAKES_ARG | TAKES_PATH}, {"-iframework", AFFECTS_CPP | TAKES_ARG | TAKES_CONCAT_ARG | TAKES_PATH}, {"-imacros", AFFECTS_CPP | TAKES_ARG | TAKES_PATH}, diff --git a/hash.c b/hash.c index 80beed2..5994a9d 100644 --- a/hash.c +++ b/hash.c @@ -128,6 +128,7 @@ hash_file(struct mdfour *md, const char *fname) fd = open(fname, O_RDONLY|O_BINARY); if (fd == -1) { + perror(fname); /* to catch strange errors. */ return false; } diff --git a/language.c b/language.c index 110066a..0714b5d 100644 --- a/language.c +++ b/language.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, 2013 Joel Rosdahl + * Copyright (C) 2010-2014 Joel Rosdahl * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -147,7 +147,7 @@ extension_for_language(const char *language) bool language_is_supported(const char *language) { - return p_language_for_language(language); + return p_language_for_language(language) != NULL; } bool diff --git a/manifest.c b/manifest.c index 6eb53fd..de78c03 100644 --- a/manifest.c +++ b/manifest.c @@ -70,6 +70,7 @@ static const uint32_t MAGIC = 0x63436d46U; static const uint32_t MAX_MANIFEST_ENTRIES = 100; static const uint32_t MAX_MANIFEST_FILE_INFO_ENTRIES = 10000; + #define ccache_static_assert(e) \ do { enum { ccache_static_assert__ = 1/(e) }; } while (false) @@ -598,11 +599,31 @@ add_object_entry(struct manifest *mf, obj = &mf->objects[n]; n = hashtable_count(included_files); +#if defined(CI_DEBUG) + { + char tmpbuf[128]; + snprintf(tmpbuf, 128, "add_object_entry: hashtable_count = %d", n); + cc_log(tmpbuf); + } +#endif + obj->n_file_info_indexes = n; obj->file_info_indexes = x_malloc(n * sizeof(*obj->file_info_indexes)); add_file_info_indexes(obj->file_info_indexes, n, mf, included_files); memcpy(obj->hash.hash, object_hash->hash, mf->hash_size); obj->hash.size = object_hash->size; +#if defined(CI_DEBUG) + { + int i; + static char tmpbuf[1024]; + unsigned char *cp = (unsigned char *) obj->hash.hash ; + n = snprintf(tmpbuf, 1024,"add_object_entry: "); + for(i = 0; i < mf->hash_size; i++) + snprintf(&tmpbuf[n+i*2], 1024 - n - i * 2, "%02x", cp[i]); + snprintf(&tmpbuf[1024 - n - i * 2 + 2], 1024, "\n"); + cc_log(tmpbuf); + } +#endif } /* diff --git a/mdfour.c b/mdfour.c index 06bc17f..7659f3f 100644 --- a/mdfour.c +++ b/mdfour.c @@ -163,7 +163,7 @@ mdfour_update(struct mdfour *md, const unsigned char *in, size_t n) m = md; - if (!in) { + if (in == NULL) { if (!md->finalized) { mdfour_tail(md->tail, md->tail_len); md->finalized = 1; diff --git a/system.h b/system.h index 0692dcd..d9ca63c 100644 --- a/system.h +++ b/system.h @@ -38,6 +38,7 @@ #include #include #include +/* strange: without signal.h, the compilation succeeded ? */ #include #include #include diff --git a/util.c b/util.c index 80eb58b..01955a9 100644 --- a/util.c +++ b/util.c @@ -108,15 +108,28 @@ path_max(const char *path) } static void +warn_log_fail() { + fprintf(stderr, + "Writing to logfile failed.\n" + "Check the permission and make sure there is enough room for writing to the filesystem.\n"); + /* maybe we should exit here? */ +} + +static void vlog(const char *format, va_list ap, bool log_updated_time) { + int rc1, rc2; if (!init_log()) { return; } log_prefix(log_updated_time); - vfprintf(logfile, format, ap); - fprintf(logfile, "\n"); + rc1 = vfprintf(logfile, format, ap); + rc2 = fprintf(logfile, "\n"); + if(rc1 < 0 || rc2 < 0) { + warn_log_fail(); + } + } /* @@ -153,6 +166,7 @@ cc_bulklog(const char *format, ...) void cc_log_argv(const char *prefix, char **argv) { + int rc; if (!init_log()) { return; } @@ -160,7 +174,9 @@ cc_log_argv(const char *prefix, char **argv) log_prefix(true); fputs(prefix, logfile); print_command(logfile, argv); - fflush(logfile); + rc = fflush(logfile); + if(rc) + warn_log_fail(); } /* something went badly wrong! */ @@ -245,7 +261,7 @@ get_umask(void) int copy_file(const char *src, const char *dest, int compress_level) { - int fd_in, fd_out; + int fd_in = -1, fd_out = -1; gzFile gz_in = NULL, gz_out = NULL; char buf[10240]; int n, written; @@ -1073,7 +1089,6 @@ create_tmp_fd(char **fname) if (fd == -1) { fatal("Failed to create file %s: %s", template, strerror(errno)); } - #ifndef _WIN32 fchmod(fd, 0666 & ~get_umask()); #endif @@ -1318,8 +1333,14 @@ x_rename(const char *oldpath, const char *newpath) int tmp_unlink(const char *path) { - cc_log("Unlink %s", path); - return unlink(path); + /* verbose for debugging -gsplit-dwarf */ + int rc; + cc_log("tmp_unlink:Unlink %s", path); + rc = unlink(path); + if(rc) { + cc_log("tmp_unlink:Unlink failed: rc = %d", rc); + } + return rc; } /* @@ -1335,7 +1356,7 @@ x_unlink(const char *path) */ char *tmp_name = format("%s.rm.%s", path, tmp_string()); int result = 0; - cc_log("Unlink %s via %s", path, tmp_name); + cc_log("x_unlink: Unlink %s via %s", path, tmp_name); if (x_rename(path, tmp_name) == -1) { result = -1; goto out; @@ -1348,6 +1369,10 @@ x_unlink(const char *path) } out: free(tmp_name); + if(result) { + /* verbose for debugging -gsplit-dwarf */ + cc_log("x_unlink: failed result = %d", result); + } return result; }