Skip to content

Commit

Permalink
[profile] Add binary id into profiles
Browse files Browse the repository at this point in the history
This patch adds binary id into profiles to easily associate binaries
with the corresponding profiles. There is an RFC that discusses
the motivation, design and implementation in more detail:
https://lists.llvm.org/pipermail/llvm-dev/2021-June/151154.html

Differential Revision: https://reviews.llvm.org/D102039
  • Loading branch information
gulfemsavrun committed Jul 23, 2021
1 parent 120b187 commit e50a388
Show file tree
Hide file tree
Showing 23 changed files with 256 additions and 24 deletions.
3 changes: 2 additions & 1 deletion compiler-rt/include/profile/InstrProfData.inc
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ INSTR_PROF_RAW_HEADER(uint64_t, NamesSize, NamesSize)
INSTR_PROF_RAW_HEADER(uint64_t, CountersDelta, (uintptr_t)CountersBegin)
INSTR_PROF_RAW_HEADER(uint64_t, NamesDelta, (uintptr_t)NamesBegin)
INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last)
INSTR_PROF_RAW_HEADER(uint64_t, BinaryIdsSize, __llvm_write_binary_ids(NULL))
#undef INSTR_PROF_RAW_HEADER
/* INSTR_PROF_RAW_HEADER end */

Expand Down Expand Up @@ -645,7 +646,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
(uint64_t)'f' << 16 | (uint64_t)'R' << 8 | (uint64_t)129

/* Raw profile format version (start from 1). */
#define INSTR_PROF_RAW_VERSION 5
#define INSTR_PROF_RAW_VERSION 6
/* Indexed profile format version (start from 1). */
#define INSTR_PROF_INDEX_VERSION 7
/* Coverage mapping format version (start from 0). */
Expand Down
6 changes: 6 additions & 0 deletions compiler-rt/lib/profile/InstrProfilingInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,10 @@ COMPILER_RT_VISIBILITY extern ValueProfNode *CurrentVNode;
COMPILER_RT_VISIBILITY extern ValueProfNode *EndVNode;
extern void (*VPMergeHook)(struct ValueProfData *, __llvm_profile_data *);

/*
* Write binary ids into profiles if writer is given.
* Return -1 if an error occurs, otherwise, return total size of binary ids.
*/
int __llvm_write_binary_ids(ProfDataWriter *Writer);

#endif
6 changes: 6 additions & 0 deletions compiler-rt/lib/profile/InstrProfilingPlatformDarwin.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// with freestanding compilation. See `darwin_add_builtin_libraries`.

#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"

#if defined(__APPLE__)
/* Use linker magic to find the bounds of the Data section. */
Expand Down Expand Up @@ -67,4 +68,9 @@ ValueProfNode *__llvm_profile_end_vnodes(void) { return &VNodesEnd; }

COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = &VNodesStart;
COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = &VNodesEnd;

COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) {
return 0;
}

#endif
108 changes: 108 additions & 0 deletions compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
#if defined(__linux__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \
(defined(__sun__) && defined(__svr4__)) || defined(__NetBSD__)

#include <elf.h>
#include <link.h>
#include <stdlib.h>
#include <string.h>

#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"

