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