diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2021-02-16 20:13:02 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2021-02-16 20:13:02 +0000 |
commit | b60736ec1405bb0a8dd40989f67ef4c93da068ab (patch) | |
tree | 5c43fbb7c9fc45f0f87e0e6795a86267dbd12f9d /llvm/lib/ProfileData/SampleProfReader.cpp | |
parent | cfca06d7963fa0909f90483b42a6d7d194d01e08 (diff) |
Diffstat (limited to 'llvm/lib/ProfileData/SampleProfReader.cpp')
-rw-r--r-- | llvm/lib/ProfileData/SampleProfReader.cpp | 257 |
1 files changed, 209 insertions, 48 deletions
diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp index 03f1ac190b91..c42931174bc0 100644 --- a/llvm/lib/ProfileData/SampleProfReader.cpp +++ b/llvm/lib/ProfileData/SampleProfReader.cpp @@ -83,26 +83,52 @@ static bool ParseHead(const StringRef &Input, StringRef &FName, /// Returns true if line offset \p L is legal (only has 16 bits). static bool isOffsetLegal(unsigned L) { return (L & 0xffff) == L; } +/// Parse \p Input that contains metadata. +/// Possible metadata: +/// - CFG Checksum information: +/// !CFGChecksum: 12345 +/// Stores the FunctionHash (a.k.a. CFG Checksum) into \p FunctionHash. +static bool parseMetadata(const StringRef &Input, uint64_t &FunctionHash) { + if (!Input.startswith("!CFGChecksum:")) + return false; + + StringRef CFGInfo = Input.substr(strlen("!CFGChecksum:")).trim(); + return !CFGInfo.getAsInteger(10, FunctionHash); +} + +enum class LineType { + CallSiteProfile, + BodyProfile, + Metadata, +}; + /// Parse \p Input as line sample. /// /// \param Input input line. -/// \param IsCallsite true if the line represents an inlined callsite. +/// \param LineTy Type of this line. /// \param Depth the depth of the inline stack. /// \param NumSamples total samples of the line/inlined callsite. /// \param LineOffset line offset to the start of the function. /// \param Discriminator discriminator of the line. /// \param TargetCountMap map from indirect call target to count. +/// \param FunctionHash the function's CFG hash, used by pseudo probe. /// /// returns true if parsing is successful. -static bool ParseLine(const StringRef &Input, bool &IsCallsite, uint32_t &Depth, +static bool ParseLine(const StringRef &Input, LineType &LineTy, uint32_t &Depth, uint64_t &NumSamples, uint32_t &LineOffset, uint32_t &Discriminator, StringRef &CalleeName, - DenseMap<StringRef, uint64_t> &TargetCountMap) { + DenseMap<StringRef, uint64_t> &TargetCountMap, + uint64_t &FunctionHash) { for (Depth = 0; Input[Depth] == ' '; Depth++) ; if (Depth == 0) return false; + if (Depth == 1 && Input[Depth] == '!') { + LineTy = LineType::Metadata; + return parseMetadata(Input.substr(Depth), FunctionHash); + } + size_t n1 = Input.find(':'); StringRef Loc = Input.substr(Depth, n1 - Depth); size_t n2 = Loc.find('.'); @@ -118,8 +144,8 @@ static bool ParseLine(const StringRef &Input, bool &IsCallsite, uint32_t &Depth, } StringRef Rest = Input.substr(n1 + 2); - if (Rest[0] >= '0' && Rest[0] <= '9') { - IsCallsite = false; + if (isDigit(Rest[0])) { + LineTy = LineType::BodyProfile; size_t n3 = Rest.find(' '); if (n3 == StringRef::npos) { if (Rest.getAsInteger(10, NumSamples)) @@ -176,7 +202,7 @@ static bool ParseLine(const StringRef &Input, bool &IsCallsite, uint32_t &Depth, n3 = n4; } } else { - IsCallsite = true; + LineTy = LineType::CallSiteProfile; size_t n3 = Rest.find_last_of(':'); CalleeName = Rest.substr(0, n3); if (Rest.substr(n3 + 1).getAsInteger(10, NumSamples)) @@ -196,6 +222,13 @@ std::error_code SampleProfileReaderText::readImpl() { sampleprof_error Result = sampleprof_error::success; InlineCallStack InlineStack; + int CSProfileCount = 0; + int RegularProfileCount = 0; + uint32_t ProbeProfileCount = 0; + + // SeenMetadata tracks whether we have processed metadata for the current + // top-level function profile. + bool SeenMetadata = false; for (; !LineIt.is_at_eof(); ++LineIt) { if ((*LineIt)[(*LineIt).find_first_not_of(' ')] == '#') @@ -220,9 +253,16 @@ std::error_code SampleProfileReaderText::readImpl() { "Expected 'mangled_name:NUM:NUM', found " + *LineIt); return sampleprof_error::malformed; } - Profiles[FName] = FunctionSamples(); - FunctionSamples &FProfile = Profiles[FName]; - FProfile.setName(FName); + SeenMetadata = false; + SampleContext FContext(FName); + if (FContext.hasContext()) + ++CSProfileCount; + else + ++RegularProfileCount; + Profiles[FContext] = FunctionSamples(); + FunctionSamples &FProfile = Profiles[FContext]; + FProfile.setName(FContext.getName()); + FProfile.setContext(FContext); MergeResult(Result, FProfile.addTotalSamples(NumSamples)); MergeResult(Result, FProfile.addHeadSamples(NumHeadSamples)); InlineStack.clear(); @@ -231,25 +271,35 @@ std::error_code SampleProfileReaderText::readImpl() { uint64_t NumSamples; StringRef FName; DenseMap<StringRef, uint64_t> TargetCountMap; - bool IsCallsite; uint32_t Depth, LineOffset, Discriminator; - if (!ParseLine(*LineIt, IsCallsite, Depth, NumSamples, LineOffset, - Discriminator, FName, TargetCountMap)) { + LineType LineTy; + uint64_t FunctionHash; + if (!ParseLine(*LineIt, LineTy, Depth, NumSamples, LineOffset, + Discriminator, FName, TargetCountMap, FunctionHash)) { reportError(LineIt.line_number(), "Expected 'NUM[.NUM]: NUM[ mangled_name:NUM]*', found " + *LineIt); return sampleprof_error::malformed; } - if (IsCallsite) { - while (InlineStack.size() > Depth) { - InlineStack.pop_back(); - } + if (SeenMetadata && LineTy != LineType::Metadata) { + // Metadata must be put at the end of a function profile. + reportError(LineIt.line_number(), + "Found non-metadata after metadata: " + *LineIt); + return sampleprof_error::malformed; + } + while (InlineStack.size() > Depth) { + InlineStack.pop_back(); + } + switch (LineTy) { + case LineType::CallSiteProfile: { FunctionSamples &FSamples = InlineStack.back()->functionSamplesAt( LineLocation(LineOffset, Discriminator))[std::string(FName)]; FSamples.setName(FName); MergeResult(Result, FSamples.addTotalSamples(NumSamples)); InlineStack.push_back(&FSamples); - } else { + break; + } + case LineType::BodyProfile: { while (InlineStack.size() > Depth) { InlineStack.pop_back(); } @@ -261,9 +311,27 @@ std::error_code SampleProfileReaderText::readImpl() { } MergeResult(Result, FProfile.addBodySamples(LineOffset, Discriminator, NumSamples)); + break; + } + case LineType::Metadata: { + FunctionSamples &FProfile = *InlineStack.back(); + FProfile.setFunctionHash(FunctionHash); + ++ProbeProfileCount; + SeenMetadata = true; + break; + } } } } + + assert((RegularProfileCount == 0 || CSProfileCount == 0) && + "Cannot have both context-sensitive and regular profile"); + ProfileIsCS = (CSProfileCount > 0); + assert((ProbeProfileCount == 0 || ProbeProfileCount == Profiles.size()) && + "Cannot have both probe-based profiles and regular profiles"); + ProfileIsProbeBased = (ProbeProfileCount > 0); + FunctionSamples::ProfileIsProbeBased = ProfileIsProbeBased; + if (Result == sampleprof_error::success) computeSummary(); @@ -354,6 +422,34 @@ ErrorOr<StringRef> SampleProfileReaderBinary::readStringFromTable() { return NameTable[*Idx]; } +ErrorOr<StringRef> SampleProfileReaderExtBinaryBase::readStringFromTable() { + if (!FixedLengthMD5) + return SampleProfileReaderBinary::readStringFromTable(); + + // read NameTable index. + auto Idx = readStringIndex(NameTable); + if (std::error_code EC = Idx.getError()) + return EC; + + // Check whether the name to be accessed has been accessed before, + // if not, read it from memory directly. + StringRef &SR = NameTable[*Idx]; + if (SR.empty()) { + const uint8_t *SavedData = Data; + Data = MD5NameMemStart + ((*Idx) * sizeof(uint64_t)); + auto FID = readUnencodedNumber<uint64_t>(); + if (std::error_code EC = FID.getError()) + return EC; + // Save the string converted from uint64_t in MD5StringBuf. All the + // references to the name are all StringRefs refering to the string + // in MD5StringBuf. + MD5StringBuf->push_back(std::to_string(*FID)); + SR = MD5StringBuf->back(); + Data = SavedData; + } + return SR; +} + ErrorOr<StringRef> SampleProfileReaderCompactBinary::readStringFromTable() { auto Idx = readStringIndex(NameTable); if (std::error_code EC = Idx.getError()) @@ -470,7 +566,7 @@ std::error_code SampleProfileReaderBinary::readImpl() { return sampleprof_error::success; } -std::error_code SampleProfileReaderExtBinary::readOneSection( +std::error_code SampleProfileReaderExtBinaryBase::readOneSection( const uint8_t *Start, uint64_t Size, const SecHdrTableEntry &Entry) { Data = Start; End = Start + Size; @@ -481,37 +577,56 @@ std::error_code SampleProfileReaderExtBinary::readOneSection( if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagPartial)) Summary->setPartialProfile(true); break; - case SecNameTable: - if (std::error_code EC = readNameTableSec( - hasSecFlag(Entry, SecNameTableFlags::SecFlagMD5Name))) + case SecNameTable: { + FixedLengthMD5 = + hasSecFlag(Entry, SecNameTableFlags::SecFlagFixedLengthMD5); + bool UseMD5 = hasSecFlag(Entry, SecNameTableFlags::SecFlagMD5Name); + assert((!FixedLengthMD5 || UseMD5) && + "If FixedLengthMD5 is true, UseMD5 has to be true"); + if (std::error_code EC = readNameTableSec(UseMD5)) return EC; break; + } case SecLBRProfile: if (std::error_code EC = readFuncProfiles()) return EC; break; - case SecProfileSymbolList: - if (std::error_code EC = readProfileSymbolList()) - return EC; - break; case SecFuncOffsetTable: if (std::error_code EC = readFuncOffsetTable()) return EC; break; + case SecFuncMetadata: + ProfileIsProbeBased = + hasSecFlag(Entry, SecFuncMetadataFlags::SecFlagIsProbeBased); + FunctionSamples::ProfileIsProbeBased = ProfileIsProbeBased; + if (std::error_code EC = readFuncMetadata()) + return EC; + break; + case SecProfileSymbolList: + if (std::error_code EC = readProfileSymbolList()) + return EC; + break; default: + if (std::error_code EC = readCustomSection(Entry)) + return EC; break; } return sampleprof_error::success; } -void SampleProfileReaderExtBinary::collectFuncsFrom(const Module &M) { +void SampleProfileReaderExtBinaryBase::collectFuncsFrom(const Module &M) { UseAllFuncs = false; FuncsToUse.clear(); for (auto &F : M) FuncsToUse.insert(FunctionSamples::getCanonicalFnName(F)); } -std::error_code SampleProfileReaderExtBinary::readFuncOffsetTable() { +std::error_code SampleProfileReaderExtBinaryBase::readFuncOffsetTable() { + // If there are more than one FuncOffsetTable, the profile read associated + // with previous FuncOffsetTable has to be done before next FuncOffsetTable + // is read. + FuncOffsetTable.clear(); + auto Size = readNumber<uint64_t>(); if (std::error_code EC = Size.getError()) return EC; @@ -531,7 +646,7 @@ std::error_code SampleProfileReaderExtBinary::readFuncOffsetTable() { return sampleprof_error::success; } -std::error_code SampleProfileReaderExtBinary::readFuncProfiles() { +std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() { const uint8_t *Start = Data; if (UseAllFuncs) { while (Data < End) { @@ -576,7 +691,7 @@ std::error_code SampleProfileReaderExtBinary::readFuncProfiles() { return sampleprof_error::success; } -std::error_code SampleProfileReaderExtBinary::readProfileSymbolList() { +std::error_code SampleProfileReaderExtBinaryBase::readProfileSymbolList() { if (!ProfSymList) ProfSymList = std::make_unique<ProfileSymbolList>(); @@ -625,6 +740,10 @@ std::error_code SampleProfileReaderExtBinaryBase::readImpl() { if (!Entry.Size) continue; + // Skip sections without context when SkipFlatProf is true. + if (SkipFlatProf && hasSecFlag(Entry, SecCommonFlags::SecFlagFlat)) + continue; + const uint8_t *SecStart = BufStart + Entry.Offset; uint64_t SecSize = Entry.Size; @@ -709,7 +828,7 @@ std::error_code SampleProfileReaderBinary::readNameTable() { auto Size = readNumber<uint32_t>(); if (std::error_code EC = Size.getError()) return EC; - NameTable.reserve(*Size); + NameTable.reserve(*Size + NameTable.size()); for (uint32_t I = 0; I < *Size; ++I) { auto Name(readString()); if (std::error_code EC = Name.getError()) @@ -720,13 +839,24 @@ std::error_code SampleProfileReaderBinary::readNameTable() { return sampleprof_error::success; } -std::error_code SampleProfileReaderExtBinary::readMD5NameTable() { +std::error_code SampleProfileReaderExtBinaryBase::readMD5NameTable() { auto Size = readNumber<uint64_t>(); if (std::error_code EC = Size.getError()) return EC; - NameTable.reserve(*Size); MD5StringBuf = std::make_unique<std::vector<std::string>>(); MD5StringBuf->reserve(*Size); + if (FixedLengthMD5) { + // Preallocate and initialize NameTable so we can check whether a name + // index has been read before by checking whether the element in the + // NameTable is empty, meanwhile readStringIndex can do the boundary + // check using the size of NameTable. + NameTable.resize(*Size + NameTable.size()); + + MD5NameMemStart = Data; + Data = Data + (*Size) * sizeof(uint64_t); + return sampleprof_error::success; + } + NameTable.reserve(*Size); for (uint32_t I = 0; I < *Size; ++I) { auto FID = readNumber<uint64_t>(); if (std::error_code EC = FID.getError()) @@ -739,12 +869,29 @@ std::error_code SampleProfileReaderExtBinary::readMD5NameTable() { return sampleprof_error::success; } -std::error_code SampleProfileReaderExtBinary::readNameTableSec(bool IsMD5) { +std::error_code SampleProfileReaderExtBinaryBase::readNameTableSec(bool IsMD5) { if (IsMD5) return readMD5NameTable(); return SampleProfileReaderBinary::readNameTable(); } +std::error_code SampleProfileReaderExtBinaryBase::readFuncMetadata() { + if (!ProfileIsProbeBased) + return sampleprof_error::success; + for (unsigned I = 0; I < Profiles.size(); ++I) { + auto FName(readStringFromTable()); + if (std::error_code EC = FName.getError()) + return EC; + + auto Checksum = readNumber<uint64_t>(); + if (std::error_code EC = Checksum.getError()) + return EC; + + Profiles[*FName].setFunctionHash(*Checksum); + } + return sampleprof_error::success; +} + std::error_code SampleProfileReaderCompactBinary::readNameTable() { auto Size = readNumber<uint64_t>(); if (std::error_code EC = Size.getError()) @@ -759,7 +906,8 @@ std::error_code SampleProfileReaderCompactBinary::readNameTable() { return sampleprof_error::success; } -std::error_code SampleProfileReaderExtBinaryBase::readSecHdrTableEntry() { +std::error_code +SampleProfileReaderExtBinaryBase::readSecHdrTableEntry(uint32_t Idx) { SecHdrTableEntry Entry; auto Type = readUnencodedNumber<uint64_t>(); if (std::error_code EC = Type.getError()) @@ -781,6 +929,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readSecHdrTableEntry() { return EC; Entry.Size = *Size; + Entry.LayoutIndex = Idx; SecHdrTable.push_back(std::move(Entry)); return sampleprof_error::success; } @@ -791,7 +940,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readSecHdrTable() { return EC; for (uint32_t i = 0; i < (*EntryNum); i++) - if (std::error_code EC = readSecHdrTableEntry()) + if (std::error_code EC = readSecHdrTableEntry(i)) return EC; return sampleprof_error::success; @@ -813,11 +962,12 @@ std::error_code SampleProfileReaderExtBinaryBase::readHeader() { } uint64_t SampleProfileReaderExtBinaryBase::getSectionSize(SecType Type) { + uint64_t Size = 0; for (auto &Entry : SecHdrTable) { if (Entry.Type == Type) - return Entry.Size; + Size += Entry.Size; } - return 0; + return Size; } uint64_t SampleProfileReaderExtBinaryBase::getFileSize() { @@ -840,9 +990,14 @@ static std::string getSecFlagsStr(const SecHdrTableEntry &Entry) { else Flags.append("{"); + if (hasSecFlag(Entry, SecCommonFlags::SecFlagFlat)) + Flags.append("flat,"); + switch (Entry.Type) { case SecNameTable: - if (hasSecFlag(Entry, SecNameTableFlags::SecFlagMD5Name)) + if (hasSecFlag(Entry, SecNameTableFlags::SecFlagFixedLengthMD5)) + Flags.append("fixlenmd5,"); + else if (hasSecFlag(Entry, SecNameTableFlags::SecFlagMD5Name)) Flags.append("md5,"); break; case SecProfSummary: @@ -867,7 +1022,7 @@ bool SampleProfileReaderExtBinaryBase::dumpSectionInfo(raw_ostream &OS) { << ", Size: " << Entry.Size << ", Flags: " << getSecFlagsStr(Entry) << "\n"; ; - TotalSecsSize += getSectionSize(Entry.Type); + TotalSecsSize += Entry.Size; } uint64_t HeaderSize = SecHdrTable.front().Offset; assert(HeaderSize + TotalSecsSize == getFileSize() && @@ -1201,7 +1356,7 @@ std::error_code SampleProfileReaderGCC::readOneFunctionProfile( InlineCallStack NewStack; NewStack.push_back(FProfile); - NewStack.insert(NewStack.end(), InlineStack.begin(), InlineStack.end()); + llvm::append_range(NewStack, InlineStack); if (Update) { // Walk up the inline stack, adding the samples on this line to // the total sample count of the callers in the chain. @@ -1249,7 +1404,7 @@ std::error_code SampleProfileReaderGCC::readOneFunctionProfile( return sampleprof_error::truncated; InlineCallStack NewStack; NewStack.push_back(FProfile); - NewStack.insert(NewStack.end(), InlineStack.begin(), InlineStack.end()); + llvm::append_range(NewStack, InlineStack); if (std::error_code EC = readOneFunctionProfile(NewStack, Update, Offset)) return EC; } @@ -1290,19 +1445,25 @@ void SampleProfileReaderItaniumRemapper::applyRemapping(LLVMContext &Ctx) { return; } + // CSSPGO-TODO: Remapper is not yet supported. + // We will need to remap the entire context string. assert(Remappings && "should be initialized while creating remapper"); - for (auto &Sample : Reader.getProfiles()) - if (auto Key = Remappings->insert(Sample.first())) - SampleMap.insert({Key, &Sample.second}); + for (auto &Sample : Reader.getProfiles()) { + DenseSet<StringRef> NamesInSample; + Sample.second.findAllNames(NamesInSample); + for (auto &Name : NamesInSample) + if (auto Key = Remappings->insert(Name)) + NameMap.insert({Key, Name}); + } RemappingApplied = true; } -FunctionSamples * -SampleProfileReaderItaniumRemapper::getSamplesFor(StringRef Fname) { +Optional<StringRef> +SampleProfileReaderItaniumRemapper::lookUpNameInProfile(StringRef Fname) { if (auto Key = Remappings->lookup(Fname)) - return SampleMap.lookup(Key); - return nullptr; + return NameMap.lookup(Key); + return None; } /// Prepare a memory buffer for the contents of \p Filename. |