qiongsiwu1 created this revision.
qiongsiwu1 added reviewers: w2yehia, davidxl, vsk, ellis.
Herald added subscribers: wlei, Enna1, wenlei.
Herald added a project: All.
qiongsiwu1 requested review of this revision.
Herald added subscribers: Sanitizers, cfe-commits, MaskRay.
Herald added projects: clang, Sanitizers.

Currently, the PGO runtime only creates unique per process profile files when 
both of the following conditions are true

1. __llvm_profile_initialize is triggered through global variable 
initialization (e.g. when exec starts an instrumented binary).
2. When there are no existing file patterns set. See 
https://github.com/llvm/llvm-project/blob/bb6d60bf9dfea82814d9e50c5bb457c47d010611/compiler-rt/lib/profile/InstrProfilingFile.c#L795

This patch changes the behaviour so that a new profile file name is generate as 
soon as a fork system call generates a new process from an instrumented binary. 
The patch uses the `pthread_atfork` hook to register the initialization code, 
and modifies the file name pattern parsing logic so that when `%p` is present 
in the pattern, a unique file name can be generated even though the pattern 
already exists.

Additionally, the unique profile files are created before the profiling starts. 
If a process is not terminated gracefully by calling `exit`, an empty profile 
file for that process will exist, indicating that the profiling may be 
incomplete due to the process's abnormal termination.

Note that the unique profile per process is only generated when `%p` is present 
in the profile file name pattern. Additionally, this patch does not support 
Windows because the implementation relies on `pthread`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D155290

Files:
  clang/lib/Driver/ToolChains/AIX.cpp
  compiler-rt/lib/profile/InstrProfilingFile.c
  compiler-rt/test/profile/Posix/instrprof-fork-int.c
  compiler-rt/test/profile/Posix/instrprof-fork.c

Index: compiler-rt/test/profile/Posix/instrprof-fork.c
===================================================================
--- /dev/null
+++ compiler-rt/test/profile/Posix/instrprof-fork.c
@@ -0,0 +1,25 @@
+/// Testing fork with out exec. The runtime should generate two profile files.
+// UNSUPPORTED: target={{.*windows.*}}
+// RUN: rm -rf %t.d
+// RUN: mkdir -p %t.d && cd %t.d
+// RUN: %clang_pgogen %s -o %t.exe
+// RUN: LLVM_PROFILE_FILE="%%p.profraw" %t.exe > %t.out
+// RUN: PROFILE1=`head -n 1 %t.out`
+// RUN: ls $PROFILE1
+// RUN: PROFILE2=`tail -n 1 %t.out`
+// RUN: ls $PROFILE2
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+  pid_t pid;
+  pid = fork();
+  if (!pid) {
+    printf("%ld.profraw\n", (long)getpid());
+  } else {
+    printf("%ld.profraw\n", (long)getpid());
+  }
+  return 0;
+}
Index: compiler-rt/test/profile/Posix/instrprof-fork-int.c
===================================================================
--- /dev/null
+++ compiler-rt/test/profile/Posix/instrprof-fork-int.c
@@ -0,0 +1,27 @@
+/// Testing fork with out exec. The child is interrupted.
+/// Make sure we have an empty profile file.
+// UNSUPPORTED: target={{.*windows.*}}
+// RUN: rm -rf %t.d
+// RUN: mkdir -p %t.d && cd %t.d
+// RUN: %clang_pgogen %s -o %t.exe
+// RUN: LLVM_PROFILE_FILE="%%p.profraw" %t.exe > %t.out
+// RUN: PROFILE=`cat %t.out`
+// RUN: ls $PROFILE
+// RUN: wc $PROFILE | FileCheck %s
+
+// CHECK: 0       0       0
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+
+int main(int argc, char **argv) {
+  pid_t pid;
+  pid = fork();
+  if (!pid) {
+    printf("%ld.profraw\n", (long)getpid());
+    fflush(stdout);
+    raise(SIGSEGV);
+  }
+  return 0;
+}
Index: compiler-rt/lib/profile/InstrProfilingFile.c
===================================================================
--- compiler-rt/lib/profile/InstrProfilingFile.c
+++ compiler-rt/lib/profile/InstrProfilingFile.c
@@ -29,6 +29,8 @@
 #if defined(__linux__)
 #include <sys/types.h>
 #endif
+/* For pthread_atfork(). Not supported on Windows. */
+#include <pthread.h>
 #endif
 
 #include "InstrProfiling.h"
@@ -90,7 +92,6 @@
 
 static lprofFilename lprofCurFilename = {0,   0, 0, {0}, NULL,
                                          {0}, 0, 0, 0,   PNS_unknown};
-
 static int ProfileMergeRequested = 0;
 static int getProfileFileSizeForMerging(FILE *ProfileFile,
                                         uint64_t *ProfileFileSize);
