(Reland with changes) Adding a function for setting coverage output file.
authorSajjad Mirza <sajjadm@google.com>
Mon, 24 Jun 2019 21:32:50 +0000 (21:32 +0000)
committerSajjad Mirza <sajjadm@google.com>
Mon, 24 Jun 2019 21:32:50 +0000 (21:32 +0000)
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

compiler-rt/lib/profile/InstrProfiling.h
compiler-rt/lib/profile/InstrProfilingFile.c
compiler-rt/lib/profile/InstrProfilingUtil.c
compiler-rt/lib/profile/InstrProfilingUtil.h
compiler-rt/test/profile/instrprof-set-file-object-merging.c [new file with mode: 0644]
compiler-rt/test/profile/instrprof-set-file-object.c [new file with mode: 0644]

index 91afd5e6b92df5892beeb5fa35d837119f8be804..4d196a89b4920723c4c97372cbd49549c4c13ba5 100644 (file)
@@ -10,6 +10,7 @@
 #define PROFILE_INSTRPROFILING_H_
 
 #include "InstrProfilingPort.h"
+#include <stdio.h>
 
 #define INSTR_PROF_VISIBILITY COMPILER_RT_VISIBILITY
 #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.
  * 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 @@ int __llvm_orderfile_dump(void);
  */
 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);
 
index 8c32bde9d604ab5de50da0f83442781b1da6504f..e7996e2828946e8a44a2ef5cb6429193791f27ff 100644 (file)
@@ -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 @@ typedef struct lprofFilename {
 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 @@ static void createProfileDir(const char *Filename) {
  * 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 @@ static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) {
   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 @@ static int writeFile(const char *OutputName) {
 
   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 @@ static int writeFile(const char *OutputName) {
   initFileWriter(&fileWriter, OutputFile);
   RetVal = lprofWriteData(&fileWriter, lprofGetVPDataReader(), MergeDone);
 
-  fclose(OutputFile);
+  if (OutputFile == getProfileFile()) {
+    fflush(OutputFile);
+    if (doMerging()) {
+      lprofUnlockFileHandle(OutputFile);
+    }
+  } else {
+    fclose(OutputFile);
+  }
+
   return RetVal;
 }
 
@@ -591,7 +632,7 @@ void __llvm_profile_initialize_file(void) {
 
   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 __llvm_profile_write_file(void) {
   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;
   }
 
index 5e479ae7150acb3db4b6d0f339817b7af3f052d7..02d100792db8643892a8354077d4e110a2f98377 100644 (file)
@@ -154,6 +154,26 @@ COMPILER_RT_VISIBILITY int lprofUnlockFd(int fd) {
 #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;
index 9cd0860fda2373c9507670eb7ec94be14c6ffecf..efba94ca763963b538f0dca659516ffb1948c208 100644 (file)
@@ -23,6 +23,8 @@ unsigned __llvm_profile_get_dir_mode(void);
 
 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/test/profile/instrprof-set-file-object-merging.c b/compiler-rt/test/profile/instrprof-set-file-object-merging.c
new file mode 100644 (file)
index 0000000..3f71a81
--- /dev/null
@@ -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|}
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 (file)
index 0000000..8131300
--- /dev/null
@@ -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|}