InstrProf: Support profiling dlopen'd shared libraries

Shared objects are hard.  After this commit, we do the right thing when
profiling two separate shared objects that have been dlopen'd with
`RTLD_LOCAL`, when the main executable is *not* being profiled.

This mainly simplifies the writer logic.

  - At initialization, determine the output filename and truncate the
    file.  Depending on whether shared objects can see each other, this
    may happen multiple times.

  - At exit, each executable writes its own profile in append mode.

<rdar://problem/16918688>

llvm-svn: 209053
This commit is contained in:
Duncan P. N. Exon Smith 2014-05-17 01:27:30 +00:00
parent b0869036c1
commit 55e4d66f0c
7 changed files with 150 additions and 80 deletions

View File

@ -83,8 +83,8 @@ void __llvm_profile_set_filename(const char *Name);
/*! \brief Register to write instrumentation data to file at exit. */ /*! \brief Register to write instrumentation data to file at exit. */
int __llvm_profile_register_write_file_atexit(void); int __llvm_profile_register_write_file_atexit(void);
/*! \brief Register the write file function for this executable. */ /*! \brief Initialize file handling. */
void __llvm_profile_register_write_file(void); void __llvm_profile_initialize_file(void);
/*! \brief Get the magic token for the file format. */ /*! \brief Get the magic token for the file format. */
uint64_t __llvm_profile_get_magic(void); uint64_t __llvm_profile_get_magic(void);

View File

