diff --git a/clang/lib/Driver/ToolChains/AIX.cpp b/clang/lib/Driver/ToolChains/AIX.cpp --- a/clang/lib/Driver/ToolChains/AIX.cpp +++ b/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); } diff --git a/compiler-rt/lib/profile/InstrProfilingFile.c b/compiler-rt/lib/profile/InstrProfilingFile.c --- a/compiler-rt/lib/profile/InstrProfilingFile.c +++ b/compiler-rt/lib/profile/InstrProfilingFile.c @@ -29,6 +29,8 @@ #if defined(__linux__) #include #endif +/* For pthread_atfork(). Not supported on Windows. */ +#include #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); } diff --git a/compiler-rt/test/profile/Posix/instrprof-fork-int.c b/compiler-rt/test/profile/Posix/instrprof-fork-int.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/profile/Posix/instrprof-fork-int.c @@ -0,0 +1,26 @@ +/// Testing fork without 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: not llvm-profdata show `cat %t.out` 2>&1 | FileCheck %s + +// CHECK: error: {{[0-9]+}}.profraw: empty raw profile file + +#include +#include +#include +#include + +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; +} diff --git a/compiler-rt/test/profile/Posix/instrprof-fork.c b/compiler-rt/test/profile/Posix/instrprof-fork.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/profile/Posix/instrprof-fork.c @@ -0,0 +1,28 @@ +/// Testing fork without 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: llvm-profdata show $PROFILE1 | FileCheck %s +// RUN: PROFILE2=`tail -n 1 %t.out` +// RUN: llvm-profdata show $PROFILE2 | FileCheck %s + +// CHECK: Instrumentation level: IR entry_first = 0 +// CHECK-NEXT: Total functions: 1 + +#include +#include +#include + +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; +}