This change is the first of 3 patches to add support for specifying
the profile output from the command line via -fprofile-instr-generate=<path>,
where the specified output path/file will be overridden by the
LLVM_PROFILE_FILE environment variable.

Several changes are made to the runtime to support this:

Add a new interface __llvm_profile_override_default_filename that will
set the profile output filename, but allows LLVM_PROFILE_FILE to override.
This is the interface used by the new option.

Refactor the pid-expansion done for LLVM_PROFILE_FILE into a separate
routine that can be shared by the various filename setting routines
(so that the filename from the option can also use the "%p" syntax).

Move the truncation into setFilename, and only truncate if there is a
new filename specified (to maintain support for appending to the same
profile file in the case of multiple shared objects built with profiling).

Move the handling for a NULL filename passed to __llvm_profile_set_filename and
__llvm_profile_override_default_filename into the new setFilenamePossiblyWithPid
routine. This now correctly resets the output file to default.profraw
instead of NULL.
The handling for a null LLVM_PROFILE_FILE (which should not reset) is done
by caller setFilenameFromEnvironment.

Patch by Teresa Johnson.


git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@236055 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/profile/InstrProfiling.h b/lib/profile/InstrProfiling.h
index 2b1bd00..84b673c 100644
--- a/lib/profile/InstrProfiling.h
+++ b/lib/profile/InstrProfiling.h
@@ -62,7 +62,9 @@
  *
  * Writes to the file with the last name given to \a __llvm_profile_set_filename(),
  * or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable,
- * or if that's not set, \c "default.profdata".
+ * or if that's not set, the last name given to
+ * \a __llvm_profile_override_default_filename(), or if that's not set,
+ * \c "default.profdata".
  */
 int __llvm_profile_write_file(void);
 
@@ -77,6 +79,19 @@
  */
 void __llvm_profile_set_filename(const char *Name);
 
+/*!
+ * \brief Set the filename for writing instrumentation data, unless the
+ * \c LLVM_PROFILE_FILE environment variable was set.
+ *
+ * Unless overridden, sets the filename to be used for subsequent calls to
+ * \a __llvm_profile_write_file().
+ *
+ * \c Name is not copied, so it must remain valid.  Passing NULL resets the
+ * filename logic to the default behaviour (unless the \c LLVM_PROFILE_FILE
+ * was set in which case it has no effect).
+ */
+void __llvm_profile_override_default_filename(const char *Name);
+
 /*! \brief Register to write instrumentation data to file at exit. */
 int __llvm_profile_register_write_file_atexit(void);
 
diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c
index d9acbbe..0102a25 100644
--- a/lib/profile/InstrProfilingFile.c
+++ b/lib/profile/InstrProfilingFile.c
@@ -76,14 +76,6 @@
 __attribute__((weak)) int __llvm_profile_OwnsFilename = 0;
 __attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL;
 
-static void setFilename(const char *Filename, int OwnsFilename) {
-  if (__llvm_profile_OwnsFilename)
-    free(UNCONST(__llvm_profile_CurrentFilename));
-
-  __llvm_profile_CurrentFilename = Filename;
-  __llvm_profile_OwnsFilename = OwnsFilename;
-}
-
 static void truncateCurrentFile(void) {
   const char *Filename;
   FILE *File;
@@ -99,19 +91,36 @@
   fclose(File);
 }
 
-static void setDefaultFilename(void) { setFilename("default.profraw", 0); }
+static void setFilename(const char *Filename, int OwnsFilename) {
+  /* Check if this is a new filename and therefore needs truncation. */
+  int NewFile = !__llvm_profile_CurrentFilename ||
+      (Filename && strcmp(Filename, __llvm_profile_CurrentFilename));
+  if (__llvm_profile_OwnsFilename)
+    free(UNCONST(__llvm_profile_CurrentFilename));
+
+  __llvm_profile_CurrentFilename = Filename;
+  __llvm_profile_OwnsFilename = OwnsFilename;
+
+  /* If not a new file, append to support profiling multiple shared objects. */
+  if (NewFile)
+    truncateCurrentFile();
+}
+
+static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); }
 
 int getpid(void);
-static int setFilenameFromEnvironment(void) {
-  const char *Filename = getenv("LLVM_PROFILE_FILE");
+static int setFilenamePossiblyWithPid(const char *Filename) {
 #define MAX_PID_SIZE 16
   char PidChars[MAX_PID_SIZE] = {0};
   int NumPids = 0, PidLength = 0;
   char *Allocated;
   int I, J;
 
-  if (!Filename || !Filename[0])
-    return -1;
+  /* Reset filename on NULL, except with env var which is checked by caller. */
+  if (!Filename) {
+    resetFilenameToDefault();
+    return 0;
+  }
 
   /* Check the filename for "%p", which indicates a pid-substitution. */
   for (I = 0; Filename[I]; ++I)
@@ -148,11 +157,20 @@
   return 0;
 }
 
+static int setFilenameFromEnvironment(void) {
+  const char *Filename = getenv("LLVM_PROFILE_FILE");
+
+  if (!Filename || !Filename[0])
+    return -1;
+
+  return setFilenamePossiblyWithPid(Filename);
+}
+
 static void setFilenameAutomatically(void) {
   if (!setFilenameFromEnvironment())
     return;
 
-  setDefaultFilename();
+  resetFilenameToDefault();
 }
 
 __attribute__((visibility("hidden")))
@@ -163,13 +181,20 @@
 
   /* Detect the filename and truncate. */
   setFilenameAutomatically();
-  truncateCurrentFile();
 }
 
 __attribute__((visibility("hidden")))
 void __llvm_profile_set_filename(const char *Filename) {
-  setFilename(Filename, 0);
-  truncateCurrentFile();
+  setFilenamePossiblyWithPid(Filename);
+}
+
+__attribute__((visibility("hidden")))
+void __llvm_profile_override_default_filename(const char *Filename) {
+  /* If the env var is set, skip setting filename from argument. */
+  const char *Env_Filename = getenv("LLVM_PROFILE_FILE");
+  if (Env_Filename && Env_Filename[0])
+    return;
+  setFilenamePossiblyWithPid(Filename);
 }
 
 __attribute__((visibility("hidden")))