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 @@ -10,6 +10,7 @@ #define PROFILE_INSTRPROFILING_H_ #include "InstrProfilingPort.h" +#include #define INSTR_PROF_VISIBILITY COMPILER_RT_VISIBILITY #include "InstrProfData.inc" @@ -125,7 +126,7 @@ /*! * \brief this is a wrapper interface to \c __llvm_profile_write_file. * After this interface is invoked, a arleady dumped flag will be set - * so that profile won't be dumped again during program exit. + * so that profile won't be dumped again during program exit. * Invocation of interface __llvm_profile_reset_counters will clear * the flag. This interface is designed to be used to collect profile * data from user selected hot regions. The use model is @@ -157,6 +158,24 @@ */ void __llvm_profile_set_filename(const char *Name); +/*! + * \brief Set the FILE object for writing instrumentation data. + * + * Sets the FILE object to be used for subsequent calls to + * \a __llvm_profile_write_file(). The profile file name set by environment + * variable, command-line option, or calls to \a __llvm_profile_set_filename + * will be ignored. + * + * \c File will not be closed after a call to \a __llvm_profile_write_file() but + * it may be flushed. Passing NULL restores default behavior. + * + * If \c EnableMerge is nonzero, the runtime will always merge profiling data + * with the contents of the profiling file. If EnableMerge is zero, the runtime + * may still merge the data if it would have merged for another reason (for + * example, because of a %m specifier in the file name). + */ +void __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 @@ -37,7 +37,7 @@ /* From where is profile name specified. * The order the enumerators define their * precedence. Re-order them may lead to - * runtime behavior change. */ + * runtime behavior change. */ typedef enum ProfileNameSpecifier { PNS_unknown = 0, PNS_default, @@ -89,9 +89,27 @@ COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, 0, {0}, {0}, 0, 0, 0, PNS_unknown}; +static int ProfileMergeRequested = 0; +static int isProfileMergeRequested() { return ProfileMergeRequested; } +static void setProfileMergeRequested(int EnableMerge) { + ProfileMergeRequested = EnableMerge; +} + +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) { + setProfileFile(File); + setProfileMergeRequested(EnableMerge); +} + static int getCurFilenameLength(); static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf); -static unsigned doMerging() { return lprofCurFilename.MergePoolSize; } +static unsigned doMerging() { + return lprofCurFilename.MergePoolSize || isProfileMergeRequested(); +} /* Return 1 if there is an error, otherwise return 0. */ static uint32_t fileWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, @@ -225,11 +243,16 @@ * its instrumented shared libraries dump profile data into their own data file. */ static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) { - FILE *ProfileFile; + FILE *ProfileFile = NULL; int rc; - createProfileDir(ProfileFileName); - ProfileFile = lprofOpenFileEx(ProfileFileName); + ProfileFile = getProfileFile(); + if (ProfileFile) { + lprofLockFileHandle(ProfileFile); + } else { + createProfileDir(ProfileFileName); + ProfileFile = lprofOpenFileEx(ProfileFileName); + } if (!ProfileFile) return NULL; @@ -244,6 +267,16 @@ return ProfileFile; } +static FILE *GetFileObject(const char *OutputName) { + FILE *File; + File = getProfileFile(); + if (File != NULL) { + return File; + } + + return fopen(OutputName, "ab"); +} + /* Write profile data to file \c OutputName. */ static int writeFile(const char *OutputName) { int RetVal; @@ -251,10 +284,10 @@ int MergeDone = 0; VPMergeHook = &lprofMergeValueProfData; - if (!doMerging()) - OutputFile = fopen(OutputName, "ab"); - else + if (doMerging()) OutputFile = openFileForMerging(OutputName, &MergeDone); + else + OutputFile = GetFileObject(OutputName); if (!OutputFile) return -1; @@ -265,7 +298,15 @@ initFileWriter(&fileWriter, OutputFile); RetVal = lprofWriteData(&fileWriter, lprofGetVPDataReader(), MergeDone); - fclose(OutputFile); + if (doMerging()) { + lprofUnlockFileHandle(OutputFile); + } + + if (OutputFile == getProfileFile()) + fflush(OutputFile); + else + fclose(OutputFile); + return RetVal; } @@ -591,7 +632,7 @@ EnvFilenamePat = getFilenamePatFromEnv(); if (EnvFilenamePat) { - /* Pass CopyFilenamePat = 1, to ensure that the filename would be valid + /* Pass CopyFilenamePat = 1, to ensure that the filename would be valid at the moment when __llvm_profile_write_file() gets executed. */ parseAndSetFilename(EnvFilenamePat, PNS_environment, 1); return; @@ -627,8 +668,7 @@ int PDeathSig = 0; if (lprofProfileDumped()) { - PROF_NOTE("Profile data not written to file: %s.\n", - "already written"); + PROF_NOTE("Profile data not written to file: %s.\n", "already written"); return 0; } diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.h b/compiler-rt/lib/profile/InstrProfilingUtil.h --- a/compiler-rt/lib/profile/InstrProfilingUtil.h +++ b/compiler-rt/lib/profile/InstrProfilingUtil.h @@ -23,6 +23,8 @@ int lprofLockFd(int fd); int lprofUnlockFd(int fd); +int lprofLockFileHandle(FILE *F); +int lprofUnlockFileHandle(FILE *F); /*! Open file \c Filename for read+write with write * lock for exclusive access. The caller will block diff --git a/compiler-rt/lib/profile/InstrProfilingUtil.c b/compiler-rt/lib/profile/InstrProfilingUtil.c --- a/compiler-rt/lib/profile/InstrProfilingUtil.c +++ b/compiler-rt/lib/profile/InstrProfilingUtil.c @@ -154,6 +154,26 @@ #endif } +COMPILER_RT_VISIBILITY int lprofLockFileHandle(FILE *F) { + int fd; +#if defined(_WIN32) + fd = _fileno(F); +#else + fd = fileno(F); +#endif + return lprofLockFd(fd); +} + +COMPILER_RT_VISIBILITY int lprofUnlockFileHandle(FILE *F) { + int fd; +#if defined(_WIN32) + fd = _fileno(F); +#else + fd = fileno(F); +#endif + return lprofUnlockFd(fd); +} + COMPILER_RT_VISIBILITY FILE *lprofOpenFileEx(const char *ProfileName) { FILE *f; int fd; diff --git a/compiler-rt/test/profile/instrprof-set-file-object-merging.c b/compiler-rt/test/profile/instrprof-set-file-object-merging.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/profile/instrprof-set-file-object-merging.c @@ -0,0 +1,43 @@ +// Test that the specified output merges the profiling data. +// Run the program twice so that the counters accumulate. +// RUN: %clang -fprofile-instr-generate -fcoverage-mapping -o %t %s +// RUN: %run %t %t.merging.profraw +// RUN: %run %t %t.merging.profraw +// RUN: test -f %t.merging.profraw +// RUN: llvm-profdata merge -o %t.merging.profdata %t.merging.profraw +// RUN: llvm-cov show -instr-profile %t.merging.profdata %t | FileCheck %s --match-full-lines +// RUN: rm %t.merging.profdata %t.merging.profraw +#include + +extern void __llvm_profile_set_file_object(FILE *, int); + +int main(int argc, const char *argv[]) { + if (argc < 2) + return 1; + + FILE *F = fopen(argv[1], "r+b"); + if (!F) { + // File might not exist, try opening with truncation + F = fopen(argv[1], "w+b"); + } + __llvm_profile_set_file_object(F, 1); + + return 0; +} +// CHECK: 10| |#include +// CHECK: 11| | +// CHECK: 12| |extern void __llvm_profile_set_file_object(FILE *, int); +// CHECK: 13| | +// CHECK: 14| 2|int main(int argc, const char *argv[]) { +// CHECK: 15| 2| if (argc < 2) +// CHECK: 16| 0| return 1; +// CHECK: 17| 2| +// CHECK: 18| 2| FILE *F = fopen(argv[1], "r+b"); +// CHECK: 19| 2| if (!F) { +// CHECK: 20| 1| // File might not exist, try opening with truncation +// CHECK: 21| 1| F = fopen(argv[1], "w+b"); +// CHECK: 22| 1| } +// CHECK: 23| 2| __llvm_profile_set_file_object(F, 1); +// CHECK: 24| 2| +// CHECK: 25| 2| return 0; +// CHECK: 26| 2|} diff --git a/compiler-rt/test/profile/instrprof-set-file-object.c b/compiler-rt/test/profile/instrprof-set-file-object.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/profile/instrprof-set-file-object.c @@ -0,0 +1,31 @@ +// Test that the specified output has profiling data. +// RUN: %clang -fprofile-instr-generate -fcoverage-mapping -o %t %s +// RUN: %run %t %t.file.profraw +// RUN: test -f %t.file.profraw +// RUN: llvm-profdata merge -o %t.file.profdata %t.file.profraw +// RUN: llvm-cov show -instr-profile %t.file.profdata %t | FileCheck %s --match-full-lines +// RUN: rm %t.file.profraw %t.file.profdata +#include + +extern void __llvm_profile_set_file_object(FILE *, int); + +int main(int argc, const char *argv[]) { + if (argc < 2) + return 1; + + FILE *F = fopen(argv[1], "w+b"); + __llvm_profile_set_file_object(F, 0); + return 0; +} +// CHECK: 8| |#include +// CHECK: 9| | +// CHECK: 10| |extern void __llvm_profile_set_file_object(FILE *, int); +// CHECK: 11| | +// CHECK: 12| 1|int main(int argc, const char *argv[]) { +// CHECK: 13| 1| if (argc < 2) +// CHECK: 14| 0| return 1; +// CHECK: 15| 1| +// CHECK: 16| 1| FILE *F = fopen(argv[1], "w+b"); +// CHECK: 17| 1| __llvm_profile_set_file_object(F, 0); +// CHECK: 18| 1| return 0; +// CHECK: 19| 1|}