ProfileData: Allow multiple profiles in RawInstrProfReader

Allow multiple raw profiles to coexist in a single .profraw file,
given the following conditions:

- Zero padding at the end of or between profiles will be skipped.
- Each profile must start with a valid header.
- Mixing endianness or pointer sizes in concatenated profiles files is
  not allowed.

This is needed to handle cases where a program's shared libraries are
profiled as well as the main executable itself, as we'll need to emit
each executable's counters. Combining the tables in the runtime would
be expensive for the instrumented program.

rdar://16918688

llvm-svn: 208938
This commit is contained in:
Justin Bogner 2014-05-16 00:38:00 +00:00
parent c21d3d5f90
commit a119f32344
3 changed files with 95 additions and 4 deletions

View File

@ -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 <class IntT>
IntT swap(IntT Int) const {

View File

@ -172,6 +172,29 @@ error_code RawInstrProfReader<IntPtrT>::readHeader() {
return readHeader(*Header);
}
template <class IntPtrT>
error_code RawInstrProfReader<IntPtrT>::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<const uint64_t *>(CurrentPos);
if (Magic != swap(getRawMagic<IntPtrT>()))
return instrprof_error::bad_magic;
// There's another profile to read, so we need to process the header.
auto *Header = reinterpret_cast<const RawHeader *>(CurrentPos);
return readHeader(*Header);
}
static uint64_t getRawVersion() {
return 1;
}
@ -190,16 +213,17 @@ error_code RawInstrProfReader<IntPtrT>::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<const char *>(&Header);
if (Start + ProfileSize > DataBuffer->getBufferEnd())
return error(instrprof_error::bad_header);
const char *Start = DataBuffer->getBufferStart();
Data = reinterpret_cast<const ProfileData *>(Start + DataOffset);
DataEnd = Data + DataSize;
CountersStart = reinterpret_cast<const uint64_t *>(Start + CountersOffset);
NamesStart = Start + NamesOffset;
ProfileEnd = Start + ProfileSize;
return success();
}
@ -208,7 +232,8 @@ template <class IntPtrT>
error_code
RawInstrProfReader<IntPtrT>::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));

View File

@ -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