@ -53,75 +53,60 @@ static int writeFile(FILE *File) {
return 0; return 0;
} }
typedef struct __llvm_profile_writer {
struct __llvm_profile_writer *Next;
int (*Data)(FILE *);
} __llvm_profile_writer;
__attribute__((weak)) __llvm_profile_writer *__llvm_profile_HeadWriter = NULL;
static __llvm_profile_writer Writer = {NULL, writeFile};
__attribute__((visibility("hidden")))
void __llvm_profile_register_write_file(void) {
static int HasBeenRegistered = 0;
if (HasBeenRegistered)
return;
HasBeenRegistered = 1;
Writer.Next = __llvm_profile_HeadWriter;
__llvm_profile_HeadWriter = &Writer;
}
static int writeFileWithName(const char *OutputName) { static int writeFileWithName(const char *OutputName) {
int RetVal; int RetVal;
FILE *OutputFile; FILE *OutputFile;
if (!OutputName || !OutputName[0]) if (!OutputName || !OutputName[0])
return -1; return -1;
OutputFile = fopen(OutputName, "w");
/* Append to the file to support profiling multiple shared objects. */
OutputFile = fopen(OutputName, "a");
if (!OutputFile) if (!OutputFile)
return -1; return -1;
__llvm_profile_writer *Writer = __llvm_profile_HeadWriter;
if (Writer)
for (; Writer; Writer = Writer->Next) {
RetVal = Writer->Data(OutputFile);
if (RetVal != 0)
break;
}
else
// Default to calling this executable's writeFile.
RetVal = writeFile(OutputFile); RetVal = writeFile(OutputFile);
fclose(OutputFile); fclose(OutputFile);
return RetVal; return RetVal;
} }
__attribute__((weak)) int __llvm_profile_OwnsFilename = 0;
__attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL; __attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL;
__attribute__((weak)) void __llvm_profile_set_filename(const char *Filename) {
static void setFilename(const char *Filename, int OwnsFilename) {
if (__llvm_profile_OwnsFilename)
free((char *)__llvm_profile_CurrentFilename);
__llvm_profile_CurrentFilename = Filename; __llvm_profile_CurrentFilename = Filename;
__llvm_profile_OwnsFilename = OwnsFilename;
} }
int getpid(void); static void truncateCurrentFile(void) {
__attribute__((weak)) int __llvm_profile_write_file(void) {
char *AllocatedFilename = NULL;
int I, J;
int RetVal;
#define MAX_PID_SIZE 16
char PidChars[MAX_PID_SIZE] = { 0 };
int PidLength = 0;
int NumPids = 0;
/* Get the filename. */
const char *Filename = __llvm_profile_CurrentFilename; const char *Filename = __llvm_profile_CurrentFilename;
#define UPDATE_FILENAME(NextFilename) \ if (!Filename || !Filename[0])
if (!Filename || !Filename[0]) Filename = NextFilename return;
UPDATE_FILENAME(getenv("LLVM_PROFILE_FILE"));
UPDATE_FILENAME("default.profraw"); /* Truncate the file. Later we'll reopen and append. */
#undef UPDATE_FILENAME FILE *File = fopen(Filename, "w");
if (!File)
return;
fclose(File);
}
static void setDefaultFilename(void) { setFilename("default.profraw", 0); }
int getpid(void);
static int setFilenameFromEnvironment(void) {
const char *Filename = getenv("LLVM_PROFILE_FILE");
if (!Filename || !Filename[0])
return -1;
/* Check the filename for "%p", which indicates a pid-substitution. */ /* Check the filename for "%p", which indicates a pid-substitution. */
#define MAX_PID_SIZE 16
char PidChars[MAX_PID_SIZE] = {0};
int NumPids = 0;
int PidLength = 0;
int I;
for (I = 0; Filename[I]; ++I) for (I = 0; Filename[I]; ++I)
if (Filename[I] == '%' && Filename[++I] == 'p') if (Filename[I] == '%' && Filename[++I] == 'p')
if (!NumPids++) { if (!NumPids++) {
@ -129,43 +114,74 @@ __attribute__((weak)) int __llvm_profile_write_file(void) {
if (PidLength <= 0) if (PidLength <= 0)
return -1; return -1;
} }
if (NumPids) { if (!NumPids) {
setFilename(Filename, 0);
return 0;
}
/* Allocate enough space for the substituted filename. */ /* Allocate enough space for the substituted filename. */
AllocatedFilename = (char*)malloc(I + NumPids*(PidLength - 2) + 1); char *Allocated = (char*)malloc(I + NumPids*(PidLength - 2) + 1);
if (!AllocatedFilename) if (!Allocated)
return -1; return -1;
/* Construct the new filename. */ /* Construct the new filename. */
int J;
for (I = 0, J = 0; Filename[I]; ++I) for (I = 0, J = 0; Filename[I]; ++I)
if (Filename[I] == '%') { if (Filename[I] == '%') {
if (Filename[++I] == 'p') { if (Filename[++I] == 'p') {
memcpy(AllocatedFilename + J, PidChars, PidLength); memcpy(Allocated + J, PidChars, PidLength);
J += PidLength; J += PidLength;
} }
/* Drop any unknown substitutions. */ /* Drop any unknown substitutions. */
} else } else
AllocatedFilename[J++] = Filename[I]; Allocated[J++] = Filename[I];
AllocatedFilename[J] = 0; Allocated[J] = 0;
/* Actually use the computed name. */ /* Use the computed name. */
Filename = AllocatedFilename; setFilename(Allocated, 1);
return 0;
} }
static void setFilenameAutomatically(void) {
if (!setFilenameFromEnvironment())
return;
setDefaultFilename();
}
__attribute__((visibility("hidden")))
void __llvm_profile_initialize_file(void) {
/* Check if the filename has been initialized. */
if (__llvm_profile_CurrentFilename)
return;
/* Detect the filename and truncate. */
setFilenameAutomatically();
truncateCurrentFile();
}
__attribute__((visibility("hidden")))
void __llvm_profile_set_filename(const char *Filename) {
setFilename(Filename, 0);
truncateCurrentFile();
}
__attribute__((visibility("hidden")))
int __llvm_profile_write_file(void) {
/* Check the filename. */
if (!__llvm_profile_CurrentFilename)
return -1;
/* Write the file. */ /* Write the file. */
RetVal = writeFileWithName(Filename); return writeFileWithName(__llvm_profile_CurrentFilename);
/* Free the filename. */
if (AllocatedFilename)
free(AllocatedFilename);
return RetVal;
} }
static void writeFileWithoutReturn(void) { static void writeFileWithoutReturn(void) {
__llvm_profile_write_file(); __llvm_profile_write_file();
} }
__attribute__((weak)) int __llvm_profile_register_write_file_atexit(void) { __attribute__((visibility("hidden")))
int __llvm_profile_register_write_file_atexit(void) {
static int HasBeenRegistered = 0; static int HasBeenRegistered = 0;
if (HasBeenRegistered) if (HasBeenRegistered)

View File

@ -21,7 +21,7 @@ class RegisterRuntime {
public: public:
RegisterRuntime() { RegisterRuntime() {
__llvm_profile_register_write_file_atexit(); __llvm_profile_register_write_file_atexit();
__llvm_profile_register_write_file(); __llvm_profile_initialize_file();
} }
}; };

View File

@ -0,0 +1 @@
void func(int K) { if (K) {} }

View File

@ -0,0 +1 @@
void func2(int K) { if (K) {} }

View File

@ -0,0 +1,18 @@
#ifdef DLOPEN_FUNC_DIR
#include <dlfcn.h>
#else
void func(int K);
void func2(int K);
#endif
int main(int argc, char *argv[]) {
#ifdef DLOPEN_FUNC_DIR
void *f1_handle = dlopen(DLOPEN_FUNC_DIR"/func.shared", DLOPEN_FLAGS);
void (*func)(int) = (void (*)(int))dlsym(f1_handle, "func");
void *f2_handle = dlopen(DLOPEN_FUNC_DIR"/func2.shared", DLOPEN_FLAGS);
void (*func2)(int) = (void (*)(int))dlsym(f2_handle, "func2");
#endif
func(1);
func2(0);
return 0;
}

View File

@ -0,0 +1,34 @@
RUN: mkdir -p %t.d
RUN: %clang_profgen -o %t.d/func.shared -fPIC -shared %S/Inputs/instrprof-dlopen-func.c
RUN: %clang_profgen -o %t.d/func2.shared -fPIC -shared %S/Inputs/instrprof-dlopen-func2.c
RUN: %clang -o %t-local -fPIC -DDLOPEN_FUNC_DIR=\"%t.d\" -DDLOPEN_FLAGS=RTLD_LOCAL %S/Inputs/instrprof-dlopen-main.c
RUN: %clang -o %t-global -fPIC -DDLOPEN_FUNC_DIR=\"%t.d\" -DDLOPEN_FLAGS=RTLD_GLOBAL %S/Inputs/instrprof-dlopen-main.c
RUN: %clang -c -o %t.d/main.o %S/Inputs/instrprof-dlopen-main.c
RUN: %clang_profgen -o %t-static %S/Inputs/instrprof-dlopen-func.c %S/Inputs/instrprof-dlopen-func2.c %t.d/main.o
RUN: env LLVM_PROFILE_FILE=%t-static.profraw %run %t-static
RUN: env LLVM_PROFILE_FILE=%t-local.profraw %run %t-local
RUN: env LLVM_PROFILE_FILE=%t-global.profraw %run %t-global
RUN: llvm-profdata merge -o %t-static.profdata %t-static.profraw
RUN: llvm-profdata merge -o %t-local.profdata %t-local.profraw
RUN: llvm-profdata merge -o %t-global.profdata %t-global.profraw
RUN: %clang_profuse=%t-static.profdata -o %t-func.static.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func.c
RUN: %clang_profuse=%t-local.profdata -o %t-func.local.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func.c
RUN: %clang_profuse=%t-global.profdata -o %t-func.global.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func.c
RUN: diff %t-func.static.ll %t-func.local.ll
RUN: diff %t-func.static.ll %t-func.global.ll
RUN: %clang_profuse=%t-static.profdata -o %t-func2.static.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func2.c
RUN: %clang_profuse=%t-local.profdata -o %t-func2.local.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func2.c
RUN: %clang_profuse=%t-global.profdata -o %t-func2.global.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-func2.c
RUN: diff %t-func2.static.ll %t-func2.local.ll
RUN: diff %t-func2.static.ll %t-func2.global.ll
RUN: %clang_profuse=%t-static.profdata -o %t-main.static.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-main.c
RUN: %clang_profuse=%t-local.profdata -o %t-main.local.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-main.c
RUN: %clang_profuse=%t-local.profdata -o %t-main.global.ll -S -emit-llvm %S/Inputs/instrprof-dlopen-main.c
RUN: diff %t-main.static.ll %t-main.local.ll
RUN: diff %t-main.static.ll %t-main.global.ll