@@ -246,6 +247,11 @@
   return lprofCurFilename.MergePoolSize || isProfileMergeRequested();
 }
 
+// Flag to indicate if a new profile name should be used when a new
+// process is forked. The flag cannot be true on Windows,
+// because the implementation relies on pthread.
+static int ResetNameAtFork = 0;
+
 /* Return 1 if there is an error, otherwise return  0.  */
 static uint32_t fileWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs,
                            uint32_t NumIOVecs) {
@@ -520,21 +526,27 @@
   if (!Filename)
     return;
 
-  /* Only create the profile directory and truncate an existing profile once.
-   * In continuous mode, this is necessary, as the profile is written-to by the
-   * runtime initializer. */
+  /* Only create the profile directory once. In continuous mode, this is
+   * necessary, as the profile is written-to by the runtime initializer. On the
+   * other hand, truncate an existing profile once if the runtime does not
+   * reset the profile file name at fork, because the runtime creates empty
+   * profile files for each processes forked to indicate profiling has started.
+   */
   int initialized = getenv(LPROF_INIT_ONCE_ENV) != NULL;
-  if (initialized)
+  if (initialized && !ResetNameAtFork)
     return;
+
+  if (!initialized) {
 #if defined(_WIN32)
-  _putenv(LPROF_INIT_ONCE_ENV "=" LPROF_INIT_ONCE_ENV);
+    _putenv(LPROF_INIT_ONCE_ENV "=" LPROF_INIT_ONCE_ENV);
 #else
-  setenv(LPROF_INIT_ONCE_ENV, LPROF_INIT_ONCE_ENV, 1);
+    setenv(LPROF_INIT_ONCE_ENV, LPROF_INIT_ONCE_ENV, 1);
 #endif
 
-  /* Create the profile dir (even if online merging is enabled), so that
-   * the profile file can be set up if continuous mode is enabled. */
-  createProfileDir(Filename);
+    /* Create the profile dir (even if online merging is enabled), so that
+     * the profile file can be set up if continuous mode is enabled. */
+    createProfileDir(Filename);
+  }
 
   /* By pass file truncation to allow online raw profile merging. */
   if (lprofCurFilename.MergePoolSize)
@@ -786,14 +798,20 @@
   if (!FilenamePat)
     FilenamePat = DefaultProfileName;
 
-  if (OldFilenamePat && !strcmp(OldFilenamePat, FilenamePat)) {
+#if !defined(_WIN32)
+  ResetNameAtFork = strstr(FilenamePat, "%p") != NULL;
+#endif
+
+  if (OldFilenamePat && !strcmp(OldFilenamePat, FilenamePat) &&
+      !ResetNameAtFork) {
     lprofCurFilename.PNS = PNS;
     return;
   }
 
-  /* When PNS >= OldPNS, the last one wins. */
-  if (!FilenamePat || parseFilenamePattern(FilenamePat, CopyFilenamePat))
+  if (parseFilenamePattern(FilenamePat, CopyFilenamePat))
     resetFilenameToDefault();
+
+  /* When PNS >= OldPNS, the last one wins. */
   lprofCurFilename.PNS = PNS;
 
   if (!OldFilenamePat) {
@@ -988,6 +1006,12 @@
 COMPILER_RT_VISIBILITY
 void __llvm_profile_initialize(void) {
   __llvm_profile_initialize_file();
+
+#if !defined(_WIN32)
+  if (ResetNameAtFork)
+    pthread_atfork(NULL, NULL, __llvm_profile_initialize_file);
+#endif
+
   if (!__llvm_profile_is_continuous_mode_enabled())
     __llvm_profile_register_write_file_atexit();
 }
@@ -1000,6 +1024,7 @@
 void __llvm_profile_set_filename(const char *FilenamePat) {
   if (__llvm_profile_is_continuous_mode_enabled())
     return;
+
   parseAndSetFilename(FilenamePat, PNS_runtime_api, 1);
 }
 
Index: clang/lib/Driver/ToolChains/AIX.cpp
===================================================================
--- clang/lib/Driver/ToolChains/AIX.cpp
+++ clang/lib/Driver/ToolChains/AIX.cpp
@@ -431,9 +431,14 @@
                            llvm::opt::ArgStringList &CmdArgs) const {
   // Add linker option -u__llvm_profile_runtime to cause runtime
   // initialization to occur.
-  if (needsProfileRT(Args))
+  bool NeedsProfileRT = needsProfileRT(Args);
+  if (NeedsProfileRT)
     CmdArgs.push_back(Args.MakeArgString(
         Twine("-u", llvm::getInstrProfRuntimeHookVarName())));
+
+  if (NeedsProfileRT || needsGCovInstrumentation(Args))
+    CmdArgs.push_back("-lpthreads");
+
   ToolChain::addProfileRTLibs(Args, CmdArgs);
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to