Path: blob/main/contrib/llvm-project/llvm/lib/ProfileData/SampleProfWriter.cpp
35233 views
//===- SampleProfWriter.cpp - Write 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 writes LLVM sample profiles. It9// supports two file formats: text and binary. The textual representation10// is useful for debugging and testing purposes. The binary representation11// is more compact, resulting in smaller file sizes. However, they can12// both be used interchangeably.13//14// See lib/ProfileData/SampleProfReader.cpp for documentation on each of the15// supported formats.16//17//===----------------------------------------------------------------------===//1819#include "llvm/ProfileData/SampleProfWriter.h"20#include "llvm/ADT/StringRef.h"21#include "llvm/ProfileData/ProfileCommon.h"22#include "llvm/ProfileData/SampleProf.h"23#include "llvm/Support/Compression.h"24#include "llvm/Support/Endian.h"25#include "llvm/Support/EndianStream.h"26#include "llvm/Support/ErrorOr.h"27#include "llvm/Support/FileSystem.h"28#include "llvm/Support/LEB128.h"29#include "llvm/Support/MD5.h"30#include "llvm/Support/raw_ostream.h"31#include <algorithm>32#include <cmath>33#include <cstdint>34#include <memory>35#include <set>36#include <system_error>37#include <utility>38#include <vector>3940#define DEBUG_TYPE "llvm-profdata"4142using namespace llvm;43using namespace sampleprof;4445namespace llvm {46namespace support {47namespace endian {48namespace {4950// Adapter class to llvm::support::endian::Writer for pwrite().51struct SeekableWriter {52raw_pwrite_stream &OS;53endianness Endian;54SeekableWriter(raw_pwrite_stream &OS, endianness Endian)55: OS(OS), Endian(Endian) {}5657template <typename ValueType>58void pwrite(ValueType Val, size_t Offset) {59std::string StringBuf;60raw_string_ostream SStream(StringBuf);61Writer(SStream, Endian).write(Val);62OS.pwrite(StringBuf.data(), StringBuf.size(), Offset);63}64};6566} // namespace67} // namespace endian68} // namespace support69} // namespace llvm7071DefaultFunctionPruningStrategy::DefaultFunctionPruningStrategy(72SampleProfileMap &ProfileMap, size_t OutputSizeLimit)73: FunctionPruningStrategy(ProfileMap, OutputSizeLimit) {74sortFuncProfiles(ProfileMap, SortedFunctions);75}7677void DefaultFunctionPruningStrategy::Erase(size_t CurrentOutputSize) {78double D = (double)OutputSizeLimit / CurrentOutputSize;79size_t NewSize = (size_t)round(ProfileMap.size() * D * D);80size_t NumToRemove = ProfileMap.size() - NewSize;81if (NumToRemove < 1)82NumToRemove = 1;8384assert(NumToRemove <= SortedFunctions.size());85for (const NameFunctionSamples &E :86llvm::drop_begin(SortedFunctions, SortedFunctions.size() - NumToRemove))87ProfileMap.erase(E.first);88SortedFunctions.resize(SortedFunctions.size() - NumToRemove);89}9091std::error_code SampleProfileWriter::writeWithSizeLimitInternal(92SampleProfileMap &ProfileMap, size_t OutputSizeLimit,93FunctionPruningStrategy *Strategy) {94if (OutputSizeLimit == 0)95return write(ProfileMap);9697size_t OriginalFunctionCount = ProfileMap.size();9899std::unique_ptr<raw_ostream> OriginalOutputStream;100OutputStream.swap(OriginalOutputStream);101102size_t IterationCount = 0;103size_t TotalSize;104105SmallVector<char> StringBuffer;106do {107StringBuffer.clear();108OutputStream.reset(new raw_svector_ostream(StringBuffer));109if (std::error_code EC = write(ProfileMap))110return EC;111112TotalSize = StringBuffer.size();113// On Windows every "\n" is actually written as "\r\n" to disk but not to114// memory buffer, this difference should be added when considering the total115// output size.116#ifdef _WIN32117if (Format == SPF_Text)118TotalSize += LineCount;119#endif120if (TotalSize <= OutputSizeLimit)121break;122123Strategy->Erase(TotalSize);124IterationCount++;125} while (ProfileMap.size() != 0);126127if (ProfileMap.size() == 0)128return sampleprof_error::too_large;129130OutputStream.swap(OriginalOutputStream);131OutputStream->write(StringBuffer.data(), StringBuffer.size());132LLVM_DEBUG(dbgs() << "Profile originally has " << OriginalFunctionCount133<< " functions, reduced to " << ProfileMap.size() << " in "134<< IterationCount << " iterations\n");135// Silence warning on Release build.136(void)OriginalFunctionCount;137(void)IterationCount;138return sampleprof_error::success;139}140141std::error_code142SampleProfileWriter::writeFuncProfiles(const SampleProfileMap &ProfileMap) {143std::vector<NameFunctionSamples> V;144sortFuncProfiles(ProfileMap, V);145for (const auto &I : V) {146if (std::error_code EC = writeSample(*I.second))147return EC;148}149return sampleprof_error::success;150}151152std::error_code SampleProfileWriter::write(const SampleProfileMap &ProfileMap) {153if (std::error_code EC = writeHeader(ProfileMap))154return EC;155156if (std::error_code EC = writeFuncProfiles(ProfileMap))157return EC;158159return sampleprof_error::success;160}161162/// Return the current position and prepare to use it as the start163/// position of a section given the section type \p Type and its position164/// \p LayoutIdx in SectionHdrLayout.165uint64_t166SampleProfileWriterExtBinaryBase::markSectionStart(SecType Type,167uint32_t LayoutIdx) {168uint64_t SectionStart = OutputStream->tell();169assert(LayoutIdx < SectionHdrLayout.size() && "LayoutIdx out of range");170const auto &Entry = SectionHdrLayout[LayoutIdx];171assert(Entry.Type == Type && "Unexpected section type");172// Use LocalBuf as a temporary output for writting data.173if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress))174LocalBufStream.swap(OutputStream);175return SectionStart;176}177178std::error_code SampleProfileWriterExtBinaryBase::compressAndOutput() {179if (!llvm::compression::zlib::isAvailable())180return sampleprof_error::zlib_unavailable;181std::string &UncompressedStrings =182static_cast<raw_string_ostream *>(LocalBufStream.get())->str();183if (UncompressedStrings.size() == 0)184return sampleprof_error::success;185auto &OS = *OutputStream;186SmallVector<uint8_t, 128> CompressedStrings;187compression::zlib::compress(arrayRefFromStringRef(UncompressedStrings),188CompressedStrings,189compression::zlib::BestSizeCompression);190encodeULEB128(UncompressedStrings.size(), OS);191encodeULEB128(CompressedStrings.size(), OS);192OS << toStringRef(CompressedStrings);193UncompressedStrings.clear();194return sampleprof_error::success;195}196197/// Add a new section into section header table given the section type198/// \p Type, its position \p LayoutIdx in SectionHdrLayout and the199/// location \p SectionStart where the section should be written to.200std::error_code SampleProfileWriterExtBinaryBase::addNewSection(201SecType Type, uint32_t LayoutIdx, uint64_t SectionStart) {202assert(LayoutIdx < SectionHdrLayout.size() && "LayoutIdx out of range");203const auto &Entry = SectionHdrLayout[LayoutIdx];204assert(Entry.Type == Type && "Unexpected section type");205if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress)) {206LocalBufStream.swap(OutputStream);207if (std::error_code EC = compressAndOutput())208return EC;209}210SecHdrTable.push_back({Type, Entry.Flags, SectionStart - FileStart,211OutputStream->tell() - SectionStart, LayoutIdx});212return sampleprof_error::success;213}214215std::error_code216SampleProfileWriterExtBinaryBase::write(const SampleProfileMap &ProfileMap) {217// When calling write on a different profile map, existing states should be218// cleared.219NameTable.clear();220CSNameTable.clear();221SecHdrTable.clear();222223if (std::error_code EC = writeHeader(ProfileMap))224return EC;225226std::string LocalBuf;227LocalBufStream = std::make_unique<raw_string_ostream>(LocalBuf);228if (std::error_code EC = writeSections(ProfileMap))229return EC;230231if (std::error_code EC = writeSecHdrTable())232return EC;233234return sampleprof_error::success;235}236237std::error_code SampleProfileWriterExtBinaryBase::writeContextIdx(238const SampleContext &Context) {239if (Context.hasContext())240return writeCSNameIdx(Context);241else242return SampleProfileWriterBinary::writeNameIdx(Context.getFunction());243}244245std::error_code246SampleProfileWriterExtBinaryBase::writeCSNameIdx(const SampleContext &Context) {247const auto &Ret = CSNameTable.find(Context);248if (Ret == CSNameTable.end())249return sampleprof_error::truncated_name_table;250encodeULEB128(Ret->second, *OutputStream);251return sampleprof_error::success;252}253254std::error_code255SampleProfileWriterExtBinaryBase::writeSample(const FunctionSamples &S) {256uint64_t Offset = OutputStream->tell();257auto &Context = S.getContext();258FuncOffsetTable[Context] = Offset - SecLBRProfileStart;259encodeULEB128(S.getHeadSamples(), *OutputStream);260return writeBody(S);261}262263std::error_code SampleProfileWriterExtBinaryBase::writeFuncOffsetTable() {264auto &OS = *OutputStream;265266// Write out the table size.267encodeULEB128(FuncOffsetTable.size(), OS);268269// Write out FuncOffsetTable.270auto WriteItem = [&](const SampleContext &Context, uint64_t Offset) {271if (std::error_code EC = writeContextIdx(Context))272return EC;273encodeULEB128(Offset, OS);274return (std::error_code)sampleprof_error::success;275};276277if (FunctionSamples::ProfileIsCS) {278// Sort the contexts before writing them out. This is to help fast load all279// context profiles for a function as well as their callee contexts which280// can help profile-guided importing for ThinLTO.281std::map<SampleContext, uint64_t> OrderedFuncOffsetTable(282FuncOffsetTable.begin(), FuncOffsetTable.end());283for (const auto &Entry : OrderedFuncOffsetTable) {284if (std::error_code EC = WriteItem(Entry.first, Entry.second))285return EC;286}287addSectionFlag(SecFuncOffsetTable, SecFuncOffsetFlags::SecFlagOrdered);288} else {289for (const auto &Entry : FuncOffsetTable) {290if (std::error_code EC = WriteItem(Entry.first, Entry.second))291return EC;292}293}294295FuncOffsetTable.clear();296return sampleprof_error::success;297}298299std::error_code SampleProfileWriterExtBinaryBase::writeFuncMetadata(300const FunctionSamples &FunctionProfile) {301auto &OS = *OutputStream;302if (std::error_code EC = writeContextIdx(FunctionProfile.getContext()))303return EC;304305if (FunctionSamples::ProfileIsProbeBased)306encodeULEB128(FunctionProfile.getFunctionHash(), OS);307if (FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsPreInlined) {308encodeULEB128(FunctionProfile.getContext().getAllAttributes(), OS);309}310311if (!FunctionSamples::ProfileIsCS) {312// Recursively emit attributes for all callee samples.313uint64_t NumCallsites = 0;314for (const auto &J : FunctionProfile.getCallsiteSamples())315NumCallsites += J.second.size();316encodeULEB128(NumCallsites, OS);317for (const auto &J : FunctionProfile.getCallsiteSamples()) {318for (const auto &FS : J.second) {319LineLocation Loc = J.first;320encodeULEB128(Loc.LineOffset, OS);321encodeULEB128(Loc.Discriminator, OS);322if (std::error_code EC = writeFuncMetadata(FS.second))323return EC;324}325}326}327328return sampleprof_error::success;329}330331std::error_code SampleProfileWriterExtBinaryBase::writeFuncMetadata(332const SampleProfileMap &Profiles) {333if (!FunctionSamples::ProfileIsProbeBased && !FunctionSamples::ProfileIsCS &&334!FunctionSamples::ProfileIsPreInlined)335return sampleprof_error::success;336for (const auto &Entry : Profiles) {337if (std::error_code EC = writeFuncMetadata(Entry.second))338return EC;339}340return sampleprof_error::success;341}342343std::error_code SampleProfileWriterExtBinaryBase::writeNameTable() {344if (!UseMD5)345return SampleProfileWriterBinary::writeNameTable();346347auto &OS = *OutputStream;348std::set<FunctionId> V;349stablizeNameTable(NameTable, V);350351// Write out the MD5 name table. We wrote unencoded MD5 so reader can352// retrieve the name using the name index without having to read the353// whole name table.354encodeULEB128(NameTable.size(), OS);355support::endian::Writer Writer(OS, llvm::endianness::little);356for (auto N : V)357Writer.write(N.getHashCode());358return sampleprof_error::success;359}360361std::error_code SampleProfileWriterExtBinaryBase::writeNameTableSection(362const SampleProfileMap &ProfileMap) {363for (const auto &I : ProfileMap) {364addContext(I.second.getContext());365addNames(I.second);366}367368// If NameTable contains ".__uniq." suffix, set SecFlagUniqSuffix flag369// so compiler won't strip the suffix during profile matching after370// seeing the flag in the profile.371// Original names are unavailable if using MD5, so this option has no use.372if (!UseMD5) {373for (const auto &I : NameTable) {374if (I.first.stringRef().contains(FunctionSamples::UniqSuffix)) {375addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagUniqSuffix);376break;377}378}379}380381if (auto EC = writeNameTable())382return EC;383return sampleprof_error::success;384}385386std::error_code SampleProfileWriterExtBinaryBase::writeCSNameTableSection() {387// Sort the names to make CSNameTable deterministic.388std::set<SampleContext> OrderedContexts;389for (const auto &I : CSNameTable)390OrderedContexts.insert(I.first);391assert(OrderedContexts.size() == CSNameTable.size() &&392"Unmatched ordered and unordered contexts");393uint64_t I = 0;394for (auto &Context : OrderedContexts)395CSNameTable[Context] = I++;396397auto &OS = *OutputStream;398encodeULEB128(OrderedContexts.size(), OS);399support::endian::Writer Writer(OS, llvm::endianness::little);400for (auto Context : OrderedContexts) {401auto Frames = Context.getContextFrames();402encodeULEB128(Frames.size(), OS);403for (auto &Callsite : Frames) {404if (std::error_code EC = writeNameIdx(Callsite.Func))405return EC;406encodeULEB128(Callsite.Location.LineOffset, OS);407encodeULEB128(Callsite.Location.Discriminator, OS);408}409}410411return sampleprof_error::success;412}413414std::error_code415SampleProfileWriterExtBinaryBase::writeProfileSymbolListSection() {416if (ProfSymList && ProfSymList->size() > 0)417if (std::error_code EC = ProfSymList->write(*OutputStream))418return EC;419420return sampleprof_error::success;421}422423std::error_code SampleProfileWriterExtBinaryBase::writeOneSection(424SecType Type, uint32_t LayoutIdx, const SampleProfileMap &ProfileMap) {425// The setting of SecFlagCompress should happen before markSectionStart.426if (Type == SecProfileSymbolList && ProfSymList && ProfSymList->toCompress())427setToCompressSection(SecProfileSymbolList);428if (Type == SecFuncMetadata && FunctionSamples::ProfileIsProbeBased)429addSectionFlag(SecFuncMetadata, SecFuncMetadataFlags::SecFlagIsProbeBased);430if (Type == SecFuncMetadata &&431(FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsPreInlined))432addSectionFlag(SecFuncMetadata, SecFuncMetadataFlags::SecFlagHasAttribute);433if (Type == SecProfSummary && FunctionSamples::ProfileIsCS)434addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagFullContext);435if (Type == SecProfSummary && FunctionSamples::ProfileIsPreInlined)436addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagIsPreInlined);437if (Type == SecProfSummary && FunctionSamples::ProfileIsFS)438addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagFSDiscriminator);439440uint64_t SectionStart = markSectionStart(Type, LayoutIdx);441switch (Type) {442case SecProfSummary:443computeSummary(ProfileMap);444if (auto EC = writeSummary())445return EC;446break;447case SecNameTable:448if (auto EC = writeNameTableSection(ProfileMap))449return EC;450break;451case SecCSNameTable:452if (auto EC = writeCSNameTableSection())453return EC;454break;455case SecLBRProfile:456SecLBRProfileStart = OutputStream->tell();457if (std::error_code EC = writeFuncProfiles(ProfileMap))458return EC;459break;460case SecFuncOffsetTable:461if (auto EC = writeFuncOffsetTable())462return EC;463break;464case SecFuncMetadata:465if (std::error_code EC = writeFuncMetadata(ProfileMap))466return EC;467break;468case SecProfileSymbolList:469if (auto EC = writeProfileSymbolListSection())470return EC;471break;472default:473if (auto EC = writeCustomSection(Type))474return EC;475break;476}477if (std::error_code EC = addNewSection(Type, LayoutIdx, SectionStart))478return EC;479return sampleprof_error::success;480}481482std::error_code SampleProfileWriterExtBinary::writeDefaultLayout(483const SampleProfileMap &ProfileMap) {484// The const indices passed to writeOneSection below are specifying the485// positions of the sections in SectionHdrLayout. Look at486// initSectionHdrLayout to find out where each section is located in487// SectionHdrLayout.488if (auto EC = writeOneSection(SecProfSummary, 0, ProfileMap))489return EC;490if (auto EC = writeOneSection(SecNameTable, 1, ProfileMap))491return EC;492if (auto EC = writeOneSection(SecCSNameTable, 2, ProfileMap))493return EC;494if (auto EC = writeOneSection(SecLBRProfile, 4, ProfileMap))495return EC;496if (auto EC = writeOneSection(SecProfileSymbolList, 5, ProfileMap))497return EC;498if (auto EC = writeOneSection(SecFuncOffsetTable, 3, ProfileMap))499return EC;500if (auto EC = writeOneSection(SecFuncMetadata, 6, ProfileMap))501return EC;502return sampleprof_error::success;503}504505static void splitProfileMapToTwo(const SampleProfileMap &ProfileMap,506SampleProfileMap &ContextProfileMap,507SampleProfileMap &NoContextProfileMap) {508for (const auto &I : ProfileMap) {509if (I.second.getCallsiteSamples().size())510ContextProfileMap.insert({I.first, I.second});511else512NoContextProfileMap.insert({I.first, I.second});513}514}515516std::error_code SampleProfileWriterExtBinary::writeCtxSplitLayout(517const SampleProfileMap &ProfileMap) {518SampleProfileMap ContextProfileMap, NoContextProfileMap;519splitProfileMapToTwo(ProfileMap, ContextProfileMap, NoContextProfileMap);520521if (auto EC = writeOneSection(SecProfSummary, 0, ProfileMap))522return EC;523if (auto EC = writeOneSection(SecNameTable, 1, ProfileMap))524return EC;525if (auto EC = writeOneSection(SecLBRProfile, 3, ContextProfileMap))526return EC;527if (auto EC = writeOneSection(SecFuncOffsetTable, 2, ContextProfileMap))528return EC;529// Mark the section to have no context. Note section flag needs to be set530// before writing the section.531addSectionFlag(5, SecCommonFlags::SecFlagFlat);532if (auto EC = writeOneSection(SecLBRProfile, 5, NoContextProfileMap))533return EC;534// Mark the section to have no context. Note section flag needs to be set535// before writing the section.536addSectionFlag(4, SecCommonFlags::SecFlagFlat);537if (auto EC = writeOneSection(SecFuncOffsetTable, 4, NoContextProfileMap))538return EC;539if (auto EC = writeOneSection(SecProfileSymbolList, 6, ProfileMap))540return EC;541if (auto EC = writeOneSection(SecFuncMetadata, 7, ProfileMap))542return EC;543544return sampleprof_error::success;545}546547std::error_code SampleProfileWriterExtBinary::writeSections(548const SampleProfileMap &ProfileMap) {549std::error_code EC;550if (SecLayout == DefaultLayout)551EC = writeDefaultLayout(ProfileMap);552else if (SecLayout == CtxSplitLayout)553EC = writeCtxSplitLayout(ProfileMap);554else555llvm_unreachable("Unsupported layout");556return EC;557}558559/// Write samples to a text file.560///561/// Note: it may be tempting to implement this in terms of562/// FunctionSamples::print(). Please don't. The dump functionality is intended563/// for debugging and has no specified form.564///565/// The format used here is more structured and deliberate because566/// it needs to be parsed by the SampleProfileReaderText class.567std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {568auto &OS = *OutputStream;569if (FunctionSamples::ProfileIsCS)570OS << "[" << S.getContext().toString() << "]:" << S.getTotalSamples();571else572OS << S.getFunction() << ":" << S.getTotalSamples();573574if (Indent == 0)575OS << ":" << S.getHeadSamples();576OS << "\n";577LineCount++;578579SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());580for (const auto &I : SortedSamples.get()) {581LineLocation Loc = I->first;582const SampleRecord &Sample = I->second;583OS.indent(Indent + 1);584if (Loc.Discriminator == 0)585OS << Loc.LineOffset << ": ";586else587OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";588589OS << Sample.getSamples();590591for (const auto &J : Sample.getSortedCallTargets())592OS << " " << J.first << ":" << J.second;593OS << "\n";594LineCount++;595}596597SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(598S.getCallsiteSamples());599Indent += 1;600for (const auto &I : SortedCallsiteSamples.get())601for (const auto &FS : I->second) {602LineLocation Loc = I->first;603const FunctionSamples &CalleeSamples = FS.second;604OS.indent(Indent);605if (Loc.Discriminator == 0)606OS << Loc.LineOffset << ": ";607else608OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";609if (std::error_code EC = writeSample(CalleeSamples))610return EC;611}612Indent -= 1;613614if (FunctionSamples::ProfileIsProbeBased) {615OS.indent(Indent + 1);616OS << "!CFGChecksum: " << S.getFunctionHash() << "\n";617LineCount++;618}619620if (S.getContext().getAllAttributes()) {621OS.indent(Indent + 1);622OS << "!Attributes: " << S.getContext().getAllAttributes() << "\n";623LineCount++;624}625626return sampleprof_error::success;627}628629std::error_code630SampleProfileWriterBinary::writeContextIdx(const SampleContext &Context) {631assert(!Context.hasContext() && "cs profile is not supported");632return writeNameIdx(Context.getFunction());633}634635std::error_code SampleProfileWriterBinary::writeNameIdx(FunctionId FName) {636auto &NTable = getNameTable();637const auto &Ret = NTable.find(FName);638if (Ret == NTable.end())639return sampleprof_error::truncated_name_table;640encodeULEB128(Ret->second, *OutputStream);641return sampleprof_error::success;642}643644void SampleProfileWriterBinary::addName(FunctionId FName) {645auto &NTable = getNameTable();646NTable.insert(std::make_pair(FName, 0));647}648649void SampleProfileWriterBinary::addContext(const SampleContext &Context) {650addName(Context.getFunction());651}652653void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {654// Add all the names in indirect call targets.655for (const auto &I : S.getBodySamples()) {656const SampleRecord &Sample = I.second;657for (const auto &J : Sample.getCallTargets())658addName(J.first);659}660661// Recursively add all the names for inlined callsites.662for (const auto &J : S.getCallsiteSamples())663for (const auto &FS : J.second) {664const FunctionSamples &CalleeSamples = FS.second;665addName(CalleeSamples.getFunction());666addNames(CalleeSamples);667}668}669670void SampleProfileWriterExtBinaryBase::addContext(671const SampleContext &Context) {672if (Context.hasContext()) {673for (auto &Callsite : Context.getContextFrames())674SampleProfileWriterBinary::addName(Callsite.Func);675CSNameTable.insert(std::make_pair(Context, 0));676} else {677SampleProfileWriterBinary::addName(Context.getFunction());678}679}680681void SampleProfileWriterBinary::stablizeNameTable(682MapVector<FunctionId, uint32_t> &NameTable, std::set<FunctionId> &V) {683// Sort the names to make NameTable deterministic.684for (const auto &I : NameTable)685V.insert(I.first);686int i = 0;687for (const FunctionId &N : V)688NameTable[N] = i++;689}690691std::error_code SampleProfileWriterBinary::writeNameTable() {692auto &OS = *OutputStream;693std::set<FunctionId> V;694stablizeNameTable(NameTable, V);695696// Write out the name table.697encodeULEB128(NameTable.size(), OS);698for (auto N : V) {699OS << N;700encodeULEB128(0, OS);701}702return sampleprof_error::success;703}704705std::error_code706SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) {707auto &OS = *OutputStream;708// Write file magic identifier.709encodeULEB128(SPMagic(Format), OS);710encodeULEB128(SPVersion(), OS);711return sampleprof_error::success;712}713714std::error_code715SampleProfileWriterBinary::writeHeader(const SampleProfileMap &ProfileMap) {716// When calling write on a different profile map, existing names should be717// cleared.718NameTable.clear();719720writeMagicIdent(Format);721722computeSummary(ProfileMap);723if (auto EC = writeSummary())724return EC;725726// Generate the name table for all the functions referenced in the profile.727for (const auto &I : ProfileMap) {728addContext(I.second.getContext());729addNames(I.second);730}731732writeNameTable();733return sampleprof_error::success;734}735736void SampleProfileWriterExtBinaryBase::setToCompressAllSections() {737for (auto &Entry : SectionHdrLayout)738addSecFlag(Entry, SecCommonFlags::SecFlagCompress);739}740741void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) {742addSectionFlag(Type, SecCommonFlags::SecFlagCompress);743}744745void SampleProfileWriterExtBinaryBase::allocSecHdrTable() {746support::endian::Writer Writer(*OutputStream, llvm::endianness::little);747748Writer.write(static_cast<uint64_t>(SectionHdrLayout.size()));749SecHdrTableOffset = OutputStream->tell();750for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {751Writer.write(static_cast<uint64_t>(-1));752Writer.write(static_cast<uint64_t>(-1));753Writer.write(static_cast<uint64_t>(-1));754Writer.write(static_cast<uint64_t>(-1));755}756}757758std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() {759assert(SecHdrTable.size() == SectionHdrLayout.size() &&760"SecHdrTable entries doesn't match SectionHdrLayout");761SmallVector<uint32_t, 16> IndexMap(SecHdrTable.size(), -1);762for (uint32_t TableIdx = 0; TableIdx < SecHdrTable.size(); TableIdx++) {763IndexMap[SecHdrTable[TableIdx].LayoutIndex] = TableIdx;764}765766// Write the section header table in the order specified in767// SectionHdrLayout. SectionHdrLayout specifies the sections768// order in which profile reader expect to read, so the section769// header table should be written in the order in SectionHdrLayout.770// Note that the section order in SecHdrTable may be different771// from the order in SectionHdrLayout, for example, SecFuncOffsetTable772// needs to be computed after SecLBRProfile (the order in SecHdrTable),773// but it needs to be read before SecLBRProfile (the order in774// SectionHdrLayout). So we use IndexMap above to switch the order.775support::endian::SeekableWriter Writer(776static_cast<raw_pwrite_stream &>(*OutputStream),777llvm::endianness::little);778for (uint32_t LayoutIdx = 0; LayoutIdx < SectionHdrLayout.size();779LayoutIdx++) {780assert(IndexMap[LayoutIdx] < SecHdrTable.size() &&781"Incorrect LayoutIdx in SecHdrTable");782auto Entry = SecHdrTable[IndexMap[LayoutIdx]];783Writer.pwrite(static_cast<uint64_t>(Entry.Type),784SecHdrTableOffset + 4 * LayoutIdx * sizeof(uint64_t));785Writer.pwrite(static_cast<uint64_t>(Entry.Flags),786SecHdrTableOffset + (4 * LayoutIdx + 1) * sizeof(uint64_t));787Writer.pwrite(static_cast<uint64_t>(Entry.Offset),788SecHdrTableOffset + (4 * LayoutIdx + 2) * sizeof(uint64_t));789Writer.pwrite(static_cast<uint64_t>(Entry.Size),790SecHdrTableOffset + (4 * LayoutIdx + 3) * sizeof(uint64_t));791}792793return sampleprof_error::success;794}795796std::error_code SampleProfileWriterExtBinaryBase::writeHeader(797const SampleProfileMap &ProfileMap) {798auto &OS = *OutputStream;799FileStart = OS.tell();800writeMagicIdent(Format);801802allocSecHdrTable();803return sampleprof_error::success;804}805806std::error_code SampleProfileWriterBinary::writeSummary() {807auto &OS = *OutputStream;808encodeULEB128(Summary->getTotalCount(), OS);809encodeULEB128(Summary->getMaxCount(), OS);810encodeULEB128(Summary->getMaxFunctionCount(), OS);811encodeULEB128(Summary->getNumCounts(), OS);812encodeULEB128(Summary->getNumFunctions(), OS);813ArrayRef<ProfileSummaryEntry> Entries = Summary->getDetailedSummary();814encodeULEB128(Entries.size(), OS);815for (auto Entry : Entries) {816encodeULEB128(Entry.Cutoff, OS);817encodeULEB128(Entry.MinCount, OS);818encodeULEB128(Entry.NumCounts, OS);819}820return sampleprof_error::success;821}822std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {823auto &OS = *OutputStream;824if (std::error_code EC = writeContextIdx(S.getContext()))825return EC;826827encodeULEB128(S.getTotalSamples(), OS);828829// Emit all the body samples.830encodeULEB128(S.getBodySamples().size(), OS);831for (const auto &I : S.getBodySamples()) {832LineLocation Loc = I.first;833const SampleRecord &Sample = I.second;834encodeULEB128(Loc.LineOffset, OS);835encodeULEB128(Loc.Discriminator, OS);836encodeULEB128(Sample.getSamples(), OS);837encodeULEB128(Sample.getCallTargets().size(), OS);838for (const auto &J : Sample.getSortedCallTargets()) {839FunctionId Callee = J.first;840uint64_t CalleeSamples = J.second;841if (std::error_code EC = writeNameIdx(Callee))842return EC;843encodeULEB128(CalleeSamples, OS);844}845}846847// Recursively emit all the callsite samples.848uint64_t NumCallsites = 0;849for (const auto &J : S.getCallsiteSamples())850NumCallsites += J.second.size();851encodeULEB128(NumCallsites, OS);852for (const auto &J : S.getCallsiteSamples())853for (const auto &FS : J.second) {854LineLocation Loc = J.first;855const FunctionSamples &CalleeSamples = FS.second;856encodeULEB128(Loc.LineOffset, OS);857encodeULEB128(Loc.Discriminator, OS);858if (std::error_code EC = writeBody(CalleeSamples))859return EC;860}861862return sampleprof_error::success;863}864865/// Write samples of a top-level function to a binary file.866///867/// \returns true if the samples were written successfully, false otherwise.868std::error_code869SampleProfileWriterBinary::writeSample(const FunctionSamples &S) {870encodeULEB128(S.getHeadSamples(), *OutputStream);871return writeBody(S);872}873874/// Create a sample profile file writer based on the specified format.875///876/// \param Filename The file to create.877///878/// \param Format Encoding format for the profile file.879///880/// \returns an error code indicating the status of the created writer.881ErrorOr<std::unique_ptr<SampleProfileWriter>>882SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {883std::error_code EC;884std::unique_ptr<raw_ostream> OS;885if (Format == SPF_Binary || Format == SPF_Ext_Binary)886OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None));887else888OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_TextWithCRLF));889if (EC)890return EC;891892return create(OS, Format);893}894895/// Create a sample profile stream writer based on the specified format.896///897/// \param OS The output stream to store the profile data to.898///899/// \param Format Encoding format for the profile file.900///901/// \returns an error code indicating the status of the created writer.902ErrorOr<std::unique_ptr<SampleProfileWriter>>903SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,904SampleProfileFormat Format) {905std::error_code EC;906std::unique_ptr<SampleProfileWriter> Writer;907908// Currently only Text and Extended Binary format are supported for CSSPGO.909if ((FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsProbeBased) &&910Format == SPF_Binary)911return sampleprof_error::unsupported_writing_format;912913if (Format == SPF_Binary)914Writer.reset(new SampleProfileWriterRawBinary(OS));915else if (Format == SPF_Ext_Binary)916Writer.reset(new SampleProfileWriterExtBinary(OS));917else if (Format == SPF_Text)918Writer.reset(new SampleProfileWriterText(OS));919else if (Format == SPF_GCC)920EC = sampleprof_error::unsupported_writing_format;921else922EC = sampleprof_error::unrecognized_format;923924if (EC)925return EC;926927Writer->Format = Format;928return std::move(Writer);929}930931void SampleProfileWriter::computeSummary(const SampleProfileMap &ProfileMap) {932SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);933Summary = Builder.computeSummaryForProfiles(ProfileMap);934}935936937