diff --git a/include/llvm/ProfileData/InstrProfReader.h b/include/llvm/ProfileData/InstrProfReader.h index a45318b0a9b..1c7c5ced82c 100644 --- a/include/llvm/ProfileData/InstrProfReader.h +++ b/include/llvm/ProfileData/InstrProfReader.h @@ -19,6 +19,7 @@ #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Endian.h" #include @@ -59,20 +60,25 @@ public: InstrProfReader() : LastError(instrprof_error::success) {} virtual ~InstrProfReader() {} + /// Read the header. Required before reading first record. + virtual error_code readHeader() = 0; /// Read a single record. virtual error_code readNextRecord(InstrProfRecord &Record) = 0; /// Iterator over profile data. InstrProfIterator begin() { return InstrProfIterator(this); } InstrProfIterator end() { return InstrProfIterator(); } +protected: /// Set the current error_code and return same. error_code error(error_code EC) { LastError = EC; return EC; } + /// Clear the current error code and return a successful one. error_code success() { return error(instrprof_error::success); } +public: /// Return true if the reader has finished reading the profile data. bool isEOF() { return LastError == instrprof_error::eof; } /// Return true if the reader encountered an error reading profiling data. @@ -110,10 +116,72 @@ public: TextInstrProfReader(std::unique_ptr &DataBuffer_) : DataBuffer(DataBuffer_.release()), Line(*DataBuffer, '#') {} + /// Read the header. + error_code readHeader() override { return success(); } /// Read a single record. error_code readNextRecord(InstrProfRecord &Record) override; }; +/// Reader for the raw instrprof binary format from runtime. +/// +/// This format is a raw memory dump of the instrumentation-baed profiling data +/// from the runtime. It has no index. +class RawInstrProfReader : public InstrProfReader { +private: + /// The profile data file contents. + std::unique_ptr DataBuffer; + /// The current set of counter values. + std::vector Counts; + struct ProfileData { + const uint32_t NameSize; + const uint32_t NumCounters; + const uint64_t FuncHash; + const uint64_t NamePtr; + const uint64_t CounterPtr; + }; + struct RawHeader { + const uint64_t Magic; + const uint64_t Version; + const uint64_t DataSize; + const uint64_t CountersSize; + const uint64_t NamesSize; + const uint64_t CountersDelta; + const uint64_t NamesDelta; + }; + + bool ShouldSwapBytes; + uint64_t CountersDelta; + uint64_t NamesDelta; + const ProfileData *Data; + const ProfileData *DataEnd; + const uint64_t *CountersStart; + const char *NamesStart; + + RawInstrProfReader(const TextInstrProfReader &) LLVM_DELETED_FUNCTION; + RawInstrProfReader &operator=(const TextInstrProfReader &) + LLVM_DELETED_FUNCTION; +public: + RawInstrProfReader(std::unique_ptr &DataBuffer); + + error_code readHeader() override; + error_code readNextRecord(InstrProfRecord &Record) override; + +private: + error_code readHeader(const RawHeader &Header); + template + IntT swap(IntT Int) const { + return ShouldSwapBytes ? sys::SwapByteOrder(Int) : Int; + } + const uint64_t *getCounter(uint64_t CounterPtr) const { + ptrdiff_t Offset = (swap(CounterPtr) - CountersDelta) / sizeof(uint64_t); + return CountersStart + Offset; + } + const char *getName(uint64_t NamePtr) const { + ptrdiff_t Offset = (swap(NamePtr) - NamesDelta) / sizeof(char); + return NamesStart + Offset; + } +}; + } // end namespace llvm #endif // LLVM_PROFILEDATA_INSTRPROF_READER_H__ diff --git a/lib/ProfileData/InstrProfReader.cpp b/lib/ProfileData/InstrProfReader.cpp index 8b3600090b8..c563355599f 100644 --- a/lib/ProfileData/InstrProfReader.cpp +++ b/lib/ProfileData/InstrProfReader.cpp @@ -14,12 +14,23 @@ #include "llvm/ProfileData/InstrProfReader.h" #include "llvm/ProfileData/InstrProf.h" -#include "llvm/Support/Endian.h" #include using namespace llvm; +static uint64_t getRawMagic() { + return + uint64_t('l') << 56 | + uint64_t('p') << 48 | + uint64_t('r') << 40 | + uint64_t('o') << 32 | + uint64_t('f') << 24 | + uint64_t('r') << 16 | + uint64_t('a') << 8 | + uint64_t('w'); +} + error_code InstrProfReader::create(std::string Path, std::unique_ptr &Result) { std::unique_ptr Buffer; @@ -30,10 +41,19 @@ error_code InstrProfReader::create(std::string Path, if (Buffer->getBufferSize() > std::numeric_limits::max()) return instrprof_error::too_large; - // FIXME: This needs to determine which format the file is and construct the - // correct subclass. - Result.reset(new TextInstrProfReader(Buffer)); + if (Buffer->getBufferSize() < sizeof(uint64_t)) { + Result.reset(new TextInstrProfReader(Buffer)); + Result->readHeader(); + return instrprof_error::success; + } + uint64_t Magic = *(uint64_t *)Buffer->getBufferStart(); + uint64_t SwappedMagic = sys::SwapByteOrder(Magic); + if (Magic == getRawMagic() || SwappedMagic == getRawMagic()) + Result.reset(new RawInstrProfReader(Buffer)); + else + Result.reset(new TextInstrProfReader(Buffer)); + Result->readHeader(); return instrprof_error::success; } @@ -82,3 +102,85 @@ error_code TextInstrProfReader::readNextRecord(InstrProfRecord &Record) { return success(); } + +static uint64_t getRawVersion() { + return 1; +} +namespace { +} +RawInstrProfReader::RawInstrProfReader(std::unique_ptr &DataBuffer) + : DataBuffer(DataBuffer.release()) { } + +error_code RawInstrProfReader::readHeader() { + if (DataBuffer->getBufferSize() < sizeof(RawHeader)) + return error(instrprof_error::malformed); + const RawHeader *Header = (RawHeader *)DataBuffer->getBufferStart(); + if (Header->Magic == getRawMagic()) + ShouldSwapBytes = false; + else { + if (sys::SwapByteOrder(Header->Magic) != getRawMagic()) + return error(instrprof_error::malformed); + + ShouldSwapBytes = true; + } + return readHeader(*Header); +} + +error_code RawInstrProfReader::readHeader(const RawHeader &Header) { + if (swap(Header.Version) != getRawVersion()) + return error(instrprof_error::unsupported_version); + + CountersDelta = swap(Header.CountersDelta); + NamesDelta = swap(Header.NamesDelta); + auto DataSize = swap(Header.DataSize); + auto CountersSize = swap(Header.CountersSize); + auto NamesSize = swap(Header.NamesSize); + + 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; + + if (FileSize != DataBuffer->getBufferSize()) + return error(instrprof_error::malformed); + + Data = (ProfileData *)(DataBuffer->getBufferStart() + DataOffset); + DataEnd = Data + DataSize; + CountersStart = (uint64_t *)(DataBuffer->getBufferStart() + CountersOffset); + NamesStart = DataBuffer->getBufferStart() + NamesOffset; + + return success(); +} + +error_code RawInstrProfReader::readNextRecord(InstrProfRecord &Record) { + if (Data == DataEnd) + return error(instrprof_error::eof); + + // Get the raw data. + StringRef RawName(getName(Data->NamePtr), swap(Data->NameSize)); + auto RawCounts = makeArrayRef(getCounter(Data->CounterPtr), + swap(Data->NumCounters)); + + // Check bounds. + if (RawName.data() < NamesStart || + RawName.data() + RawName.size() > DataBuffer->getBufferEnd() || + RawCounts.data() < CountersStart || + RawCounts.data() + RawCounts.size() > (uint64_t *)NamesStart) + return error(instrprof_error::malformed); + + // Store the data in Record, byte-swapping as necessary. + Record.Hash = swap(Data->FuncHash); + Record.Name = RawName; + if (ShouldSwapBytes) { + Counts.clear(); + Counts.reserve(RawCounts.size()); + for (uint64_t Count : RawCounts) + Counts.push_back(swap(Count)); + Record.Counts = Counts; + } else + Record.Counts = RawCounts; + + // Iterate. + ++Data; + return success(); +} diff --git a/test/tools/llvm-profdata/Inputs/binary-compare.profdata b/test/tools/llvm-profdata/Inputs/binary-compare.profdata new file mode 100644 index 00000000000..a1d56104a21 --- /dev/null +++ b/test/tools/llvm-profdata/Inputs/binary-compare.profdata @@ -0,0 +1,150 @@ +simple_loops +4 +4 +1 +100 +100 +75 + +conditionals +11 +11 +1 +100 +50 +50 +33 +33 +16 +99 +100 +99 +100 + +early_exits +9 +9 +1 +0 +51 +1 +25 +1 +25 +1 +0 + +jumps +22 +22 +1 +1 +0 +1 +0 +0 +1 +0 +1 +2 +3 +2 +0 +3 +0 +1 +1 +1 +10 +0 +10 +9 + +switches +19 +19 +1 +1 +1 +15 +7 +1 +0 +2 +2 +3 +3 +4 +4 +0 +4 +4 +5 +1 +0 + +big_switch +17 +17 +1 +32 +32 +1 +0 +1 +1 +11 +11 +1 +1 +15 +15 +1 +1 +2 +2 + +boolean_operators +8 +8 +1 +100 +34 +66 +17 +34 +33 +50 + +boolop_loops +9 +9 +1 +50 +51 +50 +26 +50 +51 +50 +26 + +do_fallthrough +4 +4 +1 +10 +2 +8 + +main +1 +1 +1 + +c-general.c:static_func +2 +2 +1 +10 + diff --git a/test/tools/llvm-profdata/Inputs/binary.profdata b/test/tools/llvm-profdata/Inputs/binary.profdata new file mode 100644 index 00000000000..156c483d15f Binary files /dev/null and b/test/tools/llvm-profdata/Inputs/binary.profdata differ diff --git a/test/tools/llvm-profdata/binary.test b/test/tools/llvm-profdata/binary.test new file mode 100644 index 00000000000..d3a1c3fae09 --- /dev/null +++ b/test/tools/llvm-profdata/binary.test @@ -0,0 +1,15 @@ +REGENERATE: You need a checkout of clang with compiler-rt to generate the +REGENERATE: binary file here. These shell commands can be used to regenerate +REGENERATE: it. +REGENERATE: +REGENERATE: $ SRC=path/to/llvm +REGENERATE: $ CFE=$SRC/tools/clang +REGENERATE: $ TESTDIR=$SRC/test/tools/llvm-profdata +REGENERATE: $ CFE_TESTDIR=$CFE/test/Profile +REGENERATE: $ clang -o a.out -fprofile-instr-generate $CFE_TESTDIR/test/Profile/c-general.c +REGENERATE: $ LLVM_PROFILE_FILE=$TESTDIR/Inputs/binary.profdata ./a.out +REGENERATE: $ cp $CFE_TESTDIR/Inputs/c-general.profdata $TESTDIR/Inputs/binary-compare.profdata + +RUN: llvm-profdata show %p/Inputs/binary.profdata -o %t1 +RUN: llvm-profdata show %p/Inputs/binary-compare.profdata -o %t2 +RUN: diff -up %t1 %t2