diff options
Diffstat (limited to 'compiler-rt/lib/profile/GCDAProfiling.c')
-rw-r--r-- | compiler-rt/lib/profile/GCDAProfiling.c | 264 |
1 files changed, 155 insertions, 109 deletions
diff --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c index 81f2cdd26450..57d8dec423cc 100644 --- a/compiler-rt/lib/profile/GCDAProfiling.c +++ b/compiler-rt/lib/profile/GCDAProfiling.c @@ -32,8 +32,10 @@ #include <windows.h> #include "WindowsMMap.h" #else -#include <sys/mman.h> #include <sys/file.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <unistd.h> #endif #if defined(__FreeBSD__) && defined(__i386__) @@ -62,27 +64,18 @@ typedef unsigned long long uint64_t; #include "InstrProfiling.h" #include "InstrProfilingUtil.h" -#ifndef _WIN32 -#include <pthread.h> -static pthread_mutex_t gcov_flush_mutex = PTHREAD_MUTEX_INITIALIZER; -static __inline void gcov_flush_lock() { - pthread_mutex_lock(&gcov_flush_mutex); -} -static __inline void gcov_flush_unlock() { - pthread_mutex_unlock(&gcov_flush_mutex); -} -#else -#include <windows.h> -static SRWLOCK gcov_flush_mutex = SRWLOCK_INIT; -static __inline void gcov_flush_lock() { - AcquireSRWLockExclusive(&gcov_flush_mutex); -} -static __inline void gcov_flush_unlock() { - ReleaseSRWLockExclusive(&gcov_flush_mutex); -} -#endif - /* #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 --- */ @@ -106,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 @@ -138,6 +132,11 @@ struct fn_list writeout_fn_list; */ struct fn_list flush_fn_list; +/* + * A list of reset functions, shared between all dynamic objects. + */ +struct fn_list reset_fn_list; + static void fn_list_insert(struct fn_list* list, fn_ptr fn) { struct fn_node* new_node = malloc(sizeof(struct fn_node)); new_node->fn = fn; @@ -215,7 +214,12 @@ static uint32_t length_of_string(const char *s) { return (strlen(s) / 4) + 1; } -static void write_string(const char *s) { +// Remove when we support libgcov 9 current_working_directory. +#if !defined(_MSC_VER) && defined(__clang__) +__attribute__((unused)) +#endif +static void +write_string(const char *s) { uint32_t len = length_of_string(s); write_32bit_value(len); write_bytes(s, strlen(s)); @@ -233,18 +237,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. @@ -273,8 +265,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; @@ -357,30 +349,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; + } } } } @@ -393,27 +391,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 @@ -448,30 +449,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 @@ -483,11 +479,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); @@ -510,7 +506,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); @@ -528,8 +524,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; @@ -537,46 +531,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; @@ -628,8 +628,14 @@ 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. +__attribute__((destructor(100))) +#endif +static void llvm_writeout_and_clear(void) { + llvm_writeout_files(); fn_list_remove(&writeout_fn_list); } @@ -639,16 +645,12 @@ void llvm_register_flush_function(fn_ptr fn) { } void __gcov_flush() { - gcov_flush_lock(); - struct fn_node* curr = flush_fn_list.head; while (curr) { curr->fn(); curr = curr->next; } - - gcov_flush_unlock(); } COMPILER_RT_VISIBILITY @@ -657,7 +659,46 @@ void llvm_delete_flush_function_list(void) { } COMPILER_RT_VISIBILITY -void llvm_gcov_init(fn_ptr wfn, fn_ptr ffn) { +void llvm_register_reset_function(fn_ptr fn) { + fn_list_insert(&reset_fn_list, fn); +} + +COMPILER_RT_VISIBILITY +void llvm_delete_reset_function_list(void) { fn_list_remove(&reset_fn_list); } + +COMPILER_RT_VISIBILITY +void llvm_reset_counters(void) { + struct fn_node *curr = reset_fn_list.head; + + while (curr) { + if (curr->id == CURRENT_ID) { + curr->fn(); + } + curr = curr->next; + } +} + +#if !defined(_WIN32) +COMPILER_RT_VISIBILITY +pid_t __gcov_fork() { + pid_t parent_pid = getpid(); + pid_t pid = fork(); + + if (pid == 0) { + pid_t child_pid = getpid(); + if (child_pid != parent_pid) { + // The pid changed so we've a fork (one could have its own fork function) + // Just reset the counters for this child process + // threads. + llvm_reset_counters(); + } + } + return pid; +} +#endif + +COMPILER_RT_VISIBILITY +void llvm_gcov_init(fn_ptr wfn, fn_ptr ffn, fn_ptr rfn) { static int atexit_ran = 0; if (wfn) @@ -666,13 +707,18 @@ void llvm_gcov_init(fn_ptr wfn, fn_ptr ffn) { if (ffn) llvm_register_flush_function(ffn); + if (rfn) + llvm_register_reset_function(rfn); + if (atexit_ran == 0) { atexit_ran = 1; /* 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 } } |