InstrProf: Fix shared object profiling
Change the API of the instrumented profiling library to work with shared objects. - Most things are now declared hidden, so that each executable gets its own copy. - Initialization hooks up a linked list of writers. - The raw format with shared objects that are profiled consists of a concatenated series of profiles. llvm-profdata knows how to deal with that since r208938. <rdar://problem/16918688> llvm-svn: 208940
This commit is contained in:
parent
9121220804
commit
084398857a
|
@ -10,6 +10,7 @@
|
|||
#include "InstrProfiling.h"
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
uint64_t __llvm_profile_get_magic(void) {
|
||||
/* Magic number to detect file format and endianness.
|
||||
*
|
||||
|
@ -32,11 +33,13 @@ uint64_t __llvm_profile_get_magic(void) {
|
|||
(uint64_t)129;
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
uint64_t __llvm_profile_get_version(void) {
|
||||
/* This should be bumped any time the output format changes. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
void __llvm_profile_reset_counters(void) {
|
||||
uint64_t *I = __llvm_profile_counters_begin();
|
||||
uint64_t *E = __llvm_profile_counters_end();
|
||||
|
|
|
@ -83,6 +83,9 @@ void __llvm_profile_set_filename(const char *Name);
|
|||
/*! \brief Register to write instrumentation data to file at exit. */
|
||||
int __llvm_profile_register_write_file_atexit(void);
|
||||
|
||||
/*! \brief Register the write file function for this executable. */
|
||||
void __llvm_profile_register_write_file(void);
|
||||
|
||||
/*! \brief Get the magic token for the file format. */
|
||||
uint64_t __llvm_profile_get_magic(void);
|
||||
|
||||
|
|
|
@ -10,14 +10,18 @@
|
|||
#include "InstrProfiling.h"
|
||||
#include <string.h>
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
uint64_t __llvm_profile_get_size_for_buffer(void) {
|
||||
/* Match logic in __llvm_profile_write_buffer(). */
|
||||
const uint64_t NamesSize = PROFILE_RANGE_SIZE(names) * sizeof(char);
|
||||
const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t);
|
||||
return sizeof(uint64_t) * PROFILE_HEADER_SIZE +
|
||||
PROFILE_RANGE_SIZE(data) * sizeof(__llvm_profile_data) +
|
||||
PROFILE_RANGE_SIZE(counters) * sizeof(uint64_t) +
|
||||
PROFILE_RANGE_SIZE(names) * sizeof(char);
|
||||
NamesSize + Padding;
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
int __llvm_profile_write_buffer(char *Buffer) {
|
||||
/* Match logic in __llvm_profile_get_size_for_buffer().
|
||||
* Match logic in __llvm_profile_write_file().
|
||||
|
@ -33,6 +37,10 @@ int __llvm_profile_write_buffer(char *Buffer) {
|
|||
const uint64_t DataSize = DataEnd - DataBegin;
|
||||
const uint64_t CountersSize = CountersEnd - CountersBegin;
|
||||
const uint64_t NamesSize = NamesEnd - NamesBegin;
|
||||
const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t);
|
||||
|
||||
/* Enough zeroes for padding. */
|
||||
const char Zeroes[sizeof(uint64_t)] = {0};
|
||||
|
||||
/* Create the header. */
|
||||
uint64_t Header[PROFILE_HEADER_SIZE];
|
||||
|
@ -54,6 +62,7 @@ int __llvm_profile_write_buffer(char *Buffer) {
|
|||
UPDATE_memcpy(DataBegin, DataSize * sizeof(__llvm_profile_data));
|
||||
UPDATE_memcpy(CountersBegin, CountersSize * sizeof(uint64_t));
|
||||
UPDATE_memcpy(NamesBegin, NamesSize * sizeof(char));
|
||||
UPDATE_memcpy(Zeroes, Padding * sizeof(char));
|
||||
#undef UPDATE_memcpy
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -25,6 +25,10 @@ static int writeFile(FILE *File) {
|
|||
const uint64_t DataSize = DataEnd - DataBegin;
|
||||
const uint64_t CountersSize = CountersEnd - CountersBegin;
|
||||
const uint64_t NamesSize = NamesEnd - NamesBegin;
|
||||
const uint64_t Padding = sizeof(uint64_t) - NamesSize % sizeof(uint64_t);
|
||||
|
||||
/* Enough zeroes for padding. */
|
||||
const char Zeroes[sizeof(uint64_t)] = {0};
|
||||
|
||||
/* Create the header. */
|
||||
uint64_t Header[PROFILE_HEADER_SIZE];
|
||||
|
@ -43,9 +47,30 @@ static int writeFile(FILE *File) {
|
|||
CHECK_fwrite(DataBegin, sizeof(__llvm_profile_data), DataSize, File);
|
||||
CHECK_fwrite(CountersBegin, sizeof(uint64_t), CountersSize, File);
|
||||
CHECK_fwrite(NamesBegin, sizeof(char), NamesSize, File);
|
||||
CHECK_fwrite(Zeroes, sizeof(char), Padding, File);
|
||||
#undef CHECK_fwrite
|
||||
|
||||
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) {
|
||||
|
@ -57,19 +82,28 @@ static int writeFileWithName(const char *OutputName) {
|
|||
if (!OutputFile)
|
||||
return -1;
|
||||
|
||||
RetVal = writeFile(OutputFile);
|
||||
__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);
|
||||
|
||||
fclose(OutputFile);
|
||||
return RetVal;
|
||||
}
|
||||
|
||||
static const char *CurrentFilename = NULL;
|
||||
void __llvm_profile_set_filename(const char *Filename) {
|
||||
CurrentFilename = Filename;
|
||||
__attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL;
|
||||
__attribute__((weak)) void __llvm_profile_set_filename(const char *Filename) {
|
||||
__llvm_profile_CurrentFilename = Filename;
|
||||
}
|
||||
|
||||
int getpid(void);
|
||||
int __llvm_profile_write_file(void) {
|
||||
__attribute__((weak)) int __llvm_profile_write_file(void) {
|
||||
char *AllocatedFilename = NULL;
|
||||
int I, J;
|
||||
int RetVal;
|
||||
|
@ -80,7 +114,7 @@ int __llvm_profile_write_file(void) {
|
|||
int NumPids = 0;
|
||||
|
||||
/* Get the filename. */
|
||||
const char *Filename = CurrentFilename;
|
||||
const char *Filename = __llvm_profile_CurrentFilename;
|
||||
#define UPDATE_FILENAME(NextFilename) \
|
||||
if (!Filename || !Filename[0]) Filename = NextFilename
|
||||
UPDATE_FILENAME(getenv("LLVM_PROFILE_FILE"));
|
||||
|
@ -131,7 +165,7 @@ static void writeFileWithoutReturn(void) {
|
|||
__llvm_profile_write_file();
|
||||
}
|
||||
|
||||
int __llvm_profile_register_write_file_atexit(void) {
|
||||
__attribute__((weak)) int __llvm_profile_register_write_file_atexit(void) {
|
||||
static int HasBeenRegistered = 0;
|
||||
|
||||
if (HasBeenRegistered)
|
||||
|
|
|
@ -11,21 +11,33 @@
|
|||
|
||||
#if defined(__APPLE__)
|
||||
/* Use linker magic to find the bounds of the Data section. */
|
||||
__attribute__((visibility("hidden")))
|
||||
extern __llvm_profile_data DataStart __asm("section$start$__DATA$__llvm_prf_data");
|
||||
__attribute__((visibility("hidden")))
|
||||
extern __llvm_profile_data DataEnd __asm("section$end$__DATA$__llvm_prf_data");
|
||||
__attribute__((visibility("hidden")))
|
||||
extern char NamesStart __asm("section$start$__DATA$__llvm_prf_names");
|
||||
__attribute__((visibility("hidden")))
|
||||
extern char NamesEnd __asm("section$end$__DATA$__llvm_prf_names");
|
||||
__attribute__((visibility("hidden")))
|
||||
extern uint64_t CountersStart __asm("section$start$__DATA$__llvm_prf_cnts");
|
||||
__attribute__((visibility("hidden")))
|
||||
extern uint64_t CountersEnd __asm("section$end$__DATA$__llvm_prf_cnts");
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
const __llvm_profile_data *__llvm_profile_data_begin(void) {
|
||||
return &DataStart;
|
||||
}
|
||||
__attribute__((visibility("hidden")))
|
||||
const __llvm_profile_data *__llvm_profile_data_end(void) {
|
||||
return &DataEnd;
|
||||
}
|
||||
__attribute__((visibility("hidden")))
|
||||
const char *__llvm_profile_names_begin(void) { return &NamesStart; }
|
||||
__attribute__((visibility("hidden")))
|
||||
const char *__llvm_profile_names_end(void) { return &NamesEnd; }
|
||||
__attribute__((visibility("hidden")))
|
||||
uint64_t *__llvm_profile_counters_begin(void) { return &CountersStart; }
|
||||
__attribute__((visibility("hidden")))
|
||||
uint64_t *__llvm_profile_counters_end(void) { return &CountersEnd; }
|
||||
#endif
|
||||
|
|
|
@ -26,6 +26,7 @@ static uint64_t *CountersLast = NULL;
|
|||
* calls are only required (and only emitted) on targets where we haven't
|
||||
* implemented linker magic to find the bounds of the sections.
|
||||
*/
|
||||
__attribute__((visibility("hidden")))
|
||||
void __llvm_profile_register_function(void *Data_) {
|
||||
/* TODO: Only emit this function if we can't use linker magic. */
|
||||
const __llvm_profile_data *Data = (__llvm_profile_data*)Data_;
|
||||
|
@ -54,14 +55,20 @@ void __llvm_profile_register_function(void *Data_) {
|
|||
#undef UPDATE_LAST
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden")))
|
||||
const __llvm_profile_data *__llvm_profile_data_begin(void) {
|
||||
return DataFirst;
|
||||
}
|
||||
__attribute__((visibility("hidden")))
|
||||
const __llvm_profile_data *__llvm_profile_data_end(void) {
|
||||
return DataLast;
|
||||
}
|
||||
__attribute__((visibility("hidden")))
|
||||
const char *__llvm_profile_names_begin(void) { return NamesFirst; }
|
||||
__attribute__((visibility("hidden")))
|
||||
const char *__llvm_profile_names_end(void) { return NamesLast; }
|
||||
__attribute__((visibility("hidden")))
|
||||
uint64_t *__llvm_profile_counters_begin(void) { return CountersFirst; }
|
||||
__attribute__((visibility("hidden")))
|
||||
uint64_t *__llvm_profile_counters_end(void) { return CountersLast; }
|
||||
#endif
|
||||
|
|
|
@ -11,17 +11,20 @@ extern "C" {
|
|||
|
||||
#include "InstrProfiling.h"
|
||||
|
||||
int __llvm_profile_runtime;
|
||||
__attribute__((visibility("hidden"))) int __llvm_profile_runtime;
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class RegisterAtExit {
|
||||
class RegisterRuntime {
|
||||
public:
|
||||
RegisterAtExit() { __llvm_profile_register_write_file_atexit(); }
|
||||
RegisterRuntime() {
|
||||
__llvm_profile_register_write_file_atexit();
|
||||
__llvm_profile_register_write_file();
|
||||
}
|
||||
};
|
||||
|
||||
RegisterAtExit Registration;
|
||||
RegisterRuntime Registration;
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#include "instrprof-dynamic-header.h"
|
||||
void a() {
|
||||
if (true) {
|
||||
bar<void>();
|
||||
bar<char>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include "instrprof-dynamic-header.h"
|
||||
void b() {
|
||||
if (true) {
|
||||
bar<void>();
|
||||
bar<int>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
template <class T> void bar() {
|
||||
if (true) {}
|
||||
}
|
||||
void a();
|
||||
void b();
|
|
@ -0,0 +1,9 @@
|
|||
#include "instrprof-dynamic-header.h"
|
||||
void foo(int K) { if (K) {} }
|
||||
int main(int argc, char *argv[]) {
|
||||
foo(5);
|
||||
bar<void>();
|
||||
a();
|
||||
b();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
RUN: mkdir -p %t.d
|
||||
RUN: %clang_profgen -o %t.d/a.shared -fPIC -shared %S/Inputs/instrprof-dynamic-a.cpp
|
||||
RUN: %clang_profgen -o %t-shared -fPIC -rpath %t.d %t.d/a.shared %S/Inputs/instrprof-dynamic-b.cpp %S/Inputs/instrprof-dynamic-main.cpp
|
||||
|
||||
RUN: %clang_profgen -o %t-static %S/Inputs/instrprof-dynamic-a.cpp %S/Inputs/instrprof-dynamic-b.cpp %S/Inputs/instrprof-dynamic-main.cpp
|
||||
|
||||
RUN: env LLVM_PROFILE_FILE=%t-static.profraw %run %t-static
|
||||
RUN: env LLVM_PROFILE_FILE=%t-shared.profraw %run %t-shared
|
||||
|
||||
RUN: llvm-profdata merge -o %t-static.profdata %t-static.profraw
|
||||
RUN: llvm-profdata merge -o %t-shared.profdata %t-shared.profraw
|
||||
|
||||
RUN: %clang_profuse=%t-static.profdata -o %t-a.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-a.cpp
|
||||
RUN: %clang_profuse=%t-shared.profdata -o %t-a.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-a.cpp
|
||||
RUN: diff %t-a.static.ll %t-a.shared.ll
|
||||
|
||||
RUN: %clang_profuse=%t-static.profdata -o %t-b.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-b.cpp
|
||||
RUN: %clang_profuse=%t-shared.profdata -o %t-b.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-b.cpp
|
||||
RUN: diff %t-b.static.ll %t-b.shared.ll
|
||||
|
||||
RUN: %clang_profuse=%t-static.profdata -o %t-main.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-main.cpp
|
||||
RUN: %clang_profuse=%t-shared.profdata -o %t-main.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-main.cpp
|
||||
RUN: diff %t-main.static.ll %t-main.shared.ll
|
|
@ -0,0 +1,24 @@
|
|||
RUN: mkdir -p %t.d
|
||||
RUN: %clang_profgen -o %t.d/a.shared -fPIC -shared %S/Inputs/instrprof-dynamic-a.cpp
|
||||
RUN: %clang_profgen -o %t.d/b.shared -fPIC -shared %S/Inputs/instrprof-dynamic-b.cpp
|
||||
RUN: %clang_profgen -o %t-shared -fPIC -rpath %t.d %t.d/a.shared %t.d/b.shared %S/Inputs/instrprof-dynamic-main.cpp
|
||||
|
||||
RUN: %clang_profgen -o %t-static %S/Inputs/instrprof-dynamic-a.cpp %S/Inputs/instrprof-dynamic-b.cpp %S/Inputs/instrprof-dynamic-main.cpp
|
||||
|
||||
RUN: env LLVM_PROFILE_FILE=%t-static.profraw %run %t-static
|
||||
RUN: env LLVM_PROFILE_FILE=%t-shared.profraw %run %t-shared
|
||||
|
||||
RUN: llvm-profdata merge -o %t-static.profdata %t-static.profraw
|
||||
RUN: llvm-profdata merge -o %t-shared.profdata %t-shared.profraw
|
||||
|
||||
RUN: %clang_profuse=%t-static.profdata -o %t-a.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-a.cpp
|
||||
RUN: %clang_profuse=%t-shared.profdata -o %t-a.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-a.cpp
|
||||
RUN: diff %t-a.static.ll %t-a.shared.ll
|
||||
|
||||
RUN: %clang_profuse=%t-static.profdata -o %t-b.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-b.cpp
|
||||
RUN: %clang_profuse=%t-shared.profdata -o %t-b.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-b.cpp
|
||||
RUN: diff %t-b.static.ll %t-b.shared.ll
|
||||
|
||||
RUN: %clang_profuse=%t-static.profdata -o %t-main.static.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-main.cpp
|
||||
RUN: %clang_profuse=%t-shared.profdata -o %t-main.shared.ll -S -emit-llvm %S/Inputs/instrprof-dynamic-main.cpp
|
||||
RUN: diff %t-main.static.ll %t-main.shared.ll
|
|
@ -27,6 +27,9 @@ if config.test_exec_root is None:
|
|||
# Test suffixes.
|
||||
config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm', '.ll', '.test']
|
||||
|
||||
# What to exclude.
|
||||
config.excludes = ['Inputs']
|
||||
|
||||
# Clang flags.
|
||||
clang_cflags = [config.target_cflags]
|
||||
|
||||
|
|
Loading…
Reference in New Issue