diff --git a/compiler-rt/lib/profile/InstrProfiling.h b/compiler-rt/lib/profile/InstrProfiling.h --- a/compiler-rt/lib/profile/InstrProfiling.h +++ b/compiler-rt/lib/profile/InstrProfiling.h @@ -194,7 +194,8 @@ void __llvm_profile_set_filename(const char *Name); /*! - * \brief Set the FILE object for writing instrumentation data. + * \brief Set the FILE object for writing instrumentation data. Return 0 if set + * successfully or return 1 if failed. * * Sets the FILE object to be used for subsequent calls to * \a __llvm_profile_write_file(). The profile file name set by environment @@ -213,13 +214,12 @@ * instrumented image/DSO). This API only modifies the file object within the * copy of the runtime available to the calling image. * - * Warning: This is a no-op if continuous mode (\ref - * __llvm_profile_is_continuous_mode_enabled) is on. The reason for this is - * that in continuous mode, profile counters are mmap()'d to the profile at - * program initialization time. Support for transferring the mmap'd profile - * counts to a new file has not been implemented. + * Warning: This is a no-op if EnableMerge is 0 in continuous mode (\ref + * __llvm_profile_is_continuous_mode_enabled), because disable merging requires + * copying the old profile file to new profile file and this function is usually + * used when the proess doesn't have permission to open file. */ -void __llvm_profile_set_file_object(FILE *File, int EnableMerge); +int __llvm_profile_set_file_object(FILE *File, int EnableMerge); /*! \brief Register to write instrumentation data to file at exit. */ int __llvm_profile_register_write_file_atexit(void); diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -92,15 +92,71 @@ {0}, 0, 0, 0, PNS_unknown}; static int ProfileMergeRequested = 0; +static int getProfileFileSizeForMerging(FILE *ProfileFile, + uint64_t *ProfileFileSize); #if defined(__APPLE__) static const int ContinuousModeSupported = 1; static const int UseBiasVar = 0; static const char *FileOpenMode = "a+b"; -static intptr_t INSTR_PROF_PROFILE_COUNTER_BIAS_VAR; static void *BiasAddr = NULL; static void *BiasDefaultAddr = NULL; -static int MmapFlags = MAP_FIXED | MAP_SHARED; +static int mmapForContinuousMode(uint64_t CurrentFileOffset, FILE *File) { + /* 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(); + 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(); + const uint64_t NamesSize = (NamesEnd - NamesBegin) * sizeof(char); + uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); + uint64_t CountersSize = CountersEnd - CountersBegin; + + /* Check that the counter and data sections in this image are + * page-aligned. */ + unsigned PageSize = getpagesize(); + if ((intptr_t)CountersBegin % PageSize != 0) { + PROF_ERR("Counters section not page-aligned (start = %p, pagesz = %u).\n", + CountersBegin, PageSize); + return 1; + } + if ((intptr_t)DataBegin % PageSize != 0) { + PROF_ERR("Data section not page-aligned (start = %p, pagesz = %u).\n", + DataBegin, PageSize); + return 1; + } + int Fileno = fileno(File); + /* Determine how much padding is needed before/after the counters and + * after the names. */ + uint64_t PaddingBytesBeforeCounters, PaddingBytesAfterCounters, + PaddingBytesAfterNames; + __llvm_profile_get_padding_sizes_for_counters( + DataSize, CountersSize, NamesSize, &PaddingBytesBeforeCounters, + &PaddingBytesAfterCounters, &PaddingBytesAfterNames); + + uint64_t PageAlignedCountersLength = + (CountersSize * sizeof(uint64_t)) + PaddingBytesAfterCounters; + uint64_t FileOffsetToCounters = + CurrentFileOffset + sizeof(__llvm_profile_header) + + (DataSize * sizeof(__llvm_profile_data)) + PaddingBytesBeforeCounters; + uint64_t *CounterMmap = (uint64_t *)mmap( + (void *)CountersBegin, PageAlignedCountersLength, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_SHARED, Fileno, FileOffsetToCounters); + if (CounterMmap != CountersBegin) { + PROF_ERR( + "Continuous counter sync mode is enabled, but mmap() failed (%s).\n" + " - CountersBegin: %p\n" + " - PageAlignedCountersLength: %" PRIu64 "\n" + " - Fileno: %d\n" + " - FileOffsetToCounters: %" PRIu64 "\n", + strerror(errno), CountersBegin, PageAlignedCountersLength, Fileno, + FileOffsetToCounters); + return 1; + } + return 0; +} #elif defined(__ELF__) || defined(_WIN32) #define INSTR_PROF_PROFILE_COUNTER_BIAS_DEFAULT_VAR \ @@ -134,15 +190,46 @@ * used and runtime provides a weak alias so we can check if it's defined. */ static void *BiasAddr = &INSTR_PROF_PROFILE_COUNTER_BIAS_VAR; static void *BiasDefaultAddr = &INSTR_PROF_PROFILE_COUNTER_BIAS_DEFAULT_VAR; -static int MmapFlags = MAP_SHARED; +static int mmapForContinuousMode(uint64_t CurrentFileOffset, FILE *File) { + /* 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(); + const uint64_t *CountersBegin = __llvm_profile_begin_counters(); + const uint64_t *CountersEnd = __llvm_profile_end_counters(); + uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); + /* Get the file size. */ + uint64_t FileSize = 0; + if (getProfileFileSizeForMerging(File, &FileSize)) + return 1; + + /* Map the profile. */ + char *Profile = (char *)mmap(NULL, FileSize, PROT_READ | PROT_WRITE, + MAP_SHARED, fileno(File), 0); + if (Profile == MAP_FAILED) { + PROF_ERR("Unable to mmap profile: %s\n", strerror(errno)); + return 1; + } + const uint64_t CountersOffsetInBiasMode = + sizeof(__llvm_profile_header) + __llvm_write_binary_ids(NULL) + + (DataSize * sizeof(__llvm_profile_data)); + /* Update the profile fields based on the current mapping. */ + INSTR_PROF_PROFILE_COUNTER_BIAS_VAR = + (intptr_t)Profile - (uintptr_t)CountersBegin + CountersOffsetInBiasMode; + + /* Return the memory allocated for counters to OS. */ + lprofReleaseMemoryPagesToOS((uintptr_t)CountersBegin, (uintptr_t)CountersEnd); + return 0; +} #else static const int ContinuousModeSupported = 0; static const int UseBiasVar = 0; static const char *FileOpenMode = "a+b"; -static intptr_t INSTR_PROF_PROFILE_COUNTER_BIAS_VAR; static void *BiasAddr = NULL; static void *BiasDefaultAddr = NULL; -static int MmapFlags = MAP_SHARED; +static int mmapForContinuousMode(uint64_t CurrentFileOffset, FILE *File) { + return 0; +} #endif static int isProfileMergeRequested() { return ProfileMergeRequested; } @@ -154,18 +241,6 @@ static FILE *getProfileFile() { return ProfileFile; } static void setProfileFile(FILE *File) { ProfileFile = File; } -COMPILER_RT_VISIBILITY void __llvm_profile_set_file_object(FILE *File, - int EnableMerge) { - if (__llvm_profile_is_continuous_mode_enabled()) { - PROF_WARN("__llvm_profile_set_file_object(fd=%d) not supported, because " - "continuous sync mode (%%c) is enabled", - fileno(File)); - return; - } - setProfileFile(File); - setProfileMergeRequested(EnableMerge); -} - static int getCurFilenameLength(); static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf); static unsigned doMerging() { @@ -502,16 +577,9 @@ 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(); + /* Get the sizes of counter section. */ 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(); - const uint64_t NamesSize = (NamesEnd - NamesBegin) * sizeof(char); - uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); uint64_t CountersSize = CountersEnd - CountersBegin; int Length = getCurFilenameLength(); @@ -577,75 +645,8 @@ /* mmap() the profile counters so long as there is at least one counter. * If there aren't any counters, mmap() would fail with EINVAL. */ - if (CountersSize > 0) { - int Fileno = fileno(File); - if (UseBiasVar) { - /* Get the file size. */ - uint64_t FileSize = ftell(File); - - /* Map the profile. */ - char *Profile = (char *)mmap(NULL, FileSize, PROT_READ | PROT_WRITE, - MmapFlags, Fileno, 0); - if (Profile == MAP_FAILED) { - PROF_ERR("Unable to mmap profile: %s\n", strerror(errno)); - return; - } - const uint64_t CountersOffsetInBiasMode = - sizeof(__llvm_profile_header) + __llvm_write_binary_ids(NULL) + - (DataSize * sizeof(__llvm_profile_data)); - /* Update the profile fields based on the current mapping. */ - INSTR_PROF_PROFILE_COUNTER_BIAS_VAR = (intptr_t)Profile - - (uintptr_t)CountersBegin + - CountersOffsetInBiasMode; - - /* Return the memory allocated for counters to OS. */ - lprofReleaseMemoryPagesToOS((uintptr_t)CountersBegin, - (uintptr_t)CountersEnd); - } else { - /* Check that the counter and data sections in this image are - * page-aligned. */ - unsigned PageSize = getpagesize(); - if ((intptr_t)CountersBegin % PageSize != 0) { - PROF_ERR( - "Counters section not page-aligned (start = %p, pagesz = %u).\n", - CountersBegin, PageSize); - return; - } - if ((intptr_t)DataBegin % PageSize != 0) { - PROF_ERR("Data section not page-aligned (start = %p, pagesz = %u).\n", - DataBegin, PageSize); - return; - } - /* Determine how much padding is needed before/after the counters and - * after the names. */ - uint64_t PaddingBytesBeforeCounters, PaddingBytesAfterCounters, - PaddingBytesAfterNames; - __llvm_profile_get_padding_sizes_for_counters( - DataSize, CountersSize, NamesSize, &PaddingBytesBeforeCounters, - &PaddingBytesAfterCounters, &PaddingBytesAfterNames); - - uint64_t PageAlignedCountersLength = - (CountersSize * sizeof(uint64_t)) + PaddingBytesAfterCounters; - uint64_t FileOffsetToCounters = - CurrentFileOffset + sizeof(__llvm_profile_header) + - (DataSize * sizeof(__llvm_profile_data)) + PaddingBytesBeforeCounters; - - uint64_t *CounterMmap = - (uint64_t *)mmap((void *)CountersBegin, PageAlignedCountersLength, - PROT_READ | PROT_WRITE, MmapFlags, - Fileno, FileOffsetToCounters); - if (CounterMmap != CountersBegin) { - PROF_ERR( - "Continuous counter sync mode is enabled, but mmap() failed (%s).\n" - " - CountersBegin: %p\n" - " - PageAlignedCountersLength: %" PRIu64 "\n" - " - Fileno: %d\n" - " - FileOffsetToCounters: %" PRIu64 "\n", - strerror(errno), CountersBegin, PageAlignedCountersLength, Fileno, - FileOffsetToCounters); - } - } - } + if (CountersSize > 0) + mmapForContinuousMode(CurrentFileOffset, File); if (doMerging()) { lprofUnlockFileHandle(File); @@ -1143,4 +1144,49 @@ return atexit(writeFileWithoutReturn); } +COMPILER_RT_VISIBILITY int __llvm_profile_set_file_object(FILE *File, + int EnableMerge) { + if (__llvm_profile_is_continuous_mode_enabled()) { + if (!EnableMerge) { + PROF_WARN("__llvm_profile_set_file_object(fd=%d) not supported in " + "continuous sync mode when merging is disabled\n", + fileno(File)); + return 1; + } + lprofLockFileHandle(File); + uint64_t ProfileFileSize = 0; + if (getProfileFileSizeForMerging(File, &ProfileFileSize) == -1) { + lprofUnlockFileHandle(File); + return 1; + } + if (ProfileFileSize == 0) { + FreeHook = &free; + setupIOBuffer(); + ProfDataWriter fileWriter; + initFileWriter(&fileWriter, File); + if (lprofWriteData(&fileWriter, 0, 0)) { + lprofUnlockFileHandle(File); + PROF_ERR("Failed to write file \"%d\": %s\n", fileno(File), + strerror(errno)); + return 1; + } + } else { + /* The merged profile has a non-zero length. Check that it is compatible + * with the data in this process. */ + char *ProfileBuffer; + if (mmapProfileForMerging(File, ProfileFileSize, &ProfileBuffer) == -1) { + lprofUnlockFileHandle(File); + return 1; + } + (void)munmap(ProfileBuffer, ProfileFileSize); + } + mmapForContinuousMode(0, File); + lprofUnlockFileHandle(File); + } else { + setProfileFile(File); + setProfileMergeRequested(EnableMerge); + } + return 0; +} + #endif diff --git a/compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c b/compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c --- a/compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c +++ b/compiler-rt/test/profile/ContinuousSyncMode/set-file-object.c @@ -1,34 +1,84 @@ -// REQUIRES: darwin +// REQUIRES: darwin || linux -// RUN: %clang_pgogen -o %t.exe %s -// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe %t.bad 2>&1 | FileCheck %s +// Test using __llvm_profile_set_file_object in continuous mode (%c). +// Create & cd into a temporary directory. +// RUN: rm -rf %t.dir && mkdir -p %t.dir && cd %t.dir -// CHECK: __llvm_profile_set_file_object(fd={{[0-9]+}}) not supported -// CHECK: Profile data not written to file: already written. +// The -mllvm -runtime-counter-relocation=true flag has effect only on linux. +// RUN: %clang -fprofile-instr-generate -fcoverage-mapping -mllvm -instrprof-atomic-counter-update-all=1 -mllvm -runtime-counter-relocation=true -o main.exe %s -#include +// Test continuous mode with __llvm_profile_set_file_object with mergin disabled. +// RUN: env LLVM_PROFILE_FILE="%t.dir/profdir/%c%mprofraw.old" %run %t.dir/main.exe nomerge %t.dir/profdir/profraw.new 2>&1 | FileCheck %s -check-prefix=WARN +// WARN: LLVM Profile Warning: __llvm_profile_set_file_object(fd={{[0-9]+}}) not supported in continuous sync mode when merging is disabled -extern int __llvm_profile_is_continuous_mode_enabled(void); -extern void __llvm_profile_set_file_object(FILE *, int); -extern int __llvm_profile_write_file(void); +// Test continuous mode with __llvm_profile_set_file_object with mergin enabled. +// RUN: rm -rf %t.dir/profdir/ +// RUN: env LLVM_PROFILE_FILE="%t.dir/profdir/%c%mprofraw.old" %run %t.dir/main.exe merge %t.dir/profdir/profraw.new 'LLVM_PROFILE_FILE=%t.dir/profdir/%c%m.profraw' +// RUN: llvm-profdata merge -o %t.dir/profdir/profdata %t.dir/profdir/profraw.new +// RUN: llvm-profdata show --counts --all-functions %t.dir/profdir/profdata | FileCheck %s -check-prefix=MERGE +// RUN: llvm-profdata show --counts --all-functions %t.dir/profdir/*profraw.old | FileCheck %s -check-prefix=ZERO -int main(int argc, char **argv) { - if (!__llvm_profile_is_continuous_mode_enabled()) - return 1; +// MERGE: Counters: +// MERGE: coverage_test: +// MERGE: Hash: {{.*}} +// MERGE: Counters: 1 +// MERGE: Function count: 32 +// MERGE: Block counts: [] +// MERGE: Instrumentation level: Front-end + +// ZERO: Counters: +// ZERO: coverage_test: +// ZERO: Hash: {{.*}} +// ZERO: Counters: 1 +// ZERO: Function count: 0 +// ZERO: Block counts: [] +// ZERO: Instrumentation level: Front-end - FILE *f = fopen(argv[1], "a+b"); - if (!f) - return 1; +#include +#include +#include - __llvm_profile_set_file_object(f, 0); // Try to set the file to "%t.bad". +const int num_child_procs_to_spawn = 32; - if (__llvm_profile_write_file() != 0) - return 1; +extern int __llvm_profile_is_continuous_mode_enabled(void); +extern int __llvm_profile_set_file_object(FILE *, int); - f = fopen(argv[1], "r"); - if (!f) - return 1; +int coverage_test() { + return 0; +} - fseek(f, 0, SEEK_END); - return ftell(f); // Check that the "%t.bad" is empty. +int main(int argc, char **argv) { + char *file_name = argv[2]; + FILE *file = fopen(file_name, "a+b"); + if (strcmp(argv[1], "nomerge") == 0) + __llvm_profile_set_file_object(file, 0); + else if (strcmp(argv[1], "merge") == 0) { + // Parent process. + int I; + pid_t child_pids[num_child_procs_to_spawn]; + char *const child_argv[] = {argv[0], "set", file_name, NULL}; + char *const child_envp[] = {argv[3], NULL}; + for (I = 0; I < num_child_procs_to_spawn; ++I) { + int ret = + posix_spawn(&child_pids[I], argv[0], NULL, NULL, child_argv, child_envp); + if (ret != 0) { + fprintf(stderr, "Child %d could not be spawned: ret = %d, msg = %s\n", + I, ret, strerror(ret)); + return 1; + } + } + } else if (strcmp(argv[1], "set") == 0) { + // Child processes. + if (!__llvm_profile_is_continuous_mode_enabled()) { + fprintf(stderr, "Continuous mode disabled\n"); + return 1; + } + if (__llvm_profile_set_file_object(file, 1)) { + fprintf(stderr, "Call to __llvm_profile_set_file_object failed\n"); + return 1; + } + // After set file object, counter should be written into new file. + coverage_test(); + } + return 0; }