Path: blob/main/contrib/llvm-project/llvm/lib/ProfileData/SampleProfReader.cpp
35232 views
//===- SampleProfReader.cpp - Read LLVM sample profile data ---------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the class that reads LLVM sample profiles. It9// supports three file formats: text, binary and gcov.10//11// The textual representation is useful for debugging and testing purposes. The12// binary representation is more compact, resulting in smaller file sizes.13//14// The gcov encoding is the one generated by GCC's AutoFDO profile creation15// tool (https://github.com/google/autofdo)16//17// All three encodings can be used interchangeably as an input sample profile.18//19//===----------------------------------------------------------------------===//2021#include "llvm/ProfileData/SampleProfReader.h"22#include "llvm/ADT/DenseMap.h"23#include "llvm/ADT/STLExtras.h"24#include "llvm/ADT/StringRef.h"25#include "llvm/IR/Module.h"26#include "llvm/IR/ProfileSummary.h"27#include "llvm/ProfileData/ProfileCommon.h"28#include "llvm/ProfileData/SampleProf.h"29#include "llvm/Support/CommandLine.h"30#include "llvm/Support/Compression.h"31#include "llvm/Support/ErrorOr.h"32#include "llvm/Support/JSON.h"33#include "llvm/Support/LEB128.h"34#include "llvm/Support/LineIterator.h"35#include "llvm/Support/MD5.h"36#include "llvm/Support/MemoryBuffer.h"37#include "llvm/Support/VirtualFileSystem.h"38#include "llvm/Support/raw_ostream.h"39#include <algorithm>40#include <cstddef>41#include <cstdint>42#include <limits>43#include <memory>44#include <system_error>45#include <vector>4647using namespace llvm;48using namespace sampleprof;4950#define DEBUG_TYPE "samplepgo-reader"5152// This internal option specifies if the profile uses FS discriminators.53// It only applies to text, and binary format profiles.54// For ext-binary format profiles, the flag is set in the summary.55static cl::opt<bool> ProfileIsFSDisciminator(56"profile-isfs", cl::Hidden, cl::init(false),57cl::desc("Profile uses flow sensitive discriminators"));5859/// Dump the function profile for \p FName.60///61/// \param FContext Name + context of the function to print.62/// \param OS Stream to emit the output to.63void SampleProfileReader::dumpFunctionProfile(const FunctionSamples &FS,64raw_ostream &OS) {65OS << "Function: " << FS.getContext().toString() << ": " << FS;66}6768/// Dump all the function profiles found on stream \p OS.69void SampleProfileReader::dump(raw_ostream &OS) {70std::vector<NameFunctionSamples> V;71sortFuncProfiles(Profiles, V);72for (const auto &I : V)73dumpFunctionProfile(*I.second, OS);74}7576static void dumpFunctionProfileJson(const FunctionSamples &S,77json::OStream &JOS, bool TopLevel = false) {78auto DumpBody = [&](const BodySampleMap &BodySamples) {79for (const auto &I : BodySamples) {80const LineLocation &Loc = I.first;81const SampleRecord &Sample = I.second;82JOS.object([&] {83JOS.attribute("line", Loc.LineOffset);84if (Loc.Discriminator)85JOS.attribute("discriminator", Loc.Discriminator);86JOS.attribute("samples", Sample.getSamples());8788auto CallTargets = Sample.getSortedCallTargets();89if (!CallTargets.empty()) {90JOS.attributeArray("calls", [&] {91for (const auto &J : CallTargets) {92JOS.object([&] {93JOS.attribute("function", J.first.str());94JOS.attribute("samples", J.second);95});96}97});98}99});100}101};102103auto DumpCallsiteSamples = [&](const CallsiteSampleMap &CallsiteSamples) {104for (const auto &I : CallsiteSamples)105for (const auto &FS : I.second) {106const LineLocation &Loc = I.first;107const FunctionSamples &CalleeSamples = FS.second;108JOS.object([&] {109JOS.attribute("line", Loc.LineOffset);110if (Loc.Discriminator)111JOS.attribute("discriminator", Loc.Discriminator);112JOS.attributeArray(113"samples", [&] { dumpFunctionProfileJson(CalleeSamples, JOS); });114});115}116};117118JOS.object([&] {119JOS.attribute("name", S.getFunction().str());120JOS.attribute("total", S.getTotalSamples());121if (TopLevel)122JOS.attribute("head", S.getHeadSamples());123124const auto &BodySamples = S.getBodySamples();125if (!BodySamples.empty())126JOS.attributeArray("body", [&] { DumpBody(BodySamples); });127128const auto &CallsiteSamples = S.getCallsiteSamples();129if (!CallsiteSamples.empty())130JOS.attributeArray("callsites",131[&] { DumpCallsiteSamples(CallsiteSamples); });132});133}134135/// Dump all the function profiles found on stream \p OS in the JSON format.136void SampleProfileReader::dumpJson(raw_ostream &OS) {137std::vector<NameFunctionSamples> V;138sortFuncProfiles(Profiles, V);139json::OStream JOS(OS, 2);140JOS.arrayBegin();141for (const auto &F : V)142dumpFunctionProfileJson(*F.second, JOS, true);143JOS.arrayEnd();144145// Emit a newline character at the end as json::OStream doesn't emit one.146OS << "\n";147}148149/// Parse \p Input as function head.150///151/// Parse one line of \p Input, and update function name in \p FName,152/// function's total sample count in \p NumSamples, function's entry153/// count in \p NumHeadSamples.154///155/// \returns true if parsing is successful.156static bool ParseHead(const StringRef &Input, StringRef &FName,157uint64_t &NumSamples, uint64_t &NumHeadSamples) {158if (Input[0] == ' ')159return false;160size_t n2 = Input.rfind(':');161size_t n1 = Input.rfind(':', n2 - 1);162FName = Input.substr(0, n1);163if (Input.substr(n1 + 1, n2 - n1 - 1).getAsInteger(10, NumSamples))164return false;165if (Input.substr(n2 + 1).getAsInteger(10, NumHeadSamples))166return false;167return true;168}169170/// Returns true if line offset \p L is legal (only has 16 bits).171static bool isOffsetLegal(unsigned L) { return (L & 0xffff) == L; }172173/// Parse \p Input that contains metadata.174/// Possible metadata:175/// - CFG Checksum information:176/// !CFGChecksum: 12345177/// - CFG Checksum information:178/// !Attributes: 1179/// Stores the FunctionHash (a.k.a. CFG Checksum) into \p FunctionHash.180static bool parseMetadata(const StringRef &Input, uint64_t &FunctionHash,181uint32_t &Attributes) {182if (Input.starts_with("!CFGChecksum:")) {183StringRef CFGInfo = Input.substr(strlen("!CFGChecksum:")).trim();184return !CFGInfo.getAsInteger(10, FunctionHash);185}186187if (Input.starts_with("!Attributes:")) {188StringRef Attrib = Input.substr(strlen("!Attributes:")).trim();189return !Attrib.getAsInteger(10, Attributes);190}191192return false;193}194195enum class LineType {196CallSiteProfile,197BodyProfile,198Metadata,199};200201/// Parse \p Input as line sample.202///203/// \param Input input line.204/// \param LineTy Type of this line.205/// \param Depth the depth of the inline stack.206/// \param NumSamples total samples of the line/inlined callsite.207/// \param LineOffset line offset to the start of the function.208/// \param Discriminator discriminator of the line.209/// \param TargetCountMap map from indirect call target to count.210/// \param FunctionHash the function's CFG hash, used by pseudo probe.211///212/// returns true if parsing is successful.213static bool ParseLine(const StringRef &Input, LineType &LineTy, uint32_t &Depth,214uint64_t &NumSamples, uint32_t &LineOffset,215uint32_t &Discriminator, StringRef &CalleeName,216DenseMap<StringRef, uint64_t> &TargetCountMap,217uint64_t &FunctionHash, uint32_t &Attributes) {218for (Depth = 0; Input[Depth] == ' '; Depth++)219;220if (Depth == 0)221return false;222223if (Input[Depth] == '!') {224LineTy = LineType::Metadata;225return parseMetadata(Input.substr(Depth), FunctionHash, Attributes);226}227228size_t n1 = Input.find(':');229StringRef Loc = Input.substr(Depth, n1 - Depth);230size_t n2 = Loc.find('.');231if (n2 == StringRef::npos) {232if (Loc.getAsInteger(10, LineOffset) || !isOffsetLegal(LineOffset))233return false;234Discriminator = 0;235} else {236if (Loc.substr(0, n2).getAsInteger(10, LineOffset))237return false;238if (Loc.substr(n2 + 1).getAsInteger(10, Discriminator))239return false;240}241242StringRef Rest = Input.substr(n1 + 2);243if (isDigit(Rest[0])) {244LineTy = LineType::BodyProfile;245size_t n3 = Rest.find(' ');246if (n3 == StringRef::npos) {247if (Rest.getAsInteger(10, NumSamples))248return false;249} else {250if (Rest.substr(0, n3).getAsInteger(10, NumSamples))251return false;252}253// Find call targets and their sample counts.254// Note: In some cases, there are symbols in the profile which are not255// mangled. To accommodate such cases, use colon + integer pairs as the256// anchor points.257// An example:258// _M_construct<char *>:1000 string_view<std::allocator<char> >:437259// ":1000" and ":437" are used as anchor points so the string above will260// be interpreted as261// target: _M_construct<char *>262// count: 1000263// target: string_view<std::allocator<char> >264// count: 437265while (n3 != StringRef::npos) {266n3 += Rest.substr(n3).find_first_not_of(' ');267Rest = Rest.substr(n3);268n3 = Rest.find_first_of(':');269if (n3 == StringRef::npos || n3 == 0)270return false;271272StringRef Target;273uint64_t count, n4;274while (true) {275// Get the segment after the current colon.276StringRef AfterColon = Rest.substr(n3 + 1);277// Get the target symbol before the current colon.278Target = Rest.substr(0, n3);279// Check if the word after the current colon is an integer.280n4 = AfterColon.find_first_of(' ');281n4 = (n4 != StringRef::npos) ? n3 + n4 + 1 : Rest.size();282StringRef WordAfterColon = Rest.substr(n3 + 1, n4 - n3 - 1);283if (!WordAfterColon.getAsInteger(10, count))284break;285286// Try to find the next colon.287uint64_t n5 = AfterColon.find_first_of(':');288if (n5 == StringRef::npos)289return false;290n3 += n5 + 1;291}292293// An anchor point is found. Save the {target, count} pair294TargetCountMap[Target] = count;295if (n4 == Rest.size())296break;297// Change n3 to the next blank space after colon + integer pair.298n3 = n4;299}300} else {301LineTy = LineType::CallSiteProfile;302size_t n3 = Rest.find_last_of(':');303CalleeName = Rest.substr(0, n3);304if (Rest.substr(n3 + 1).getAsInteger(10, NumSamples))305return false;306}307return true;308}309310/// Load samples from a text file.311///312/// See the documentation at the top of the file for an explanation of313/// the expected format.314///315/// \returns true if the file was loaded successfully, false otherwise.316std::error_code SampleProfileReaderText::readImpl() {317line_iterator LineIt(*Buffer, /*SkipBlanks=*/true, '#');318sampleprof_error Result = sampleprof_error::success;319320InlineCallStack InlineStack;321uint32_t TopLevelProbeProfileCount = 0;322323// DepthMetadata tracks whether we have processed metadata for the current324// top-level or nested function profile.325uint32_t DepthMetadata = 0;326327ProfileIsFS = ProfileIsFSDisciminator;328FunctionSamples::ProfileIsFS = ProfileIsFS;329for (; !LineIt.is_at_eof(); ++LineIt) {330size_t pos = LineIt->find_first_not_of(' ');331if (pos == LineIt->npos || (*LineIt)[pos] == '#')332continue;333// Read the header of each function.334//335// Note that for function identifiers we are actually expecting336// mangled names, but we may not always get them. This happens when337// the compiler decides not to emit the function (e.g., it was inlined338// and removed). In this case, the binary will not have the linkage339// name for the function, so the profiler will emit the function's340// unmangled name, which may contain characters like ':' and '>' in its341// name (member functions, templates, etc).342//343// The only requirement we place on the identifier, then, is that it344// should not begin with a number.345if ((*LineIt)[0] != ' ') {346uint64_t NumSamples, NumHeadSamples;347StringRef FName;348if (!ParseHead(*LineIt, FName, NumSamples, NumHeadSamples)) {349reportError(LineIt.line_number(),350"Expected 'mangled_name:NUM:NUM', found " + *LineIt);351return sampleprof_error::malformed;352}353DepthMetadata = 0;354SampleContext FContext(FName, CSNameTable);355if (FContext.hasContext())356++CSProfileCount;357FunctionSamples &FProfile = Profiles.create(FContext);358mergeSampleProfErrors(Result, FProfile.addTotalSamples(NumSamples));359mergeSampleProfErrors(Result, FProfile.addHeadSamples(NumHeadSamples));360InlineStack.clear();361InlineStack.push_back(&FProfile);362} else {363uint64_t NumSamples;364StringRef FName;365DenseMap<StringRef, uint64_t> TargetCountMap;366uint32_t Depth, LineOffset, Discriminator;367LineType LineTy;368uint64_t FunctionHash = 0;369uint32_t Attributes = 0;370if (!ParseLine(*LineIt, LineTy, Depth, NumSamples, LineOffset,371Discriminator, FName, TargetCountMap, FunctionHash,372Attributes)) {373reportError(LineIt.line_number(),374"Expected 'NUM[.NUM]: NUM[ mangled_name:NUM]*', found " +375*LineIt);376return sampleprof_error::malformed;377}378if (LineTy != LineType::Metadata && Depth == DepthMetadata) {379// Metadata must be put at the end of a function profile.380reportError(LineIt.line_number(),381"Found non-metadata after metadata: " + *LineIt);382return sampleprof_error::malformed;383}384385// Here we handle FS discriminators.386Discriminator &= getDiscriminatorMask();387388while (InlineStack.size() > Depth) {389InlineStack.pop_back();390}391switch (LineTy) {392case LineType::CallSiteProfile: {393FunctionSamples &FSamples = InlineStack.back()->functionSamplesAt(394LineLocation(LineOffset, Discriminator))[FunctionId(FName)];395FSamples.setFunction(FunctionId(FName));396mergeSampleProfErrors(Result, FSamples.addTotalSamples(NumSamples));397InlineStack.push_back(&FSamples);398DepthMetadata = 0;399break;400}401case LineType::BodyProfile: {402while (InlineStack.size() > Depth) {403InlineStack.pop_back();404}405FunctionSamples &FProfile = *InlineStack.back();406for (const auto &name_count : TargetCountMap) {407mergeSampleProfErrors(Result, FProfile.addCalledTargetSamples(408LineOffset, Discriminator,409FunctionId(name_count.first),410name_count.second));411}412mergeSampleProfErrors(413Result,414FProfile.addBodySamples(LineOffset, Discriminator, NumSamples));415break;416}417case LineType::Metadata: {418FunctionSamples &FProfile = *InlineStack.back();419if (FunctionHash) {420FProfile.setFunctionHash(FunctionHash);421if (Depth == 1)422++TopLevelProbeProfileCount;423}424FProfile.getContext().setAllAttributes(Attributes);425if (Attributes & (uint32_t)ContextShouldBeInlined)426ProfileIsPreInlined = true;427DepthMetadata = Depth;428break;429}430}431}432}433434assert((CSProfileCount == 0 || CSProfileCount == Profiles.size()) &&435"Cannot have both context-sensitive and regular profile");436ProfileIsCS = (CSProfileCount > 0);437assert((TopLevelProbeProfileCount == 0 ||438TopLevelProbeProfileCount == Profiles.size()) &&439"Cannot have both probe-based profiles and regular profiles");440ProfileIsProbeBased = (TopLevelProbeProfileCount > 0);441FunctionSamples::ProfileIsProbeBased = ProfileIsProbeBased;442FunctionSamples::ProfileIsCS = ProfileIsCS;443FunctionSamples::ProfileIsPreInlined = ProfileIsPreInlined;444445if (Result == sampleprof_error::success)446computeSummary();447448return Result;449}450451bool SampleProfileReaderText::hasFormat(const MemoryBuffer &Buffer) {452bool result = false;453454// Check that the first non-comment line is a valid function header.455line_iterator LineIt(Buffer, /*SkipBlanks=*/true, '#');456if (!LineIt.is_at_eof()) {457if ((*LineIt)[0] != ' ') {458uint64_t NumSamples, NumHeadSamples;459StringRef FName;460result = ParseHead(*LineIt, FName, NumSamples, NumHeadSamples);461}462}463464return result;465}466467template <typename T> ErrorOr<T> SampleProfileReaderBinary::readNumber() {468unsigned NumBytesRead = 0;469uint64_t Val = decodeULEB128(Data, &NumBytesRead);470471if (Val > std::numeric_limits<T>::max()) {472std::error_code EC = sampleprof_error::malformed;473reportError(0, EC.message());474return EC;475} else if (Data + NumBytesRead > End) {476std::error_code EC = sampleprof_error::truncated;477reportError(0, EC.message());478return EC;479}480481Data += NumBytesRead;482return static_cast<T>(Val);483}484485ErrorOr<StringRef> SampleProfileReaderBinary::readString() {486StringRef Str(reinterpret_cast<const char *>(Data));487if (Data + Str.size() + 1 > End) {488std::error_code EC = sampleprof_error::truncated;489reportError(0, EC.message());490return EC;491}492493Data += Str.size() + 1;494return Str;495}496497template <typename T>498ErrorOr<T> SampleProfileReaderBinary::readUnencodedNumber() {499if (Data + sizeof(T) > End) {500std::error_code EC = sampleprof_error::truncated;501reportError(0, EC.message());502return EC;503}504505using namespace support;506T Val = endian::readNext<T, llvm::endianness::little>(Data);507return Val;508}509510template <typename T>511inline ErrorOr<size_t> SampleProfileReaderBinary::readStringIndex(T &Table) {512auto Idx = readNumber<size_t>();513if (std::error_code EC = Idx.getError())514return EC;515if (*Idx >= Table.size())516return sampleprof_error::truncated_name_table;517return *Idx;518}519520ErrorOr<FunctionId>521SampleProfileReaderBinary::readStringFromTable(size_t *RetIdx) {522auto Idx = readStringIndex(NameTable);523if (std::error_code EC = Idx.getError())524return EC;525if (RetIdx)526*RetIdx = *Idx;527return NameTable[*Idx];528}529530ErrorOr<SampleContextFrames>531SampleProfileReaderBinary::readContextFromTable(size_t *RetIdx) {532auto ContextIdx = readNumber<size_t>();533if (std::error_code EC = ContextIdx.getError())534return EC;535if (*ContextIdx >= CSNameTable.size())536return sampleprof_error::truncated_name_table;537if (RetIdx)538*RetIdx = *ContextIdx;539return CSNameTable[*ContextIdx];540}541542ErrorOr<std::pair<SampleContext, uint64_t>>543SampleProfileReaderBinary::readSampleContextFromTable() {544SampleContext Context;545size_t Idx;546if (ProfileIsCS) {547auto FContext(readContextFromTable(&Idx));548if (std::error_code EC = FContext.getError())549return EC;550Context = SampleContext(*FContext);551} else {552auto FName(readStringFromTable(&Idx));553if (std::error_code EC = FName.getError())554return EC;555Context = SampleContext(*FName);556}557// Since MD5SampleContextStart may point to the profile's file data, need to558// make sure it is reading the same value on big endian CPU.559uint64_t Hash = support::endian::read64le(MD5SampleContextStart + Idx);560// Lazy computing of hash value, write back to the table to cache it. Only561// compute the context's hash value if it is being referenced for the first562// time.563if (Hash == 0) {564assert(MD5SampleContextStart == MD5SampleContextTable.data());565Hash = Context.getHashCode();566support::endian::write64le(&MD5SampleContextTable[Idx], Hash);567}568return std::make_pair(Context, Hash);569}570571std::error_code572SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) {573auto NumSamples = readNumber<uint64_t>();574if (std::error_code EC = NumSamples.getError())575return EC;576FProfile.addTotalSamples(*NumSamples);577578// Read the samples in the body.579auto NumRecords = readNumber<uint32_t>();580if (std::error_code EC = NumRecords.getError())581return EC;582583for (uint32_t I = 0; I < *NumRecords; ++I) {584auto LineOffset = readNumber<uint64_t>();585if (std::error_code EC = LineOffset.getError())586return EC;587588if (!isOffsetLegal(*LineOffset)) {589return std::error_code();590}591592auto Discriminator = readNumber<uint64_t>();593if (std::error_code EC = Discriminator.getError())594return EC;595596auto NumSamples = readNumber<uint64_t>();597if (std::error_code EC = NumSamples.getError())598return EC;599600auto NumCalls = readNumber<uint32_t>();601if (std::error_code EC = NumCalls.getError())602return EC;603604// Here we handle FS discriminators:605uint32_t DiscriminatorVal = (*Discriminator) & getDiscriminatorMask();606607for (uint32_t J = 0; J < *NumCalls; ++J) {608auto CalledFunction(readStringFromTable());609if (std::error_code EC = CalledFunction.getError())610return EC;611612auto CalledFunctionSamples = readNumber<uint64_t>();613if (std::error_code EC = CalledFunctionSamples.getError())614return EC;615616FProfile.addCalledTargetSamples(*LineOffset, DiscriminatorVal,617*CalledFunction, *CalledFunctionSamples);618}619620FProfile.addBodySamples(*LineOffset, DiscriminatorVal, *NumSamples);621}622623// Read all the samples for inlined function calls.624auto NumCallsites = readNumber<uint32_t>();625if (std::error_code EC = NumCallsites.getError())626return EC;627628for (uint32_t J = 0; J < *NumCallsites; ++J) {629auto LineOffset = readNumber<uint64_t>();630if (std::error_code EC = LineOffset.getError())631return EC;632633auto Discriminator = readNumber<uint64_t>();634if (std::error_code EC = Discriminator.getError())635return EC;636637auto FName(readStringFromTable());638if (std::error_code EC = FName.getError())639return EC;640641// Here we handle FS discriminators:642uint32_t DiscriminatorVal = (*Discriminator) & getDiscriminatorMask();643644FunctionSamples &CalleeProfile = FProfile.functionSamplesAt(645LineLocation(*LineOffset, DiscriminatorVal))[*FName];646CalleeProfile.setFunction(*FName);647if (std::error_code EC = readProfile(CalleeProfile))648return EC;649}650651return sampleprof_error::success;652}653654std::error_code655SampleProfileReaderBinary::readFuncProfile(const uint8_t *Start) {656Data = Start;657auto NumHeadSamples = readNumber<uint64_t>();658if (std::error_code EC = NumHeadSamples.getError())659return EC;660661auto FContextHash(readSampleContextFromTable());662if (std::error_code EC = FContextHash.getError())663return EC;664665auto &[FContext, Hash] = *FContextHash;666// Use the cached hash value for insertion instead of recalculating it.667auto Res = Profiles.try_emplace(Hash, FContext, FunctionSamples());668FunctionSamples &FProfile = Res.first->second;669FProfile.setContext(FContext);670FProfile.addHeadSamples(*NumHeadSamples);671672if (FContext.hasContext())673CSProfileCount++;674675if (std::error_code EC = readProfile(FProfile))676return EC;677return sampleprof_error::success;678}679680std::error_code SampleProfileReaderBinary::readImpl() {681ProfileIsFS = ProfileIsFSDisciminator;682FunctionSamples::ProfileIsFS = ProfileIsFS;683while (Data < End) {684if (std::error_code EC = readFuncProfile(Data))685return EC;686}687688return sampleprof_error::success;689}690691std::error_code SampleProfileReaderExtBinaryBase::readOneSection(692const uint8_t *Start, uint64_t Size, const SecHdrTableEntry &Entry) {693Data = Start;694End = Start + Size;695switch (Entry.Type) {696case SecProfSummary:697if (std::error_code EC = readSummary())698return EC;699if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagPartial))700Summary->setPartialProfile(true);701if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagFullContext))702FunctionSamples::ProfileIsCS = ProfileIsCS = true;703if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagIsPreInlined))704FunctionSamples::ProfileIsPreInlined = ProfileIsPreInlined = true;705if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagFSDiscriminator))706FunctionSamples::ProfileIsFS = ProfileIsFS = true;707break;708case SecNameTable: {709bool FixedLengthMD5 =710hasSecFlag(Entry, SecNameTableFlags::SecFlagFixedLengthMD5);711bool UseMD5 = hasSecFlag(Entry, SecNameTableFlags::SecFlagMD5Name);712// UseMD5 means if THIS section uses MD5, ProfileIsMD5 means if the entire713// profile uses MD5 for function name matching in IPO passes.714ProfileIsMD5 = ProfileIsMD5 || UseMD5;715FunctionSamples::HasUniqSuffix =716hasSecFlag(Entry, SecNameTableFlags::SecFlagUniqSuffix);717if (std::error_code EC = readNameTableSec(UseMD5, FixedLengthMD5))718return EC;719break;720}721case SecCSNameTable: {722if (std::error_code EC = readCSNameTableSec())723return EC;724break;725}726case SecLBRProfile:727if (std::error_code EC = readFuncProfiles())728return EC;729break;730case SecFuncOffsetTable:731// If module is absent, we are using LLVM tools, and need to read all732// profiles, so skip reading the function offset table.733if (!M) {734Data = End;735} else {736assert((!ProfileIsCS ||737hasSecFlag(Entry, SecFuncOffsetFlags::SecFlagOrdered)) &&738"func offset table should always be sorted in CS profile");739if (std::error_code EC = readFuncOffsetTable())740return EC;741}742break;743case SecFuncMetadata: {744ProfileIsProbeBased =745hasSecFlag(Entry, SecFuncMetadataFlags::SecFlagIsProbeBased);746FunctionSamples::ProfileIsProbeBased = ProfileIsProbeBased;747bool HasAttribute =748hasSecFlag(Entry, SecFuncMetadataFlags::SecFlagHasAttribute);749if (std::error_code EC = readFuncMetadata(HasAttribute))750return EC;751break;752}753case SecProfileSymbolList:754if (std::error_code EC = readProfileSymbolList())755return EC;756break;757default:758if (std::error_code EC = readCustomSection(Entry))759return EC;760break;761}762return sampleprof_error::success;763}764765bool SampleProfileReaderExtBinaryBase::useFuncOffsetList() const {766// If profile is CS, the function offset section is expected to consist of767// sequences of contexts in pre-order layout768// (e.g. [A, A:1 @ B, A:1 @ B:2.3 @ C] [D, D:1 @ E]), so that when a matched769// context in the module is found, the profiles of all its callees are770// recursively loaded. A list is needed since the order of profiles matters.771if (ProfileIsCS)772return true;773774// If the profile is MD5, use the map container to lookup functions in775// the module. A remapper has no use on MD5 names.776if (useMD5())777return false;778779// Profile is not MD5 and if a remapper is present, the remapped name of780// every function needed to be matched against the module, so use the list781// container since each entry is accessed.782if (Remapper)783return true;784785// Otherwise use the map container for faster lookup.786// TODO: If the cardinality of the function offset section is much smaller787// than the number of functions in the module, using the list container can788// be always faster, but we need to figure out the constant factor to789// determine the cutoff.790return false;791}792793794bool SampleProfileReaderExtBinaryBase::collectFuncsFromModule() {795if (!M)796return false;797FuncsToUse.clear();798for (auto &F : *M)799FuncsToUse.insert(FunctionSamples::getCanonicalFnName(F));800return true;801}802803std::error_code SampleProfileReaderExtBinaryBase::readFuncOffsetTable() {804// If there are more than one function offset section, the profile associated805// with the previous section has to be done reading before next one is read.806FuncOffsetTable.clear();807FuncOffsetList.clear();808809auto Size = readNumber<uint64_t>();810if (std::error_code EC = Size.getError())811return EC;812813bool UseFuncOffsetList = useFuncOffsetList();814if (UseFuncOffsetList)815FuncOffsetList.reserve(*Size);816else817FuncOffsetTable.reserve(*Size);818819for (uint64_t I = 0; I < *Size; ++I) {820auto FContextHash(readSampleContextFromTable());821if (std::error_code EC = FContextHash.getError())822return EC;823824auto &[FContext, Hash] = *FContextHash;825auto Offset = readNumber<uint64_t>();826if (std::error_code EC = Offset.getError())827return EC;828829if (UseFuncOffsetList)830FuncOffsetList.emplace_back(FContext, *Offset);831else832// Because Porfiles replace existing value with new value if collision833// happens, we also use the latest offset so that they are consistent.834FuncOffsetTable[Hash] = *Offset;835}836837return sampleprof_error::success;838}839840std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {841// Collect functions used by current module if the Reader has been842// given a module.843// collectFuncsFromModule uses FunctionSamples::getCanonicalFnName844// which will query FunctionSamples::HasUniqSuffix, so it has to be845// called after FunctionSamples::HasUniqSuffix is set, i.e. after846// NameTable section is read.847bool LoadFuncsToBeUsed = collectFuncsFromModule();848849// When LoadFuncsToBeUsed is false, we are using LLVM tool, need to read all850// profiles.851const uint8_t *Start = Data;852if (!LoadFuncsToBeUsed) {853while (Data < End) {854if (std::error_code EC = readFuncProfile(Data))855return EC;856}857assert(Data == End && "More data is read than expected");858} else {859// Load function profiles on demand.860if (Remapper) {861for (auto Name : FuncsToUse) {862Remapper->insert(Name);863}864}865866if (ProfileIsCS) {867assert(useFuncOffsetList());868DenseSet<uint64_t> FuncGuidsToUse;869if (useMD5()) {870for (auto Name : FuncsToUse)871FuncGuidsToUse.insert(Function::getGUID(Name));872}873874// For each function in current module, load all context profiles for875// the function as well as their callee contexts which can help profile876// guided importing for ThinLTO. This can be achieved by walking877// through an ordered context container, where contexts are laid out878// as if they were walked in preorder of a context trie. While879// traversing the trie, a link to the highest common ancestor node is880// kept so that all of its decendants will be loaded.881const SampleContext *CommonContext = nullptr;882for (const auto &NameOffset : FuncOffsetList) {883const auto &FContext = NameOffset.first;884FunctionId FName = FContext.getFunction();885StringRef FNameString;886if (!useMD5())887FNameString = FName.stringRef();888889// For function in the current module, keep its farthest ancestor890// context. This can be used to load itself and its child and891// sibling contexts.892if ((useMD5() && FuncGuidsToUse.count(FName.getHashCode())) ||893(!useMD5() && (FuncsToUse.count(FNameString) ||894(Remapper && Remapper->exist(FNameString))))) {895if (!CommonContext || !CommonContext->isPrefixOf(FContext))896CommonContext = &FContext;897}898899if (CommonContext == &FContext ||900(CommonContext && CommonContext->isPrefixOf(FContext))) {901// Load profile for the current context which originated from902// the common ancestor.903const uint8_t *FuncProfileAddr = Start + NameOffset.second;904if (std::error_code EC = readFuncProfile(FuncProfileAddr))905return EC;906}907}908} else if (useMD5()) {909assert(!useFuncOffsetList());910for (auto Name : FuncsToUse) {911auto GUID = MD5Hash(Name);912auto iter = FuncOffsetTable.find(GUID);913if (iter == FuncOffsetTable.end())914continue;915const uint8_t *FuncProfileAddr = Start + iter->second;916if (std::error_code EC = readFuncProfile(FuncProfileAddr))917return EC;918}919} else if (Remapper) {920assert(useFuncOffsetList());921for (auto NameOffset : FuncOffsetList) {922SampleContext FContext(NameOffset.first);923auto FuncName = FContext.getFunction();924StringRef FuncNameStr = FuncName.stringRef();925if (!FuncsToUse.count(FuncNameStr) && !Remapper->exist(FuncNameStr))926continue;927const uint8_t *FuncProfileAddr = Start + NameOffset.second;928if (std::error_code EC = readFuncProfile(FuncProfileAddr))929return EC;930}931} else {932assert(!useFuncOffsetList());933for (auto Name : FuncsToUse) {934auto iter = FuncOffsetTable.find(MD5Hash(Name));935if (iter == FuncOffsetTable.end())936continue;937const uint8_t *FuncProfileAddr = Start + iter->second;938if (std::error_code EC = readFuncProfile(FuncProfileAddr))939return EC;940}941}942Data = End;943}944assert((CSProfileCount == 0 || CSProfileCount == Profiles.size()) &&945"Cannot have both context-sensitive and regular profile");946assert((!CSProfileCount || ProfileIsCS) &&947"Section flag should be consistent with actual profile");948return sampleprof_error::success;949}950951std::error_code SampleProfileReaderExtBinaryBase::readProfileSymbolList() {952if (!ProfSymList)953ProfSymList = std::make_unique<ProfileSymbolList>();954955if (std::error_code EC = ProfSymList->read(Data, End - Data))956return EC;957958Data = End;959return sampleprof_error::success;960}961962std::error_code SampleProfileReaderExtBinaryBase::decompressSection(963const uint8_t *SecStart, const uint64_t SecSize,964const uint8_t *&DecompressBuf, uint64_t &DecompressBufSize) {965Data = SecStart;966End = SecStart + SecSize;967auto DecompressSize = readNumber<uint64_t>();968if (std::error_code EC = DecompressSize.getError())969return EC;970DecompressBufSize = *DecompressSize;971972auto CompressSize = readNumber<uint64_t>();973if (std::error_code EC = CompressSize.getError())974return EC;975976if (!llvm::compression::zlib::isAvailable())977return sampleprof_error::zlib_unavailable;978979uint8_t *Buffer = Allocator.Allocate<uint8_t>(DecompressBufSize);980size_t UCSize = DecompressBufSize;981llvm::Error E = compression::zlib::decompress(ArrayRef(Data, *CompressSize),982Buffer, UCSize);983if (E)984return sampleprof_error::uncompress_failed;985DecompressBuf = reinterpret_cast<const uint8_t *>(Buffer);986return sampleprof_error::success;987}988989std::error_code SampleProfileReaderExtBinaryBase::readImpl() {990const uint8_t *BufStart =991reinterpret_cast<const uint8_t *>(Buffer->getBufferStart());992993for (auto &Entry : SecHdrTable) {994// Skip empty section.995if (!Entry.Size)996continue;997998// Skip sections without context when SkipFlatProf is true.999if (SkipFlatProf && hasSecFlag(Entry, SecCommonFlags::SecFlagFlat))1000continue;10011002const uint8_t *SecStart = BufStart + Entry.Offset;1003uint64_t SecSize = Entry.Size;10041005// If the section is compressed, decompress it into a buffer1006// DecompressBuf before reading the actual data. The pointee of1007// 'Data' will be changed to buffer hold by DecompressBuf1008// temporarily when reading the actual data.1009bool isCompressed = hasSecFlag(Entry, SecCommonFlags::SecFlagCompress);1010if (isCompressed) {1011const uint8_t *DecompressBuf;1012uint64_t DecompressBufSize;1013if (std::error_code EC = decompressSection(1014SecStart, SecSize, DecompressBuf, DecompressBufSize))1015return EC;1016SecStart = DecompressBuf;1017SecSize = DecompressBufSize;1018}10191020if (std::error_code EC = readOneSection(SecStart, SecSize, Entry))1021return EC;1022if (Data != SecStart + SecSize)1023return sampleprof_error::malformed;10241025// Change the pointee of 'Data' from DecompressBuf to original Buffer.1026if (isCompressed) {1027Data = BufStart + Entry.Offset;1028End = BufStart + Buffer->getBufferSize();1029}1030}10311032return sampleprof_error::success;1033}10341035std::error_code SampleProfileReaderRawBinary::verifySPMagic(uint64_t Magic) {1036if (Magic == SPMagic())1037return sampleprof_error::success;1038return sampleprof_error::bad_magic;1039}10401041std::error_code SampleProfileReaderExtBinary::verifySPMagic(uint64_t Magic) {1042if (Magic == SPMagic(SPF_Ext_Binary))1043return sampleprof_error::success;1044return sampleprof_error::bad_magic;1045}10461047std::error_code SampleProfileReaderBinary::readNameTable() {1048auto Size = readNumber<size_t>();1049if (std::error_code EC = Size.getError())1050return EC;10511052// Normally if useMD5 is true, the name table should have MD5 values, not1053// strings, however in the case that ExtBinary profile has multiple name1054// tables mixing string and MD5, all of them have to be normalized to use MD5,1055// because optimization passes can only handle either type.1056bool UseMD5 = useMD5();10571058NameTable.clear();1059NameTable.reserve(*Size);1060if (!ProfileIsCS) {1061MD5SampleContextTable.clear();1062if (UseMD5)1063MD5SampleContextTable.reserve(*Size);1064else1065// If we are using strings, delay MD5 computation since only a portion of1066// names are used by top level functions. Use 0 to indicate MD5 value is1067// to be calculated as no known string has a MD5 value of 0.1068MD5SampleContextTable.resize(*Size);1069}1070for (size_t I = 0; I < *Size; ++I) {1071auto Name(readString());1072if (std::error_code EC = Name.getError())1073return EC;1074if (UseMD5) {1075FunctionId FID(*Name);1076if (!ProfileIsCS)1077MD5SampleContextTable.emplace_back(FID.getHashCode());1078NameTable.emplace_back(FID);1079} else1080NameTable.push_back(FunctionId(*Name));1081}1082if (!ProfileIsCS)1083MD5SampleContextStart = MD5SampleContextTable.data();1084return sampleprof_error::success;1085}10861087std::error_code1088SampleProfileReaderExtBinaryBase::readNameTableSec(bool IsMD5,1089bool FixedLengthMD5) {1090if (FixedLengthMD5) {1091if (!IsMD5)1092errs() << "If FixedLengthMD5 is true, UseMD5 has to be true";1093auto Size = readNumber<size_t>();1094if (std::error_code EC = Size.getError())1095return EC;10961097assert(Data + (*Size) * sizeof(uint64_t) == End &&1098"Fixed length MD5 name table does not contain specified number of "1099"entries");1100if (Data + (*Size) * sizeof(uint64_t) > End)1101return sampleprof_error::truncated;11021103NameTable.clear();1104NameTable.reserve(*Size);1105for (size_t I = 0; I < *Size; ++I) {1106using namespace support;1107uint64_t FID = endian::read<uint64_t, endianness::little, unaligned>(1108Data + I * sizeof(uint64_t));1109NameTable.emplace_back(FunctionId(FID));1110}1111if (!ProfileIsCS)1112MD5SampleContextStart = reinterpret_cast<const uint64_t *>(Data);1113Data = Data + (*Size) * sizeof(uint64_t);1114return sampleprof_error::success;1115}11161117if (IsMD5) {1118assert(!FixedLengthMD5 && "FixedLengthMD5 should be unreachable here");1119auto Size = readNumber<size_t>();1120if (std::error_code EC = Size.getError())1121return EC;11221123NameTable.clear();1124NameTable.reserve(*Size);1125if (!ProfileIsCS)1126MD5SampleContextTable.resize(*Size);1127for (size_t I = 0; I < *Size; ++I) {1128auto FID = readNumber<uint64_t>();1129if (std::error_code EC = FID.getError())1130return EC;1131if (!ProfileIsCS)1132support::endian::write64le(&MD5SampleContextTable[I], *FID);1133NameTable.emplace_back(FunctionId(*FID));1134}1135if (!ProfileIsCS)1136MD5SampleContextStart = MD5SampleContextTable.data();1137return sampleprof_error::success;1138}11391140return SampleProfileReaderBinary::readNameTable();1141}11421143// Read in the CS name table section, which basically contains a list of context1144// vectors. Each element of a context vector, aka a frame, refers to the1145// underlying raw function names that are stored in the name table, as well as1146// a callsite identifier that only makes sense for non-leaf frames.1147std::error_code SampleProfileReaderExtBinaryBase::readCSNameTableSec() {1148auto Size = readNumber<size_t>();1149if (std::error_code EC = Size.getError())1150return EC;11511152CSNameTable.clear();1153CSNameTable.reserve(*Size);1154if (ProfileIsCS) {1155// Delay MD5 computation of CS context until they are needed. Use 0 to1156// indicate MD5 value is to be calculated as no known string has a MD51157// value of 0.1158MD5SampleContextTable.clear();1159MD5SampleContextTable.resize(*Size);1160MD5SampleContextStart = MD5SampleContextTable.data();1161}1162for (size_t I = 0; I < *Size; ++I) {1163CSNameTable.emplace_back(SampleContextFrameVector());1164auto ContextSize = readNumber<uint32_t>();1165if (std::error_code EC = ContextSize.getError())1166return EC;1167for (uint32_t J = 0; J < *ContextSize; ++J) {1168auto FName(readStringFromTable());1169if (std::error_code EC = FName.getError())1170return EC;1171auto LineOffset = readNumber<uint64_t>();1172if (std::error_code EC = LineOffset.getError())1173return EC;11741175if (!isOffsetLegal(*LineOffset))1176return std::error_code();11771178auto Discriminator = readNumber<uint64_t>();1179if (std::error_code EC = Discriminator.getError())1180return EC;11811182CSNameTable.back().emplace_back(1183FName.get(), LineLocation(LineOffset.get(), Discriminator.get()));1184}1185}11861187return sampleprof_error::success;1188}11891190std::error_code1191SampleProfileReaderExtBinaryBase::readFuncMetadata(bool ProfileHasAttribute,1192FunctionSamples *FProfile) {1193if (Data < End) {1194if (ProfileIsProbeBased) {1195auto Checksum = readNumber<uint64_t>();1196if (std::error_code EC = Checksum.getError())1197return EC;1198if (FProfile)1199FProfile->setFunctionHash(*Checksum);1200}12011202if (ProfileHasAttribute) {1203auto Attributes = readNumber<uint32_t>();1204if (std::error_code EC = Attributes.getError())1205return EC;1206if (FProfile)1207FProfile->getContext().setAllAttributes(*Attributes);1208}12091210if (!ProfileIsCS) {1211// Read all the attributes for inlined function calls.1212auto NumCallsites = readNumber<uint32_t>();1213if (std::error_code EC = NumCallsites.getError())1214return EC;12151216for (uint32_t J = 0; J < *NumCallsites; ++J) {1217auto LineOffset = readNumber<uint64_t>();1218if (std::error_code EC = LineOffset.getError())1219return EC;12201221auto Discriminator = readNumber<uint64_t>();1222if (std::error_code EC = Discriminator.getError())1223return EC;12241225auto FContextHash(readSampleContextFromTable());1226if (std::error_code EC = FContextHash.getError())1227return EC;12281229auto &[FContext, Hash] = *FContextHash;1230FunctionSamples *CalleeProfile = nullptr;1231if (FProfile) {1232CalleeProfile = const_cast<FunctionSamples *>(1233&FProfile->functionSamplesAt(LineLocation(1234*LineOffset,1235*Discriminator))[FContext.getFunction()]);1236}1237if (std::error_code EC =1238readFuncMetadata(ProfileHasAttribute, CalleeProfile))1239return EC;1240}1241}1242}12431244return sampleprof_error::success;1245}12461247std::error_code1248SampleProfileReaderExtBinaryBase::readFuncMetadata(bool ProfileHasAttribute) {1249while (Data < End) {1250auto FContextHash(readSampleContextFromTable());1251if (std::error_code EC = FContextHash.getError())1252return EC;1253auto &[FContext, Hash] = *FContextHash;1254FunctionSamples *FProfile = nullptr;1255auto It = Profiles.find(FContext);1256if (It != Profiles.end())1257FProfile = &It->second;12581259if (std::error_code EC = readFuncMetadata(ProfileHasAttribute, FProfile))1260return EC;1261}12621263assert(Data == End && "More data is read than expected");1264return sampleprof_error::success;1265}12661267std::error_code1268SampleProfileReaderExtBinaryBase::readSecHdrTableEntry(uint64_t Idx) {1269SecHdrTableEntry Entry;1270auto Type = readUnencodedNumber<uint64_t>();1271if (std::error_code EC = Type.getError())1272return EC;1273Entry.Type = static_cast<SecType>(*Type);12741275auto Flags = readUnencodedNumber<uint64_t>();1276if (std::error_code EC = Flags.getError())1277return EC;1278Entry.Flags = *Flags;12791280auto Offset = readUnencodedNumber<uint64_t>();1281if (std::error_code EC = Offset.getError())1282return EC;1283Entry.Offset = *Offset;12841285auto Size = readUnencodedNumber<uint64_t>();1286if (std::error_code EC = Size.getError())1287return EC;1288Entry.Size = *Size;12891290Entry.LayoutIndex = Idx;1291SecHdrTable.push_back(std::move(Entry));1292return sampleprof_error::success;1293}12941295std::error_code SampleProfileReaderExtBinaryBase::readSecHdrTable() {1296auto EntryNum = readUnencodedNumber<uint64_t>();1297if (std::error_code EC = EntryNum.getError())1298return EC;12991300for (uint64_t i = 0; i < (*EntryNum); i++)1301if (std::error_code EC = readSecHdrTableEntry(i))1302return EC;13031304return sampleprof_error::success;1305}13061307std::error_code SampleProfileReaderExtBinaryBase::readHeader() {1308const uint8_t *BufStart =1309reinterpret_cast<const uint8_t *>(Buffer->getBufferStart());1310Data = BufStart;1311End = BufStart + Buffer->getBufferSize();13121313if (std::error_code EC = readMagicIdent())1314return EC;13151316if (std::error_code EC = readSecHdrTable())1317return EC;13181319return sampleprof_error::success;1320}13211322uint64_t SampleProfileReaderExtBinaryBase::getSectionSize(SecType Type) {1323uint64_t Size = 0;1324for (auto &Entry : SecHdrTable) {1325if (Entry.Type == Type)1326Size += Entry.Size;1327}1328return Size;1329}13301331uint64_t SampleProfileReaderExtBinaryBase::getFileSize() {1332// Sections in SecHdrTable is not necessarily in the same order as1333// sections in the profile because section like FuncOffsetTable needs1334// to be written after section LBRProfile but needs to be read before1335// section LBRProfile, so we cannot simply use the last entry in1336// SecHdrTable to calculate the file size.1337uint64_t FileSize = 0;1338for (auto &Entry : SecHdrTable) {1339FileSize = std::max(Entry.Offset + Entry.Size, FileSize);1340}1341return FileSize;1342}13431344static std::string getSecFlagsStr(const SecHdrTableEntry &Entry) {1345std::string Flags;1346if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress))1347Flags.append("{compressed,");1348else1349Flags.append("{");13501351if (hasSecFlag(Entry, SecCommonFlags::SecFlagFlat))1352Flags.append("flat,");13531354switch (Entry.Type) {1355case SecNameTable:1356if (hasSecFlag(Entry, SecNameTableFlags::SecFlagFixedLengthMD5))1357Flags.append("fixlenmd5,");1358else if (hasSecFlag(Entry, SecNameTableFlags::SecFlagMD5Name))1359Flags.append("md5,");1360if (hasSecFlag(Entry, SecNameTableFlags::SecFlagUniqSuffix))1361Flags.append("uniq,");1362break;1363case SecProfSummary:1364if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagPartial))1365Flags.append("partial,");1366if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagFullContext))1367Flags.append("context,");1368if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagIsPreInlined))1369Flags.append("preInlined,");1370if (hasSecFlag(Entry, SecProfSummaryFlags::SecFlagFSDiscriminator))1371Flags.append("fs-discriminator,");1372break;1373case SecFuncOffsetTable:1374if (hasSecFlag(Entry, SecFuncOffsetFlags::SecFlagOrdered))1375Flags.append("ordered,");1376break;1377case SecFuncMetadata:1378if (hasSecFlag(Entry, SecFuncMetadataFlags::SecFlagIsProbeBased))1379Flags.append("probe,");1380if (hasSecFlag(Entry, SecFuncMetadataFlags::SecFlagHasAttribute))1381Flags.append("attr,");1382break;1383default:1384break;1385}1386char &last = Flags.back();1387if (last == ',')1388last = '}';1389else1390Flags.append("}");1391return Flags;1392}13931394bool SampleProfileReaderExtBinaryBase::dumpSectionInfo(raw_ostream &OS) {1395uint64_t TotalSecsSize = 0;1396for (auto &Entry : SecHdrTable) {1397OS << getSecName(Entry.Type) << " - Offset: " << Entry.Offset1398<< ", Size: " << Entry.Size << ", Flags: " << getSecFlagsStr(Entry)1399<< "\n";1400;1401TotalSecsSize += Entry.Size;1402}1403uint64_t HeaderSize = SecHdrTable.front().Offset;1404assert(HeaderSize + TotalSecsSize == getFileSize() &&1405"Size of 'header + sections' doesn't match the total size of profile");14061407OS << "Header Size: " << HeaderSize << "\n";1408OS << "Total Sections Size: " << TotalSecsSize << "\n";1409OS << "File Size: " << getFileSize() << "\n";1410return true;1411}14121413std::error_code SampleProfileReaderBinary::readMagicIdent() {1414// Read and check the magic identifier.1415auto Magic = readNumber<uint64_t>();1416if (std::error_code EC = Magic.getError())1417return EC;1418else if (std::error_code EC = verifySPMagic(*Magic))1419return EC;14201421// Read the version number.1422auto Version = readNumber<uint64_t>();1423if (std::error_code EC = Version.getError())1424return EC;1425else if (*Version != SPVersion())1426return sampleprof_error::unsupported_version;14271428return sampleprof_error::success;1429}14301431std::error_code SampleProfileReaderBinary::readHeader() {1432Data = reinterpret_cast<const uint8_t *>(Buffer->getBufferStart());1433End = Data + Buffer->getBufferSize();14341435if (std::error_code EC = readMagicIdent())1436return EC;14371438if (std::error_code EC = readSummary())1439return EC;14401441if (std::error_code EC = readNameTable())1442return EC;1443return sampleprof_error::success;1444}14451446std::error_code SampleProfileReaderBinary::readSummaryEntry(1447std::vector<ProfileSummaryEntry> &Entries) {1448auto Cutoff = readNumber<uint64_t>();1449if (std::error_code EC = Cutoff.getError())1450return EC;14511452auto MinBlockCount = readNumber<uint64_t>();1453if (std::error_code EC = MinBlockCount.getError())1454return EC;14551456auto NumBlocks = readNumber<uint64_t>();1457if (std::error_code EC = NumBlocks.getError())1458return EC;14591460Entries.emplace_back(*Cutoff, *MinBlockCount, *NumBlocks);1461return sampleprof_error::success;1462}14631464std::error_code SampleProfileReaderBinary::readSummary() {1465auto TotalCount = readNumber<uint64_t>();1466if (std::error_code EC = TotalCount.getError())1467return EC;14681469auto MaxBlockCount = readNumber<uint64_t>();1470if (std::error_code EC = MaxBlockCount.getError())1471return EC;14721473auto MaxFunctionCount = readNumber<uint64_t>();1474if (std::error_code EC = MaxFunctionCount.getError())1475return EC;14761477auto NumBlocks = readNumber<uint64_t>();1478if (std::error_code EC = NumBlocks.getError())1479return EC;14801481auto NumFunctions = readNumber<uint64_t>();1482if (std::error_code EC = NumFunctions.getError())1483return EC;14841485auto NumSummaryEntries = readNumber<uint64_t>();1486if (std::error_code EC = NumSummaryEntries.getError())1487return EC;14881489std::vector<ProfileSummaryEntry> Entries;1490for (unsigned i = 0; i < *NumSummaryEntries; i++) {1491std::error_code EC = readSummaryEntry(Entries);1492if (EC != sampleprof_error::success)1493return EC;1494}1495Summary = std::make_unique<ProfileSummary>(1496ProfileSummary::PSK_Sample, Entries, *TotalCount, *MaxBlockCount, 0,1497*MaxFunctionCount, *NumBlocks, *NumFunctions);14981499return sampleprof_error::success;1500}15011502bool SampleProfileReaderRawBinary::hasFormat(const MemoryBuffer &Buffer) {1503const uint8_t *Data =1504reinterpret_cast<const uint8_t *>(Buffer.getBufferStart());1505uint64_t Magic = decodeULEB128(Data);1506return Magic == SPMagic();1507}15081509bool SampleProfileReaderExtBinary::hasFormat(const MemoryBuffer &Buffer) {1510const uint8_t *Data =1511reinterpret_cast<const uint8_t *>(Buffer.getBufferStart());1512uint64_t Magic = decodeULEB128(Data);1513return Magic == SPMagic(SPF_Ext_Binary);1514}15151516std::error_code SampleProfileReaderGCC::skipNextWord() {1517uint32_t dummy;1518if (!GcovBuffer.readInt(dummy))1519return sampleprof_error::truncated;1520return sampleprof_error::success;1521}15221523template <typename T> ErrorOr<T> SampleProfileReaderGCC::readNumber() {1524if (sizeof(T) <= sizeof(uint32_t)) {1525uint32_t Val;1526if (GcovBuffer.readInt(Val) && Val <= std::numeric_limits<T>::max())1527return static_cast<T>(Val);1528} else if (sizeof(T) <= sizeof(uint64_t)) {1529uint64_t Val;1530if (GcovBuffer.readInt64(Val) && Val <= std::numeric_limits<T>::max())1531return static_cast<T>(Val);1532}15331534std::error_code EC = sampleprof_error::malformed;1535reportError(0, EC.message());1536return EC;1537}15381539ErrorOr<StringRef> SampleProfileReaderGCC::readString() {1540StringRef Str;1541if (!GcovBuffer.readString(Str))1542return sampleprof_error::truncated;1543return Str;1544}15451546std::error_code SampleProfileReaderGCC::readHeader() {1547// Read the magic identifier.1548if (!GcovBuffer.readGCDAFormat())1549return sampleprof_error::unrecognized_format;15501551// Read the version number. Note - the GCC reader does not validate this1552// version, but the profile creator generates v704.1553GCOV::GCOVVersion version;1554if (!GcovBuffer.readGCOVVersion(version))1555return sampleprof_error::unrecognized_format;15561557if (version != GCOV::V407)1558return sampleprof_error::unsupported_version;15591560// Skip the empty integer.1561if (std::error_code EC = skipNextWord())1562return EC;15631564return sampleprof_error::success;1565}15661567std::error_code SampleProfileReaderGCC::readSectionTag(uint32_t Expected) {1568uint32_t Tag;1569if (!GcovBuffer.readInt(Tag))1570return sampleprof_error::truncated;15711572if (Tag != Expected)1573return sampleprof_error::malformed;15741575if (std::error_code EC = skipNextWord())1576return EC;15771578return sampleprof_error::success;1579}15801581std::error_code SampleProfileReaderGCC::readNameTable() {1582if (std::error_code EC = readSectionTag(GCOVTagAFDOFileNames))1583return EC;15841585uint32_t Size;1586if (!GcovBuffer.readInt(Size))1587return sampleprof_error::truncated;15881589for (uint32_t I = 0; I < Size; ++I) {1590StringRef Str;1591if (!GcovBuffer.readString(Str))1592return sampleprof_error::truncated;1593Names.push_back(std::string(Str));1594}15951596return sampleprof_error::success;1597}15981599std::error_code SampleProfileReaderGCC::readFunctionProfiles() {1600if (std::error_code EC = readSectionTag(GCOVTagAFDOFunction))1601return EC;16021603uint32_t NumFunctions;1604if (!GcovBuffer.readInt(NumFunctions))1605return sampleprof_error::truncated;16061607InlineCallStack Stack;1608for (uint32_t I = 0; I < NumFunctions; ++I)1609if (std::error_code EC = readOneFunctionProfile(Stack, true, 0))1610return EC;16111612computeSummary();1613return sampleprof_error::success;1614}16151616std::error_code SampleProfileReaderGCC::readOneFunctionProfile(1617const InlineCallStack &InlineStack, bool Update, uint32_t Offset) {1618uint64_t HeadCount = 0;1619if (InlineStack.size() == 0)1620if (!GcovBuffer.readInt64(HeadCount))1621return sampleprof_error::truncated;16221623uint32_t NameIdx;1624if (!GcovBuffer.readInt(NameIdx))1625return sampleprof_error::truncated;16261627StringRef Name(Names[NameIdx]);16281629uint32_t NumPosCounts;1630if (!GcovBuffer.readInt(NumPosCounts))1631return sampleprof_error::truncated;16321633uint32_t NumCallsites;1634if (!GcovBuffer.readInt(NumCallsites))1635return sampleprof_error::truncated;16361637FunctionSamples *FProfile = nullptr;1638if (InlineStack.size() == 0) {1639// If this is a top function that we have already processed, do not1640// update its profile again. This happens in the presence of1641// function aliases. Since these aliases share the same function1642// body, there will be identical replicated profiles for the1643// original function. In this case, we simply not bother updating1644// the profile of the original function.1645FProfile = &Profiles[FunctionId(Name)];1646FProfile->addHeadSamples(HeadCount);1647if (FProfile->getTotalSamples() > 0)1648Update = false;1649} else {1650// Otherwise, we are reading an inlined instance. The top of the1651// inline stack contains the profile of the caller. Insert this1652// callee in the caller's CallsiteMap.1653FunctionSamples *CallerProfile = InlineStack.front();1654uint32_t LineOffset = Offset >> 16;1655uint32_t Discriminator = Offset & 0xffff;1656FProfile = &CallerProfile->functionSamplesAt(1657LineLocation(LineOffset, Discriminator))[FunctionId(Name)];1658}1659FProfile->setFunction(FunctionId(Name));16601661for (uint32_t I = 0; I < NumPosCounts; ++I) {1662uint32_t Offset;1663if (!GcovBuffer.readInt(Offset))1664return sampleprof_error::truncated;16651666uint32_t NumTargets;1667if (!GcovBuffer.readInt(NumTargets))1668return sampleprof_error::truncated;16691670uint64_t Count;1671if (!GcovBuffer.readInt64(Count))1672return sampleprof_error::truncated;16731674// The line location is encoded in the offset as:1675// high 16 bits: line offset to the start of the function.1676// low 16 bits: discriminator.1677uint32_t LineOffset = Offset >> 16;1678uint32_t Discriminator = Offset & 0xffff;16791680InlineCallStack NewStack;1681NewStack.push_back(FProfile);1682llvm::append_range(NewStack, InlineStack);1683if (Update) {1684// Walk up the inline stack, adding the samples on this line to1685// the total sample count of the callers in the chain.1686for (auto *CallerProfile : NewStack)1687CallerProfile->addTotalSamples(Count);16881689// Update the body samples for the current profile.1690FProfile->addBodySamples(LineOffset, Discriminator, Count);1691}16921693// Process the list of functions called at an indirect call site.1694// These are all the targets that a function pointer (or virtual1695// function) resolved at runtime.1696for (uint32_t J = 0; J < NumTargets; J++) {1697uint32_t HistVal;1698if (!GcovBuffer.readInt(HistVal))1699return sampleprof_error::truncated;17001701if (HistVal != HIST_TYPE_INDIR_CALL_TOPN)1702return sampleprof_error::malformed;17031704uint64_t TargetIdx;1705if (!GcovBuffer.readInt64(TargetIdx))1706return sampleprof_error::truncated;1707StringRef TargetName(Names[TargetIdx]);17081709uint64_t TargetCount;1710if (!GcovBuffer.readInt64(TargetCount))1711return sampleprof_error::truncated;17121713if (Update)1714FProfile->addCalledTargetSamples(LineOffset, Discriminator,1715FunctionId(TargetName),1716TargetCount);1717}1718}17191720// Process all the inlined callers into the current function. These1721// are all the callsites that were inlined into this function.1722for (uint32_t I = 0; I < NumCallsites; I++) {1723// The offset is encoded as:1724// high 16 bits: line offset to the start of the function.1725// low 16 bits: discriminator.1726uint32_t Offset;1727if (!GcovBuffer.readInt(Offset))1728return sampleprof_error::truncated;1729InlineCallStack NewStack;1730NewStack.push_back(FProfile);1731llvm::append_range(NewStack, InlineStack);1732if (std::error_code EC = readOneFunctionProfile(NewStack, Update, Offset))1733return EC;1734}17351736return sampleprof_error::success;1737}17381739/// Read a GCC AutoFDO profile.1740///1741/// This format is generated by the Linux Perf conversion tool at1742/// https://github.com/google/autofdo.1743std::error_code SampleProfileReaderGCC::readImpl() {1744assert(!ProfileIsFSDisciminator && "Gcc profiles not support FSDisciminator");1745// Read the string table.1746if (std::error_code EC = readNameTable())1747return EC;17481749// Read the source profile.1750if (std::error_code EC = readFunctionProfiles())1751return EC;17521753return sampleprof_error::success;1754}17551756bool SampleProfileReaderGCC::hasFormat(const MemoryBuffer &Buffer) {1757StringRef Magic(reinterpret_cast<const char *>(Buffer.getBufferStart()));1758return Magic == "adcg*704";1759}17601761void SampleProfileReaderItaniumRemapper::applyRemapping(LLVMContext &Ctx) {1762// If the reader uses MD5 to represent string, we can't remap it because1763// we don't know what the original function names were.1764if (Reader.useMD5()) {1765Ctx.diagnose(DiagnosticInfoSampleProfile(1766Reader.getBuffer()->getBufferIdentifier(),1767"Profile data remapping cannot be applied to profile data "1768"using MD5 names (original mangled names are not available).",1769DS_Warning));1770return;1771}17721773// CSSPGO-TODO: Remapper is not yet supported.1774// We will need to remap the entire context string.1775assert(Remappings && "should be initialized while creating remapper");1776for (auto &Sample : Reader.getProfiles()) {1777DenseSet<FunctionId> NamesInSample;1778Sample.second.findAllNames(NamesInSample);1779for (auto &Name : NamesInSample) {1780StringRef NameStr = Name.stringRef();1781if (auto Key = Remappings->insert(NameStr))1782NameMap.insert({Key, NameStr});1783}1784}17851786RemappingApplied = true;1787}17881789std::optional<StringRef>1790SampleProfileReaderItaniumRemapper::lookUpNameInProfile(StringRef Fname) {1791if (auto Key = Remappings->lookup(Fname)) {1792StringRef Result = NameMap.lookup(Key);1793if (!Result.empty())1794return Result;1795}1796return std::nullopt;1797}17981799/// Prepare a memory buffer for the contents of \p Filename.1800///1801/// \returns an error code indicating the status of the buffer.1802static ErrorOr<std::unique_ptr<MemoryBuffer>>1803setupMemoryBuffer(const Twine &Filename, vfs::FileSystem &FS) {1804auto BufferOrErr = Filename.str() == "-" ? MemoryBuffer::getSTDIN()1805: FS.getBufferForFile(Filename);1806if (std::error_code EC = BufferOrErr.getError())1807return EC;1808auto Buffer = std::move(BufferOrErr.get());18091810return std::move(Buffer);1811}18121813/// Create a sample profile reader based on the format of the input file.1814///1815/// \param Filename The file to open.1816///1817/// \param C The LLVM context to use to emit diagnostics.1818///1819/// \param P The FSDiscriminatorPass.1820///1821/// \param RemapFilename The file used for profile remapping.1822///1823/// \returns an error code indicating the status of the created reader.1824ErrorOr<std::unique_ptr<SampleProfileReader>>1825SampleProfileReader::create(StringRef Filename, LLVMContext &C,1826vfs::FileSystem &FS, FSDiscriminatorPass P,1827StringRef RemapFilename) {1828auto BufferOrError = setupMemoryBuffer(Filename, FS);1829if (std::error_code EC = BufferOrError.getError())1830return EC;1831return create(BufferOrError.get(), C, FS, P, RemapFilename);1832}18331834/// Create a sample profile remapper from the given input, to remap the1835/// function names in the given profile data.1836///1837/// \param Filename The file to open.1838///1839/// \param Reader The profile reader the remapper is going to be applied to.1840///1841/// \param C The LLVM context to use to emit diagnostics.1842///1843/// \returns an error code indicating the status of the created reader.1844ErrorOr<std::unique_ptr<SampleProfileReaderItaniumRemapper>>1845SampleProfileReaderItaniumRemapper::create(StringRef Filename,1846vfs::FileSystem &FS,1847SampleProfileReader &Reader,1848LLVMContext &C) {1849auto BufferOrError = setupMemoryBuffer(Filename, FS);1850if (std::error_code EC = BufferOrError.getError())1851return EC;1852return create(BufferOrError.get(), Reader, C);1853}18541855/// Create a sample profile remapper from the given input, to remap the1856/// function names in the given profile data.1857///1858/// \param B The memory buffer to create the reader from (assumes ownership).1859///1860/// \param C The LLVM context to use to emit diagnostics.1861///1862/// \param Reader The profile reader the remapper is going to be applied to.1863///1864/// \returns an error code indicating the status of the created reader.1865ErrorOr<std::unique_ptr<SampleProfileReaderItaniumRemapper>>1866SampleProfileReaderItaniumRemapper::create(std::unique_ptr<MemoryBuffer> &B,1867SampleProfileReader &Reader,1868LLVMContext &C) {1869auto Remappings = std::make_unique<SymbolRemappingReader>();1870if (Error E = Remappings->read(*B)) {1871handleAllErrors(1872std::move(E), [&](const SymbolRemappingParseError &ParseError) {1873C.diagnose(DiagnosticInfoSampleProfile(B->getBufferIdentifier(),1874ParseError.getLineNum(),1875ParseError.getMessage()));1876});1877return sampleprof_error::malformed;1878}18791880return std::make_unique<SampleProfileReaderItaniumRemapper>(1881std::move(B), std::move(Remappings), Reader);1882}18831884/// Create a sample profile reader based on the format of the input data.1885///1886/// \param B The memory buffer to create the reader from (assumes ownership).1887///1888/// \param C The LLVM context to use to emit diagnostics.1889///1890/// \param P The FSDiscriminatorPass.1891///1892/// \param RemapFilename The file used for profile remapping.1893///1894/// \returns an error code indicating the status of the created reader.1895ErrorOr<std::unique_ptr<SampleProfileReader>>1896SampleProfileReader::create(std::unique_ptr<MemoryBuffer> &B, LLVMContext &C,1897vfs::FileSystem &FS, FSDiscriminatorPass P,1898StringRef RemapFilename) {1899std::unique_ptr<SampleProfileReader> Reader;1900if (SampleProfileReaderRawBinary::hasFormat(*B))1901Reader.reset(new SampleProfileReaderRawBinary(std::move(B), C));1902else if (SampleProfileReaderExtBinary::hasFormat(*B))1903Reader.reset(new SampleProfileReaderExtBinary(std::move(B), C));1904else if (SampleProfileReaderGCC::hasFormat(*B))1905Reader.reset(new SampleProfileReaderGCC(std::move(B), C));1906else if (SampleProfileReaderText::hasFormat(*B))1907Reader.reset(new SampleProfileReaderText(std::move(B), C));1908else1909return sampleprof_error::unrecognized_format;19101911if (!RemapFilename.empty()) {1912auto ReaderOrErr = SampleProfileReaderItaniumRemapper::create(1913RemapFilename, FS, *Reader, C);1914if (std::error_code EC = ReaderOrErr.getError()) {1915std::string Msg = "Could not create remapper: " + EC.message();1916C.diagnose(DiagnosticInfoSampleProfile(RemapFilename, Msg));1917return EC;1918}1919Reader->Remapper = std::move(ReaderOrErr.get());1920}19211922if (std::error_code EC = Reader->readHeader()) {1923return EC;1924}19251926Reader->setDiscriminatorMaskedBitFrom(P);19271928return std::move(Reader);1929}19301931// For text and GCC file formats, we compute the summary after reading the1932// profile. Binary format has the profile summary in its header.1933void SampleProfileReader::computeSummary() {1934SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);1935Summary = Builder.computeSummaryForProfiles(Profiles);1936}193719381939