diff options
Diffstat (limited to 'gnu/llvm/compiler-rt/lib/profile/GCDAProfiling.c')
-rw-r--r-- | gnu/llvm/compiler-rt/lib/profile/GCDAProfiling.c | 192 |
1 files changed, 99 insertions, 93 deletions
diff --git a/gnu/llvm/compiler-rt/lib/profile/GCDAProfiling.c b/gnu/llvm/compiler-rt/lib/profile/GCDAProfiling.c index 124be3c13af..82369357e98 100644 --- a/gnu/llvm/compiler-rt/lib/profile/GCDAProfiling.c +++ b/gnu/llvm/compiler-rt/lib/profile/GCDAProfiling.c @@ -66,6 +66,16 @@ typedef unsigned long long uint64_t; /* #define DEBUG_GCDAPROFILING */ +enum { + GCOV_DATA_MAGIC = 0x67636461, // "gcda" + + GCOV_TAG_FUNCTION = 0x01000000, + GCOV_TAG_COUNTER_ARCS = 0x01a10000, + // GCOV_TAG_OBJECT_SUMMARY superseded GCOV_TAG_PROGRAM_SUMMARY in GCC 9. + GCOV_TAG_OBJECT_SUMMARY = 0xa1000000, + GCOV_TAG_PROGRAM_SUMMARY = 0xa3000000, +}; + /* * --- GCOV file format I/O primitives --- */ @@ -89,6 +99,7 @@ static uint64_t cur_buffer_size = 0; static uint64_t cur_pos = 0; static uint64_t file_size = 0; static int new_file = 0; +static int gcov_version; #if defined(_WIN32) static HANDLE mmap_handle = NULL; #endif @@ -199,17 +210,6 @@ static void write_64bit_value(uint64_t i) { write_32bit_value(hi); } -static uint32_t length_of_string(const char *s) { - return (strlen(s) / 4) + 1; -} - -static void write_string(const char *s) { - uint32_t len = length_of_string(s); - write_32bit_value(len); - write_bytes(s, strlen(s)); - write_bytes("\0\0\0\0", 4 - (strlen(s) % 4)); -} - static uint32_t read_32bit_value() { uint32_t val; @@ -221,18 +221,6 @@ static uint32_t read_32bit_value() { return val; } -static uint32_t read_le_32bit_value() { - uint32_t val = 0; - int i; - - if (new_file) - return (uint32_t)-1; - - for (i = 0; i < 4; i++) - val |= write_buffer[cur_pos++] << (8*i); - return val; -} - static uint64_t read_64bit_value() { // GCOV uses a lo-/hi-word format even on big-endian systems. // See also GCOVBuffer::readInt64 in LLVM. @@ -261,8 +249,8 @@ static int map_file() { fseek(output_file, 0L, SEEK_END); file_size = ftell(output_file); - /* A size of 0 is invalid to `mmap'. Return a fail here, but don't issue an - * error message because it should "just work" for the user. */ + /* A size of 0 means the file has been created just now (possibly by another + * process in lock-after-open race condition). No need to mmap. */ if (file_size == 0) return -1; @@ -345,30 +333,36 @@ static void unmap_file() { * started at a time. */ COMPILER_RT_VISIBILITY -void llvm_gcda_start_file(const char *orig_filename, const char version[4], +void llvm_gcda_start_file(const char *orig_filename, uint32_t version, uint32_t checksum) { const char *mode = "r+b"; filename = mangle_filename(orig_filename); /* Try just opening the file. */ - new_file = 0; fd = open(filename, O_RDWR | O_BINARY); if (fd == -1) { - /* Try opening the file, creating it if necessary. */ - new_file = 1; - mode = "w+b"; - fd = open(filename, O_RDWR | O_CREAT | O_BINARY, 0644); - if (fd == -1) { + /* Try creating the file. */ + fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0644); + if (fd != -1) { + mode = "w+b"; + } else { /* Try creating the directories first then opening the file. */ __llvm_profile_recursive_mkdir(filename); - fd = open(filename, O_RDWR | O_CREAT | O_BINARY, 0644); - if (fd == -1) { - /* Bah! It's hopeless. */ - int errnum = errno; - fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, - strerror(errnum)); - return; + fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0644); + if (fd != -1) { + mode = "w+b"; + } else { + /* Another process may have created the file just now. + * Try opening it without O_CREAT and O_EXCL. */ + fd = open(filename, O_RDWR | O_BINARY); + if (fd == -1) { + /* Bah! It's hopeless. */ + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, + strerror(errnum)); + return; + } } } } @@ -381,27 +375,30 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4], output_file = fdopen(fd, mode); /* Initialize the write buffer. */ + new_file = 0; write_buffer = NULL; cur_buffer_size = 0; cur_pos = 0; - if (new_file) { + if (map_file() == -1) { + /* The file has been created just now (file_size == 0) or mmap failed + * unexpectedly. In the latter case, try to recover by clobbering. */ + new_file = 1; + write_buffer = NULL; resize_write_buffer(WRITE_BUFFER_SIZE); memset(write_buffer, 0, WRITE_BUFFER_SIZE); - } else { - if (map_file() == -1) { - /* mmap failed, try to recover by clobbering */ - new_file = 1; - write_buffer = NULL; - cur_buffer_size = 0; - resize_write_buffer(WRITE_BUFFER_SIZE); - memset(write_buffer, 0, WRITE_BUFFER_SIZE); - } } /* gcda file, version, stamp checksum. */ - write_bytes("adcg", 4); - write_bytes(version, 4); + { + uint8_t c3 = version >> 24; + uint8_t c2 = (version >> 16) & 255; + uint8_t c1 = (version >> 8) & 255; + gcov_version = c3 >= 'A' ? (c3 - 'A') * 100 + (c2 - '0') * 10 + c1 - '0' + : (c3 - '0') * 10 + c1 - '0'; + } + write_32bit_value(GCOV_DATA_MAGIC); + write_32bit_value(version); write_32bit_value(checksum); #ifdef DEBUG_GCDAPROFILING @@ -436,30 +433,25 @@ void llvm_gcda_increment_indirect_counter(uint32_t *predecessor, } COMPILER_RT_VISIBILITY -void llvm_gcda_emit_function(uint32_t ident, const char *function_name, - uint32_t func_checksum, uint8_t use_extra_checksum, +void llvm_gcda_emit_function(uint32_t ident, uint32_t func_checksum, uint32_t cfg_checksum) { uint32_t len = 2; + int use_extra_checksum = gcov_version >= 47; if (use_extra_checksum) len++; #ifdef DEBUG_GCDAPROFILING - fprintf(stderr, "llvmgcda: function id=0x%08x name=%s\n", ident, - function_name ? function_name : "NULL"); + fprintf(stderr, "llvmgcda: function id=0x%08x\n", ident); #endif if (!output_file) return; /* function tag */ - write_bytes("\0\0\0\1", 4); - if (function_name) - len += 1 + length_of_string(function_name); + write_32bit_value(GCOV_TAG_FUNCTION); write_32bit_value(len); write_32bit_value(ident); write_32bit_value(func_checksum); if (use_extra_checksum) write_32bit_value(cfg_checksum); - if (function_name) - write_string(function_name); } COMPILER_RT_VISIBILITY @@ -471,11 +463,11 @@ void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { if (!output_file) return; - val = read_le_32bit_value(); + val = read_32bit_value(); if (val != (uint32_t)-1) { /* There are counters present in the file. Merge them. */ - if (val != 0x01a10000) { + if (val != GCOV_TAG_COUNTER_ARCS) { fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: " "corrupt arc tag (0x%08x)\n", filename, val); @@ -498,7 +490,7 @@ void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { cur_pos = save_cur_pos; /* Counter #1 (arcs) tag */ - write_bytes("\0\0\xa1\1", 4); + write_32bit_value(GCOV_TAG_COUNTER_ARCS); write_32bit_value(num_counters * 2); for (i = 0; i < num_counters; ++i) { counters[i] += (old_ctrs ? old_ctrs[i] : 0); @@ -516,8 +508,6 @@ void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { COMPILER_RT_VISIBILITY void llvm_gcda_summary_info() { - const uint32_t obj_summary_len = 9; /* Length for gcov compatibility. */ - uint32_t i; uint32_t runs = 1; static uint32_t run_counted = 0; // We only want to increase the run count once. uint32_t val = 0; @@ -525,46 +515,52 @@ void llvm_gcda_summary_info() { if (!output_file) return; - val = read_le_32bit_value(); + val = read_32bit_value(); if (val != (uint32_t)-1) { /* There are counters present in the file. Merge them. */ - if (val != 0xa1000000) { - fprintf(stderr, "profiling: %s: cannot merge previous run count: " - "corrupt object tag (0x%08x)\n", + if (val != (gcov_version >= 90 ? GCOV_TAG_OBJECT_SUMMARY + : GCOV_TAG_PROGRAM_SUMMARY)) { + fprintf(stderr, + "profiling: %s: cannot merge previous run count: " + "corrupt object tag (0x%08x)\n", filename, val); return; } val = read_32bit_value(); /* length */ - if (val != obj_summary_len) { - fprintf(stderr, "profiling: %s: cannot merge previous run count: " - "mismatched object length (%d)\n", - filename, val); - return; + uint32_t prev_runs; + if (gcov_version < 90) { + read_32bit_value(); + read_32bit_value(); + prev_runs = read_32bit_value(); + } else { + prev_runs = read_32bit_value(); + read_32bit_value(); } - - read_32bit_value(); /* checksum, unused */ - read_32bit_value(); /* num, unused */ - uint32_t prev_runs = read_32bit_value(); + for (uint32_t i = gcov_version < 90 ? 3 : 2; i < val; ++i) + read_32bit_value(); /* Add previous run count to new counter, if not already counted before. */ runs = run_counted ? prev_runs : prev_runs + 1; } cur_pos = save_cur_pos; - /* Object summary tag */ - write_bytes("\0\0\0\xa1", 4); - write_32bit_value(obj_summary_len); - write_32bit_value(0); /* checksum, unused */ - write_32bit_value(0); /* num, unused */ - write_32bit_value(runs); - for (i = 3; i < obj_summary_len; ++i) + if (gcov_version >= 90) { + write_32bit_value(GCOV_TAG_OBJECT_SUMMARY); + write_32bit_value(2); + write_32bit_value(runs); + write_32bit_value(0); // sum_max + } else { + // Before gcov 4.8 (r190952), GCOV_TAG_SUMMARY_LENGTH was 9. r190952 set + // GCOV_TAG_SUMMARY_LENGTH to 22. We simply use the smallest length which + // can make gcov read "Runs:". + write_32bit_value(GCOV_TAG_PROGRAM_SUMMARY); + write_32bit_value(3); write_32bit_value(0); - - /* Program summary tag */ - write_bytes("\0\0\0\xa3", 4); /* tag indicates 1 program */ - write_32bit_value(0); /* 0 length */ + write_32bit_value(0); + write_32bit_value(runs); + } run_counted = 1; @@ -616,8 +612,17 @@ void llvm_writeout_files(void) { } } -COMPILER_RT_VISIBILITY -void llvm_delete_writeout_function_list(void) { +#ifndef _WIN32 +// __attribute__((destructor)) and destructors whose priorities are greater than +// 100 run before this function and can thus be tracked. The priority is +// compatible with GCC 7 onwards. +#if __GNUC__ >= 9 +#pragma GCC diagnostic ignored "-Wprio-ctor-dtor" +#endif +__attribute__((destructor(100))) +#endif +static void llvm_writeout_and_clear(void) { + llvm_writeout_files(); fn_list_remove(&writeout_fn_list); } @@ -698,8 +703,9 @@ void llvm_gcov_init(fn_ptr wfn, fn_ptr ffn, fn_ptr rfn) { /* Make sure we write out the data and delete the data structures. */ atexit(llvm_delete_reset_function_list); atexit(llvm_delete_flush_function_list); - atexit(llvm_delete_writeout_function_list); - atexit(llvm_writeout_files); +#ifdef _WIN32 + atexit(llvm_writeout_and_clear); +#endif } } |