summaryrefslogtreecommitdiff
path: root/compiler-rt/lib/profile/GCDAProfiling.c
diff options
context:
space:
mode:
Diffstat (limited to 'compiler-rt/lib/profile/GCDAProfiling.c')
-rw-r--r--compiler-rt/lib/profile/GCDAProfiling.c264
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
}
}