| //===- InstrProfReader.cpp - Instrumented profiling reader ----------------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file contains support for reading profiling data for clang's | |
| // instrumentation based PGO and coverage. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ProfileData/InstrProfReader.h" | |
| #include "llvm/ADT/ArrayRef.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/IR/ProfileSummary.h" | |
| #include "llvm/ProfileData/InstrProf.h" | |
| #include "llvm/ProfileData/ProfileCommon.h" | |
| #include "llvm/Support/Endian.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/ErrorOr.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/SwapByteOrder.h" | |
| #include <algorithm> | |
| #include <cctype> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <limits> | |
| #include <memory> | |
| #include <system_error> | |
| #include <utility> | |
| #include <vector> | |
| using namespace llvm; | |
| static Expected<std::unique_ptr<MemoryBuffer>> | |
| setupMemoryBuffer(const Twine &Path) { | |
| ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = | |
| MemoryBuffer::getFileOrSTDIN(Path); | |
| if (std::error_code EC = BufferOrErr.getError()) | |
| return errorCodeToError(EC); | |
| return std::move(BufferOrErr.get()); | |
| } | |
| static Error initializeReader(InstrProfReader &Reader) { | |
| return Reader.readHeader(); | |
| } | |
| Expected<std::unique_ptr<InstrProfReader>> | |
| InstrProfReader::create(const Twine &Path) { | |
| // Set up the buffer to read. | |
| auto BufferOrError = setupMemoryBuffer(Path); | |
| if (Error E = BufferOrError.takeError()) | |
| return std::move(E); | |
| return InstrProfReader::create(std::move(BufferOrError.get())); | |
| } | |
| Expected<std::unique_ptr<InstrProfReader>> | |
| InstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) { | |
| // Sanity check the buffer. | |
| if (uint64_t(Buffer->getBufferSize()) > std::numeric_limits<unsigned>::max()) | |
| return make_error<InstrProfError>(instrprof_error::too_large); | |
| if (Buffer->getBufferSize() == 0) | |
| return make_error<InstrProfError>(instrprof_error::empty_raw_profile); | |
| std::unique_ptr<InstrProfReader> Result; | |
| // Create the reader. | |
| if (IndexedInstrProfReader::hasFormat(*Buffer)) | |
| Result.reset(new IndexedInstrProfReader(std::move(Buffer))); | |
| else if (RawInstrProfReader64::hasFormat(*Buffer)) | |
| Result.reset(new RawInstrProfReader64(std::move(Buffer))); | |
| else if (RawInstrProfReader32::hasFormat(*Buffer)) | |
| Result.reset(new RawInstrProfReader32(std::move(Buffer))); | |
| else if (TextInstrProfReader::hasFormat(*Buffer)) | |
| Result.reset(new TextInstrProfReader(std::move(Buffer))); | |
| else | |
| return make_error<InstrProfError>(instrprof_error::unrecognized_format); | |
| // Initialize the reader and return the result. | |
| if (Error E = initializeReader(*Result)) | |
| return std::move(E); | |
| return std::move(Result); | |
| } | |
| Expected<std::unique_ptr<IndexedInstrProfReader>> | |
| IndexedInstrProfReader::create(const Twine &Path) { | |
| // Set up the buffer to read. | |
| auto BufferOrError = setupMemoryBuffer(Path); | |
| if (Error E = BufferOrError.takeError()) | |
| return std::move(E); | |
| return IndexedInstrProfReader::create(std::move(BufferOrError.get())); | |
| } | |
| Expected<std::unique_ptr<IndexedInstrProfReader>> | |
| IndexedInstrProfReader::create(std::unique_ptr<MemoryBuffer> Buffer) { | |
| // Sanity check the buffer. | |
| if (uint64_t(Buffer->getBufferSize()) > std::numeric_limits<unsigned>::max()) | |
| return make_error<InstrProfError>(instrprof_error::too_large); | |
| // Create the reader. | |
| if (!IndexedInstrProfReader::hasFormat(*Buffer)) | |
| return make_error<InstrProfError>(instrprof_error::bad_magic); | |
| auto Result = llvm::make_unique<IndexedInstrProfReader>(std::move(Buffer)); | |
| // Initialize the reader and return the result. | |
| if (Error E = initializeReader(*Result)) | |
| return std::move(E); | |
| return std::move(Result); | |
| } | |
| void InstrProfIterator::Increment() { | |
| if (auto E = Reader->readNextRecord(Record)) { | |
| // Handle errors in the reader. | |
| InstrProfError::take(std::move(E)); | |
| *this = InstrProfIterator(); | |
| } | |
| } | |
| bool TextInstrProfReader::hasFormat(const MemoryBuffer &Buffer) { | |
| // Verify that this really looks like plain ASCII text by checking a | |
| // 'reasonable' number of characters (up to profile magic size). | |
| size_t count = std::min(Buffer.getBufferSize(), sizeof(uint64_t)); | |
| StringRef buffer = Buffer.getBufferStart(); | |
| return count == 0 || | |
| std::all_of(buffer.begin(), buffer.begin() + count, | |
| [](char c) { return isPrint(c) || ::isspace(c); }); | |
| } | |
| // Read the profile variant flag from the header: ":FE" means this is a FE | |
| // generated profile. ":IR" means this is an IR level profile. Other strings | |
| // with a leading ':' will be reported an error format. | |
| Error TextInstrProfReader::readHeader() { | |
| Symtab.reset(new InstrProfSymtab()); | |
| bool IsIRInstr = false; | |
| if (!Line->startswith(":")) { | |
| IsIRLevelProfile = false; | |
| return success(); | |
| } | |
| StringRef Str = (Line)->substr(1); | |
| if (Str.equals_lower("ir")) | |
| IsIRInstr = true; | |
| else if (Str.equals_lower("fe")) | |
| IsIRInstr = false; | |
| else | |
| return error(instrprof_error::bad_header); | |
| ++Line; | |
| IsIRLevelProfile = IsIRInstr; | |
| return success(); | |
| } | |
| Error | |
| TextInstrProfReader::readValueProfileData(InstrProfRecord &Record) { | |
| #define CHECK_LINE_END(Line) \ | |
| if (Line.is_at_end()) \ | |
| return error(instrprof_error::truncated); | |
| #define READ_NUM(Str, Dst) \ | |
| if ((Str).getAsInteger(10, (Dst))) \ | |
| return error(instrprof_error::malformed); | |
| #define VP_READ_ADVANCE(Val) \ | |
| CHECK_LINE_END(Line); \ | |
| uint32_t Val; \ | |
| READ_NUM((*Line), (Val)); \ | |
| Line++; | |
| if (Line.is_at_end()) | |
| return success(); | |
| uint32_t NumValueKinds; | |
| if (Line->getAsInteger(10, NumValueKinds)) { | |
| // No value profile data | |
| return success(); | |
| } | |
| if (NumValueKinds == 0 || NumValueKinds > IPVK_Last + 1) | |
| return error(instrprof_error::malformed); | |
| Line++; | |
| for (uint32_t VK = 0; VK < NumValueKinds; VK++) { | |
| VP_READ_ADVANCE(ValueKind); | |
| if (ValueKind > IPVK_Last) | |
| return error(instrprof_error::malformed); | |
| VP_READ_ADVANCE(NumValueSites); | |
| if (!NumValueSites) | |
| continue; | |
| Record.reserveSites(VK, NumValueSites); | |
| for (uint32_t S = 0; S < NumValueSites; S++) { | |
| VP_READ_ADVANCE(NumValueData); | |
| std::vector<InstrProfValueData> CurrentValues; | |
| for (uint32_t V = 0; V < NumValueData; V++) { | |
| CHECK_LINE_END(Line); | |
| std::pair<StringRef, StringRef> VD = Line->rsplit(':'); | |
| uint64_t TakenCount, Value; | |
| if (ValueKind == IPVK_IndirectCallTarget) { | |
| if (InstrProfSymtab::isExternalSymbol(VD.first)) { | |
| Value = 0; | |
| } else { | |
| if (Error E = Symtab->addFuncName(VD.first)) | |
| return E; | |
| Value = IndexedInstrProf::ComputeHash(VD.first); | |
| } | |
| } else { | |
| READ_NUM(VD.first, Value); | |
| } | |
| READ_NUM(VD.second, TakenCount); | |
| CurrentValues.push_back({Value, TakenCount}); | |
| Line++; | |
| } | |
| Record.addValueData(ValueKind, S, CurrentValues.data(), NumValueData, | |
| nullptr); | |
| } | |
| } | |
| return success(); | |
| #undef CHECK_LINE_END | |
| #undef READ_NUM | |
| #undef VP_READ_ADVANCE | |
| } | |
| Error TextInstrProfReader::readNextRecord(NamedInstrProfRecord &Record) { | |
| // Skip empty lines and comments. | |
| while (!Line.is_at_end() && (Line->empty() || Line->startswith("#"))) | |
| ++Line; | |
| // If we hit EOF while looking for a name, we're done. | |
| if (Line.is_at_end()) { | |
| return error(instrprof_error::eof); | |
| } | |
| // Read the function name. | |
| Record.Name = *Line++; | |
| if (Error E = Symtab->addFuncName(Record.Name)) | |
| return error(std::move(E)); | |
| // Read the function hash. | |
| if (Line.is_at_end()) | |
| return error(instrprof_error::truncated); | |
| if ((Line++)->getAsInteger(0, Record.Hash)) | |
| return error(instrprof_error::malformed); | |
| // Read the number of counters. | |
| uint64_t NumCounters; | |
| if (Line.is_at_end()) | |
| return error(instrprof_error::truncated); | |
| if ((Line++)->getAsInteger(10, NumCounters)) | |
| return error(instrprof_error::malformed); | |
| if (NumCounters == 0) | |
| return error(instrprof_error::malformed); | |
| // Read each counter and fill our internal storage with the values. | |
| Record.Clear(); | |
| Record.Counts.reserve(NumCounters); | |
| for (uint64_t I = 0; I < NumCounters; ++I) { | |
| if (Line.is_at_end()) | |
| return error(instrprof_error::truncated); | |
| uint64_t Count; | |
| if ((Line++)->getAsInteger(10, Count)) | |
| return error(instrprof_error::malformed); | |
| Record.Counts.push_back(Count); | |
| } | |
| // Check if value profile data exists and read it if so. | |
| if (Error E = readValueProfileData(Record)) | |
| return error(std::move(E)); | |
| return success(); | |
| } | |
| template <class IntPtrT> | |
| bool RawInstrProfReader<IntPtrT>::hasFormat(const MemoryBuffer &DataBuffer) { | |
| if (DataBuffer.getBufferSize() < sizeof(uint64_t)) | |
| return false; | |
| uint64_t Magic = | |
| *reinterpret_cast<const uint64_t *>(DataBuffer.getBufferStart()); | |
| return RawInstrProf::getMagic<IntPtrT>() == Magic || | |
| sys::getSwappedBytes(RawInstrProf::getMagic<IntPtrT>()) == Magic; | |
| } | |
| template <class IntPtrT> | |
| Error RawInstrProfReader<IntPtrT>::readHeader() { | |
| if (!hasFormat(*DataBuffer)) | |
| return error(instrprof_error::bad_magic); | |
| if (DataBuffer->getBufferSize() < sizeof(RawInstrProf::Header)) | |
| return error(instrprof_error::bad_header); | |
| auto *Header = reinterpret_cast<const RawInstrProf::Header *>( | |
| DataBuffer->getBufferStart()); | |
| ShouldSwapBytes = Header->Magic != RawInstrProf::getMagic<IntPtrT>(); | |
| return readHeader(*Header); | |
| } | |
| template <class IntPtrT> | |
| Error 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 make_error<InstrProfError>(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(RawInstrProf::Header) > End) | |
| return make_error<InstrProfError>(instrprof_error::malformed); | |
| // The writer ensures each profile is padded to start at an aligned address. | |
| if (reinterpret_cast<size_t>(CurrentPos) % alignof(uint64_t)) | |
| return make_error<InstrProfError>(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(RawInstrProf::getMagic<IntPtrT>())) | |
| return make_error<InstrProfError>(instrprof_error::bad_magic); | |
| // There's another profile to read, so we need to process the header. | |
| auto *Header = reinterpret_cast<const RawInstrProf::Header *>(CurrentPos); | |
| return readHeader(*Header); | |
| } | |
| template <class IntPtrT> | |
| Error RawInstrProfReader<IntPtrT>::createSymtab(InstrProfSymtab &Symtab) { | |
| if (Error E = Symtab.create(StringRef(NamesStart, NamesSize))) | |
| return error(std::move(E)); | |
| for (const RawInstrProf::ProfileData<IntPtrT> *I = Data; I != DataEnd; ++I) { | |
| const IntPtrT FPtr = swap(I->FunctionPointer); | |
| if (!FPtr) | |
| continue; | |
| Symtab.mapAddress(FPtr, I->NameRef); | |
| } | |
| return success(); | |
| } | |
| template <class IntPtrT> | |
| Error RawInstrProfReader<IntPtrT>::readHeader( | |
| const RawInstrProf::Header &Header) { | |
| Version = swap(Header.Version); | |
| if (GET_VERSION(Version) != RawInstrProf::Version) | |
| return error(instrprof_error::unsupported_version); | |
| CountersDelta = swap(Header.CountersDelta); | |
| NamesDelta = swap(Header.NamesDelta); | |
| auto DataSize = swap(Header.DataSize); | |
| auto CountersSize = swap(Header.CountersSize); | |
| NamesSize = swap(Header.NamesSize); | |
| ValueKindLast = swap(Header.ValueKindLast); | |
| auto DataSizeInBytes = DataSize * sizeof(RawInstrProf::ProfileData<IntPtrT>); | |
| auto PaddingSize = getNumPaddingBytes(NamesSize); | |
| ptrdiff_t DataOffset = sizeof(RawInstrProf::Header); | |
| ptrdiff_t CountersOffset = DataOffset + DataSizeInBytes; | |
| ptrdiff_t NamesOffset = CountersOffset + sizeof(uint64_t) * CountersSize; | |
| ptrdiff_t ValueDataOffset = NamesOffset + NamesSize + PaddingSize; | |
| auto *Start = reinterpret_cast<const char *>(&Header); | |
| if (Start + ValueDataOffset > DataBuffer->getBufferEnd()) | |
| return error(instrprof_error::bad_header); | |
| Data = reinterpret_cast<const RawInstrProf::ProfileData<IntPtrT> *>( | |
| Start + DataOffset); | |
| DataEnd = Data + DataSize; | |
| CountersStart = reinterpret_cast<const uint64_t *>(Start + CountersOffset); | |
| NamesStart = Start + NamesOffset; | |
| ValueDataStart = reinterpret_cast<const uint8_t *>(Start + ValueDataOffset); | |
| std::unique_ptr<InstrProfSymtab> NewSymtab = make_unique<InstrProfSymtab>(); | |
| if (Error E = createSymtab(*NewSymtab.get())) | |
| return E; | |
| Symtab = std::move(NewSymtab); | |
| return success(); | |
| } | |
| template <class IntPtrT> | |
| Error RawInstrProfReader<IntPtrT>::readName(NamedInstrProfRecord &Record) { | |
| Record.Name = getName(Data->NameRef); | |
| return success(); | |
| } | |
| template <class IntPtrT> | |
| Error RawInstrProfReader<IntPtrT>::readFuncHash(NamedInstrProfRecord &Record) { | |
| Record.Hash = swap(Data->FuncHash); | |
| return success(); | |
| } | |
| template <class IntPtrT> | |
| Error RawInstrProfReader<IntPtrT>::readRawCounts( | |
| InstrProfRecord &Record) { | |
| uint32_t NumCounters = swap(Data->NumCounters); | |
| IntPtrT CounterPtr = Data->CounterPtr; | |
| if (NumCounters == 0) | |
| return error(instrprof_error::malformed); | |
| auto RawCounts = makeArrayRef(getCounter(CounterPtr), NumCounters); | |
| auto *NamesStartAsCounter = reinterpret_cast<const uint64_t *>(NamesStart); | |
| // Check bounds. | |
| if (RawCounts.data() < CountersStart || | |
| RawCounts.data() + RawCounts.size() > NamesStartAsCounter) | |
| return error(instrprof_error::malformed); | |
| if (ShouldSwapBytes) { | |
| Record.Counts.clear(); | |
| Record.Counts.reserve(RawCounts.size()); | |
| for (uint64_t Count : RawCounts) | |
| Record.Counts.push_back(swap(Count)); | |
| } else | |
| Record.Counts = RawCounts; | |
| return success(); | |
| } | |
| template <class IntPtrT> | |
| Error RawInstrProfReader<IntPtrT>::readValueProfilingData( | |
| InstrProfRecord &Record) { | |
| Record.clearValueData(); | |
| CurValueDataSize = 0; | |
| // Need to match the logic in value profile dumper code in compiler-rt: | |
| uint32_t NumValueKinds = 0; | |
| for (uint32_t I = 0; I < IPVK_Last + 1; I++) | |
| NumValueKinds += (Data->NumValueSites[I] != 0); | |
| if (!NumValueKinds) | |
| return success(); | |
| Expected<std::unique_ptr<ValueProfData>> VDataPtrOrErr = | |
| ValueProfData::getValueProfData( | |
| ValueDataStart, (const unsigned char *)DataBuffer->getBufferEnd(), | |
| getDataEndianness()); | |
| if (Error E = VDataPtrOrErr.takeError()) | |
| return E; | |
| // Note that besides deserialization, this also performs the conversion for | |
| // indirect call targets. The function pointers from the raw profile are | |
| // remapped into function name hashes. | |
| VDataPtrOrErr.get()->deserializeTo(Record, Symtab.get()); | |
| CurValueDataSize = VDataPtrOrErr.get()->getSize(); | |
| return success(); | |
| } | |
| template <class IntPtrT> | |
| Error RawInstrProfReader<IntPtrT>::readNextRecord(NamedInstrProfRecord &Record) { | |
| if (atEnd()) | |
| // 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)) | |
| return error(std::move(E)); | |
| // Read FuncHash and set it in Record. | |
| if (Error E = readFuncHash(Record)) | |
| return error(std::move(E)); | |
| // Read raw counts and set Record. | |
| if (Error E = readRawCounts(Record)) | |
| return error(std::move(E)); | |
| // Read value data and set Record. | |
| if (Error E = readValueProfilingData(Record)) | |
| return error(std::move(E)); | |
| // Iterate. | |
| advanceData(); | |
| return success(); | |
| } | |
| namespace llvm { | |
| template class RawInstrProfReader<uint32_t>; | |
| template class RawInstrProfReader<uint64_t>; | |
| } // end namespace llvm | |
| InstrProfLookupTrait::hash_value_type | |
| InstrProfLookupTrait::ComputeHash(StringRef K) { | |
| return IndexedInstrProf::ComputeHash(HashType, K); | |
| } | |
| using data_type = InstrProfLookupTrait::data_type; | |
| using offset_type = InstrProfLookupTrait::offset_type; | |
| bool InstrProfLookupTrait::readValueProfilingData( | |
| const unsigned char *&D, const unsigned char *const End) { | |
| Expected<std::unique_ptr<ValueProfData>> VDataPtrOrErr = | |
| ValueProfData::getValueProfData(D, End, ValueProfDataEndianness); | |
| if (VDataPtrOrErr.takeError()) | |
| return false; | |
| VDataPtrOrErr.get()->deserializeTo(DataBuffer.back(), nullptr); | |
| D += VDataPtrOrErr.get()->TotalSize; | |
| return true; | |
| } | |
| data_type InstrProfLookupTrait::ReadData(StringRef K, const unsigned char *D, | |
| offset_type N) { | |
| using namespace support; | |
| // Check if the data is corrupt. If so, don't try to read it. | |
| if (N % sizeof(uint64_t)) | |
| return data_type(); | |
| DataBuffer.clear(); | |
| std::vector<uint64_t> CounterBuffer; | |
| const unsigned char *End = D + N; | |
| while (D < End) { | |
| // Read hash. | |
| if (D + sizeof(uint64_t) >= End) | |
| return data_type(); | |
| uint64_t Hash = endian::readNext<uint64_t, little, unaligned>(D); | |
| // Initialize number of counters for GET_VERSION(FormatVersion) == 1. | |
| uint64_t CountsSize = N / sizeof(uint64_t) - 1; | |
| // If format version is different then read the number of counters. | |
| if (GET_VERSION(FormatVersion) != IndexedInstrProf::ProfVersion::Version1) { | |
| if (D + sizeof(uint64_t) > End) | |
| return data_type(); | |
| CountsSize = endian::readNext<uint64_t, little, unaligned>(D); | |
| } | |
| // Read counter values. | |
| if (D + CountsSize * sizeof(uint64_t) > End) | |
| return data_type(); | |
| CounterBuffer.clear(); | |
| CounterBuffer.reserve(CountsSize); | |
| for (uint64_t J = 0; J < CountsSize; ++J) | |
| CounterBuffer.push_back(endian::readNext<uint64_t, little, unaligned>(D)); | |
| DataBuffer.emplace_back(K, Hash, std::move(CounterBuffer)); | |
| // Read value profiling data. | |
| if (GET_VERSION(FormatVersion) > IndexedInstrProf::ProfVersion::Version2 && | |
| !readValueProfilingData(D, End)) { | |
| DataBuffer.clear(); | |
| return data_type(); | |
| } | |
| } | |
| return DataBuffer; | |
| } | |
| template <typename HashTableImpl> | |
| Error InstrProfReaderIndex<HashTableImpl>::getRecords( | |
| StringRef FuncName, ArrayRef<NamedInstrProfRecord> &Data) { | |
| auto Iter = HashTable->find(FuncName); | |
| if (Iter == HashTable->end()) | |
| return make_error<InstrProfError>(instrprof_error::unknown_function); | |
| Data = (*Iter); | |
| if (Data.empty()) | |
| return make_error<InstrProfError>(instrprof_error::malformed); | |
| return Error::success(); | |
| } | |
| template <typename HashTableImpl> | |
| Error InstrProfReaderIndex<HashTableImpl>::getRecords( | |
| ArrayRef<NamedInstrProfRecord> &Data) { | |
| if (atEnd()) | |
| return make_error<InstrProfError>(instrprof_error::eof); | |
| Data = *RecordIterator; | |
| if (Data.empty()) | |
| return make_error<InstrProfError>(instrprof_error::malformed); | |
| return Error::success(); | |
| } | |
| template <typename HashTableImpl> | |
| InstrProfReaderIndex<HashTableImpl>::InstrProfReaderIndex( | |
| const unsigned char *Buckets, const unsigned char *const Payload, | |
| const unsigned char *const Base, IndexedInstrProf::HashT HashType, | |
| uint64_t Version) { | |
| FormatVersion = Version; | |
| HashTable.reset(HashTableImpl::Create( | |
| Buckets, Payload, Base, | |
| typename HashTableImpl::InfoType(HashType, Version))); | |
| RecordIterator = HashTable->data_begin(); | |
| } | |
| bool IndexedInstrProfReader::hasFormat(const MemoryBuffer &DataBuffer) { | |
| using namespace support; | |
| if (DataBuffer.getBufferSize() < 8) | |
| return false; | |
| uint64_t Magic = | |
| endian::read<uint64_t, little, aligned>(DataBuffer.getBufferStart()); | |
| // Verify that it's magical. | |
| return Magic == IndexedInstrProf::Magic; | |
| } | |
| const unsigned char * | |
| IndexedInstrProfReader::readSummary(IndexedInstrProf::ProfVersion Version, | |
| const unsigned char *Cur) { | |
| using namespace IndexedInstrProf; | |
| using namespace support; | |
| if (Version >= IndexedInstrProf::Version4) { | |
| const IndexedInstrProf::Summary *SummaryInLE = | |
| reinterpret_cast<const IndexedInstrProf::Summary *>(Cur); | |
| uint64_t NFields = | |
| endian::byte_swap<uint64_t, little>(SummaryInLE->NumSummaryFields); | |
| uint64_t NEntries = | |
| endian::byte_swap<uint64_t, little>(SummaryInLE->NumCutoffEntries); | |
| uint32_t SummarySize = | |
| IndexedInstrProf::Summary::getSize(NFields, NEntries); | |
| std::unique_ptr<IndexedInstrProf::Summary> SummaryData = | |
| IndexedInstrProf::allocSummary(SummarySize); | |
| const uint64_t *Src = reinterpret_cast<const uint64_t *>(SummaryInLE); | |
| uint64_t *Dst = reinterpret_cast<uint64_t *>(SummaryData.get()); | |
| for (unsigned I = 0; I < SummarySize / sizeof(uint64_t); I++) | |
| Dst[I] = endian::byte_swap<uint64_t, little>(Src[I]); | |
| SummaryEntryVector DetailedSummary; | |
| for (unsigned I = 0; I < SummaryData->NumCutoffEntries; I++) { | |
| const IndexedInstrProf::Summary::Entry &Ent = SummaryData->getEntry(I); | |
| DetailedSummary.emplace_back((uint32_t)Ent.Cutoff, Ent.MinBlockCount, | |
| Ent.NumBlocks); | |
| } | |
| // initialize InstrProfSummary using the SummaryData from disk. | |
| this->Summary = llvm::make_unique<ProfileSummary>( | |
| ProfileSummary::PSK_Instr, DetailedSummary, | |
| SummaryData->get(Summary::TotalBlockCount), | |
| SummaryData->get(Summary::MaxBlockCount), | |
| SummaryData->get(Summary::MaxInternalBlockCount), | |
| SummaryData->get(Summary::MaxFunctionCount), | |
| SummaryData->get(Summary::TotalNumBlocks), | |
| SummaryData->get(Summary::TotalNumFunctions)); | |
| return Cur + SummarySize; | |
| } else { | |
| // For older version of profile data, we need to compute on the fly: | |
| using namespace IndexedInstrProf; | |
| InstrProfSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); | |
| // FIXME: This only computes an empty summary. Need to call addRecord for | |
| // all NamedInstrProfRecords to get the correct summary. | |
| this->Summary = Builder.getSummary(); | |
| return Cur; | |
| } | |
| } | |
| Error IndexedInstrProfReader::readHeader() { | |
| using namespace support; | |
| const unsigned char *Start = | |
| (const unsigned char *)DataBuffer->getBufferStart(); | |
| const unsigned char *Cur = Start; | |
| if ((const unsigned char *)DataBuffer->getBufferEnd() - Cur < 24) | |
| return error(instrprof_error::truncated); | |
| auto *Header = reinterpret_cast<const IndexedInstrProf::Header *>(Cur); | |
| Cur += sizeof(IndexedInstrProf::Header); | |
| // Check the magic number. | |
| uint64_t Magic = endian::byte_swap<uint64_t, little>(Header->Magic); | |
| if (Magic != IndexedInstrProf::Magic) | |
| return error(instrprof_error::bad_magic); | |
| // Read the version. | |
| uint64_t FormatVersion = endian::byte_swap<uint64_t, little>(Header->Version); | |
| if (GET_VERSION(FormatVersion) > | |
| IndexedInstrProf::ProfVersion::CurrentVersion) | |
| return error(instrprof_error::unsupported_version); | |
| Cur = readSummary((IndexedInstrProf::ProfVersion)FormatVersion, Cur); | |
| // Read the hash type and start offset. | |
| IndexedInstrProf::HashT HashType = static_cast<IndexedInstrProf::HashT>( | |
| endian::byte_swap<uint64_t, little>(Header->HashType)); | |
| if (HashType > IndexedInstrProf::HashT::Last) | |
| return error(instrprof_error::unsupported_hash_type); | |
| uint64_t HashOffset = endian::byte_swap<uint64_t, little>(Header->HashOffset); | |
| // The rest of the file is an on disk hash table. | |
| InstrProfReaderIndexBase *IndexPtr = nullptr; | |
| IndexPtr = new InstrProfReaderIndex<OnDiskHashTableImplV3>( | |
| Start + HashOffset, Cur, Start, HashType, FormatVersion); | |
| Index.reset(IndexPtr); | |
| return success(); | |
| } | |
| InstrProfSymtab &IndexedInstrProfReader::getSymtab() { | |
| if (Symtab.get()) | |
| return *Symtab.get(); | |
| std::unique_ptr<InstrProfSymtab> NewSymtab = make_unique<InstrProfSymtab>(); | |
| if (Error E = Index->populateSymtab(*NewSymtab.get())) { | |
| consumeError(error(InstrProfError::take(std::move(E)))); | |
| } | |
| Symtab = std::move(NewSymtab); | |
| return *Symtab.get(); | |
| } | |
| Expected<InstrProfRecord> | |
| IndexedInstrProfReader::getInstrProfRecord(StringRef FuncName, | |
| uint64_t FuncHash) { | |
| ArrayRef<NamedInstrProfRecord> Data; | |
| Error Err = Index->getRecords(FuncName, Data); | |
| if (Err) | |
| return std::move(Err); | |
| // Found it. Look for counters with the right hash. | |
| for (unsigned I = 0, E = Data.size(); I < E; ++I) { | |
| // Check for a match and fill the vector if there is one. | |
| if (Data[I].Hash == FuncHash) { | |
| return std::move(Data[I]); | |
| } | |
| } | |
| return error(instrprof_error::hash_mismatch); | |
| } | |
| Error IndexedInstrProfReader::getFunctionCounts(StringRef FuncName, | |
| uint64_t FuncHash, | |
| std::vector<uint64_t> &Counts) { | |
| Expected<InstrProfRecord> Record = getInstrProfRecord(FuncName, FuncHash); | |
| if (Error E = Record.takeError()) | |
| return error(std::move(E)); | |
| Counts = Record.get().Counts; | |
| return success(); | |
| } | |
| Error IndexedInstrProfReader::readNextRecord(NamedInstrProfRecord &Record) { | |
| ArrayRef<NamedInstrProfRecord> Data; | |
| Error E = Index->getRecords(Data); | |
| if (E) | |
| return error(std::move(E)); | |
| Record = Data[RecordIndex++]; | |
| if (RecordIndex >= Data.size()) { | |
| Index->advanceToNextKey(); | |
| RecordIndex = 0; | |
| } | |
| return success(); | |
| } |