#define PROF_DATA_START INSTR_PROF_SECT_START(INSTR_PROF_DATA_COMMON)
#define PROF_DATA_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_DATA_COMMON)
Expand Down Expand Up @@ -72,4 +76,108 @@ COMPILER_RT_VISIBILITY ValueProfNode *__llvm_profile_end_vnodes(void) {
COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = &PROF_VNODES_START;
COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = &PROF_VNODES_STOP;

static size_t RoundUp(size_t size, size_t align) {
return (size + align - 1) & ~(align - 1);
}

/*
* Write binary id length and then its data, because binary id does not
* have a fixed length.
*/
int WriteOneBinaryId(ProfDataWriter *Writer, uint64_t BinaryIdLen,
const uint8_t *BinaryIdData) {
ProfDataIOVec BinaryIdIOVec[] = {
{&BinaryIdLen, sizeof(uint64_t), 1, 0},
{BinaryIdData, sizeof(uint8_t), BinaryIdLen, 0}};
if (Writer->Write(Writer, BinaryIdIOVec,
sizeof(BinaryIdIOVec) / sizeof(*BinaryIdIOVec)))
return -1;

/* Successfully wrote binary id, report success. */
return 0;
}

/*
* Look for the note that has the name "GNU\0" and type NT_GNU_BUILD_ID
* that contains build id. If build id exists, write binary id.
*
* Each note in notes section starts with a struct which includes
* n_namesz, n_descsz, and n_type members. It is followed by the name
* (whose length is defined in n_namesz) and then by the descriptor
* (whose length is defined in n_descsz).
*
* Note sections like .note.ABI-tag and .note.gnu.build-id are aligned
* to 4 bytes, so round n_namesz and n_descsz to the nearest 4 bytes.
*/
int WriteBinaryIdForNote(ProfDataWriter *Writer, const ElfW(Nhdr) * Note) {
int BinaryIdSize = 0;

const char *NoteName = (const char *)Note + sizeof(ElfW(Nhdr));
if (Note->n_type == NT_GNU_BUILD_ID && Note->n_namesz == 4 &&
memcmp(NoteName, "GNU\0", 4) == 0) {

uint64_t BinaryIdLen = Note->n_descsz;
const uint8_t *BinaryIdData =
(const uint8_t *)(NoteName + RoundUp(Note->n_namesz, 4));
if (Writer != NULL &&
WriteOneBinaryId(Writer, BinaryIdLen, BinaryIdData) == -1)
return -1;

BinaryIdSize = sizeof(BinaryIdLen) + BinaryIdLen;
}

return BinaryIdSize;
}

/*
* Helper function that iterates through notes section and find build ids.
* If writer is given, write binary ids into profiles.
* If an error happens while writing, return -1.
*/
int WriteBinaryIds(ProfDataWriter *Writer, const ElfW(Nhdr) * Note,
const ElfW(Nhdr) * NotesEnd) {
int TotalBinaryIdsSize = 0;
while (Note < NotesEnd) {
int Result = WriteBinaryIdForNote(Writer, Note);
if (Result == -1)
return -1;
TotalBinaryIdsSize += Result;

/* Calculate the offset of the next note in notes section. */
size_t NoteOffset = sizeof(ElfW(Nhdr)) + RoundUp(Note->n_namesz, 4) +
RoundUp(Note->n_descsz, 4);
Note = (const ElfW(Nhdr) *)((const char *)(Note) + NoteOffset);
}

return TotalBinaryIdsSize;
}

/*
* Write binary ids into profiles if writer is given.
* Return the total size of binary ids.
* If an error happens while writing, return -1.
*/
COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) {
extern const ElfW(Ehdr) __ehdr_start __attribute__((visibility("hidden")));
const ElfW(Ehdr) *ElfHeader = &__ehdr_start;
const ElfW(Phdr) *ProgramHeader =
(const ElfW(Phdr) *)((uintptr_t)ElfHeader + ElfHeader->e_phoff);

uint32_t I;
/* Iterate through entries in the program header. */
for (I = 0; I < ElfHeader->e_phnum; I++) {
/* Look for the notes section in program header entries. */
if (ProgramHeader[I].p_type != PT_NOTE)
continue;

const ElfW(Nhdr) *Note =
(const ElfW(Nhdr) *)((uintptr_t)ElfHeader + ProgramHeader[I].p_offset);
const ElfW(Nhdr) *NotesEnd =
(const ElfW(Nhdr) *)((const char *)(Note) + ProgramHeader[I].p_filesz);
return WriteBinaryIds(Writer, Note, NotesEnd);
}

return 0;
}

#endif
5 changes: 5 additions & 0 deletions compiler-rt/lib/profile/InstrProfilingPlatformOther.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <stdio.h>

