[Profile] implement interface to get profile path prefix

Differential Revision:  http://reviews.llvm.org/D22546

llvm-svn: 276083
diff --git a/compiler-rt/lib/profile/InstrProfiling.h b/compiler-rt/lib/profile/InstrProfiling.h
index b23bed8..69abb2c 100644
--- a/compiler-rt/lib/profile/InstrProfiling.h
+++ b/compiler-rt/lib/profile/InstrProfiling.h
@@ -148,6 +148,16 @@
 /*! \brief Initialize file handling. */
 void __llvm_profile_initialize_file(void);
 
+/*!
+ * \brief Return path prefix (excluding the base filename) of the profile data.
+ * This is useful for users using \c -fprofile-generate=./path_prefix who do
+ * not care about the default raw profile name. It is also useful to collect
+ * more than more profile data files dumped in the same directory (Online
+ * merge mode is turned on for instrumented programs with shared libs).
+ * Side-effect: this API call will invoke malloc with dynamic memory allocation.
+ */
+const char *__llvm_profile_get_path_prefix();
+
 /*! \brief Get the magic token for the file format. */
 uint64_t __llvm_profile_get_magic(void);
 
diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c
index 6670a23..52715dd 100644
--- a/compiler-rt/lib/profile/InstrProfilingFile.c
+++ b/compiler-rt/lib/profile/InstrProfilingFile.c
@@ -63,6 +63,7 @@
 typedef struct lprofFilename {
   /* File name string possibly with %p or %h specifiers. */
   const char *FilenamePat;
+  const char *ProfilePathPrefix;
   char PidChars[MAX_PID_SIZE];
   char Hostname[COMPILER_RT_MAX_HOSTLEN];
   unsigned NumPids;
@@ -78,7 +79,7 @@
   ProfileNameSpecifier PNS;
 } lprofFilename;
 
-lprofFilename lprofCurFilename = {0, {0}, {0}, 0, 0, 0, PNS_unknown};
+lprofFilename lprofCurFilename = {0, 0, {0}, {0}, 0, 0, 0, PNS_unknown};
 
 int getpid(void);
 static int getCurFilenameLength();
@@ -344,6 +345,12 @@
               getPNSStr(PNS));
   }
 
+  /* Clean up cached prefix.  */
+  if (lprofCurFilename.ProfilePathPrefix) {
+    free((void*)lprofCurFilename.ProfilePathPrefix);
+    lprofCurFilename.ProfilePathPrefix = NULL;
+  }
+
   if (!lprofCurFilename.MergePoolSize)
     truncateCurrentFile();
 }
@@ -425,6 +432,37 @@
   return Filename;
 }
 
+COMPILER_RT_VISIBILITY
+const char *__llvm_profile_get_path_prefix(void) {
+  int Length;
+  char *FilenameBuf, *Prefix;
+  const char *Filename, *PrefixEnd;
+
+  if (lprofCurFilename.ProfilePathPrefix)
+    return lprofCurFilename.ProfilePathPrefix;
+
+  Length = getCurFilenameLength();
+  FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1);
+  Filename = getCurFilename(FilenameBuf);
+  if (!Filename)
+    return "\0";
+
+  PrefixEnd = lprofFindLastDirSeparator(Filename);
+  if (!PrefixEnd)
+    return "\0";
+
+  Length = PrefixEnd - Filename + 1;
+  Prefix = (char *)malloc(Length + 1);
+  if (!Prefix) {
+    PROF_ERR("Failed to %s\n", "allocate memory.");
+    return "\0";
+  }
+  memcpy(Prefix, Filename, Length);
+  Prefix[Length] = '\0';
+  lprofCurFilename.ProfilePathPrefix = Prefix;
+  return Prefix;
+}
+
 /* This method is invoked by the runtime initialization hook
  * InstrProfilingRuntime.o if it is linked in. Both user specified
  * profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE
diff --git a/compiler-rt/test/profile/instrprof-path.c b/compiler-rt/test/profile/instrprof-path.c
new file mode 100644
index 0000000..28ee8ad
--- /dev/null
+++ b/compiler-rt/test/profile/instrprof-path.c
@@ -0,0 +1,39 @@
+// RUN: %clang_pgogen -O2 -o %t.0 %s
+// RUN: %clang_pgogen=%t.d1 -O2 -o %t.1 %s
+// RUN: %clang_pgogen=%t.d1/%t.d2 -O2 -o %t.2 %s
+//
+// RUN: %run %t.0  ""
+// RUN: env LLVM_PROFILE_FILE=%t.d1/default.profraw %run %t.0  %t.d1/
+// RUN: env LLVM_PROFILE_FILE=%t.d1/%t.d2/default.profraw %run %t.0 %t.d1/%t.d2/
+// RUN: %run %t.1 %t.d1/
+// RUN: %run %t.2 %t.d1/%t.d2/
+// RUN: %run %t.2 %t.d1/%t.d2/ %t.d1/%t.d2/%t.d3/blah.profraw %t.d1/%t.d2/%t.d3/
+
+#include <string.h>
+
+const char *__llvm_profile_get_path_prefix();
+void __llvm_profile_set_filanem(const char*);
+
+int main(int argc, const char *argv[]) {
+  int i;
+  const char *expected;
+  const char *prefix;
+  if (argc < 2)
+    return 1;
+
+  expected = argv[1];
+  prefix = __llvm_profile_get_path_prefix();
+
+  if (strcmp(prefix, expected))
+    return 1;
+
+  if (argc == 4) {
+    __llvm_profile_set_filename(argv[2]);
+    prefix = __llvm_profile_get_path_prefix();
+    expected = argv[3];
+    if (strcmp(prefix, expected))
+      return 1;
+  }
+
+  return 0;
+}