forked from OSchip/llvm-project
(Reland with changes) Adding a function for setting coverage output file.
Summary: User code can open a file on its own and pass it to the runtime, rather than specifying a name and having the runtime open the file. This supports the use case where a process cannot open a file on its own but can receive a file descriptor from another process. Relanding https://reviews.llvm.org/D62541. The original revision unlocked the file before calling flush, this revision fixes that. Reviewers: Dor1s, davidxl Reviewed By: Dor1s Subscribers: #sanitizers, llvm-commits Tags: #sanitizers, #llvm Differential Revision: https://reviews.llvm.org/D63581 llvm-svn: 364231
This commit is contained in:
parent
8242f35d50
commit
6694b2b36b
|
@ -10,6 +10,7 @@
|
||||||
#define PROFILE_INSTRPROFILING_H_
|
#define PROFILE_INSTRPROFILING_H_
|
||||||
|
|
||||||
#include "InstrProfilingPort.h"
|
#include "InstrProfilingPort.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define INSTR_PROF_VISIBILITY COMPILER_RT_VISIBILITY
|
#define INSTR_PROF_VISIBILITY COMPILER_RT_VISIBILITY
|
||||||
#include "InstrProfData.inc"
|
#include "InstrProfData.inc"
|
||||||
|
@ -125,7 +126,7 @@ int __llvm_orderfile_write_file(void);
|
||||||
/*!
|
/*!
|
||||||
* \brief this is a wrapper interface to \c __llvm_profile_write_file.
|
* \brief this is a wrapper interface to \c __llvm_profile_write_file.
|
||||||
* After this interface is invoked, a arleady dumped flag will be set
|
* 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
|
* Invocation of interface __llvm_profile_reset_counters will clear
|
||||||
* the flag. This interface is designed to be used to collect profile
|
* the flag. This interface is designed to be used to collect profile
|
||||||
* data from user selected hot regions. The use model is
|
* data from user selected hot regions. The use model is
|
||||||
|
@ -157,6 +158,24 @@ int __llvm_orderfile_dump(void);
|
||||||
*/
|
*/
|
||||||
void __llvm_profile_set_filename(const char *Name);
|
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. */
|
/*! \brief Register to write instrumentation data to file at exit. */
|
||||||
int __llvm_profile_register_write_file_atexit(void);
|
int __llvm_profile_register_write_file_atexit(void);
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
/* From where is profile name specified.
|
/* From where is profile name specified.
|
||||||
* The order the enumerators define their
|
* The order the enumerators define their
|
||||||
* precedence. Re-order them may lead to
|
* precedence. Re-order them may lead to
|
||||||
* runtime behavior change. */
|
* runtime behavior change. */
|
||||||
typedef enum ProfileNameSpecifier {
|
typedef enum ProfileNameSpecifier {
|
||||||
PNS_unknown = 0,
|
PNS_unknown = 0,
|
||||||
PNS_default,
|
PNS_default,
|
||||||
|
@ -89,9 +89,27 @@ typedef struct lprofFilename {
|
||||||
COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, 0, {0},
|
COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, 0, {0},
|
||||||
{0}, 0, 0, 0, PNS_unknown};
|
{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 int getCurFilenameLength();
|
||||||
static const char *getCurFilename(char *FilenameBuf, int ForceUseBuf);
|
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. */
|
/* Return 1 if there is an error, otherwise return 0. */
|
||||||
static uint32_t fileWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs,
|
static uint32_t fileWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs,
|
||||||
|
@ -225,11 +243,16 @@ static void createProfileDir(const char *Filename) {
|
||||||
* its instrumented shared libraries dump profile data into their own data file.
|
* its instrumented shared libraries dump profile data into their own data file.
|
||||||
*/
|
*/
|
||||||
static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) {
|
static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) {
|
||||||
FILE *ProfileFile;
|
FILE *ProfileFile = NULL;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
createProfileDir(ProfileFileName);
|
ProfileFile = getProfileFile();
|
||||||
ProfileFile = lprofOpenFileEx(ProfileFileName);
|
if (ProfileFile) {
|
||||||
|
lprofLockFileHandle(ProfileFile);
|
||||||
|
} else {
|
||||||
|
createProfileDir(ProfileFileName);
|
||||||
|
ProfileFile = lprofOpenFileEx(ProfileFileName);
|
||||||
|
}
|
||||||
if (!ProfileFile)
|
if (!ProfileFile)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -244,6 +267,16 @@ static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) {
|
||||||
return ProfileFile;
|
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. */
|
/* Write profile data to file \c OutputName. */
|
||||||
static int writeFile(const char *OutputName) {
|
static int writeFile(const char *OutputName) {
|
||||||
int RetVal;
|
int RetVal;
|
||||||
|
@ -251,10 +284,10 @@ static int writeFile(const char *OutputName) {
|
||||||
|
|
||||||
int MergeDone = 0;
|
int MergeDone = 0;
|
||||||
VPMergeHook = &lprofMergeValueProfData;
|
VPMergeHook = &lprofMergeValueProfData;
|
||||||
if (!doMerging())
|
if (doMerging())
|
||||||
OutputFile = fopen(OutputName, "ab");
|
|
||||||
else
|
|
||||||
OutputFile = openFileForMerging(OutputName, &MergeDone);
|
OutputFile = openFileForMerging(OutputName, &MergeDone);
|
||||||
|
else
|
||||||
|
OutputFile = getFileObject(OutputName);
|
||||||
|
|
||||||
if (!OutputFile)
|
if (!OutputFile)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -265,7 +298,15 @@ static int writeFile(const char *OutputName) {
|
||||||
initFileWriter(&fileWriter, OutputFile);
|
initFileWriter(&fileWriter, OutputFile);
|
||||||
RetVal = lprofWriteData(&fileWriter, lprofGetVPDataReader(), MergeDone);
|
RetVal = lprofWriteData(&fileWriter, lprofGetVPDataReader(), MergeDone);
|
||||||
|
|
||||||
fclose(OutputFile);
|
if (OutputFile == getProfileFile()) {
|
||||||
|
fflush(OutputFile);
|
||||||
|
if (doMerging()) {
|
||||||
|
lprofUnlockFileHandle(OutputFile);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fclose(OutputFile);
|
||||||
|
}
|
||||||
|
|
||||||
return RetVal;
|
return RetVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,7 +632,7 @@ void __llvm_profile_initialize_file(void) {
|
||||||
|
|
||||||
EnvFilenamePat = getFilenamePatFromEnv();
|
EnvFilenamePat = getFilenamePatFromEnv();
|
||||||
if (EnvFilenamePat) {
|
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. */
|
at the moment when __llvm_profile_write_file() gets executed. */
|
||||||
parseAndSetFilename(EnvFilenamePat, PNS_environment, 1);
|
parseAndSetFilename(EnvFilenamePat, PNS_environment, 1);
|
||||||
return;
|
return;
|
||||||
|
@ -627,8 +668,7 @@ int __llvm_profile_write_file(void) {
|
||||||
int PDeathSig = 0;
|
int PDeathSig = 0;
|
||||||
|
|
||||||
if (lprofProfileDumped()) {
|
if (lprofProfileDumped()) {
|
||||||
PROF_NOTE("Profile data not written to file: %s.\n",
|
PROF_NOTE("Profile data not written to file: %s.\n", "already written");
|
||||||
"already written");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,26 @@ COMPILER_RT_VISIBILITY int lprofUnlockFd(int fd) {
|
||||||
#endif
|
#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) {
|
COMPILER_RT_VISIBILITY FILE *lprofOpenFileEx(const char *ProfileName) {
|
||||||
FILE *f;
|
FILE *f;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
|
@ -23,6 +23,8 @@ unsigned __llvm_profile_get_dir_mode(void);
|
||||||
|
|
||||||
int lprofLockFd(int fd);
|
int lprofLockFd(int fd);
|
||||||
int lprofUnlockFd(int fd);
|
int lprofUnlockFd(int fd);
|
||||||
|
int lprofLockFileHandle(FILE *F);
|
||||||
|
int lprofUnlockFileHandle(FILE *F);
|
||||||
|
|
||||||
/*! Open file \c Filename for read+write with write
|
/*! Open file \c Filename for read+write with write
|
||||||
* lock for exclusive access. The caller will block
|
* lock for exclusive access. The caller will block
|
||||||
|
|
|
@ -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 <stdio.h>
|
||||||
|
|
||||||
|
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 <stdio.h>
|
||||||
|
// 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|}
|
|
@ -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 <stdio.h>
|
||||||
|
|
||||||
|
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 <stdio.h>
|
||||||
|
// 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|}
|
Loading…
Reference in New Issue