#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"

static const __llvm_profile_data *DataFirst = NULL;
static const __llvm_profile_data *DataLast = NULL;
Expand Down Expand Up @@ -97,4 +98,8 @@ ValueProfNode *__llvm_profile_end_vnodes(void) { return 0; }
COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = 0;
COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = 0;

COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) {
return 0;
}

#endif
5 changes: 5 additions & 0 deletions compiler-rt/lib/profile/InstrProfilingPlatformWindows.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
\*===----------------------------------------------------------------------===*/

#include "InstrProfiling.h"
#include "InstrProfilingInternal.h"

#if defined(_WIN32)

Expand Down Expand Up @@ -65,4 +66,8 @@ ValueProfNode *__llvm_profile_end_vnodes(void) { return &VNodesEnd; }
ValueProfNode *CurrentVNode = &VNodesStart + 1;
ValueProfNode *EndVNode = &VNodesEnd;

COMPILER_RT_VISIBILITY int __llvm_write_binary_ids(ProfDataWriter *Writer) {
return 0;
}

#endif
16 changes: 12 additions & 4 deletions compiler-rt/lib/profile/InstrProfilingWriter.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,16 +283,24 @@ lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin,
#define INSTR_PROF_RAW_HEADER(Type, Name, Init) Header.Name = Init;
#include "profile/InstrProfData.inc"

