diff --git a/include/llvm/ProfileData/InstrProfReader.h b/include/llvm/ProfileData/InstrProfReader.h index 3bb482e20f3..3e18c76c57d 100644 --- a/include/llvm/ProfileData/InstrProfReader.h +++ b/include/llvm/ProfileData/InstrProfReader.h @@ -165,6 +165,7 @@ private: const ProfileData *DataEnd; const uint64_t *CountersStart; const char *NamesStart; + const char *ProfileEnd; RawInstrProfReader(const TextInstrProfReader &) LLVM_DELETED_FUNCTION; RawInstrProfReader &operator=(const TextInstrProfReader &) @@ -178,6 +179,7 @@ public: error_code readNextRecord(InstrProfRecord &Record) override; private: + error_code readNextHeader(const char *CurrentPos); error_code readHeader(const RawHeader &Header); template IntT swap(IntT Int) const { diff --git a/lib/ProfileData/InstrProfReader.cpp b/lib/ProfileData/InstrProfReader.cpp index 2ab0eb9449c..7014f5e5cc8 100644 --- a/lib/ProfileData/InstrProfReader.cpp +++ b/lib/ProfileData/InstrProfReader.cpp @@ -172,6 +172,29 @@ error_code RawInstrProfReader::readHeader() { return readHeader(*Header); } +template +error_code RawInstrProfReader::readNextHeader(const char *CurrentPos) { + const char *End = DataBuffer->getBufferEnd(); + // Skip zero padding between profiles. + while (CurrentPos != End && *CurrentPos == 0) + ++CurrentPos; + // If there's nothing left, we're done. + if (CurrentPos == End) + return instrprof_error::eof; + // If there isn't enough space for another header, this is probably just + // garbage at the end of the file. + if (CurrentPos + sizeof(RawHeader) > End) + return instrprof_error::malformed; + // The magic should have the same byte order as in the previous header. + uint64_t Magic = *reinterpret_cast(CurrentPos); + if (Magic != swap(getRawMagic())) + return instrprof_error::bad_magic; + + // There's another profile to read, so we need to process the header. + auto *Header = reinterpret_cast(CurrentPos); + return readHeader(*Header); +} + static uint64_t getRawVersion() { return 1; } @@ -190,16 +213,17 @@ error_code RawInstrProfReader::readHeader(const RawHeader &Header) { ptrdiff_t DataOffset = sizeof(RawHeader); ptrdiff_t CountersOffset = DataOffset + sizeof(ProfileData) * DataSize; ptrdiff_t NamesOffset = CountersOffset + sizeof(uint64_t) * CountersSize; - size_t FileSize = NamesOffset + sizeof(char) * NamesSize; + size_t ProfileSize = NamesOffset + sizeof(char) * NamesSize; - if (FileSize != DataBuffer->getBufferSize()) + auto *Start = reinterpret_cast(&Header); + if (Start + ProfileSize > DataBuffer->getBufferEnd()) return error(instrprof_error::bad_header); - const char *Start = DataBuffer->getBufferStart(); Data = reinterpret_cast(Start + DataOffset); DataEnd = Data + DataSize; CountersStart = reinterpret_cast(Start + CountersOffset); NamesStart = Start + NamesOffset; + ProfileEnd = Start + ProfileSize; return success(); } @@ -208,7 +232,8 @@ template error_code RawInstrProfReader::readNextRecord(InstrProfRecord &Record) { if (Data == DataEnd) - return error(instrprof_error::eof); + if (error_code EC = readNextHeader(ProfileEnd)) + return EC; // Get the raw data. StringRef RawName(getName(Data->NamePtr), swap(Data->NameSize)); diff --git a/test/tools/llvm-profdata/raw-two-profiles.test b/test/tools/llvm-profdata/raw-two-profiles.test new file mode 100644 index 00000000000..3260836ba66 --- /dev/null +++ b/test/tools/llvm-profdata/raw-two-profiles.test @@ -0,0 +1,64 @@ +RUN: printf '\201rforpl\377' > %t-foo.profraw +RUN: printf '\1\0\0\0\0\0\0\0' >> %t-foo.profraw +RUN: printf '\1\0\0\0\0\0\0\0' >> %t-foo.profraw +RUN: printf '\1\0\0\0\0\0\0\0' >> %t-foo.profraw +RUN: printf '\3\0\0\0\0\0\0\0' >> %t-foo.profraw +RUN: printf '\0\0\4\0\1\0\0\0' >> %t-foo.profraw +RUN: printf '\0\0\4\0\2\0\0\0' >> %t-foo.profraw + +RUN: printf '\3\0\0\0' >> %t-foo.profraw +RUN: printf '\1\0\0\0' >> %t-foo.profraw +RUN: printf '\1\0\0\0\0\0\0\0' >> %t-foo.profraw +RUN: printf '\0\0\4\0\2\0\0\0' >> %t-foo.profraw +RUN: printf '\0\0\4\0\1\0\0\0' >> %t-foo.profraw + +RUN: printf '\023\0\0\0\0\0\0\0' >> %t-foo.profraw +RUN: printf 'foo' >> %t-foo.profraw + +RUN: printf '\201rforpl\377' > %t-bar.profraw +RUN: printf '\1\0\0\0\0\0\0\0' >> %t-bar.profraw +RUN: printf '\1\0\0\0\0\0\0\0' >> %t-bar.profraw +RUN: printf '\2\0\0\0\0\0\0\0' >> %t-bar.profraw +RUN: printf '\3\0\0\0\0\0\0\0' >> %t-bar.profraw +RUN: printf '\0\0\6\0\1\0\0\0' >> %t-bar.profraw +RUN: printf '\0\0\6\0\2\0\0\0' >> %t-bar.profraw + +RUN: printf '\3\0\0\0' >> %t-bar.profraw +RUN: printf '\2\0\0\0' >> %t-bar.profraw +RUN: printf '\2\0\0\0\0\0\0\0' >> %t-bar.profraw +RUN: printf '\0\0\6\0\2\0\0\0' >> %t-bar.profraw +RUN: printf '\0\0\6\0\1\0\0\0' >> %t-bar.profraw + +RUN: printf '\067\0\0\0\0\0\0\0' >> %t-bar.profraw +RUN: printf '\101\0\0\0\0\0\0\0' >> %t-bar.profraw +RUN: printf 'bar' >> %t-bar.profraw + +Versions of the profiles that are padded to eight byte alignment. +RUN: cat %t-foo.profraw > %t-foo-padded.profraw +RUN: printf '\0\0\0\0\0' >> %t-foo-padded.profraw +RUN: cat %t-bar.profraw > %t-bar-padded.profraw +RUN: printf '\0\0\0\0\0' >> %t-bar-padded.profraw + +RUN: cat %t-foo.profraw %t-bar.profraw > %t-nopad.profraw +RUN: cat %t-foo-padded.profraw %t-bar.profraw > %t-pad-between.profraw +RUN: cat %t-foo-padded.profraw %t-bar-padded.profraw > %t-pad.profraw + +RUN: llvm-profdata show %t-nopad.profraw -all-functions -counts | FileCheck %s +RUN: llvm-profdata show %t-pad-between.profraw -all-functions -counts | FileCheck %s +RUN: llvm-profdata show %t-pad.profraw -all-functions -counts | FileCheck %s + +CHECK: Counters: +CHECK: foo: +CHECK: Hash: 0x0000000000000001 +CHECK: Counters: 1 +CHECK: Function count: 19 +CHECK: Block counts: [] +CHECK: bar: +CHECK: Hash: 0x0000000000000002 +CHECK: Counters: 2 +CHECK: Function count: 55 +CHECK: Block counts: [65] +CHECK: Functions shown: 2 +CHECK: Total functions: 2 +CHECK: Maximum function count: 55 +CHECK: Maximum internal block count: 65