*debug* (*CCACHE_DEBUG* or *CCACHE_NODEBUG*, see <<_boolean_values,Boolean values>> above)::
- If true, the debug mode will be used. The default is false. In debug mode,
- the hash input and the log file will be stored next to the object file.
+ If true, the debug mode will be used. The default is false. In the debug mode,
+ the hash input and the debug log file will be stored next to the output files.
+ The input to the hashes is shown both in binary (hashc/hashd/hashp) as well as
+ in a text (input) format for easier reading. See <<_cache_debugging_,debugging>>.
*direct_mode* (*CCACHE_DIRECT* or *CCACHE_NODIRECT*, see <<_boolean_values,Boolean values>> above)::
|==============================================================================
+Cache debugging
+---------------
+
+When investigating why you are not getting hits, and what information
+that ccache is actually hashing, you can enable the "debugging" mode.
+
+In this mode, ccache will add some additional output files next to the
+object files with results from the hashing and a copy of the debug log.
+
+There are several binary hash files created, for different partial input:
+
+* Common (*'c'*)
+* Direct mode (*'d'*)
+* Preprocessor mode (*'p'*)
+
+The hash is the checksum of the common + direct or common + preprocessor.
+
+There is also a human readable text file, for easier reading and diffing.
+
+
How ccache works
----------------
closedir(dir);
}
+static void
+debug_start(const char *path)
+{
+ char *hash_bin = format("%s%s", path, ".ccache-hashX");
+ char *hash_txt = format("%s%s", path, ".ccache-input");
+ hash_debug(hash_bin, hash_txt);
+ free(hash_bin);
+ free(hash_txt);
+}
+
+static void
+debug_end(bool hit)
+{
+ (void) hit;
+ char *path = format("%s%s", output_obj, ".ccache-log");
+ cc_copylog(path);
+ free(path);
+}
+
static enum guessed_compiler
guess_compiler(const char *path)
{
free(cwd);
}
if (should_hash_inc_path) {
- hash_string(hash, inc_path);
+ hash_buffer(hash, inc_path, strlen(inc_path));
}
remember_include_file(inc_path, hash, system);
break;
}
+ if (conf->debug) {
+ debug_end(true);
+ }
+
// And exit with the right status code.
x_exit(0);
}
cc_log("Object file: %s", output_obj);
+ if (conf->debug) {
+ debug_start(output_obj);
+ }
+
struct mdfour common_hash;
hash_start(&common_hash);
+ mdfour_identify(&common_hash, 'c');
calculate_common_hash(preprocessor_args, &common_hash);
// Try to find the hash using the manifest.
struct mdfour direct_hash = common_hash;
+ mdfour_identify(&direct_hash, 'd');
bool put_object_in_manifest = false;
struct file_hash *object_hash = NULL;
struct file_hash *object_hash_from_manifest = NULL;
// Find the hash using the preprocessed output. Also updates included_files.
struct mdfour cpp_hash = common_hash;
+ mdfour_identify(&cpp_hash, 'p');
object_hash = calculate_object_hash(preprocessor_args, &cpp_hash, 0);
if (!object_hash) {
fatal("internal error: object hash from cpp returned NULL");
// Run real compiler, sending output to cache.
to_cache(compiler_args);
+ if (conf->debug) {
+ debug_end(false);
+ }
+
x_exit(0);
}
// ----------------------------------------------------------------------------
// hash.c
+void hash_debug(const char *bin, const char *txt);
void hash_start(struct mdfour *md);
void hash_buffer(struct mdfour *md, const void *s, size_t len);
char *hash_result(struct mdfour *md);
#include "ccache.h"
#define HASH_DELIMITER "\000cCaChE"
+#define HASH_DEBUG_DELIMITER "### "
+
+// binary input, for hashing
+char *debug_hash_bin;
+
+// text input, for debugging
+char *debug_hash_txt;
+
+void hash_debug(const char *bin, const char *txt)
+{
+ static char *hash_types = "cdp"; // common, direct, cpp
+ if (bin) {
+ debug_hash_bin = x_strdup(bin);
+ assert(debug_hash_bin[strlen(debug_hash_bin)-1] == 'X');
+ for (char *p = hash_types; *p != '\0'; p++) {
+ debug_hash_bin[strlen(debug_hash_bin)-1] = *p;
+ x_try_unlink(debug_hash_bin);
+ }
+ }
+ if (txt) {
+ debug_hash_txt = x_strdup(txt);
+ x_try_unlink(debug_hash_txt);
+ }
+}
+
+static void
+hash_binary_buffer(struct mdfour *md, const void *s, size_t len)
+{
+ mdfour_update(md, (unsigned char *)s, len);
+ if (!md->identifier || len == 0) {
+ return;
+ }
+ if (debug_hash_bin) {
+ // log to different files, for the different hash types
+ debug_hash_bin[strlen(debug_hash_bin)-1] = md->identifier;
+
+ FILE *f = fopen(debug_hash_bin, "a");
+ fwrite(s, 1, len, f);
+ fclose(f);
+ }
+}
+
+static void
+hash_debug_buffer(struct mdfour *md, const void *s, size_t len)
+{
+ if (!md->identifier || len == 0) {
+ return;
+ }
+ if (debug_hash_txt) {
+ FILE *f = fopen(debug_hash_txt, "a");
+ fwrite(s, 1, len, f);
+ fclose(f);
+ }
+}
void
hash_start(struct mdfour *md)
void
hash_buffer(struct mdfour *md, const void *s, size_t len)
{
- mdfour_update(md, (unsigned char *)s, len);
+ hash_binary_buffer(md, s, len);
+ hash_debug_buffer(md, s, len);
}
// Return the hash result as a hex string. Caller frees.
void
hash_result_as_bytes(struct mdfour *md, unsigned char *out)
{
- hash_buffer(md, NULL, 0);
+ mdfour_update(md, NULL, 0);
mdfour_result(md, out);
}
void
hash_delimiter(struct mdfour *md, const char *type)
{
- hash_buffer(md, HASH_DELIMITER, sizeof(HASH_DELIMITER));
- hash_buffer(md, type, strlen(type) + 1); // Include NUL.
+ hash_binary_buffer(md, HASH_DELIMITER, sizeof(HASH_DELIMITER));
+ hash_binary_buffer(md, type, strlen(type) + 1); // Include NUL.
+ hash_debug_buffer(md, HASH_DEBUG_DELIMITER, strlen(HASH_DEBUG_DELIMITER));
+ hash_debug_buffer(md, type, strlen(type));
+ hash_debug_buffer(md, "\n", 1);
}
void
void
hash_string_length(struct mdfour *md, const char *s, int length)
{
- hash_buffer(md, s, length);
+ hash_binary_buffer(md, s, length);
+ hash_debug_buffer(md, s, length);
+ hash_debug_buffer(md, "\n", 1);
}
void
hash_int(struct mdfour *md, int x)
{
- hash_buffer(md, (char *)&x, sizeof(x));
+ hash_binary_buffer(md, (char *)&x, sizeof(x));
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d", x);
+ hash_debug_buffer(md, buf, strlen(buf));
+ hash_debug_buffer(md, "\n", 1);
}
// Add contents of an open file to the hash. Returns true on success, otherwise
break;
}
if (n > 0) {
- hash_buffer(md, buf, n);
+ hash_binary_buffer(md, buf, n);
+ hash_debug_buffer(md, buf, n);
}
}
return n == 0;
}
// Hash the source string.
- hash_buffer(hash, str, len);
+ hash_string_length(hash, str, len);
if (result & HASH_SOURCE_CODE_FOUND_DATE) {
// Make sure that the hash sum changes if the (potential) expansion of
struct tm *now = localtime(&t);
cc_log("Found __DATE__ in %s", path);
hash_delimiter(hash, "date");
- hash_buffer(hash, &now->tm_year, sizeof(now->tm_year));
- hash_buffer(hash, &now->tm_mon, sizeof(now->tm_mon));
- hash_buffer(hash, &now->tm_mday, sizeof(now->tm_mday));
+ hash_int(hash, now->tm_year);
+ hash_int(hash, now->tm_mon);
+ hash_int(hash, now->tm_mday);
}
if (result & HASH_SOURCE_CODE_FOUND_TIME) {
// We don't know for sure that the program actually uses the __TIME__
md->totalN = 0;
md->tail_len = 0;
md->finalized = 0;
+ md->identifier = 0;
+}
+
+void mdfour_identify(struct mdfour *md, int identifier)
+{
+ md->identifier = identifier;
}
static
void
mdfour_update(struct mdfour *md, const unsigned char *in, size_t n)
{
-#ifdef CCACHE_DEBUG_HASH
- if (n > 0 && getenv("CCACHE_DEBUG_HASH")) {
- FILE *f = fopen("ccache-debug-hash.bin", "a");
- fwrite(in, 1, n, f);
- fclose(f);
- }
-#endif
-
m = md;
if (!in) {
unsigned char tail[64];
size_t tail_len;
int finalized;
+ int identifier;
};
void mdfour_begin(struct mdfour *md);
+void mdfour_identify(struct mdfour *md, int identifier);
void mdfour_update(struct mdfour *md, const unsigned char *in, size_t n);
void mdfour_result(struct mdfour *md, unsigned char *out);