[Profile] Support __llvm_profile_set_file_object in continuous mode.

Replace D107203, because __llvm_profile_set_file_object is usually used when the
process doesn't have permission to open/create file. That patch trying to copy
from old profile to new profile contradicts with the usage.

Differential Revision: https://reviews.llvm.org/D108242
This commit is contained in:
Zequan Wu 2021-08-17 14:18:52 -07:00
parent 8f859cc349
commit 1b05245119
3 changed files with 222 additions and 126 deletions

View File

@ -194,7 +194,8 @@ int __llvm_orderfile_dump(void);
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 @@ void __llvm_profile_set_filename(const char *Name);
* 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);

View File

@ -92,15 +92,71 @@ static lprofFilename lprofCurFilename = {0, 0, 0, {0}, NULL,
{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 @@ static const char *FileOpenMode = "w+b";
* 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 *ProfileFile = NULL;
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 @@ static void initializeProfileForContinuousMode(void) {
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 @@ static void initializeProfileForContinuousMode(void) {
/* 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 @@ int __llvm_profile_register_write_file_atexit(void) {
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

View File

@ -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
// 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
// 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
// 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
#include <spawn.h>
#include <stdio.h>
#include <string.h>
const int num_child_procs_to_spawn = 32;
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);
extern int __llvm_profile_set_file_object(FILE *, int);
int coverage_test() {
return 0;
}
int main(int argc, char **argv) {
if (!__llvm_profile_is_continuous_mode_enabled())
return 1;
FILE *f = fopen(argv[1], "a+b");
if (!f)
return 1;
__llvm_profile_set_file_object(f, 0); // Try to set the file to "%t.bad".
if (__llvm_profile_write_file() != 0)
return 1;
f = fopen(argv[1], "r");
if (!f)
return 1;
fseek(f, 0, SEEK_END);
return ftell(f); // Check that the "%t.bad" is empty.
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;
}