diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2015-01-08 19:47:10 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2015-01-08 19:47:10 +0000 |
commit | f4341a5a66d0fa0f3c9599ea5c5d2d7b2b240c1d (patch) | |
tree | f47eabbd2a48be6d6fec3ddeeefae5b4aeb87dbc /contrib/compiler-rt/lib/profile | |
parent | bbf686ed602a158a5d2fcf9dd90f4bda25feef5e (diff) | |
parent | ca9211ecdede9bdedb812b2243a4abdb8dacd1b9 (diff) | |
download | src-f4341a5a66d0fa0f3c9599ea5c5d2d7b2b240c1d.tar.gz src-f4341a5a66d0fa0f3c9599ea5c5d2d7b2b240c1d.zip |
Update compiler-rt to trunk r224034. This brings a number of new
builtins, and also the various sanitizers. Support for these will be
added in a later commit.
Notes
Notes:
svn path=/head/; revision=276851
Diffstat (limited to 'contrib/compiler-rt/lib/profile')
9 files changed, 1212 insertions, 0 deletions
diff --git a/contrib/compiler-rt/lib/profile/GCDAProfiling.c b/contrib/compiler-rt/lib/profile/GCDAProfiling.c new file mode 100644 index 000000000000..45fbd07e544b --- /dev/null +++ b/contrib/compiler-rt/lib/profile/GCDAProfiling.c @@ -0,0 +1,587 @@ +/*===- GCDAProfiling.c - Support library for GCDA file emission -----------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +|*===----------------------------------------------------------------------===*| +|* +|* This file implements the call back routines for the gcov profiling +|* instrumentation pass. Link against this library when running code through +|* the -insert-gcov-profiling LLVM pass. +|* +|* We emit files in a corrupt version of GCOV's "gcda" file format. These files +|* are only close enough that LCOV will happily parse them. Anything that lcov +|* ignores is missing. +|* +|* TODO: gcov is multi-process safe by having each exit open the existing file +|* and append to it. We'd like to achieve that and be thread-safe too. +|* +\*===----------------------------------------------------------------------===*/ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#ifdef _WIN32 +#include <direct.h> +#endif + +#define I386_FREEBSD (defined(__FreeBSD__) && defined(__i386__)) + +#if !I386_FREEBSD +#include <sys/stat.h> +#include <sys/types.h> +#endif + +#if !defined(_MSC_VER) && !I386_FREEBSD +#include <stdint.h> +#endif + +#if defined(_MSC_VER) +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +#elif I386_FREEBSD +/* System headers define 'size_t' incorrectly on x64 FreeBSD (prior to + * FreeBSD 10, r232261) when compiled in 32-bit mode. + */ +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +int mkdir(const char*, unsigned short); +#endif + +/* #define DEBUG_GCDAPROFILING */ + +/* + * --- GCOV file format I/O primitives --- + */ + +/* + * The current file name we're outputting. Used primarily for error logging. + */ +static char *filename = NULL; + +/* + * The current file we're outputting. + */ +static FILE *output_file = NULL; + +/* + * Buffer that we write things into. + */ +#define WRITE_BUFFER_SIZE (128 * 1024) +static char *write_buffer = NULL; +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 fd = -1; + +/* + * A list of functions to write out the data. + */ +typedef void (*writeout_fn)(); + +struct writeout_fn_node { + writeout_fn fn; + struct writeout_fn_node *next; +}; + +static struct writeout_fn_node *writeout_fn_head = NULL; +static struct writeout_fn_node *writeout_fn_tail = NULL; + +/* + * A list of flush functions that our __gcov_flush() function should call. + */ +typedef void (*flush_fn)(); + +struct flush_fn_node { + flush_fn fn; + struct flush_fn_node *next; +}; + +static struct flush_fn_node *flush_fn_head = NULL; +static struct flush_fn_node *flush_fn_tail = NULL; + +static void resize_write_buffer(uint64_t size) { + if (!new_file) return; + size += cur_pos; + if (size <= cur_buffer_size) return; + size = (size - 1) / WRITE_BUFFER_SIZE + 1; + size *= WRITE_BUFFER_SIZE; + write_buffer = realloc(write_buffer, size); + cur_buffer_size = size; +} + +static void write_bytes(const char *s, size_t len) { + resize_write_buffer(len); + memcpy(&write_buffer[cur_pos], s, len); + cur_pos += len; +} + +static void write_32bit_value(uint32_t i) { + write_bytes((char*)&i, 4); +} + +static void write_64bit_value(uint64_t i) { + write_bytes((char*)&i, 8); +} + +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; + + if (new_file) + return (uint32_t)-1; + + val = *(uint32_t*)&write_buffer[cur_pos]; + cur_pos += 4; + return val; +} + +static uint64_t read_64bit_value() { + uint64_t val; + + if (new_file) + return (uint64_t)-1; + + val = *(uint64_t*)&write_buffer[cur_pos]; + cur_pos += 8; + return val; +} + +static char *mangle_filename(const char *orig_filename) { + char *new_filename; + size_t filename_len, prefix_len; + int prefix_strip; + int level = 0; + const char *fname, *ptr; + const char *prefix = getenv("GCOV_PREFIX"); + const char *prefix_strip_str = getenv("GCOV_PREFIX_STRIP"); + + if (prefix == NULL || prefix[0] == '\0') + return strdup(orig_filename); + + if (prefix_strip_str) { + prefix_strip = atoi(prefix_strip_str); + + /* Negative GCOV_PREFIX_STRIP values are ignored */ + if (prefix_strip < 0) + prefix_strip = 0; + } else { + prefix_strip = 0; + } + + fname = orig_filename; + for (level = 0, ptr = fname + 1; level < prefix_strip; ++ptr) { + if (*ptr == '\0') + break; + if (*ptr != '/') + continue; + fname = ptr; + ++level; + } + + filename_len = strlen(fname); + prefix_len = strlen(prefix); + new_filename = malloc(prefix_len + 1 + filename_len + 1); + memcpy(new_filename, prefix, prefix_len); + + if (prefix[prefix_len - 1] != '/') + new_filename[prefix_len++] = '/'; + memcpy(new_filename + prefix_len, fname, filename_len + 1); + + return new_filename; +} + +static void recursive_mkdir(char *path) { + int i; + + for (i = 1; path[i] != '\0'; ++i) { + if (path[i] != '/') continue; + path[i] = '\0'; +#ifdef _WIN32 + _mkdir(path); +#else + mkdir(path, 0755); /* Some of these will fail, ignore it. */ +#endif + path[i] = '/'; + } +} + +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. */ + if (file_size == 0) + return -1; + + write_buffer = mmap(0, file_size, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, 0); + if (write_buffer == (void *)-1) { + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot map: %s\n", filename, + strerror(errnum)); + return -1; + } + return 0; +} + +static void unmap_file() { + if (msync(write_buffer, file_size, MS_SYNC) == -1) { + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot msync: %s\n", filename, + strerror(errnum)); + } + + /* We explicitly ignore errors from unmapping because at this point the data + * is written and we don't care. + */ + (void)munmap(write_buffer, file_size); + write_buffer = NULL; + file_size = 0; +} + +/* + * --- LLVM line counter API --- + */ + +/* A file in this case is a translation unit. Each .o file built with line + * profiling enabled will emit to a different file. Only one file may be + * started at a time. + */ +void llvm_gcda_start_file(const char *orig_filename, const char version[4], + 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); + + if (fd == -1) { + /* Try opening the file, creating it if necessary. */ + new_file = 1; + mode = "w+b"; + fd = open(filename, O_RDWR | O_CREAT, 0644); + if (fd == -1) { + /* Try creating the directories first then opening the file. */ + recursive_mkdir(filename); + fd = open(filename, O_RDWR | O_CREAT, 0644); + if (fd == -1) { + /* Bah! It's hopeless. */ + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, + strerror(errnum)); + return; + } + } + } + + output_file = fdopen(fd, mode); + + /* Initialize the write buffer. */ + write_buffer = NULL; + cur_buffer_size = 0; + cur_pos = 0; + + if (new_file) { + 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); + write_32bit_value(checksum); + +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: [%s]\n", orig_filename); +#endif +} + +/* Given an array of pointers to counters (counters), increment the n-th one, + * where we're also given a pointer to n (predecessor). + */ +void llvm_gcda_increment_indirect_counter(uint32_t *predecessor, + uint64_t **counters) { + uint64_t *counter; + uint32_t pred; + + pred = *predecessor; + if (pred == 0xffffffff) + return; + counter = counters[pred]; + + /* Don't crash if the pred# is out of sync. This can happen due to threads, + or because of a TODO in GCOVProfiling.cpp buildEdgeLookupTable(). */ + if (counter) + ++*counter; +#ifdef DEBUG_GCDAPROFILING + else + fprintf(stderr, + "llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n", + *counter, *predecessor); +#endif +} + +void llvm_gcda_emit_function(uint32_t ident, const char *function_name, + uint32_t func_checksum, uint8_t use_extra_checksum, + uint32_t cfg_checksum) { + uint32_t len = 2; + + if (use_extra_checksum) + len++; +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: function id=0x%08x name=%s\n", ident, + function_name ? function_name : "NULL"); +#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(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); +} + +void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) { + uint32_t i; + uint64_t *old_ctrs = NULL; + uint32_t val = 0; + uint64_t save_cur_pos = cur_pos; + + if (!output_file) return; + + val = read_32bit_value(); + + if (val != (uint32_t)-1) { + /* There are counters present in the file. Merge them. */ + if (val != 0x01a10000) { + fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: " + "corrupt arc tag (0x%08x)\n", + filename, val); + return; + } + + val = read_32bit_value(); + if (val == (uint32_t)-1 || val / 2 != num_counters) { + fprintf(stderr, "profiling: %s: cannot merge previous GCDA file: " + "mismatched number of counters (%d)\n", + filename, val); + return; + } + + old_ctrs = malloc(sizeof(uint64_t) * num_counters); + for (i = 0; i < num_counters; ++i) + old_ctrs[i] = read_64bit_value(); + } + + cur_pos = save_cur_pos; + + /* Counter #1 (arcs) tag */ + write_bytes("\0\0\xa1\1", 4); + write_32bit_value(num_counters * 2); + for (i = 0; i < num_counters; ++i) { + counters[i] += (old_ctrs ? old_ctrs[i] : 0); + write_64bit_value(counters[i]); + } + + free(old_ctrs); + +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: %u arcs\n", num_counters); + for (i = 0; i < num_counters; ++i) + fprintf(stderr, "llvmgcda: %llu\n", (unsigned long long)counters[i]); +#endif +} + +void llvm_gcda_summary_info() { + const uint32_t obj_summary_len = 9; /* Length for gcov compatibility. */ + uint32_t i; + uint32_t runs = 1; + uint32_t val = 0; + uint64_t save_cur_pos = cur_pos; + + if (!output_file) return; + + 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", + 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; + } + + read_32bit_value(); /* checksum, unused */ + read_32bit_value(); /* num, unused */ + runs += read_32bit_value(); /* Add previous run count to new counter. */ + } + + 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) + write_32bit_value(0); + + /* Program summary tag */ + write_bytes("\0\0\0\xa3", 4); /* tag indicates 1 program */ + write_32bit_value(0); /* 0 length */ + +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: %u runs\n", runs); +#endif +} + +void llvm_gcda_end_file() { + /* Write out EOF record. */ + if (output_file) { + write_bytes("\0\0\0\0\0\0\0\0", 8); + + if (new_file) { + fwrite(write_buffer, cur_pos, 1, output_file); + free(write_buffer); + } else { + unmap_file(); + } + + fclose(output_file); + output_file = NULL; + write_buffer = NULL; + } + free(filename); + +#ifdef DEBUG_GCDAPROFILING + fprintf(stderr, "llvmgcda: -----\n"); +#endif +} + +void llvm_register_writeout_function(writeout_fn fn) { + struct writeout_fn_node *new_node = malloc(sizeof(struct writeout_fn_node)); + new_node->fn = fn; + new_node->next = NULL; + + if (!writeout_fn_head) { + writeout_fn_head = writeout_fn_tail = new_node; + } else { + writeout_fn_tail->next = new_node; + writeout_fn_tail = new_node; + } +} + +void llvm_writeout_files() { + struct writeout_fn_node *curr = writeout_fn_head; + + while (curr) { + curr->fn(); + curr = curr->next; + } +} + +void llvm_delete_writeout_function_list() { + while (writeout_fn_head) { + struct writeout_fn_node *node = writeout_fn_head; + writeout_fn_head = writeout_fn_head->next; + free(node); + } + + writeout_fn_head = writeout_fn_tail = NULL; +} + +void llvm_register_flush_function(flush_fn fn) { + struct flush_fn_node *new_node = malloc(sizeof(struct flush_fn_node)); + new_node->fn = fn; + new_node->next = NULL; + + if (!flush_fn_head) { + flush_fn_head = flush_fn_tail = new_node; + } else { + flush_fn_tail->next = new_node; + flush_fn_tail = new_node; + } +} + +void __gcov_flush() { + struct flush_fn_node *curr = flush_fn_head; + + while (curr) { + curr->fn(); + curr = curr->next; + } +} + +void llvm_delete_flush_function_list() { + while (flush_fn_head) { + struct flush_fn_node *node = flush_fn_head; + flush_fn_head = flush_fn_head->next; + free(node); + } + + flush_fn_head = flush_fn_tail = NULL; +} + +void llvm_gcov_init(writeout_fn wfn, flush_fn ffn) { + static int atexit_ran = 0; + + if (wfn) + llvm_register_writeout_function(wfn); + + if (ffn) + llvm_register_flush_function(ffn); + + if (atexit_ran == 0) { + atexit_ran = 1; + + /* Make sure we write out the data and delete the data structures. */ + atexit(llvm_delete_flush_function_list); + atexit(llvm_delete_writeout_function_list); + atexit(llvm_writeout_files); + } +} diff --git a/contrib/compiler-rt/lib/profile/InstrProfiling.c b/contrib/compiler-rt/lib/profile/InstrProfiling.c new file mode 100644 index 000000000000..8d010df28f18 --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfiling.c @@ -0,0 +1,48 @@ +/*===- InstrProfiling.c - Support library for PGO instrumentation ---------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfiling.h" +#include <string.h> + +__attribute__((visibility("hidden"))) +uint64_t __llvm_profile_get_magic(void) { + /* Magic number to detect file format and endianness. + * + * Use 255 at one end, since no UTF-8 file can use that character. Avoid 0, + * so that utilities, like strings, don't grab it as a string. 129 is also + * invalid UTF-8, and high enough to be interesting. + * + * Use "lprofr" in the centre to stand for "LLVM Profile Raw", or "lprofR" + * for 32-bit platforms. + */ + unsigned char R = sizeof(void *) == sizeof(uint64_t) ? 'r' : 'R'; + return + (uint64_t)255 << 56 | + (uint64_t)'l' << 48 | + (uint64_t)'p' << 40 | + (uint64_t)'r' << 32 | + (uint64_t)'o' << 24 | + (uint64_t)'f' << 16 | + (uint64_t) R << 8 | + (uint64_t)129; +} + +__attribute__((visibility("hidden"))) +uint64_t __llvm_profile_get_version(void) { + /* This should be bumped any time the output format changes. */ + return 1; +} + +__attribute__((visibility("hidden"))) +void __llvm_profile_reset_counters(void) { + uint64_t *I = __llvm_profile_begin_counters(); + uint64_t *E = __llvm_profile_end_counters(); + + memset(I, 0, sizeof(uint64_t)*(E - I)); +} diff --git a/contrib/compiler-rt/lib/profile/InstrProfiling.h b/contrib/compiler-rt/lib/profile/InstrProfiling.h new file mode 100644 index 000000000000..2b1bd003668e --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfiling.h @@ -0,0 +1,92 @@ +/*===- InstrProfiling.h- Support library for PGO instrumentation ----------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#ifndef PROFILE_INSTRPROFILING_H_ +#define PROFILE_INSTRPROFILING_H_ + +#if defined(__FreeBSD__) && defined(__i386__) + +/* System headers define 'size_t' incorrectly on x64 FreeBSD (prior to + * FreeBSD 10, r232261) when compiled in 32-bit mode. + */ +#define PRIu64 "llu" +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +typedef uint32_t uintptr_t; + +#else /* defined(__FreeBSD__) && defined(__i386__) */ + +#include <inttypes.h> +#include <stdint.h> + +#endif /* defined(__FreeBSD__) && defined(__i386__) */ + +#define PROFILE_HEADER_SIZE 7 + +typedef struct __llvm_profile_data { + const uint32_t NameSize; + const uint32_t NumCounters; + const uint64_t FuncHash; + const char *const Name; + uint64_t *const Counters; +} __llvm_profile_data; + +/*! + * \brief Get required size for profile buffer. + */ +uint64_t __llvm_profile_get_size_for_buffer(void); + +/*! + * \brief Write instrumentation data to the given buffer. + * + * \pre \c Buffer is the start of a buffer at least as big as \a + * __llvm_profile_get_size_for_buffer(). + */ +int __llvm_profile_write_buffer(char *Buffer); + +const __llvm_profile_data *__llvm_profile_begin_data(void); +const __llvm_profile_data *__llvm_profile_end_data(void); +const char *__llvm_profile_begin_names(void); +const char *__llvm_profile_end_names(void); +uint64_t *__llvm_profile_begin_counters(void); +uint64_t *__llvm_profile_end_counters(void); + +/*! + * \brief Write instrumentation data to the current file. + * + * Writes to the file with the last name given to \a __llvm_profile_set_filename(), + * or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable, + * or if that's not set, \c "default.profdata". + */ +int __llvm_profile_write_file(void); + +/*! + * \brief Set the filename for writing instrumentation data. + * + * Sets the filename to be used for subsequent calls to + * \a __llvm_profile_write_file(). + * + * \c Name is not copied, so it must remain valid. Passing NULL resets the + * filename logic to the default behaviour. + */ +void __llvm_profile_set_filename(const char *Name); + +/*! \brief Register to write instrumentation data to file at exit. */ +int __llvm_profile_register_write_file_atexit(void); + +/*! \brief Initialize file handling. */ +void __llvm_profile_initialize_file(void); + +/*! \brief Get the magic token for the file format. */ +uint64_t __llvm_profile_get_magic(void); + +/*! \brief Get the version of the file format. */ +uint64_t __llvm_profile_get_version(void); + +#endif /* PROFILE_INSTRPROFILING_H_ */ diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingBuffer.c b/contrib/compiler-rt/lib/profile/InstrProfilingBuffer.c new file mode 100644 index 000000000000..3c429c8a85ea --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfilingBuffer.c @@ -0,0 +1,104 @@ +/*===- InstrProfilingBuffer.c - Write instrumentation to a memory buffer --===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfiling.h" +#include "InstrProfilingInternal.h" + +#include <string.h> + +__attribute__((visibility("hidden"))) +uint64_t __llvm_profile_get_size_for_buffer(void) { + const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); + const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); + const uint64_t *CountersBegin = __llvm_profile_begin_counters(); + const uint64_t *CountersEnd = __llvm_profile_end_counters(); + const char *NamesBegin = __llvm_profile_begin_names(); + const char *NamesEnd = __llvm_profile_end_names(); + + return __llvm_profile_get_size_for_buffer_internal( + DataBegin, DataEnd, CountersBegin, CountersEnd, NamesBegin, NamesEnd); +} + +#define PROFILE_RANGE_SIZE(Range) (Range##End - Range##Begin) + +__attribute__((visibility("hidden"))) +uint64_t __llvm_profile_get_size_for_buffer_internal( + const __llvm_profile_data *DataBegin, + const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, + const uint64_t *CountersEnd, const char *NamesBegin, + const char *NamesEnd) { + /* Match logic in __llvm_profile_write_buffer(). */ + const uint64_t NamesSize = PROFILE_RANGE_SIZE(Names) * sizeof(char); + const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t); + return sizeof(uint64_t) * PROFILE_HEADER_SIZE + + PROFILE_RANGE_SIZE(Data) * sizeof(__llvm_profile_data) + + PROFILE_RANGE_SIZE(Counters) * sizeof(uint64_t) + + NamesSize + Padding; +} + +__attribute__((visibility("hidden"))) +int __llvm_profile_write_buffer(char *Buffer) { + /* Match logic in __llvm_profile_get_size_for_buffer(). + * Match logic in __llvm_profile_write_file(). + */ + const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); + const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); + const uint64_t *CountersBegin = __llvm_profile_begin_counters(); + const uint64_t *CountersEnd = __llvm_profile_end_counters(); + const char *NamesBegin = __llvm_profile_begin_names(); + const char *NamesEnd = __llvm_profile_end_names(); + + return __llvm_profile_write_buffer_internal(Buffer, DataBegin, DataEnd, + CountersBegin, CountersEnd, + NamesBegin, NamesEnd); +} + +__attribute__((visibility("hidden"))) +int __llvm_profile_write_buffer_internal( + char *Buffer, const __llvm_profile_data *DataBegin, + const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, + const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd) { + /* Match logic in __llvm_profile_get_size_for_buffer(). + * Match logic in __llvm_profile_write_file(). + */ + + /* Calculate size of sections. */ + const uint64_t DataSize = DataEnd - DataBegin; + const uint64_t CountersSize = CountersEnd - CountersBegin; + const uint64_t NamesSize = NamesEnd - NamesBegin; + const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t); + + /* Enough zeroes for padding. */ + const char Zeroes[sizeof(uint64_t)] = {0}; + + /* Create the header. */ + uint64_t Header[PROFILE_HEADER_SIZE]; + Header[0] = __llvm_profile_get_magic(); + Header[1] = __llvm_profile_get_version(); + Header[2] = DataSize; + Header[3] = CountersSize; + Header[4] = NamesSize; + Header[5] = (uintptr_t)CountersBegin; + Header[6] = (uintptr_t)NamesBegin; + + /* Write the data. */ +#define UPDATE_memcpy(Data, Size) \ + do { \ + memcpy(Buffer, Data, Size); \ + Buffer += Size; \ + } while (0) + UPDATE_memcpy(Header, PROFILE_HEADER_SIZE * sizeof(uint64_t)); + UPDATE_memcpy(DataBegin, DataSize * sizeof(__llvm_profile_data)); + UPDATE_memcpy(CountersBegin, CountersSize * sizeof(uint64_t)); + UPDATE_memcpy(NamesBegin, NamesSize * sizeof(char)); + UPDATE_memcpy(Zeroes, Padding * sizeof(char)); +#undef UPDATE_memcpy + + return 0; +} diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingFile.c b/contrib/compiler-rt/lib/profile/InstrProfilingFile.c new file mode 100644 index 000000000000..5aef3904b56d --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfilingFile.c @@ -0,0 +1,194 @@ +/*===- InstrProfilingFile.c - Write instrumentation to a file -------------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfiling.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define UNCONST(ptr) ((void *)(uintptr_t)(ptr)) + +static int writeFile(FILE *File) { + /* Match logic in __llvm_profile_write_buffer(). */ + const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); + const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); + const uint64_t *CountersBegin = __llvm_profile_begin_counters(); + const uint64_t *CountersEnd = __llvm_profile_end_counters(); + const char *NamesBegin = __llvm_profile_begin_names(); + const char *NamesEnd = __llvm_profile_end_names(); + + /* Calculate size of sections. */ + const uint64_t DataSize = DataEnd - DataBegin; + const uint64_t CountersSize = CountersEnd - CountersBegin; + const uint64_t NamesSize = NamesEnd - NamesBegin; + const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t); + + /* Enough zeroes for padding. */ + const char Zeroes[sizeof(uint64_t)] = {0}; + + /* Create the header. */ + uint64_t Header[PROFILE_HEADER_SIZE]; + Header[0] = __llvm_profile_get_magic(); + Header[1] = __llvm_profile_get_version(); + Header[2] = DataSize; + Header[3] = CountersSize; + Header[4] = NamesSize; + Header[5] = (uintptr_t)CountersBegin; + Header[6] = (uintptr_t)NamesBegin; + + /* Write the data. */ +#define CHECK_fwrite(Data, Size, Length, File) \ + do { if (fwrite(Data, Size, Length, File) != Length) return -1; } while (0) + CHECK_fwrite(Header, sizeof(uint64_t), PROFILE_HEADER_SIZE, File); + CHECK_fwrite(DataBegin, sizeof(__llvm_profile_data), DataSize, File); + CHECK_fwrite(CountersBegin, sizeof(uint64_t), CountersSize, File); + CHECK_fwrite(NamesBegin, sizeof(char), NamesSize, File); + CHECK_fwrite(Zeroes, sizeof(char), Padding, File); +#undef CHECK_fwrite + + return 0; +} + +static int writeFileWithName(const char *OutputName) { + int RetVal; + FILE *OutputFile; + if (!OutputName || !OutputName[0]) + return -1; + + /* Append to the file to support profiling multiple shared objects. */ + OutputFile = fopen(OutputName, "a"); + if (!OutputFile) + return -1; + + RetVal = writeFile(OutputFile); + + fclose(OutputFile); + return RetVal; +} + +__attribute__((weak)) int __llvm_profile_OwnsFilename = 0; +__attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL; + +static void setFilename(const char *Filename, int OwnsFilename) { + if (__llvm_profile_OwnsFilename) + free(UNCONST(__llvm_profile_CurrentFilename)); + + __llvm_profile_CurrentFilename = Filename; + __llvm_profile_OwnsFilename = OwnsFilename; +} + +static void truncateCurrentFile(void) { + const char *Filename = __llvm_profile_CurrentFilename; + if (!Filename || !Filename[0]) + return; + + /* Truncate the file. Later we'll reopen and append. */ + FILE *File = fopen(Filename, "w"); + if (!File) + return; + fclose(File); +} + +static void setDefaultFilename(void) { setFilename("default.profraw", 0); } + +int getpid(void); +static int setFilenameFromEnvironment(void) { + const char *Filename = getenv("LLVM_PROFILE_FILE"); + if (!Filename || !Filename[0]) + return -1; + + /* Check the filename for "%p", which indicates a pid-substitution. */ +#define MAX_PID_SIZE 16 + char PidChars[MAX_PID_SIZE] = {0}; + int NumPids = 0; + int PidLength = 0; + int I; + for (I = 0; Filename[I]; ++I) + if (Filename[I] == '%' && Filename[++I] == 'p') + if (!NumPids++) { + PidLength = snprintf(PidChars, MAX_PID_SIZE, "%d", getpid()); + if (PidLength <= 0) + return -1; + } + if (!NumPids) { + setFilename(Filename, 0); + return 0; + } + + /* Allocate enough space for the substituted filename. */ + char *Allocated = (char*)malloc(I + NumPids*(PidLength - 2) + 1); + if (!Allocated) + return -1; + + /* Construct the new filename. */ + int J; + for (I = 0, J = 0; Filename[I]; ++I) + if (Filename[I] == '%') { + if (Filename[++I] == 'p') { + memcpy(Allocated + J, PidChars, PidLength); + J += PidLength; + } + /* Drop any unknown substitutions. */ + } else + Allocated[J++] = Filename[I]; + Allocated[J] = 0; + + /* Use the computed name. */ + setFilename(Allocated, 1); + return 0; +} + +static void setFilenameAutomatically(void) { + if (!setFilenameFromEnvironment()) + return; + + setDefaultFilename(); +} + +__attribute__((visibility("hidden"))) +void __llvm_profile_initialize_file(void) { + /* Check if the filename has been initialized. */ + if (__llvm_profile_CurrentFilename) + return; + + /* Detect the filename and truncate. */ + setFilenameAutomatically(); + truncateCurrentFile(); +} + +__attribute__((visibility("hidden"))) +void __llvm_profile_set_filename(const char *Filename) { + setFilename(Filename, 0); + truncateCurrentFile(); +} + +__attribute__((visibility("hidden"))) +int __llvm_profile_write_file(void) { + /* Check the filename. */ + if (!__llvm_profile_CurrentFilename) + return -1; + + /* Write the file. */ + return writeFileWithName(__llvm_profile_CurrentFilename); +} + +static void writeFileWithoutReturn(void) { + __llvm_profile_write_file(); +} + +__attribute__((visibility("hidden"))) +int __llvm_profile_register_write_file_atexit(void) { + static int HasBeenRegistered = 0; + + if (HasBeenRegistered) + return 0; + + HasBeenRegistered = 1; + return atexit(writeFileWithoutReturn); +} diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingInternal.h b/contrib/compiler-rt/lib/profile/InstrProfilingInternal.h new file mode 100644 index 000000000000..ede39cd9d713 --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfilingInternal.h @@ -0,0 +1,40 @@ +/*===- InstrProfiling.h- Support library for PGO instrumentation ----------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#ifndef PROFILE_INSTRPROFILING_INTERNALH_ +#define PROFILE_INSTRPROFILING_INTERNALH_ + +#include "InstrProfiling.h" + +/*! + * \brief Write instrumentation data to the given buffer, given explicit + * pointers to the live data in memory. This function is probably not what you + * want. Use __llvm_profile_get_size_for_buffer instead. Use this function if + * your program has a custom memory layout. + */ +uint64_t __llvm_profile_get_size_for_buffer_internal( + const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, + const uint64_t *CountersBegin, const uint64_t *CountersEnd, + const char *NamesBegin, const char *NamesEnd); + +/*! + * \brief Write instrumentation data to the given buffer, given explicit + * pointers to the live data in memory. This function is probably not what you + * want. Use __llvm_profile_write_buffer instead. Use this function if your + * program has a custom memory layout. + * + * \pre \c Buffer is the start of a buffer at least as big as \a + * __llvm_profile_get_size_for_buffer_internal(). + */ +int __llvm_profile_write_buffer_internal( + char *Buffer, const __llvm_profile_data *DataBegin, + const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, + const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd); + +#endif diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingPlatformDarwin.c b/contrib/compiler-rt/lib/profile/InstrProfilingPlatformDarwin.c new file mode 100644 index 000000000000..02299cc4630c --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfilingPlatformDarwin.c @@ -0,0 +1,43 @@ +/*===- InstrProfilingPlatformDarwin.c - Profile data on Darwin ------------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfiling.h" + +#if defined(__APPLE__) +/* Use linker magic to find the bounds of the Data section. */ +__attribute__((visibility("hidden"))) +extern __llvm_profile_data DataStart __asm("section$start$__DATA$__llvm_prf_data"); +__attribute__((visibility("hidden"))) +extern __llvm_profile_data DataEnd __asm("section$end$__DATA$__llvm_prf_data"); +__attribute__((visibility("hidden"))) +extern char NamesStart __asm("section$start$__DATA$__llvm_prf_names"); +__attribute__((visibility("hidden"))) +extern char NamesEnd __asm("section$end$__DATA$__llvm_prf_names"); +__attribute__((visibility("hidden"))) +extern uint64_t CountersStart __asm("section$start$__DATA$__llvm_prf_cnts"); +__attribute__((visibility("hidden"))) +extern uint64_t CountersEnd __asm("section$end$__DATA$__llvm_prf_cnts"); + +__attribute__((visibility("hidden"))) +const __llvm_profile_data *__llvm_profile_begin_data(void) { + return &DataStart; +} +__attribute__((visibility("hidden"))) +const __llvm_profile_data *__llvm_profile_end_data(void) { + return &DataEnd; +} +__attribute__((visibility("hidden"))) +const char *__llvm_profile_begin_names(void) { return &NamesStart; } +__attribute__((visibility("hidden"))) +const char *__llvm_profile_end_names(void) { return &NamesEnd; } +__attribute__((visibility("hidden"))) +uint64_t *__llvm_profile_begin_counters(void) { return &CountersStart; } +__attribute__((visibility("hidden"))) +uint64_t *__llvm_profile_end_counters(void) { return &CountersEnd; } +#endif diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingPlatformOther.c b/contrib/compiler-rt/lib/profile/InstrProfilingPlatformOther.c new file mode 100644 index 000000000000..548d6a396b76 --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfilingPlatformOther.c @@ -0,0 +1,74 @@ +/*===- InstrProfilingPlatformOther.c - Profile data default platform ------===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfiling.h" + +#if !defined(__APPLE__) +#include <stdlib.h> + +static const __llvm_profile_data *DataFirst = NULL; +static const __llvm_profile_data *DataLast = NULL; +static const char *NamesFirst = NULL; +static const char *NamesLast = NULL; +static uint64_t *CountersFirst = NULL; +static uint64_t *CountersLast = NULL; + +/*! + * \brief Register an instrumented function. + * + * Calls to this are emitted by clang with -fprofile-instr-generate. Such + * calls are only required (and only emitted) on targets where we haven't + * implemented linker magic to find the bounds of the sections. + */ +__attribute__((visibility("hidden"))) +void __llvm_profile_register_function(void *Data_) { + /* TODO: Only emit this function if we can't use linker magic. */ + const __llvm_profile_data *Data = (__llvm_profile_data*)Data_; + if (!DataFirst) { + DataFirst = Data; + DataLast = Data + 1; + NamesFirst = Data->Name; + NamesLast = Data->Name + Data->NameSize; + CountersFirst = Data->Counters; + CountersLast = Data->Counters + Data->NumCounters; + return; + } + +#define UPDATE_FIRST(First, New) \ + First = New < First ? New : First + UPDATE_FIRST(DataFirst, Data); + UPDATE_FIRST(NamesFirst, Data->Name); + UPDATE_FIRST(CountersFirst, Data->Counters); +#undef UPDATE_FIRST + +#define UPDATE_LAST(Last, New) \ + Last = New > Last ? New : Last + UPDATE_LAST(DataLast, Data + 1); + UPDATE_LAST(NamesLast, Data->Name + Data->NameSize); + UPDATE_LAST(CountersLast, Data->Counters + Data->NumCounters); +#undef UPDATE_LAST +} + +__attribute__((visibility("hidden"))) +const __llvm_profile_data *__llvm_profile_begin_data(void) { + return DataFirst; +} +__attribute__((visibility("hidden"))) +const __llvm_profile_data *__llvm_profile_end_data(void) { + return DataLast; +} +__attribute__((visibility("hidden"))) +const char *__llvm_profile_begin_names(void) { return NamesFirst; } +__attribute__((visibility("hidden"))) +const char *__llvm_profile_end_names(void) { return NamesLast; } +__attribute__((visibility("hidden"))) +uint64_t *__llvm_profile_begin_counters(void) { return CountersFirst; } +__attribute__((visibility("hidden"))) +uint64_t *__llvm_profile_end_counters(void) { return CountersLast; } +#endif diff --git a/contrib/compiler-rt/lib/profile/InstrProfilingRuntime.cc b/contrib/compiler-rt/lib/profile/InstrProfilingRuntime.cc new file mode 100644 index 000000000000..081ecb29e987 --- /dev/null +++ b/contrib/compiler-rt/lib/profile/InstrProfilingRuntime.cc @@ -0,0 +1,30 @@ +//===- InstrProfilingRuntime.cpp - PGO runtime initialization -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +extern "C" { + +#include "InstrProfiling.h" + +__attribute__((visibility("hidden"))) int __llvm_profile_runtime; + +} + +namespace { + +class RegisterRuntime { +public: + RegisterRuntime() { + __llvm_profile_register_write_file_atexit(); + __llvm_profile_initialize_file(); + } +}; + +RegisterRuntime Registration; + +} |