ProfileData: Allow multiple profiles in RawInstrProfReader
authorJustin Bogner <mail@justinbogner.com>
Fri, 16 May 2014 00:38:00 +0000 (00:38 +0000)
committerJustin Bogner <mail@justinbogner.com>
Fri, 16 May 2014 00:38:00 +0000 (00:38 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@208938 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/ProfileData/InstrProfReader.h
lib/ProfileData/InstrProfReader.cpp
test/tools/llvm-profdata/raw-two-profiles.test [new file with mode: 0644]

index 3bb482e20f3d0e57a20f92099a4cb0d769302415..3e18c76c57df5bd8519e7e681fd664dfe292a0e9 100644 (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 {
index 2ab0eb9449c60864b554101771734cf64d88e509..7014f5e5cc8c834464ee70e604b74becbc89219d 100644 (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));
diff --git a/test/tools/llvm-profdata/raw-two-profiles.test b/test/tools/llvm-profdata/raw-two-profiles.test
new file mode 100644 (file)
index 0000000..3260836
--- /dev/null
@@ -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