Index: lib/profile/GCDAProfiling.c =================================================================== --- lib/profile/GCDAProfiling.c +++ lib/profile/GCDAProfiling.c @@ -341,7 +341,8 @@ COMPILER_RT_VISIBILITY void llvm_gcda_start_file(const char *orig_filename, const char version[4], uint32_t checksum) { - const char *mode = "r+b"; + const char *mode; + int errnum = 0; filename = mangle_filename(orig_filename); /* Try just opening the file. */ @@ -349,29 +350,51 @@ 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); + /* Try opening the file, creating it if necessary. + To avoid a race condition, we use O_EXCL. */ + fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0644); if (fd == -1) { - /* 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; + errnum = errno; + if (errnum == EEXIST) { + /* In the meantime, an other process has created the file. */ + fd = open(filename, O_RDWR | O_BINARY); + errnum = errno; + } else if (errnum == ENOENT) { + /* Try creating the directories first then opening the file. */ + __llvm_profile_recursive_mkdir(filename); + fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0644); + if (fd == -1) { + errnum = errno; + if (errnum == EEXIST) { + fd = open(filename, O_RDWR | O_BINARY); + errnum = errno; + } + } else { + new_file = 1; + } } + } else { + new_file = 1; } } + if (fd == -1) { + /* Bah! It's hopeless. */ + fprintf(stderr, "profiling: %s: cannot open: %s\n", filename, + strerror(errnum)); + return; + } + /* Try to flock the file to serialize concurrent processes writing out to the * same GCDA. This can fail if the filesystem doesn't support it, but in that * case we'll just carry on with the old racy behaviour and hope for the best. */ - lprofLockFd(fd); + if (lprofLockFd(fd) != 0) { + errnum = errno; + fprintf(stderr, "profiling: %s: cannot lock: %s\n", filename, + strerror(errnum)); + } + mode = new_file == 1 ? "w+b" : "r+b"; output_file = fdopen(fd, mode); /* Initialize the write buffer. */ Index: lib/profile/InstrProfilingUtil.c =================================================================== --- lib/profile/InstrProfilingUtil.c +++ lib/profile/InstrProfilingUtil.c @@ -125,8 +125,7 @@ } return 0; #else - flock(fd, LOCK_EX); - return 0; + return flock(fd, LOCK_EX); #endif } @@ -150,8 +149,7 @@ } return 0; #else - flock(fd, LOCK_UN); - return 0; + return flock(fd, LOCK_UN); #endif } Index: test/profile/Inputs/instrprof-gcov-prefix.c =================================================================== --- /dev/null +++ test/profile/Inputs/instrprof-gcov-prefix.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} Index: test/profile/Posix/instrprof-gcov-prefix.test =================================================================== --- /dev/null +++ test/profile/Posix/instrprof-gcov-prefix.test @@ -0,0 +1,11 @@ +RUN: mkdir -p %t.d +RUN: cd %t.d + +RUN: %clang --coverage -o %t %S/../Inputs/instrprof-gcov-prefix.c +RUN: test -f instrprof-gcov-prefix.gcno + +RUN: rm -fr %t.d/dir1 +RUN: GCOV_PREFIX=%t.d/dir1/dir2/dir3 %run %t +RUN: GCOV_PREFIX=%t.d/dir1/dir2/dir3 %run %t +RUN: test -f %t.d/dir1/dir2/dir3/`pwd`/instrprof-gcov-prefix.gcda +RUN: rm -fr %t.d/dir1