diff options
Diffstat (limited to 'lib/profile')
-rw-r--r-- | lib/profile/CMakeLists.txt | 5 | ||||
-rw-r--r-- | lib/profile/GCDAProfiling.c | 35 | ||||
-rw-r--r-- | lib/profile/InstrProfiling.h | 17 | ||||
-rw-r--r-- | lib/profile/InstrProfilingFile.c | 95 | ||||
-rw-r--r-- | lib/profile/InstrProfilingUtil.c | 35 | ||||
-rw-r--r-- | lib/profile/InstrProfilingUtil.h | 16 |
6 files changed, 147 insertions, 56 deletions
diff --git a/lib/profile/CMakeLists.txt b/lib/profile/CMakeLists.txt index 420d7660ee36..d03409fc45b7 100644 --- a/lib/profile/CMakeLists.txt +++ b/lib/profile/CMakeLists.txt @@ -7,11 +7,12 @@ set(PROFILE_SOURCES InstrProfilingFile.c InstrProfilingPlatformDarwin.c InstrProfilingPlatformOther.c - InstrProfilingRuntime.cc) + InstrProfilingRuntime.cc + InstrProfilingUtil.c) if(APPLE) add_compiler_rt_osx_static_runtime(clang_rt.profile_osx - ARCH ${PROFILE_SUPPORTED_ARCH} + ARCHS ${PROFILE_SUPPORTED_ARCH} SOURCES ${PROFILE_SOURCES}) add_dependencies(profile clang_rt.profile_osx) else() diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c index 45fbd07e544b..aec232856e74 100644 --- a/lib/profile/GCDAProfiling.c +++ b/lib/profile/GCDAProfiling.c @@ -20,23 +20,18 @@ |* \*===----------------------------------------------------------------------===*/ +#include "InstrProfilingUtil.h" + #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> -#ifdef _WIN32 -#include <direct.h> -#endif +#include <sys/file.h> #define I386_FREEBSD (defined(__FreeBSD__) && defined(__i386__)) -#if !I386_FREEBSD -#include <sys/stat.h> -#include <sys/types.h> -#endif - #if !defined(_MSC_VER) && !I386_FREEBSD #include <stdint.h> #endif @@ -51,7 +46,6 @@ typedef unsigned long long uint64_t; typedef unsigned char uint8_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; -int mkdir(const char*, unsigned short); #endif /* #define DEBUG_GCDAPROFILING */ @@ -208,21 +202,6 @@ static char *mangle_filename(const char *orig_filename) { return new_filename; } -static void recursive_mkdir(char *path) { - int i; - - for (i = 1; path[i] != '\0'; ++i) { - if (path[i] != '/') continue; - path[i] = '\0'; -#ifdef _WIN32 - _mkdir(path); -#else - mkdir(path, 0755); /* Some of these will fail, ignore it. */ -#endif - path[i] = '/'; - } -} - static int map_file() { fseek(output_file, 0L, SEEK_END); file_size = ftell(output_file); @@ -282,7 +261,7 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4], fd = open(filename, O_RDWR | O_CREAT, 0644); if (fd == -1) { /* Try creating the directories first then opening the file. */ - recursive_mkdir(filename); + __llvm_profile_recursive_mkdir(filename); fd = open(filename, O_RDWR | O_CREAT, 0644); if (fd == -1) { /* Bah! It's hopeless. */ @@ -294,6 +273,11 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4], } } + /* 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. + */ + flock(fd, LOCK_EX); output_file = fdopen(fd, mode); /* Initialize the write buffer. */ @@ -493,6 +477,7 @@ void llvm_gcda_end_file() { } fclose(output_file); + flock(fd, LOCK_UN); output_file = NULL; write_buffer = NULL; } diff --git a/lib/profile/InstrProfiling.h b/lib/profile/InstrProfiling.h index 2b1bd003668e..3778a88893e6 100644 --- a/lib/profile/InstrProfiling.h +++ b/lib/profile/InstrProfiling.h @@ -62,7 +62,9 @@ uint64_t *__llvm_profile_end_counters(void); * * Writes to the file with the last name given to \a __llvm_profile_set_filename(), * or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable, - * or if that's not set, \c "default.profdata". + * or if that's not set, the last name given to + * \a __llvm_profile_override_default_filename(), or if that's not set, + * \c "default.profraw". */ int __llvm_profile_write_file(void); @@ -77,6 +79,19 @@ int __llvm_profile_write_file(void); */ void __llvm_profile_set_filename(const char *Name); +/*! + * \brief Set the filename for writing instrumentation data, unless the + * \c LLVM_PROFILE_FILE environment variable was set. + * + * Unless overridden, sets the filename to be used for subsequent calls to + * \a __llvm_profile_write_file(). + * + * \c Name is not copied, so it must remain valid. Passing NULL resets the + * filename logic to the default behaviour (unless the \c LLVM_PROFILE_FILE + * was set in which case it has no effect). + */ +void __llvm_profile_override_default_filename(const char *Name); + /*! \brief Register to write instrumentation data to file at exit. */ int __llvm_profile_register_write_file_atexit(void); diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c index 346665fd5b3e..68e8c7b07871 100644 --- a/lib/profile/InstrProfilingFile.c +++ b/lib/profile/InstrProfilingFile.c @@ -8,10 +8,11 @@ \*===----------------------------------------------------------------------===*/ #include "InstrProfiling.h" +#include "InstrProfilingUtil.h" +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/errno.h> #define UNCONST(ptr) ((void *)(uintptr_t)(ptr)) @@ -76,40 +77,61 @@ static int writeFileWithName(const char *OutputName) { __attribute__((weak)) int __llvm_profile_OwnsFilename = 0; __attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL; -static void setFilename(const char *Filename, int OwnsFilename) { - if (__llvm_profile_OwnsFilename) - free(UNCONST(__llvm_profile_CurrentFilename)); - - __llvm_profile_CurrentFilename = Filename; - __llvm_profile_OwnsFilename = OwnsFilename; -} - static void truncateCurrentFile(void) { - const char *Filename = __llvm_profile_CurrentFilename; + const char *Filename; + FILE *File; + + Filename = __llvm_profile_CurrentFilename; if (!Filename || !Filename[0]) return; + /* Create the directory holding the file, if needed. */ + if (strchr(Filename, '/')) { + char *Copy = malloc(strlen(Filename) + 1); + strcpy(Copy, Filename); + __llvm_profile_recursive_mkdir(Copy); + free(Copy); + } + /* Truncate the file. Later we'll reopen and append. */ - FILE *File = fopen(Filename, "w"); + File = fopen(Filename, "w"); if (!File) return; fclose(File); } -static void setDefaultFilename(void) { setFilename("default.profraw", 0); } +static void setFilename(const char *Filename, int OwnsFilename) { + /* Check if this is a new filename and therefore needs truncation. */ + int NewFile = !__llvm_profile_CurrentFilename || + (Filename && strcmp(Filename, __llvm_profile_CurrentFilename)); + if (__llvm_profile_OwnsFilename) + free(UNCONST(__llvm_profile_CurrentFilename)); -int getpid(void); -static int setFilenameFromEnvironment(void) { - const char *Filename = getenv("LLVM_PROFILE_FILE"); - if (!Filename || !Filename[0]) - return -1; + __llvm_profile_CurrentFilename = Filename; + __llvm_profile_OwnsFilename = OwnsFilename; - /* Check the filename for "%p", which indicates a pid-substitution. */ + /* If not a new file, append to support profiling multiple shared objects. */ + if (NewFile) + truncateCurrentFile(); +} + +static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); } + +int getpid(void); +static int setFilenamePossiblyWithPid(const char *Filename) { #define MAX_PID_SIZE 16 char PidChars[MAX_PID_SIZE] = {0}; - int NumPids = 0; - int PidLength = 0; - int I; + int NumPids = 0, PidLength = 0; + char *Allocated; + int I, J; + + /* Reset filename on NULL, except with env var which is checked by caller. */ + if (!Filename) { + resetFilenameToDefault(); + return 0; + } + + /* Check the filename for "%p", which indicates a pid-substitution. */ for (I = 0; Filename[I]; ++I) if (Filename[I] == '%' && Filename[++I] == 'p') if (!NumPids++) { @@ -123,12 +145,11 @@ static int setFilenameFromEnvironment(void) { } /* Allocate enough space for the substituted filename. */ - char *Allocated = (char*)malloc(I + NumPids*(PidLength - 2) + 1); + Allocated = malloc(I + NumPids*(PidLength - 2) + 1); if (!Allocated) return -1; /* Construct the new filename. */ - int J; for (I = 0, J = 0; Filename[I]; ++I) if (Filename[I] == '%') { if (Filename[++I] == 'p') { @@ -145,11 +166,20 @@ static int setFilenameFromEnvironment(void) { return 0; } +static int setFilenameFromEnvironment(void) { + const char *Filename = getenv("LLVM_PROFILE_FILE"); + + if (!Filename || !Filename[0]) + return -1; + + return setFilenamePossiblyWithPid(Filename); +} + static void setFilenameAutomatically(void) { if (!setFilenameFromEnvironment()) return; - setDefaultFilename(); + resetFilenameToDefault(); } __attribute__((visibility("hidden"))) @@ -160,23 +190,32 @@ void __llvm_profile_initialize_file(void) { /* Detect the filename and truncate. */ setFilenameAutomatically(); - truncateCurrentFile(); } __attribute__((visibility("hidden"))) void __llvm_profile_set_filename(const char *Filename) { - setFilename(Filename, 0); - truncateCurrentFile(); + setFilenamePossiblyWithPid(Filename); +} + +__attribute__((visibility("hidden"))) +void __llvm_profile_override_default_filename(const char *Filename) { + /* If the env var is set, skip setting filename from argument. */ + const char *Env_Filename = getenv("LLVM_PROFILE_FILE"); + if (Env_Filename && Env_Filename[0]) + return; + setFilenamePossiblyWithPid(Filename); } __attribute__((visibility("hidden"))) int __llvm_profile_write_file(void) { + int rc; + /* Check the filename. */ if (!__llvm_profile_CurrentFilename) return -1; /* Write the file. */ - int rc = writeFileWithName(__llvm_profile_CurrentFilename); + rc = writeFileWithName(__llvm_profile_CurrentFilename); if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS")) fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n", __llvm_profile_CurrentFilename, strerror(errno)); diff --git a/lib/profile/InstrProfilingUtil.c b/lib/profile/InstrProfilingUtil.c new file mode 100644 index 000000000000..e146dfca83c8 --- /dev/null +++ b/lib/profile/InstrProfilingUtil.c @@ -0,0 +1,35 @@ +/*===- InstrProfilingUtil.c - Support library for PGO instrumentation -----===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfilingUtil.h" + +#ifdef _WIN32 +#include <direct.h> +#elif I386_FREEBSD +int mkdir(const char*, unsigned short); +#else +#include <sys/stat.h> +#include <sys/types.h> +#endif + +__attribute__((visibility("hidden"))) +void __llvm_profile_recursive_mkdir(char *path) { + int i; + + for (i = 1; path[i] != '\0'; ++i) { + if (path[i] != '/') continue; + path[i] = '\0'; +#ifdef _WIN32 + _mkdir(path); +#else + mkdir(path, 0755); /* Some of these will fail, ignore it. */ +#endif + path[i] = '/'; + } +} diff --git a/lib/profile/InstrProfilingUtil.h b/lib/profile/InstrProfilingUtil.h new file mode 100644 index 000000000000..756b18e7c56d --- /dev/null +++ b/lib/profile/InstrProfilingUtil.h @@ -0,0 +1,16 @@ +/*===- InstrProfilingUtil.h - Support library for PGO instrumentation -----===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#ifndef PROFILE_INSTRPROFILINGUTIL_H +#define PROFILE_INSTRPROFILINGUTIL_H + +/*! \brief Create a directory tree. */ +void __llvm_profile_recursive_mkdir(char *Pathname); + +#endif /* PROFILE_INSTRPROFILINGUTIL_H */ |