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