diff --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c --- a/compiler-rt/lib/profile/GCDAProfiling.c +++ b/compiler-rt/lib/profile/GCDAProfiling.c @@ -348,20 +348,29 @@ fd = open(filename, O_RDWR | O_BINARY); if (fd == -1) { - /* Try opening the file, creating it if necessary. */ - new_file = 1; - mode = "w+b"; - fd = open(filename, O_RDWR | O_CREAT | O_BINARY, 0644); - if (fd == -1) { + /* Try creating the file. */ + fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0644); + if (fd != -1) { + new_file = 1; + mode = "w+b"; + } else { /* Try creating the directories first then opening the file. */ __llvm_profile_recursive_mkdir(filename); - fd = open(filename, O_RDWR | O_CREAT | O_BINARY, 0644); - if (fd == -1) { - /* Bah! It's hopeless. */ - int errnum = errno; - fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, - strerror(errnum)); - return; + fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0644); + if (fd != -1) { + new_file = 1; + mode = "w+b"; + } else { + /* Another process may have created the file just now. + * Try opening it without O_CREAT and O_EXCL. */ + fd = open(filename, O_RDWR | O_BINARY); + if (fd == -1) { + /* Bah! It's hopeless. */ + int errnum = errno; + fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, + strerror(errnum)); + return; + } } } } diff --git a/compiler-rt/test/profile/Inputs/instrprof-gcov-parallel.driver.c b/compiler-rt/test/profile/Inputs/instrprof-gcov-parallel.driver.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/profile/Inputs/instrprof-gcov-parallel.driver.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include + +#define CHILDREN 7 + +int main(int argc, char *argv[]) { + _Atomic int *sync = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (sync == MAP_FAILED) + return 1; + *sync = 0; + + for (int i = 0; i < CHILDREN; i++) { + pid_t pid = fork(); + if (!pid) { + // child + while (*sync == 0) + ; // wait the parent in order to call execl simultaneously + execl(argv[1], argv[1], NULL); + } else if (pid == -1) { + *sync = 1; // release all children + return 1; + } + } + + // parent + *sync = 1; // start the program in all children simultaneously + for (int i = 0; i < CHILDREN; i++) + wait(NULL); + + return 0; +} diff --git a/compiler-rt/test/profile/Inputs/instrprof-gcov-parallel.target.c b/compiler-rt/test/profile/Inputs/instrprof-gcov-parallel.target.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/profile/Inputs/instrprof-gcov-parallel.target.c @@ -0,0 +1,9 @@ +#define COUNT 101 + +static volatile int aaa; + +int main(int argc, char *argv[]) { + for (int i = 0; i < COUNT; i++) + aaa++; + return 0; +} diff --git a/compiler-rt/test/profile/Posix/instrprof-gcov-parallel.test b/compiler-rt/test/profile/Posix/instrprof-gcov-parallel.test new file mode 100644 --- /dev/null +++ b/compiler-rt/test/profile/Posix/instrprof-gcov-parallel.test @@ -0,0 +1,16 @@ +RUN: mkdir -p %t.d +RUN: cd %t.d + +RUN: %clang -o %t.driver %S/../Inputs/instrprof-gcov-parallel.driver.c +RUN: %clang --coverage -o %t.target %S/../Inputs/instrprof-gcov-parallel.target.c +RUN: test -f instrprof-gcov-parallel.target.gcno + +RUN: rm -f instrprof-gcov-parallel.target.gcda +RUN: %run %t.driver %t.target +RUN: llvm-cov gcov instrprof-gcov-parallel.target.gcda +RUN: FileCheck --input-file instrprof-gcov-parallel.target.c.gcov %s + +# Test if the .gcda file is correctly created from one of child processes +# and counters of all processes are recorded correctly. +# 707 = CHILDREN * COUNT +CHECK: 707: {{[0-9]+}}: aaa++;