/* Write the data. */
ProfDataIOVec IOVec[] = {
{&Header, sizeof(__llvm_profile_header), 1, 0},
/* Write the profile header. */
ProfDataIOVec IOVec[] = {{&Header, sizeof(__llvm_profile_header), 1, 0}};
if (Writer->Write(Writer, IOVec, sizeof(IOVec) / sizeof(*IOVec)))
return -1;

/* Write the binary id lengths and data. */
if (__llvm_write_binary_ids(Writer) == -1)
return -1;

/* Write the profile data. */
ProfDataIOVec IOVecData[] = {
{DataBegin, sizeof(__llvm_profile_data), DataSize, 0},
{NULL, sizeof(uint8_t), PaddingBytesBeforeCounters, 1},
{CountersBegin, sizeof(uint64_t), CountersSize, 0},
{NULL, sizeof(uint8_t), PaddingBytesAfterCounters, 1},
{SkipNameDataWrite ? NULL : NamesBegin, sizeof(uint8_t), NamesSize, 0},
{NULL, sizeof(uint8_t), PaddingBytesAfterNames, 1}};
if (Writer->Write(Writer, IOVec, sizeof(IOVec) / sizeof(*IOVec)))
if (Writer->Write(Writer, IOVecData, sizeof(IOVecData) / sizeof(*IOVecData)))
return -1;

/* Value profiling is not yet supported in continuous mode. */
Expand Down
36 changes: 36 additions & 0 deletions compiler-rt/test/profile/Linux/binary-id.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// REQUIRES: linux
// RUN: %clang_profgen -Wl,--build-id=none -O2 -o %t %s
// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t
// RUN: llvm-profdata show --binary-ids %t.profraw > %t.out
// RUN: FileCheck %s --check-prefix=NO-BINARY-ID < %t.out
// RUN: llvm-profdata merge -o %t.profdata %t.profraw

// RUN: %clang_profgen -Wl,--build-id -O2 -o %t %s
// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t
// RUN: llvm-profdata show --binary-ids %t.profraw > %t.profraw.out
// RUN: FileCheck %s --check-prefix=BINARY-ID-RAW-PROF < %t.profraw.out

void foo() {
}

void bar() {
}

int main() {
foo();
bar();
return 0;
}

// NO-BINARY-ID: Instrumentation level: Front-end
// NO-BINARY-ID-NEXT: Total functions: 3
// NO-BINARY-ID-NEXT: Maximum function count: 1
// NO-BINARY-ID-NEXT: Maximum internal block count: 0
// NO-BINARY-ID-NOT: Binary IDs:

// BINARY-ID-RAW-PROF: Instrumentation level: Front-end
// BINARY-ID-RAW-PROF-NEXT: Total functions: 3
// BINARY-ID-RAW-PROF-NEXT: Maximum function count: 1
// BINARY-ID-RAW-PROF-NEXT: Maximum internal block count: 0
// BINARY-ID-RAW-PROF-NEXT: Binary IDs:
// BINARY-ID-RAW-PROF-NEXT: {{[0-9a-f]+}}
4 changes: 2 additions & 2 deletions compiler-rt/test/profile/Linux/corrupted-profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ int main(int argc, char** argv) {
bail("mmap");

// We're trying to make the first CounterPtr invalid.
// 10 64-bit words as header.
// 11 64-bit words as header.
// CounterPtr is the third 64-bit word field.
memset(&Buf[10 * 8 + 2 * 8], 0xAB, 8);
memset(&Buf[11 * 8 + 2 * 8], 0xAB, 8);

if (munmap(Buf, FileSize))
bail("munmap");
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/ProfileData/InstrProf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,7 @@ namespace RawInstrProf {
// raw header.
// Version 5: Bit 60 of FuncHash is reserved for the flag for the context
// sensitive records.
// Version 6: Added binary id.
const uint64_t Version = INSTR_PROF_RAW_VERSION;

template <class IntPtrT> inline uint64_t getMagic();
Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/ProfileData/InstrProfData.inc
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ INSTR_PROF_RAW_HEADER(uint64_t, NamesSize, NamesSize)
INSTR_PROF_RAW_HEADER(uint64_t, CountersDelta, (uintptr_t)CountersBegin)
INSTR_PROF_RAW_HEADER(uint64_t, NamesDelta, (uintptr_t)NamesBegin)
INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last)
INSTR_PROF_RAW_HEADER(uint64_t, BinaryIdsSize, __llvm_write_binary_ids(NULL))
#undef INSTR_PROF_RAW_HEADER
/* INSTR_PROF_RAW_HEADER end */

Expand Down Expand Up @@ -645,7 +646,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
(uint64_t)'f' << 16 | (uint64_t)'R' << 8 | (uint64_t)129

/* Raw profile format version (start from 1). */
#define INSTR_PROF_RAW_VERSION 5
#define INSTR_PROF_RAW_VERSION 6
/* Indexed profile format version (start from 1). */
#define INSTR_PROF_INDEX_VERSION 7
/* Coverage mapping format version (start from 0). */
Expand Down
7 changes: 7 additions & 0 deletions llvm/include/llvm/ProfileData/InstrProfReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ class InstrProfReader {
/// Read a single record.
virtual Error readNextRecord(NamedInstrProfRecord &Record) = 0;

/// Print binary ids on stream OS.
virtual Error printBinaryIds(raw_ostream &OS) { return success(); };

/// Iterator over profile data.
InstrProfIterator begin() { return InstrProfIterator(this); }
InstrProfIterator end() { return InstrProfIterator(); }
Expand Down Expand Up @@ -222,6 +225,9 @@ class RawInstrProfReader : public InstrProfReader {
uint32_t ValueKindLast;
uint32_t CurValueDataSize;

uint64_t BinaryIdsSize;
const uint8_t *BinaryIdsStart;

public:
RawInstrProfReader(std::unique_ptr<MemoryBuffer> DataBuffer)
: DataBuffer(std::move(DataBuffer)) {}
Expand All @@ -231,6 +237,7 @@ class RawInstrProfReader : public InstrProfReader {
static bool hasFormat(const MemoryBuffer &DataBuffer);
Error readHeader() override;
Error readNextRecord(NamedInstrProfRecord &Record) override;
Error printBinaryIds(raw_ostream &OS) override;

bool isIRLevelProfile() const override {
return (Version & VARIANT_MASK_IR_PROF) != 0;
Expand Down
Loading

0 comments on commit e50a388

Please sign in to comment.