diff '--exclude=test.sh' '--exclude=*.log' '--exclude=*.o' '--exclude=.gitignore' '--exclude=config.status' '--exclude=autom4te.cache' '--exclude=.deps' '--exclude=.git' '--exclude=*.txt' '--exclude=*.in' '--exclude=Makefile*' -p -ur ccache/ccache.c ccache-gsplit-dwarf-support/ccache.c --- ccache/ccache.c 2015-03-04 23:01:48.149927458 +0900 +++ ccache-gsplit-dwarf-support/ccache.c 2015-03-06 01:36:08.254903827 +0900 @@ -1,3 +1,4 @@ +/* -*- mode: C; fill-column:78 -*- */ /* * ccache -- a fast C/C++ compiler cache * @@ -39,7 +40,7 @@ static const char VERSION_TEXT[] = MYNAME " version %s\n" "\n" "Copyright (C) 2002-2007 Andrew Tridgell\n" -"Copyright (C) 2009-2011 Joel Rosdahl\n" +"Copyright (C) 2009-2015 Joel Rosdahl\n" "\n" "This program is free software; you can redistribute it and/or modify it under\n" "the terms of the GNU General Public License as published by the Free Software\n" @@ -95,9 +96,12 @@ 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; + /* * Name (represented as a struct file_hash) of the file containing the cached * object code. @@ -129,6 +133,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). */ @@ -261,6 +280,7 @@ failed(void) fatal("execv of %s failed: %s", orig_args->argv[0], strerror(errno)); } + static const char * temp_dir() { @@ -735,18 +755,66 @@ static void to_cache(struct args *args) { char *tmp_stdout, *tmp_stderr; + 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: creating .dwo file name + * + * What is the reliable way of creating the .dwo file name in the case + * of using |-o output_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 in + * this part of the code to reflect the value of the current output_obj + * to tmp_dwo. + * If -gsplit-dwarf is specified on the command line, this tmp_dwo + * will be used. + */ + + // In the extreme simple case, + // Try replacing .o with .dwo. + // + // Fact: gcc-4.8 produced the following .dwo file for the command + // below: + // 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 (works with gcc 4.9 also). + // Strip anything after the final '.' and add "dwo" instead. + + // TODO/FIXME: CHANGED. No more output_to_real_object_first + cp = strrchr(output_obj, '.'); + assert(cp); + { + /* + * String is modified in place. Since we already created the + * string by x_strdup, and format, we should be relatively + * safe and not cause unintended change even if ccache becomes + * multi-threaded. + */ + *cp = '\0'; + tmp_dwo = format("%s.dwo", output_obj); + *cp = '.'; + } + cc_log("Setting tmp_dwo to %s", tmp_dwo); + args_add(args, "-o"); args_add(args, output_obj); if (output_dia) { + cc_log("(output_dia) Outputting to final destination: %s", output_dia); args_add(args, "--serialize-diagnostics"); args_add(args, output_dia); } @@ -774,6 +842,9 @@ to_cache(struct args *args) stats_update(STATS_MISSING); tmp_unlink(tmp_stdout); tmp_unlink(tmp_stderr); + /* -gsplit-dwarf support requires the removal of a few more files.*/ + if (tmp_dwo) + tmp_unlink(tmp_dwo); failed(); } if (st.st_size != 0) { @@ -781,11 +852,20 @@ to_cache(struct args *args) stats_update(STATS_STDOUT); tmp_unlink(tmp_stdout); tmp_unlink(tmp_stderr); + /* -gsplit-dwarf support requires the removal of a few more files. */ + if (tmp_dwo) { + 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,26 +918,87 @@ to_cache(struct args *args) 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, don't we? + * As it turns out since we now generate always first in the final destination + * the following code is not necessary (?). + * I comment it out using #if 0/#endif for now. + */ +#if 0 + if (output_dia) { + int ret; + x_unlink(output_dia); + /* only make a hardlink if the cache file is uncompressed */ + ret = move_file(tmp_dia, output_dia, 0); + + if (ret == -1) { + if (errno == ENOENT) { + /* Someone removed the file just before we began copying? */ + cc_log("Diagnostic file %s just disappeared", output_dia); + stats_update(STATS_MISSING); + } else { + cc_log("Failed to move %s to %s: %s", + tmp_dia, output_dia, strerror(errno)); + stats_update(STATS_ERROR); + failed(); + } + x_unlink(tmp_dia); + } else { + cc_log("Created %s from %s", output_dia, tmp_dia); + } + } +#endif + exit(status); } + cc_log("verbose: (tmp_stderr) %s being removed", tmp_stderr); tmp_unlink(tmp_stderr); + + /* gsplit-support modification */ + if (tmp_dwo) { + tmp_unlink(tmp_dwo); + } failed(); } if (stat(output_obj, &st) != 0) { - cc_log("Compiler didn't produce an object file"); + cc_log("(output_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("(output_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 + * output_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(); } @@ -865,16 +1006,30 @@ to_cache(struct args *args) if (move_uncompressed_file( tmp_stderr, cached_stderr, conf->compression ? conf->compression_level : 0) != 0) { - cc_log("Failed to move %s to %s: %s", tmp_stderr, cached_stderr, - strerror(errno)); + cc_log("Failed to move %s to %s: %s", + tmp_stderr, cached_stderr, strerror(errno)); stats_update(STATS_ERROR); failed(); } - cc_log("Stored in cache: %s", cached_stderr); + cc_log("(cached_stderr) Stored in cache: %s", cached_stderr); + /* + * Do not forget to 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); } - stats_update_size(file_size(&st), 1); } else { tmp_unlink(tmp_stderr); if (conf->recache) { @@ -885,16 +1040,62 @@ to_cache(struct args *args) if (output_dia) { if (stat(output_dia, &st) != 0) { - cc_log("Failed to stat %s: %s", output_dia, strerror(errno)); + cc_log("(output_dia) Failed to stat %s: %s", output_dia, strerror(errno)); stats_update(STATS_ERROR); failed(); } if (st.st_size > 0) { put_file_in_cache(output_dia, cached_dia); + cc_log("(cached_dia) Stored in cache: %s", cached_dia); } } - put_file_in_cache(output_obj, cached_obj); + /* Description of processing below.: + * Perform the copy of .dwo file if --gsplit-dwarf is used. + */ + + put_file_in_cache(output_obj, cached_obj); + + cc_log("(cached_obj) Stored in cache: %s", cached_obj); + + /* stats_update(STATS_TOCACHE) is now called AFTER the processing of + * .dwo file below. + */ + + /* + * Caution: if we output to real object file first, then + * output_obj *IS* the real object, and so we should not remove it! + * And this operation mode *IS* the default! + * + * Also, in this case, 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. + */ + + /* -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); + + /* Caution: if we output to real object file first, then + * tmp_dwo *IS* the real .dwo file, and so we should not remove it! + * The original code is left as a comment line to stress this point. + */ + /* tmp_unlink(tmp_dwo); */ + } + stats_update(STATS_TOCACHE); /* Make sure we have a CACHEDIR.TAG in the cache part of cache_dir. This can @@ -915,13 +1116,15 @@ to_cache(struct args *args) * previous ccache versions. */ if (getpid() % 1000 == 0) { char *path = format("%s/CACHEDIR.TAG", conf->cache_dir); - unlink(path); + x_unlink(path); free(path); } } free(tmp_stderr); free(tmp_stdout); + if (tmp_dwo) + free(tmp_dwo); } /* @@ -1045,6 +1248,29 @@ update_cached_result_globals(struct file 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/NOTE: 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! But this is not so critical. We will purge the file eventually + * by LRU algorithm. + */ + 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); } @@ -1335,6 +1561,12 @@ calculate_object_hash(struct args *args, 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. + * So the support for -gsplit-dwarf was modeled after the + * processing to support profile. + */ if (profile_use) { /* Calculate gcda name */ char *gcda_name; @@ -1420,6 +1652,7 @@ static void from_cache(enum fromcache_call_mode mode, bool put_object_in_manifest) { int fd_stderr; + int ret; /* to keep track of error value. */ struct stat st; bool produce_dep_file; @@ -1428,28 +1661,59 @@ from_cache(enum fromcache_call_mode mode 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.) @@ -1462,16 +1726,117 @@ from_cache(enum fromcache_call_mode mode return; } - if (!str_eq(output_obj, "/dev/null")) { - get_file_from_cache(cached_obj, output_obj); + /* + * Note: The if-expr is reverted. In the else clause, do a similar + * processing for FissionDwarf file, cached_dwo in case -gsplit-dwarf + * is specified. + */ + + if (str_eq(output_obj, "/dev/null")) { + ret = 0; + } else { /* not /dev/null */ + /* we get file(s) from cache unless we generate object in direct mode */ + /* output_to_real_object_first && <=== TRUE always now*/ + if (mode == FROMCACHE_COMPILED_MODE /* called compier */) { + /* we don't need to copy from cache 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 { + /* internal sanity check */ + assert( /* output_to_real_object_first : TRUE always now.*/ + (mode == FROMCACHE_CPP_MODE + || mode == FROMCACHE_DIRECT_MODE) + /* || (!output_to_real_object_first) FALSE always now.*/ + ); + get_file_from_cache(cached_obj, output_obj); + cc_log("Created (output_obj) %s from (cached_obj) %s", + output_obj, cached_obj); + } + + /* + * if something failed internal to get_file_from_cache we may + * want to remove some files: the following x_unlink calls + * were in the original code circa 2014 summer. + * + * Similar comments are repeated in code below. + * + * Does failed(), which is called when error exit is taken, + * handle this now? If so, this comment is superflous. + * + * 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); implicitly true now. */ + /* + * we don't need to get file from cache when we called tge + * compiler in this run of ccache since the output_dwo would + * have been created by the compiler. + */ + if (mode == FROMCACHE_CPP_MODE || mode == FROMCACHE_DIRECT_MODE ) { + /* did not call compiler */ + get_file_from_cache(cached_dwo, output_dwo); + /* + * Note: if something failed internal to get_file_from_cache + * we may want to remove a flurry of files. + * There were calls to x_unlinks in the original code circa 2014 summer. + */ + } else { + /* sanity check */ + 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); + } + } + + /* 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); + /* + * Note: if something failed internal to get_file_from_cache + * we may want to remove a flurry of files. + * There were calls to x_unlinks in the original code circa 2014 summer. + */ } 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); + + /* + * Note: if something failed internal to get_file_from_cache + * we may want to remove a flurry of files. + * There were calls to x_unlinks in the original code circa 2014 summer. + */ } + /* 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); @@ -1487,13 +1852,20 @@ from_cache(enum fromcache_call_mode mode 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); } + /* Note: we do not need to put .dwo file in MANIFEST file. */ + /* Create or update the manifest file. */ if (conf->direct_mode && put_object_in_manifest @@ -1508,8 +1880,16 @@ from_cache(enum fromcache_call_mode mode 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); + /* + * Don't forget to 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); } @@ -1659,6 +2039,7 @@ cc_process_args(struct args *args, struc if (argpath[-1] == '-') { ++argpath; } + file_args = args_init_from_gcc_atfile(argpath); if (!file_args) { cc_log("Couldn't read arg file %s", argpath); @@ -1755,6 +2136,12 @@ cc_process_args(struct args *args, struc 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; } @@ -1765,6 +2152,20 @@ cc_process_args(struct args *args, struc 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 */ @@ -2154,6 +2555,20 @@ cc_process_args(struct args *args, struc /* 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. + * + * Note: Just a tidbit. When -S is given on the command line, + * the cached .o file in the cache spool actually has .o + * suffix. Interesting, isn't it? + */ + split_dwarf_p = false; + cc_log("split_dwarf_p is reset to false due to found_S_opt"); } if (!input_file) { @@ -2241,6 +2656,15 @@ cc_process_args(struct args *args, struc conf->cpp_extension = x_strdup(extension_for_language(p_language) + 1); } + /* Question: Is there an angle in which split_dwarf_p processing has + * anything to do with "-" output that is handled specialy below? + * + * For example, what do we want to do with .dwo file (produce it + * somewhere, but what path name to use)? + * + * We may find the answer in the long run. + */ + /* don't try to second guess the compilers heuristics for stdout handling */ if (output_obj && str_eq(output_obj, "-")) { stats_update(STATS_OUTSTDOUT); @@ -2249,6 +2673,53 @@ cc_process_args(struct args *args, struc goto out; } + /* -gsplit-dwarf support: + * We have probably already seen and check for + * -gsplit-dwarf by the time we reach here. (We did 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 with minor tweaks to handle special cases. + */ + + /* + * Special case 1: 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 special cases need to be handled. + * The modification below 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. + */ + + /* + * We always use |output_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); @@ -2267,6 +2738,31 @@ cc_process_args(struct args *args, struc } } + /* -gsplit-dwarf support: 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); + } + + + /* Long-term question: Do we need to cope with strange output_obj file + * name in case split_dwarf_p? + * "-gsplit-dwarf" support may encounter corner cases when strange + * file names are given. + * We may find the answer in the long run. + */ + /* cope with -o /dev/null */ if (!str_eq(output_obj,"/dev/null") && stat(output_obj, &st) == 0 @@ -2317,6 +2813,20 @@ cc_process_args(struct args *args, struc } } + /* -gsplit-dwarf support: Ordering of dependency generation. + * + * -gsplit-dwarf processing should not affect dependency generation. + * + * Note, given the command line below, dependency file is generated + * first and then ccache.o is generated. + * Observing 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. */ @@ -2504,10 +3014,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; @@ -2524,6 +3036,7 @@ cc_reset(void) output_is_precompiled_header = false; conf = conf_create(); + split_dwarf_p = false; } /* Make a copy of stderr that will not be cached, so things like @@ -2619,7 +3132,33 @@ ccache(int argc, char *argv[]) if (output_dia) { cc_log("Diagnostic file: %s", output_dia); } - cc_log("Object file: %s", output_obj); + + /* + * Note: cc_process_args ought to have generated output_dwo. But we + * may not want to do so before generating dependencies because it has + * been the implicit ordering that the dependency is generated first + * and then object file is generated. + * See a block comment that starts with + * "-gsplit-dwarf support: Ordering of dependency generation." + * in the earlier part of this file. + */ + + if (split_dwarf_p ) { + 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); @@ -2775,7 +3314,7 @@ ccache_main_options(int argc, char *argv initialize(); files = atoi(optarg); if (conf_set_value_in_file(primary_config_path, "max_files", optarg, - &errmsg)) { + &errmsg)) { if (files == 0) { printf("Unset cache file limit\n"); } else { @@ -2795,7 +3334,7 @@ ccache_main_options(int argc, char *argv fatal("invalid size: %s", optarg); } if (conf_set_value_in_file(primary_config_path, "max_size", optarg, - &errmsg)) { + &errmsg)) { if (size == 0) { printf("Unset cache size limit\n"); } else { Only in ccache-gsplit-dwarf-support: ccache.c.saved diff '--exclude=test.sh' '--exclude=*.log' '--exclude=*.o' '--exclude=.gitignore' '--exclude=config.status' '--exclude=autom4te.cache' '--exclude=.deps' '--exclude=.git' '--exclude=*.txt' '--exclude=*.in' '--exclude=Makefile*' -p -ur ccache/compopt.c ccache-gsplit-dwarf-support/compopt.c --- ccache/compopt.c 2014-12-26 00:16:41.857924683 +0900 +++ ccache-gsplit-dwarf-support/compopt.c 2014-10-31 01:23:27.861419530 +0900 @@ -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}, Only in ccache-gsplit-dwarf-support: do-diff.sh Only in ccache-gsplit-dwarf-support: do-test-split-dwarf.sh Only in ccache-gsplit-dwarf-support: do-test.sh diff '--exclude=test.sh' '--exclude=*.log' '--exclude=*.o' '--exclude=.gitignore' '--exclude=config.status' '--exclude=autom4te.cache' '--exclude=.deps' '--exclude=.git' '--exclude=*.txt' '--exclude=*.in' '--exclude=Makefile*' -p -ur ccache/execute.c ccache-gsplit-dwarf-support/execute.c --- ccache/execute.c 2015-03-04 23:01:48.149927458 +0900 +++ ccache-gsplit-dwarf-support/execute.c 2015-03-04 22:59:49.424081775 +0900 @@ -1,6 +1,6 @@ /* * Copyright (C) Andrew Tridgell 2002 - * Copyright (C) Joel Rosdahl 2011 + * Copyright (C) Joel Rosdahl 2011-2015 * * 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 @@ -174,7 +174,7 @@ win32execute(char *path, char **argv, in char full_path_win_ext[MAX_PATH] = {0}; add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext, path); ret = CreateProcess(full_path_win_ext, args, NULL, NULL, 1, 0, NULL, NULL, - &si, &pi); + &si, &pi); if (fd_stdout != -1) { close(fd_stdout); close(fd_stderr); diff '--exclude=test.sh' '--exclude=*.log' '--exclude=*.o' '--exclude=.gitignore' '--exclude=config.status' '--exclude=autom4te.cache' '--exclude=.deps' '--exclude=.git' '--exclude=*.txt' '--exclude=*.in' '--exclude=Makefile*' -p -ur ccache/hash.c ccache-gsplit-dwarf-support/hash.c --- ccache/hash.c 2014-12-26 00:16:41.857924683 +0900 +++ ccache-gsplit-dwarf-support/hash.c 2015-03-04 03:36:51.011393317 +0900 @@ -128,6 +128,7 @@ hash_file(struct mdfour *md, const char fd = open(fname, O_RDONLY|O_BINARY); if (fd == -1) { + cc_log("hash file creation (%s) failed\n", fname); /* to catch strange errors */ return false; } diff '--exclude=test.sh' '--exclude=*.log' '--exclude=*.o' '--exclude=.gitignore' '--exclude=config.status' '--exclude=autom4te.cache' '--exclude=.deps' '--exclude=.git' '--exclude=*.txt' '--exclude=*.in' '--exclude=Makefile*' -p -ur ccache/language.c ccache-gsplit-dwarf-support/language.c --- ccache/language.c 2014-12-26 00:16:41.857924683 +0900 +++ ccache-gsplit-dwarf-support/language.c 2015-03-04 22:59:49.424081775 +0900 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, 2013 Joel Rosdahl + * Copyright (C) 2010-2015 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 *langu bool language_is_supported(const char *language) { - return p_language_for_language(language); + return p_language_for_language(language) != NULL; } bool diff '--exclude=test.sh' '--exclude=*.log' '--exclude=*.o' '--exclude=.gitignore' '--exclude=config.status' '--exclude=autom4te.cache' '--exclude=.deps' '--exclude=.git' '--exclude=*.txt' '--exclude=*.in' '--exclude=Makefile*' -p -ur ccache/manifest.c ccache-gsplit-dwarf-support/manifest.c --- ccache/manifest.c 2014-12-26 00:16:41.857924683 +0900 +++ ccache-gsplit-dwarf-support/manifest.c 2015-03-04 22:59:49.424081775 +0900 @@ -598,11 +598,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 '--exclude=test.sh' '--exclude=*.log' '--exclude=*.o' '--exclude=.gitignore' '--exclude=config.status' '--exclude=autom4te.cache' '--exclude=.deps' '--exclude=.git' '--exclude=*.txt' '--exclude=*.in' '--exclude=Makefile*' -p -ur ccache/stats.c ccache-gsplit-dwarf-support/stats.c --- ccache/stats.c 2014-12-14 03:46:53.071511757 +0900 +++ ccache-gsplit-dwarf-support/stats.c 2014-12-14 03:55:46.450168960 +0900 @@ -374,7 +374,7 @@ stats_zero(void) /* Get the per directory limits */ void stats_get_obsolete_limits(const char *dir, unsigned *maxfiles, - uint64_t *maxsize) + uint64_t *maxsize) { struct counters *counters = counters_init(STATS_END); char *sname = format("%s/stats", dir); Only in ccache/test: main Only in ccache-gsplit-dwarf-support: test-file-1.c Only in ccache-gsplit-dwarf-support: test-file-2.c Only in ccache-gsplit-dwarf-support: test-file-2.s Only in ccache: testdir.18714 Only in ccache: testdir.19069 Only in ccache: testdir.20915 Only in ccache: testdir.2264 Only in ccache-gsplit-dwarf-support: testdir.3089 Only in ccache-gsplit-dwarf-support: testdir.865 diff '--exclude=test.sh' '--exclude=*.log' '--exclude=*.o' '--exclude=.gitignore' '--exclude=config.status' '--exclude=autom4te.cache' '--exclude=.deps' '--exclude=.git' '--exclude=*.txt' '--exclude=*.in' '--exclude=Makefile*' -p -ur ccache/util.c ccache-gsplit-dwarf-support/util.c --- ccache/util.c 2015-03-04 23:01:48.153927399 +0900 +++ ccache-gsplit-dwarf-support/util.c 2015-03-06 01:37:58.201687462 +0900 @@ -108,15 +108,27 @@ 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 +165,7 @@ cc_bulklog(const char *format, ...) void cc_log_argv(const char *prefix, char **argv) { + int rc; if (!init_log()) { return; } @@ -160,7 +173,9 @@ cc_log_argv(const char *prefix, char **a log_prefix(true); fputs(prefix, logfile); print_command(logfile, argv); - fflush(logfile); + rc = fflush(logfile); + if (rc) + warn_log_fail(); } /* something went badly wrong! */ @@ -827,7 +842,7 @@ traverse(const char *dir, void (*fn)(con fname = format("%s/%s", dir, de->d_name); if (lstat(fname, &st)) { if (errno != ENOENT) { - perror(fname); + cc_log("lstat on file %s failed.", fname); } free(fname); continue; @@ -1413,8 +1428,14 @@ x_rename(const char *oldpath, const char 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; } /* @@ -1430,7 +1451,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; @@ -1443,6 +1464,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; } diff '--exclude=test.sh' '--exclude=*.log' '--exclude=*.o' '--exclude=.gitignore' '--exclude=config.status' '--exclude=autom4te.cache' '--exclude=.deps' '--exclude=.git' '--exclude=*.txt' '--exclude=*.in' '--exclude=Makefile*' -p -ur ccache/version.c ccache-gsplit-dwarf-support/version.c --- ccache/version.c 2014-12-26 00:10:38.346977900 +0900 +++ ccache-gsplit-dwarf-support/version.c 2015-03-06 01:39:45.213097200 +0900 @@ -1 +1 @@ -const char CCACHE_VERSION[] = "3.2.1+1_g090af0a"; +const char CCACHE_VERSION[] = "3.1.9+361_gc38c67e_dirty";