diff --git a/compiler-rt/include/profile/InstrProfData.inc b/compiler-rt/include/profile/InstrProfData.inc --- a/compiler-rt/include/profile/InstrProfData.inc +++ b/compiler-rt/include/profile/InstrProfData.inc @@ -129,6 +129,7 @@ #endif INSTR_PROF_RAW_HEADER(uint64_t, Magic, __llvm_profile_get_magic()) INSTR_PROF_RAW_HEADER(uint64_t, Version, __llvm_profile_get_version()) +INSTR_PROF_RAW_HEADER(uint64_t, HasBuildId, 0) // TODO: Can use a bool? INSTR_PROF_RAW_HEADER(uint64_t, DataSize, DataSize) INSTR_PROF_RAW_HEADER(uint64_t, PaddingBytesBeforeCounters, PaddingBytesBeforeCounters) INSTR_PROF_RAW_HEADER(uint64_t, CountersSize, CountersSize) diff --git a/compiler-rt/lib/profile/InstrProfilingWriter.c b/compiler-rt/lib/profile/InstrProfilingWriter.c --- a/compiler-rt/lib/profile/InstrProfilingWriter.c +++ b/compiler-rt/lib/profile/InstrProfilingWriter.c @@ -18,6 +18,9 @@ #include "InstrProfiling.h" #include "InstrProfilingInternal.h" #include "InstrProfilingPort.h" +#include +#include +#include #define INSTR_PROF_VALUE_PROF_DATA #include "profile/InstrProfData.inc" @@ -32,6 +35,12 @@ COMPILER_RT_VISIBILITY uint8_t *DynamicBufferIOBuffer = 0; COMPILER_RT_VISIBILITY uint32_t VPBufferSize = 0; +/* Variable-length structure that consists of build id size and data. */ +struct BuildId { + uint32_t Size; + uint8_t *Data; +}; + /* The buffer writer is reponsponsible in keeping writer state * across the call. */ @@ -253,6 +262,55 @@ SkipNameDataWrite); } +static size_t RoundUp(size_t size, size_t align) { + return (size + align - 1) & ~(align - 1); +} + +/* If Build ID exists, return a BuildId struct that contains its size and data. + * Otherwise, return null. + */ +static const struct BuildId *readBuildId() { + 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 note 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 = Note + ProgramHeader[i].p_filesz; + + while (Note < NotesEnd) { + /* Look for the NT_GNU_BUILD_ID type in note section. */ + if (Note->n_type != NT_GNU_BUILD_ID) { + Note = Note + sizeof(ElfW(Nhdr)) + RoundUp(Note->n_namesz, 4) + + RoundUp(Note->n_descsz, 4); + continue; + } + + /* Build ID is not fixed size, so create a struct holding size and data. + * TODO: Can COMPILER_RT_ALLOCA be used for malloc? + */ + struct BuildId *BI = malloc(sizeof(struct BuildId)); + BI->Size = Note->n_descsz; + BI->Data = (uint8_t *)((uintptr_t)Note + sizeof(ElfW(Nhdr)) + + Note->n_namesz); + return BI; + } + + /* If Build ID does not exist, stop reading entries in program header. */ + break; + } + + return NULL; +} + COMPILER_RT_VISIBILITY int lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, @@ -283,6 +341,11 @@ #define INSTR_PROF_RAW_HEADER(Type, Name, Init) Header.Name = Init; #include "profile/InstrProfData.inc" + const struct BuildId *BI = readBuildId(); + /* If Build ID exists, set HasBuildId flag in the header */ + if (BI) + Header.HasBuildId = 1; + /* Write the data. */ ProfDataIOVec IOVec[] = { {&Header, sizeof(__llvm_profile_header), 1, 0}, @@ -299,5 +362,16 @@ if (__llvm_profile_is_continuous_mode_enabled()) return 0; - return writeValueProfData(Writer, VPDataReader, DataBegin, DataEnd); + if (writeValueProfData(Writer, VPDataReader, DataBegin, DataEnd) != 0) + return -1; + + /* If Build ID does not exist, do not write anything. */ + if (!BI) + return 0; + + /* Write build id size and data. */ + ProfDataIOVec BuildIdIOVec[] = { + {&BI->Size, sizeof(uint32_t), 1, 0}, + {BI->Data, sizeof(uint8_t), BI->Size, 0}}; + return Writer->Write(Writer, BuildIdIOVec, sizeof(BuildIdIOVec) / sizeof(*BuildIdIOVec)); } diff --git a/llvm/include/llvm/ProfileData/InstrProfData.inc b/llvm/include/llvm/ProfileData/InstrProfData.inc --- a/llvm/include/llvm/ProfileData/InstrProfData.inc +++ b/llvm/include/llvm/ProfileData/InstrProfData.inc @@ -129,6 +129,7 @@ #endif INSTR_PROF_RAW_HEADER(uint64_t, Magic, __llvm_profile_get_magic()) INSTR_PROF_RAW_HEADER(uint64_t, Version, __llvm_profile_get_version()) +INSTR_PROF_RAW_HEADER(uint64_t, HasBuildId, 0) // TODO: Can use a bool? INSTR_PROF_RAW_HEADER(uint64_t, DataSize, DataSize) INSTR_PROF_RAW_HEADER(uint64_t, PaddingBytesBeforeCounters, PaddingBytesBeforeCounters) INSTR_PROF_RAW_HEADER(uint64_t, CountersSize, CountersSize) diff --git a/llvm/include/llvm/ProfileData/InstrProfReader.h b/llvm/include/llvm/ProfileData/InstrProfReader.h --- a/llvm/include/llvm/ProfileData/InstrProfReader.h +++ b/llvm/include/llvm/ProfileData/InstrProfReader.h @@ -82,6 +82,14 @@ /// Read a single record. virtual Error readNextRecord(NamedInstrProfRecord &Record) = 0; + /// Read build id. + /// TODO: Consider implementing it as a pure virtual function, + /// and override it every subclass. + virtual Error readBuildId() { return success(); } + + /// Print build id on stream OS. + virtual void printBuildId(raw_ostream &OS){}; + /// Iterator over profile data. InstrProfIterator begin() { return InstrProfIterator(this); } InstrProfIterator end() { return InstrProfIterator(); } @@ -222,6 +230,10 @@ uint32_t ValueKindLast; uint32_t CurValueDataSize; + uint64_t HasBuildId; // TODO: Can use a bool? + uint32_t BuildIdSize; + const uint8_t *BuildId; + public: RawInstrProfReader(std::unique_ptr DataBuffer) : DataBuffer(std::move(DataBuffer)) {} @@ -231,6 +243,8 @@ static bool hasFormat(const MemoryBuffer &DataBuffer); Error readHeader() override; Error readNextRecord(NamedInstrProfRecord &Record) override; + Error readBuildId() override; + void printBuildId(raw_ostream &OS) override; bool isIRLevelProfile() const override { return (Version & VARIANT_MASK_IR_PROF) != 0; diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp --- a/llvm/lib/ProfileData/InstrProfReader.cpp +++ b/llvm/lib/ProfileData/InstrProfReader.cpp @@ -24,13 +24,16 @@ #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/SymbolRemappingReader.h" #include "llvm/Support/SwapByteOrder.h" +#include "llvm/Support/SymbolRemappingReader.h" #include #include #include #include +#include +#include #include +#include #include #include #include @@ -366,6 +369,7 @@ if (GET_VERSION(Version) != RawInstrProf::Version) return error(instrprof_error::unsupported_version); + HasBuildId = swap(Header.HasBuildId); CountersDelta = swap(Header.CountersDelta); NamesDelta = swap(Header.NamesDelta); auto DataSize = swap(Header.DataSize); @@ -480,10 +484,16 @@ template Error RawInstrProfReader::readNextRecord(NamedInstrProfRecord &Record) { - if (atEnd()) + if (atEnd()) { + // Read build id that starts after record. + // TODO: In which cases there are multiple headers? + if (Error E = readBuildId()) + return error(std::move(E)); + // At this point, ValueDataStart field points to the next header. if (Error E = readNextHeader(getNextHeaderPos())) return error(std::move(E)); + } // Read name ad set it in Record. if (Error E = readName(Record)) @@ -506,6 +516,36 @@ return success(); } +template Error RawInstrProfReader::readBuildId() { + if (HasBuildId == 0) + return success(); + + // Read build id size. + const uint32_t *BuildIdBuffer = + reinterpret_cast(ValueDataStart); + BuildIdSize = *BuildIdBuffer; + BuildIdBuffer++; + + // Read build id data. + BuildId = reinterpret_cast(BuildIdBuffer); + // Increment next profile start by build id size and data. + ValueDataStart += sizeof(BuildIdSize); + ValueDataStart += BuildIdSize; + + return success(); +} + +template +void RawInstrProfReader::printBuildId(raw_ostream &OS) { + if (HasBuildId == 0) + return; + + OS << "Build ID: "; + for (uint32_t I = 0; I < BuildIdSize; I++) + OS.write_hex(BuildId[I]); + OS << "\n"; +} + namespace llvm { template class RawInstrProfReader; diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -2222,6 +2222,9 @@ OS << "Total count: " << PS->getTotalCount() << "\n"; PS->printDetailedSummary(OS); } + + // This is only for testing build id prototype. + Reader->printBuildId(OS); return 0; }