diff options
Diffstat (limited to 'gnu/llvm/compiler-rt/lib/profile')
13 files changed, 390 insertions, 222 deletions
diff --git a/gnu/llvm/compiler-rt/lib/profile/CMakeLists.txt b/gnu/llvm/compiler-rt/lib/profile/CMakeLists.txt index 955d0bf7293..29c6c02f2d0 100644 --- a/gnu/llvm/compiler-rt/lib/profile/CMakeLists.txt +++ b/gnu/llvm/compiler-rt/lib/profile/CMakeLists.txt @@ -1,11 +1,11 @@ CHECK_CXX_SOURCE_COMPILES(" -#ifdef _MSC_VER -#include <Intrin.h> /* Workaround for PR19898. */ +#ifdef _WIN32 +#include <intrin.h> /* Workaround for PR19898. */ #include <windows.h> #endif int main() { -#ifdef _MSC_VER +#ifdef _WIN32 volatile LONG val = 1; MemoryBarrier(); InterlockedCompareExchange(&val, 0, 1); @@ -51,7 +51,9 @@ add_compiler_rt_component(profile) set(PROFILE_SOURCES GCDAProfiling.c InstrProfiling.c + InstrProfilingInternal.c InstrProfilingValue.c + InstrProfilingBiasVar.c InstrProfilingBuffer.c InstrProfilingFile.c InstrProfilingMerge.c 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 } } diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfiling.c b/gnu/llvm/compiler-rt/lib/profile/InstrProfiling.c index 087d1cdd2ef..31a9fe99629 100644 --- a/gnu/llvm/compiler-rt/lib/profile/InstrProfiling.c +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfiling.c @@ -25,18 +25,8 @@ COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_magic(void) { : (INSTR_PROF_RAW_MAGIC_32); } -static unsigned ProfileDumped = 0; - -COMPILER_RT_VISIBILITY unsigned lprofProfileDumped() { - return ProfileDumped; -} - -COMPILER_RT_VISIBILITY void lprofSetProfileDumped() { - ProfileDumped = 1; -} - COMPILER_RT_VISIBILITY void __llvm_profile_set_dumped() { - lprofSetProfileDumped(); + lprofSetProfileDumped(1); } /* Return the number of bytes needed to add to SizeInBytes to make it @@ -80,5 +70,5 @@ COMPILER_RT_VISIBILITY void __llvm_profile_reset_counters(void) { } } } - ProfileDumped = 0; + lprofSetProfileDumped(0); } diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfiling.h b/gnu/llvm/compiler-rt/lib/profile/InstrProfiling.h index 3a3bab3d0b4..d7a7c32332c 100644 --- a/gnu/llvm/compiler-rt/lib/profile/InstrProfiling.h +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfiling.h @@ -218,6 +218,9 @@ int __llvm_profile_register_write_file_atexit(void); /*! \brief Initialize file handling. */ void __llvm_profile_initialize_file(void); +/*! \brief Initialize the profile runtime. */ +void __llvm_profile_initialize(void); + /*! * \brief Return path prefix (excluding the base filename) of the profile data. * This is useful for users using \c -fprofile-generate=./path_prefix who do @@ -307,4 +310,11 @@ extern uint64_t INSTR_PROF_RAW_VERSION_VAR; /* __llvm_profile_raw_version */ */ extern char INSTR_PROF_PROFILE_NAME_VAR[1]; /* __llvm_profile_filename. */ +/*! + * This variable is a weak symbol defined in InstrProfilingBiasVar.c. It + * allows compiler instrumentation to provide overriding definition with + * value from compiler command line. This variable has hidden visibility. + */ +COMPILER_RT_VISIBILITY extern intptr_t __llvm_profile_counter_bias; + #endif /* PROFILE_INSTRPROFILING_H_ */ diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingBiasVar.c b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingBiasVar.c new file mode 100644 index 00000000000..05745fd858d --- /dev/null +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingBiasVar.c @@ -0,0 +1,15 @@ +/*===- InstrProfilingBiasVar.c - profile counter bias variable setup ------===*\ +|* +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +|* See https://llvm.org/LICENSE.txt for license information. +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfiling.h" + +/* The runtime should only provide its own definition of this symbol when the + * user has not specified one. Set this up by moving the runtime's copy of this + * symbol to an object file within the archive. + */ +COMPILER_RT_WEAK intptr_t __llvm_profile_counter_bias = -1; diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingBuffer.c b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingBuffer.c index 174280fd4b5..5ee44785a7a 100644 --- a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingBuffer.c +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingBuffer.c @@ -62,7 +62,8 @@ void __llvm_profile_get_padding_sizes_for_counters( uint64_t DataSize, uint64_t CountersSize, uint64_t NamesSize, uint64_t *PaddingBytesBeforeCounters, uint64_t *PaddingBytesAfterCounters, uint64_t *PaddingBytesAfterNames) { - if (!__llvm_profile_is_continuous_mode_enabled()) { + if (!__llvm_profile_is_continuous_mode_enabled() || + lprofRuntimeCounterRelocation()) { *PaddingBytesBeforeCounters = 0; *PaddingBytesAfterCounters = 0; *PaddingBytesAfterNames = __llvm_profile_get_num_padding_bytes(NamesSize); diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingFile.c b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingFile.c index 7f3727eed92..9e1a54a0c37 100644 --- a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingFile.c @@ -448,6 +448,99 @@ static void unlockProfile(int *ProfileRequiresUnlock, FILE *File) { } #endif // !defined(__Fuchsia__) && !defined(_WIN32) +static int writeMMappedFile(FILE *OutputFile, char **Profile) { + if (!OutputFile) + return -1; + + /* Write the data into a file. */ + setupIOBuffer(); + ProfDataWriter fileWriter; + initFileWriter(&fileWriter, OutputFile); + if (lprofWriteData(&fileWriter, NULL, 0)) { + PROF_ERR("Failed to write profile: %s\n", strerror(errno)); + return -1; + } + fflush(OutputFile); + + /* Get the file size. */ + uint64_t FileSize = ftell(OutputFile); + + /* Map the profile. */ + *Profile = (char *)mmap( + NULL, FileSize, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(OutputFile), 0); + if (*Profile == MAP_FAILED) { + PROF_ERR("Unable to mmap profile: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +static void relocateCounters(void) { + if (!__llvm_profile_is_continuous_mode_enabled() || + !lprofRuntimeCounterRelocation()) + return; + + /* Get the sizes of various profile data sections. Taken from + * __llvm_profile_get_size_for_buffer(). */ + const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); + const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); + uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); + const uint64_t CountersOffset = sizeof(__llvm_profile_header) + + (DataSize * sizeof(__llvm_profile_data)); + + int Length = getCurFilenameLength(); + char *FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); + const char *Filename = getCurFilename(FilenameBuf, 0); + if (!Filename) + return; + + FILE *File = NULL; + char *Profile = NULL; + + if (!doMerging()) { + File = fopen(Filename, "w+b"); + if (!File) + return; + + if (writeMMappedFile(File, &Profile) == -1) { + fclose(File); + return; + } + } else { + File = lprofOpenFileEx(Filename); + if (!File) + return; + + uint64_t ProfileFileSize = 0; + if (getProfileFileSizeForMerging(File, &ProfileFileSize) == -1) { + lprofUnlockFileHandle(File); + fclose(File); + return; + } + + if (!ProfileFileSize) { + if (writeMMappedFile(File, &Profile) == -1) { + fclose(File); + return; + } + } else { + /* The merged profile has a non-zero length. Check that it is compatible + * with the data in this process. */ + if (mmapProfileForMerging(File, ProfileFileSize, &Profile) == -1) { + fclose(File); + return; + } + } + + lprofUnlockFileHandle(File); + } + + /* Update the profile fields based on the current mapping. */ + __llvm_profile_counter_bias = (intptr_t)Profile - + (uintptr_t)__llvm_profile_begin_counters() + CountersOffset; +} + static void initializeProfileForContinuousMode(void) { if (!__llvm_profile_is_continuous_mode_enabled()) return; @@ -715,7 +808,12 @@ static void parseAndSetFilename(const char *FilenamePat, } truncateCurrentFile(); - initializeProfileForContinuousMode(); + if (__llvm_profile_is_continuous_mode_enabled()) { + if (lprofRuntimeCounterRelocation()) + relocateCounters(); + else + initializeProfileForContinuousMode(); + } } /* Return buffer length that is required to store the current profile @@ -854,10 +952,10 @@ const char *__llvm_profile_get_filename(void) { return FilenameBuf; } -/* This method is invoked by the runtime initialization hook - * InstrProfilingRuntime.o if it is linked in. Both user specified +/* This API initializes the file handling, both user specified * profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE - * environment variable can override this default value. */ + * environment variable can override this default value. + */ COMPILER_RT_VISIBILITY void __llvm_profile_initialize_file(void) { const char *EnvFilenamePat; @@ -865,6 +963,9 @@ void __llvm_profile_initialize_file(void) { ProfileNameSpecifier PNS = PNS_unknown; int hasCommandLineOverrider = (INSTR_PROF_PROFILE_NAME_VAR[0] != 0); + if (__llvm_profile_counter_bias != -1) + lprofSetRuntimeCounterRelocation(1); + EnvFilenamePat = getFilenamePatFromEnv(); if (EnvFilenamePat) { /* Pass CopyFilenamePat = 1, to ensure that the filename would be valid @@ -882,6 +983,16 @@ void __llvm_profile_initialize_file(void) { parseAndSetFilename(SelectedPat, PNS, 0); } +/* This method is invoked by the runtime initialization hook + * InstrProfilingRuntime.o if it is linked in. + */ +COMPILER_RT_VISIBILITY +void __llvm_profile_initialize(void) { + __llvm_profile_initialize_file(); + if (!__llvm_profile_is_continuous_mode_enabled()) + __llvm_profile_register_write_file_atexit(); +} + /* This API is directly called by the user application code. It has the * highest precedence compared with LLVM_PROFILE_FILE environment variable * and command line option -fprofile-instr-generate=<profile_name>. @@ -951,7 +1062,7 @@ int __llvm_profile_dump(void) { "in profile name or change profile name before dumping.\n", "online profile merging is not on"); int rc = __llvm_profile_write_file(); - lprofSetProfileDumped(); + lprofSetProfileDumped(1); return rc; } diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingInternal.c b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingInternal.c new file mode 100644 index 00000000000..d58bc19ad11 --- /dev/null +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingInternal.c @@ -0,0 +1,33 @@ +/*===- InstrProfilingInternal.c - Support library for PGO instrumentation -===*\ +|* +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +|* See https://llvm.org/LICENSE.txt for license information. +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +|* +\*===----------------------------------------------------------------------===*/ + +#if !defined(__Fuchsia__) + +#include "InstrProfilingInternal.h" + +static unsigned ProfileDumped = 0; + +COMPILER_RT_VISIBILITY unsigned lprofProfileDumped() { + return ProfileDumped; +} + +COMPILER_RT_VISIBILITY void lprofSetProfileDumped(unsigned Value) { + ProfileDumped = Value; +} + +static unsigned RuntimeCounterRelocation = 0; + +COMPILER_RT_VISIBILITY unsigned lprofRuntimeCounterRelocation(void) { + return RuntimeCounterRelocation; +} + +COMPILER_RT_VISIBILITY void lprofSetRuntimeCounterRelocation(unsigned Value) { + RuntimeCounterRelocation = Value; +} + +#endif diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingInternal.h b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingInternal.h index 0cea4876f0a..904bd394592 100644 --- a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingInternal.h +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingInternal.h @@ -181,8 +181,12 @@ uint64_t lprofGetLoadModuleSignature(); * Return non zero value if the profile data has already been * dumped to the file. */ -unsigned lprofProfileDumped(); -void lprofSetProfileDumped(); +unsigned lprofProfileDumped(void); +void lprofSetProfileDumped(unsigned); + +/* Return non zero value if counters are being relocated at runtime. */ +unsigned lprofRuntimeCounterRelocation(void); +void lprofSetRuntimeCounterRelocation(unsigned); COMPILER_RT_VISIBILITY extern void (*FreeHook)(void *); COMPILER_RT_VISIBILITY extern uint8_t *DynamicBufferIOBuffer; diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingPlatformFuchsia.c b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingPlatformFuchsia.c index 23b7efbe672..d8b7fa21d25 100644 --- a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingPlatformFuchsia.c +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingPlatformFuchsia.c @@ -34,16 +34,15 @@ #include "InstrProfilingInternal.h" #include "InstrProfilingUtil.h" -/* VMO that contains the coverage data shared across all modules. This symbol - * has default visibility and is exported in each module (executable or DSO) - * that statically links in the profiling runtime. - */ -zx_handle_t __llvm_profile_vmo; -/* Current offset within the VMO where data should be written next. This symbol - * has default visibility and is exported in each module (executable or DSO) - * that statically links in the profiling runtime. - */ -uint64_t __llvm_profile_offset; +COMPILER_RT_VISIBILITY unsigned lprofProfileDumped() { + return 1; +} +COMPILER_RT_VISIBILITY void lprofSetProfileDumped(unsigned Value) {} + +COMPILER_RT_VISIBILITY unsigned lprofRuntimeCounterRelocation(void) { + return 1; +} +COMPILER_RT_VISIBILITY void lprofSetRuntimeCounterRelocation(unsigned Value) {} static const char ProfileSinkName[] = "llvm-profile"; @@ -58,65 +57,24 @@ static inline void lprofWrite(const char *fmt, ...) { __sanitizer_log_write(s, ret + 1); } -static void createVMO() { - /* Don't create VMO if it has been alread created. */ - if (__llvm_profile_vmo != ZX_HANDLE_INVALID) - return; - - /* Get information about the current process. */ - zx_info_handle_basic_t Info; - zx_status_t Status = - _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info, - sizeof(Info), NULL, NULL); - if (Status != ZX_OK) { - lprofWrite("LLVM Profile: cannot get info about current process: %s\n", - _zx_status_get_string(Status)); - return; - } - - /* Create VMO to hold the profile data. */ - Status = _zx_vmo_create(0, ZX_VMO_RESIZABLE, &__llvm_profile_vmo); - if (Status != ZX_OK) { - lprofWrite("LLVM Profile: cannot create VMO: %s\n", - _zx_status_get_string(Status)); - return; - } - - /* Give the VMO a name including our process KOID so it's easy to spot. */ - char VmoName[ZX_MAX_NAME_LEN]; - snprintf(VmoName, sizeof(VmoName), "%s.%" PRIu64, ProfileSinkName, Info.koid); - _zx_object_set_property(__llvm_profile_vmo, ZX_PROP_NAME, VmoName, - strlen(VmoName)); - - /* Duplicate the handle since __sanitizer_publish_data consumes it. */ - zx_handle_t Handle; - Status = - _zx_handle_duplicate(__llvm_profile_vmo, ZX_RIGHT_SAME_RIGHTS, &Handle); - if (Status != ZX_OK) { - lprofWrite("LLVM Profile: cannot duplicate VMO handle: %s\n", - _zx_status_get_string(Status)); - _zx_handle_close(__llvm_profile_vmo); - __llvm_profile_vmo = ZX_HANDLE_INVALID; - return; - } - - /* Publish the VMO which contains profile data to the system. */ - __sanitizer_publish_data(ProfileSinkName, Handle); - - /* Use the dumpfile symbolizer markup element to write the name of VMO. */ - lprofWrite("LLVM Profile: {{{dumpfile:%s:%s}}}\n", ProfileSinkName, VmoName); -} +struct lprofVMOWriterCtx { + /* VMO that contains the profile data for this module. */ + zx_handle_t Vmo; + /* Current offset within the VMO where data should be written next. */ + uint64_t Offset; +}; static uint32_t lprofVMOWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, uint32_t NumIOVecs) { + struct lprofVMOWriterCtx *Ctx = (struct lprofVMOWriterCtx *)This->WriterCtx; + /* Compute the total length of data to be written. */ size_t Length = 0; for (uint32_t I = 0; I < NumIOVecs; I++) Length += IOVecs[I].ElmSize * IOVecs[I].NumElm; /* Resize the VMO to ensure there's sufficient space for the data. */ - zx_status_t Status = - _zx_vmo_set_size(__llvm_profile_vmo, __llvm_profile_offset + Length); + zx_status_t Status = _zx_vmo_set_size(Ctx->Vmo, Ctx->Offset + Length); if (Status != ZX_OK) return -1; @@ -124,74 +82,112 @@ static uint32_t lprofVMOWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, for (uint32_t I = 0; I < NumIOVecs; I++) { size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm; if (IOVecs[I].Data) { - Status = _zx_vmo_write(__llvm_profile_vmo, IOVecs[I].Data, - __llvm_profile_offset, Length); + Status = _zx_vmo_write(Ctx->Vmo, IOVecs[I].Data, Ctx->Offset, Length); if (Status != ZX_OK) return -1; } else if (IOVecs[I].UseZeroPadding) { /* Resizing the VMO should zero fill. */ } - __llvm_profile_offset += Length; + Ctx->Offset += Length; } + /* Record the profile size as a property of the VMO. */ + _zx_object_set_property(Ctx->Vmo, ZX_PROP_VMO_CONTENT_SIZE, &Ctx->Offset, + sizeof(Ctx->Offset)); + return 0; } -static void initVMOWriter(ProfDataWriter *This) { +static void initVMOWriter(ProfDataWriter *This, struct lprofVMOWriterCtx *Ctx) { This->Write = lprofVMOWriter; - This->WriterCtx = NULL; + This->WriterCtx = Ctx; } -static int dump(void) { - if (lprofProfileDumped()) { - lprofWrite("LLVM Profile: data not published: already written.\n"); - return 0; - } - +/* This method is invoked by the runtime initialization hook + * InstrProfilingRuntime.o if it is linked in. */ +COMPILER_RT_VISIBILITY +void __llvm_profile_initialize(void) { /* Check if there is llvm/runtime version mismatch. */ if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { lprofWrite("LLVM Profile: runtime and instrumentation version mismatch: " "expected %d, but got %d\n", INSTR_PROF_RAW_VERSION, (int)GET_VERSION(__llvm_profile_get_version())); - return -1; + return; } - /* Write the profile data into the mapped region. */ - ProfDataWriter VMOWriter; - initVMOWriter(&VMOWriter); - if (lprofWriteData(&VMOWriter, lprofGetVPDataReader(), 0) != 0) - return -1; + /* This symbol is defined as weak and initialized to -1 by the runtimer, but + * compiler will generate a strong definition initialized to 0 when runtime + * counter relocation is used. */ + if (__llvm_profile_counter_bias == -1) { + lprofWrite("LLVM Profile: counter relocation at runtime is required\n"); + return; + } - return 0; -} + const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); + const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); + const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); + const uint64_t CountersOffset = + sizeof(__llvm_profile_header) + (DataSize * sizeof(__llvm_profile_data)); -COMPILER_RT_VISIBILITY -int __llvm_profile_dump(void) { - int rc = dump(); - lprofSetProfileDumped(); - return rc; -} + zx_status_t Status; -static void dumpWithoutReturn(void) { dump(); } + /* Create VMO to hold the profile data. */ + zx_handle_t Vmo = ZX_HANDLE_INVALID; + Status = _zx_vmo_create(0, ZX_VMO_RESIZABLE, &Vmo); + if (Status != ZX_OK) { + lprofWrite("LLVM Profile: cannot create VMO: %s\n", + _zx_status_get_string(Status)); + return; + } -/* This method is invoked by the runtime initialization hook - * InstrProfilingRuntime.o if it is linked in. - */ -COMPILER_RT_VISIBILITY -void __llvm_profile_initialize_file(void) { createVMO(); } + /* Give the VMO a name that includes the module signature. */ + char VmoName[ZX_MAX_NAME_LEN]; + snprintf(VmoName, sizeof(VmoName), "%" PRIu64 ".profraw", + lprofGetLoadModuleSignature()); + _zx_object_set_property(Vmo, ZX_PROP_NAME, VmoName, strlen(VmoName)); -COMPILER_RT_VISIBILITY -int __llvm_profile_register_write_file_atexit(void) { - static bool HasBeenRegistered = false; + /* Write the profile data into the mapped region. */ + ProfDataWriter VMOWriter; + struct lprofVMOWriterCtx Ctx = {.Vmo = Vmo, .Offset = 0}; + initVMOWriter(&VMOWriter, &Ctx); + if (lprofWriteData(&VMOWriter, 0, 0) != 0) { + lprofWrite("LLVM Profile: failed to write data\n"); + _zx_handle_close(Vmo); + return; + } - if (HasBeenRegistered) - return 0; + uint64_t Len = 0; + Status = _zx_vmo_get_size(Vmo, &Len); + if (Status != ZX_OK) { + lprofWrite("LLVM Profile: failed to get the VMO size: %s\n", + _zx_status_get_string(Status)); + _zx_handle_close(Vmo); + return; + } - lprofSetupValueProfiler(); + uintptr_t Mapping; + Status = + _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, + Vmo, 0, Len, &Mapping); + if (Status != ZX_OK) { + lprofWrite("LLVM Profile: failed to map the VMO: %s\n", + _zx_status_get_string(Status)); + _zx_handle_close(Vmo); + return; + } + + /* Publish the VMO which contains profile data to the system. Note that this + * also consumes the VMO handle. */ + __sanitizer_publish_data(ProfileSinkName, Vmo); + + /* Use the dumpfile symbolizer markup element to write the name of VMO. */ + lprofWrite("LLVM Profile: {{{dumpfile:%s:%s}}}\n", ProfileSinkName, VmoName); - HasBeenRegistered = true; - return atexit(dumpWithoutReturn); + /* Update the profile fields based on the current mapping. */ + __llvm_profile_counter_bias = (intptr_t)Mapping - + (uintptr_t)__llvm_profile_begin_counters() + + CountersOffset; } #endif diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingPort.h b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingPort.h index 20cf5d660c6..4493dd512ff 100644 --- a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingPort.h +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingPort.h @@ -53,9 +53,9 @@ #endif #if COMPILER_RT_HAS_ATOMICS == 1 -#ifdef _MSC_VER +#ifdef _WIN32 #include <windows.h> -#if _MSC_VER < 1900 +#if defined(_MSC_VER) && _MSC_VER < 1900 #define snprintf _snprintf #endif #if defined(_WIN64) @@ -73,7 +73,7 @@ (DomType *)InterlockedExchangeAdd((LONG volatile *)&PtrVar, \ (LONG)sizeof(DomType) * PtrIncr) #endif -#else /* !defined(_MSC_VER) */ +#else /* !defined(_WIN32) */ #define COMPILER_RT_BOOL_CMPXCHG(Ptr, OldV, NewV) \ __sync_bool_compare_and_swap(Ptr, OldV, NewV) #define COMPILER_RT_PTR_FETCH_ADD(DomType, PtrVar, PtrIncr) \ diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingRuntime.cpp b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingRuntime.cpp index 5dff09d7063..4ea2bb263f5 100644 --- a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingRuntime.cpp +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingRuntime.cpp @@ -19,9 +19,7 @@ namespace { class RegisterRuntime { public: RegisterRuntime() { - __llvm_profile_initialize_file(); - if (!__llvm_profile_is_continuous_mode_enabled()) - __llvm_profile_register_write_file_atexit(); + __llvm_profile_initialize(); } }; diff --git a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingUtil.h b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingUtil.h index f0e29a8803a..5f5c85091fe 100644 --- a/gnu/llvm/compiler-rt/lib/profile/InstrProfilingUtil.h +++ b/gnu/llvm/compiler-rt/lib/profile/InstrProfilingUtil.h @@ -30,11 +30,13 @@ int lprofUnlockFileHandle(FILE *F); * lock for exclusive access. The caller will block * if the lock is already held by another process. */ FILE *lprofOpenFileEx(const char *Filename); -/* PS4 doesn't have setenv/getenv. Define a shim. */ +/* PS4 doesn't have setenv/getenv/fork. Define a shim. */ #if __ORBIS__ +#include <sys/types.h> static inline char *getenv(const char *name) { return NULL; } static inline int setenv(const char *name, const char *value, int overwrite) { return 0; } +static pid_t fork() { return -1; } #endif /* #if __ORBIS__ */ /* GCOV_PREFIX and GCOV_PREFIX_STRIP support */ |