Skip to content

Commit 9180c14

Browse files
committed
Fix simultaneous .gcda creation
The intent of the `llvm_gcda_start_file` function is that only one process create the .gcda file and initialize it to be updated by other processes later. Before this change, if multiple processes are started simultaneously, some of them may initialize the file because both the first and second `open` calls may succeed in a race condition and `new_file` becomes 1 in those processes. This leads incorrect coverage counter values. This often happens in MPI (Message Passing Interface) programs. The test program added in this change is a simple reproducer. This change ensures only one process creates/initializes the file by using the `O_EXCL` flag. Differential Revision: https://reviews.llvm.org/D76206
1 parent 3341dc7 commit 9180c14

File tree

4 files changed

+82
-12
lines changed

4 files changed

+82
-12
lines changed

compiler-rt/lib/profile/GCDAProfiling.c

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -348,20 +348,29 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4],
348348
fd = open(filename, O_RDWR | O_BINARY);
349349

350350
if (fd == -1) {
351-
/* Try opening the file, creating it if necessary. */
352-
new_file = 1;
353-
mode = "w+b";
354-
fd = open(filename, O_RDWR | O_CREAT | O_BINARY, 0644);
355-
if (fd == -1) {
351+
/* Try creating the file. */
352+
fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0644);
353+
if (fd != -1) {
354+
new_file = 1;
355+
mode = "w+b";
356+
} else {
356357
/* Try creating the directories first then opening the file. */
357358
__llvm_profile_recursive_mkdir(filename);
358-
fd = open(filename, O_RDWR | O_CREAT | O_BINARY, 0644);
359-
if (fd == -1) {
360-
/* Bah! It's hopeless. */
361-
int errnum = errno;
362-
fprintf(stderr, "profiling: %s: cannot open: %s\n", filename,
363-
strerror(errnum));
364-
return;
359+
fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0644);
360+
if (fd != -1) {
361+
new_file = 1;
362+
mode = "w+b";
363+
} else {
364+
/* Another process may have created the file just now.
365+
* Try opening it without O_CREAT and O_EXCL. */
366+
fd = open(filename, O_RDWR | O_BINARY);
367+
if (fd == -1) {
368+
/* Bah! It's hopeless. */
369+
int errnum = errno;
370+
fprintf(stderr, "profiling: %s: cannot open: %s\n", filename,
371+
strerror(errnum));
372+
return;
373+
}
365374
}
366375
}
367376
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include <stdatomic.h>
2+
#include <stdlib.h>
3+
#include <sys/mman.h>
4+
#include <sys/types.h>
5+
#include <sys/wait.h>
6+
#include <unistd.h>
7+
8+
#define CHILDREN 7
9+
10+
int main(int argc, char *argv[]) {
11+
_Atomic int *sync = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
12+
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
13+
if (sync == MAP_FAILED)
14+
return 1;
15+
*sync = 0;
16+
17+
for (int i = 0; i < CHILDREN; i++) {
18+
pid_t pid = fork();
19+
if (!pid) {
20+
// child
21+
while (*sync == 0)
22+
; // wait the parent in order to call execl simultaneously
23+
execl(argv[1], argv[1], NULL);
24+
} else if (pid == -1) {
25+
*sync = 1; // release all children
26+
return 1;
27+
}
28+
}
29+
30+
// parent
31+
*sync = 1; // start the program in all children simultaneously
32+
for (int i = 0; i < CHILDREN; i++)
33+
wait(NULL);
34+
35+
return 0;
36+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#define COUNT 101
2+
3+
static volatile int aaa;
4+
5+
int main(int argc, char *argv[]) {
6+
for (int i = 0; i < COUNT; i++)
7+
aaa++;
8+
return 0;
9+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
RUN: mkdir -p %t.d
2+
RUN: cd %t.d
3+
4+
RUN: %clang -o %t.driver %S/../Inputs/instrprof-gcov-parallel.driver.c
5+
RUN: %clang --coverage -o %t.target %S/../Inputs/instrprof-gcov-parallel.target.c
6+
RUN: test -f instrprof-gcov-parallel.target.gcno
7+
8+
RUN: rm -f instrprof-gcov-parallel.target.gcda
9+
RUN: %run %t.driver %t.target
10+
RUN: llvm-cov gcov instrprof-gcov-parallel.target.gcda
11+
RUN: FileCheck --input-file instrprof-gcov-parallel.target.c.gcov %s
12+
13+
# Test if the .gcda file is correctly created from one of child processes
14+
# and counters of all processes are recorded correctly.
15+
# 707 = CHILDREN * COUNT
16+
CHECK: 707: {{[0-9]+}}: aaa++;

0 commit comments

Comments
 (0)