From 0bdc52140c2a233fbbc227ad078bfc837246414f Mon Sep 17 00:00:00 2001 From: Xinliang David Li Date: Mon, 6 Jun 2016 03:17:58 +0000 Subject: [profile] in-process mergeing support (part-2) (Part-1 merging API is in profile runtime) This patch implements a portable file opening API with exclusive access for the process. In-process profile merge requires profile file update to be atomic/fully sychronized. git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@271864 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/profile/CMakeLists.txt | 22 ++++++++++++ lib/profile/InstrProfilingUtil.c | 55 +++++++++++++++++++++++++++- lib/profile/InstrProfilingUtil.h | 5 +++ test/profile/Inputs/instrprof-file_ex.c | 59 +++++++++++++++++++++++++++++++ test/profile/Linux/instrprof-file_ex.test | 16 +++++++++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 test/profile/Inputs/instrprof-file_ex.c create mode 100644 test/profile/Linux/instrprof-file_ex.test diff --git a/lib/profile/CMakeLists.txt b/lib/profile/CMakeLists.txt index 0905ff3e6..5bcdca129 100644 --- a/lib/profile/CMakeLists.txt +++ b/lib/profile/CMakeLists.txt @@ -22,6 +22,22 @@ int main() { } " COMPILER_RT_TARGET_HAS_ATOMICS) +CHECK_CXX_SOURCE_COMPILES(" +#if defined(__linux__) +#include +#endif +#include +int fd; +int main() { + struct flock s_flock; + + s_flock.l_type = F_WRLCK; + fcntl(fd, F_SETLKW, &s_flock); + return 0; +} + +" COMPILER_RT_TARGET_HAS_FCNTL_LCK) + add_custom_target(profile) set(PROFILE_SOURCES @@ -55,6 +71,12 @@ if(COMPILER_RT_TARGET_HAS_ATOMICS) -DCOMPILER_RT_HAS_ATOMICS=1) endif() +if(COMPILER_RT_TARGET_HAS_FCNTL_LCK) + set(EXTRA_FLAGS + ${EXTRA_FLAGS} + -DCOMPILER_RT_HAS_FCNTL_LCK=1) +endif() + if(APPLE) add_compiler_rt_runtime(clang_rt.profile STATIC diff --git a/lib/profile/InstrProfilingUtil.c b/lib/profile/InstrProfilingUtil.c index eb79d4222..24e302d4a 100644 --- a/lib/profile/InstrProfilingUtil.c +++ b/lib/profile/InstrProfilingUtil.c @@ -12,9 +12,15 @@ #ifdef _WIN32 #include +#include #else #include #include +#if defined(__linux__) +#include +#endif +#include +#include #endif #ifdef COMPILER_RT_HAS_UNAME @@ -35,7 +41,7 @@ void __llvm_profile_recursive_mkdir(char *path) { #ifdef _WIN32 _mkdir(path); #else - mkdir(path, 0755); /* Some of these will fail, ignore it. */ + mkdir(path, 0755); /* Some of these will fail, ignore it. */ #endif path[i] = save; } @@ -70,4 +76,51 @@ int lprofGetHostName(char *Name, int Len) { } #endif +FILE *lprofOpenFileEx(const char *ProfileName) { + FILE *f; + int fd; +#ifdef COMPILER_RT_HAS_FCNTL_LCK + struct flock s_flock; + + s_flock.l_whence = SEEK_SET; + s_flock.l_start = 0; + s_flock.l_len = 0; /* Until EOF. */ + s_flock.l_pid = getpid(); + + s_flock.l_type = F_WRLCK; + fd = open(ProfileName, O_RDWR | O_CREAT, 0666); + if (fd < 0) + return 0; + + while (fcntl(fd, F_SETLKW, &s_flock) && errno == EINTR) + continue; + + f = fdopen(fd, "r+b"); +#elif defined(_WIN32) + HANDLE h = CreateFile(ProfileName, GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (h == INVALID_HANDLE_VALUE) + return 0; + + fd = _open_osfhandle((intptr_t)h, 0); + if (fd == -1) { + CloseHandle(h); + return 0; + } + + f = _fdopen(fd, "r+b"); + if (f == 0) { + CloseHandle(h); + return 0; + } +#else + /* Worst case no locking applied. */ + PROF_WARN("Concurrent file access is not supported : %s\n", "lack file locking"); + fd = open(ProfileName, O_RDWR | O_CREAT, 0666); + if (fd < 0) + return 0; + f = fdopen(fd, "r+b"); +#endif + return f; +} diff --git a/lib/profile/InstrProfilingUtil.h b/lib/profile/InstrProfilingUtil.h index b2bdd6519..16d3fbf42 100644 --- a/lib/profile/InstrProfilingUtil.h +++ b/lib/profile/InstrProfilingUtil.h @@ -11,10 +11,15 @@ #define PROFILE_INSTRPROFILINGUTIL_H #include +#include /*! \brief Create a directory tree. */ void __llvm_profile_recursive_mkdir(char *Pathname); +/*! Open file \c Filename for read+write with write + * lock for exclusive access. The caller will block + * if the lock is already held by another process. */ +FILE *lprofOpenFileEx(const char *Filename); /* PS4 doesn't have getenv. Define a shim. */ #if __ORBIS__ static inline char *getenv(const char *name) { return NULL; } diff --git a/test/profile/Inputs/instrprof-file_ex.c b/test/profile/Inputs/instrprof-file_ex.c new file mode 100644 index 000000000..22e7555a1 --- /dev/null +++ b/test/profile/Inputs/instrprof-file_ex.c @@ -0,0 +1,59 @@ +/* This is a test case where the parent process forks 10 + * children which contend to write to the same file. With + * file locking support, the data from each child should not + * be lost. + */ +#include +#include +#include +#include + +extern FILE *lprofOpenFileEx(const char *); +int main(int argc, char *argv[]) { + pid_t tid; + FILE *F; + const char *FN; + int child[10]; + int c; + int i; + + if (argc < 2) { + fprintf(stderr, "Requires one argument \n"); + exit(1); + } + FN = argv[1]; + truncate(FN, 0); + + for (i = 0; i < 10; i++) { + c = fork(); + // in child: + if (c == 0) { + FILE *F = lprofOpenFileEx(FN); + if (!F) { + fprintf(stderr, "Can not open file %s from child\n", FN); + exit(1); + } + fseek(F, 0, SEEK_END); + fprintf(F, "Dump from Child %d\n", i + 11); + fclose(F); + exit(0); + } else { + child[i] = c; + } + } + + // In parent + for (i = 0; i < 10; i++) { + int child_status; + if ((tid = waitpid(child[i], &child_status, 0) == -1)) + break; + } + F = lprofOpenFileEx(FN); + if (!F) { + fprintf(stderr, "Can not open file %s from parent\n", FN); + exit(1); + } + fseek(F, 0, SEEK_END); + fprintf(F, "Dump from parent %d\n", i + 11); + return 0; +} diff --git a/test/profile/Linux/instrprof-file_ex.test b/test/profile/Linux/instrprof-file_ex.test new file mode 100644 index 000000000..af79b7398 --- /dev/null +++ b/test/profile/Linux/instrprof-file_ex.test @@ -0,0 +1,16 @@ +RUN: mkdir -p %t.d +RUN: %clang_profgen -fprofile-instr-generate %S/../Inputs/instrprof-file_ex.c -o %t +RUN: %run %t %t.d/run.dump +RUN: sort %t.d/run.dump | FileCheck %s + +CHECK: Dump from Child 11 +CHECK-NEXT: Dump from Child 12 +CHECK-NEXT: Dump from Child 13 +CHECK-NEXT: Dump from Child 14 +CHECK-NEXT: Dump from Child 15 +CHECK-NEXT: Dump from Child 16 +CHECK-NEXT: Dump from Child 17 +CHECK-NEXT: Dump from Child 18 +CHECK-NEXT: Dump from Child 19 +CHECK-NEXT: Dump from Child 20 +CHECK-NEXT: Dump from parent 21 -- cgit v1.2.3