167c32a98SDimitry Andric //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===//
267c32a98SDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
667c32a98SDimitry Andric //
767c32a98SDimitry Andric //===----------------------------------------------------------------------===//
867c32a98SDimitry Andric //
967c32a98SDimitry Andric // This file implements the class that writes LLVM sample profiles. It
1067c32a98SDimitry Andric // supports two file formats: text and binary. The textual representation
1167c32a98SDimitry Andric // is useful for debugging and testing purposes. The binary representation
1267c32a98SDimitry Andric // is more compact, resulting in smaller file sizes. However, they can
1367c32a98SDimitry Andric // both be used interchangeably.
1467c32a98SDimitry Andric //
1567c32a98SDimitry Andric // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the
1667c32a98SDimitry Andric // supported formats.
1767c32a98SDimitry Andric //
1867c32a98SDimitry Andric //===----------------------------------------------------------------------===//
1967c32a98SDimitry Andric
207ab83427SDimitry Andric #include "llvm/ProfileData/SampleProfWriter.h"
2171d5a254SDimitry Andric #include "llvm/ADT/StringRef.h"
2271d5a254SDimitry Andric #include "llvm/ProfileData/ProfileCommon.h"
2371d5a254SDimitry Andric #include "llvm/ProfileData/SampleProf.h"
241d5ae102SDimitry Andric #include "llvm/Support/Compression.h"
25d8e91e46SDimitry Andric #include "llvm/Support/Endian.h"
26d8e91e46SDimitry Andric #include "llvm/Support/EndianStream.h"
2767c32a98SDimitry Andric #include "llvm/Support/ErrorOr.h"
2871d5a254SDimitry Andric #include "llvm/Support/FileSystem.h"
2967c32a98SDimitry Andric #include "llvm/Support/LEB128.h"
30eb11fae6SDimitry Andric #include "llvm/Support/MD5.h"
3171d5a254SDimitry Andric #include "llvm/Support/raw_ostream.h"
3271d5a254SDimitry Andric #include <algorithm>
337fa27ce4SDimitry Andric #include <cmath>
3471d5a254SDimitry Andric #include <cstdint>
3571d5a254SDimitry Andric #include <memory>
366b3f41edSDimitry Andric #include <set>
3771d5a254SDimitry Andric #include <system_error>
3871d5a254SDimitry Andric #include <utility>
3971d5a254SDimitry Andric #include <vector>
4067c32a98SDimitry Andric
417fa27ce4SDimitry Andric #define DEBUG_TYPE "llvm-profdata"
427fa27ce4SDimitry Andric
4367c32a98SDimitry Andric using namespace llvm;
4471d5a254SDimitry Andric using namespace sampleprof;
4567c32a98SDimitry Andric
467fa27ce4SDimitry Andric namespace llvm {
477fa27ce4SDimitry Andric namespace support {
487fa27ce4SDimitry Andric namespace endian {
497fa27ce4SDimitry Andric namespace {
507fa27ce4SDimitry Andric
517fa27ce4SDimitry Andric // Adapter class to llvm::support::endian::Writer for pwrite().
527fa27ce4SDimitry Andric struct SeekableWriter {
537fa27ce4SDimitry Andric raw_pwrite_stream &OS;
547fa27ce4SDimitry Andric endianness Endian;
SeekableWriterllvm::support::endian::__anon81d23f950111::SeekableWriter557fa27ce4SDimitry Andric SeekableWriter(raw_pwrite_stream &OS, endianness Endian)
567fa27ce4SDimitry Andric : OS(OS), Endian(Endian) {}
577fa27ce4SDimitry Andric
587fa27ce4SDimitry Andric template <typename ValueType>
pwritellvm::support::endian::__anon81d23f950111::SeekableWriter597fa27ce4SDimitry Andric void pwrite(ValueType Val, size_t Offset) {
607fa27ce4SDimitry Andric std::string StringBuf;
617fa27ce4SDimitry Andric raw_string_ostream SStream(StringBuf);
627fa27ce4SDimitry Andric Writer(SStream, Endian).write(Val);
637fa27ce4SDimitry Andric OS.pwrite(StringBuf.data(), StringBuf.size(), Offset);
647fa27ce4SDimitry Andric }
657fa27ce4SDimitry Andric };
667fa27ce4SDimitry Andric
677fa27ce4SDimitry Andric } // namespace
687fa27ce4SDimitry Andric } // namespace endian
697fa27ce4SDimitry Andric } // namespace support
707fa27ce4SDimitry Andric } // namespace llvm
717fa27ce4SDimitry Andric
DefaultFunctionPruningStrategy(SampleProfileMap & ProfileMap,size_t OutputSizeLimit)727fa27ce4SDimitry Andric DefaultFunctionPruningStrategy::DefaultFunctionPruningStrategy(
737fa27ce4SDimitry Andric SampleProfileMap &ProfileMap, size_t OutputSizeLimit)
747fa27ce4SDimitry Andric : FunctionPruningStrategy(ProfileMap, OutputSizeLimit) {
757fa27ce4SDimitry Andric sortFuncProfiles(ProfileMap, SortedFunctions);
767fa27ce4SDimitry Andric }
777fa27ce4SDimitry Andric
Erase(size_t CurrentOutputSize)787fa27ce4SDimitry Andric void DefaultFunctionPruningStrategy::Erase(size_t CurrentOutputSize) {
797fa27ce4SDimitry Andric double D = (double)OutputSizeLimit / CurrentOutputSize;
807fa27ce4SDimitry Andric size_t NewSize = (size_t)round(ProfileMap.size() * D * D);
817fa27ce4SDimitry Andric size_t NumToRemove = ProfileMap.size() - NewSize;
827fa27ce4SDimitry Andric if (NumToRemove < 1)
837fa27ce4SDimitry Andric NumToRemove = 1;
847fa27ce4SDimitry Andric
857fa27ce4SDimitry Andric assert(NumToRemove <= SortedFunctions.size());
86b1c73532SDimitry Andric for (const NameFunctionSamples &E :
87b1c73532SDimitry Andric llvm::drop_begin(SortedFunctions, SortedFunctions.size() - NumToRemove))
88b1c73532SDimitry Andric ProfileMap.erase(E.first);
897fa27ce4SDimitry Andric SortedFunctions.resize(SortedFunctions.size() - NumToRemove);
907fa27ce4SDimitry Andric }
917fa27ce4SDimitry Andric
writeWithSizeLimitInternal(SampleProfileMap & ProfileMap,size_t OutputSizeLimit,FunctionPruningStrategy * Strategy)927fa27ce4SDimitry Andric std::error_code SampleProfileWriter::writeWithSizeLimitInternal(
937fa27ce4SDimitry Andric SampleProfileMap &ProfileMap, size_t OutputSizeLimit,
947fa27ce4SDimitry Andric FunctionPruningStrategy *Strategy) {
957fa27ce4SDimitry Andric if (OutputSizeLimit == 0)
967fa27ce4SDimitry Andric return write(ProfileMap);
977fa27ce4SDimitry Andric
987fa27ce4SDimitry Andric size_t OriginalFunctionCount = ProfileMap.size();
997fa27ce4SDimitry Andric
1007fa27ce4SDimitry Andric std::unique_ptr<raw_ostream> OriginalOutputStream;
1017fa27ce4SDimitry Andric OutputStream.swap(OriginalOutputStream);
1027fa27ce4SDimitry Andric
1037fa27ce4SDimitry Andric size_t IterationCount = 0;
1047fa27ce4SDimitry Andric size_t TotalSize;
1057fa27ce4SDimitry Andric
1067fa27ce4SDimitry Andric SmallVector<char> StringBuffer;
1077fa27ce4SDimitry Andric do {
1087fa27ce4SDimitry Andric StringBuffer.clear();
1097fa27ce4SDimitry Andric OutputStream.reset(new raw_svector_ostream(StringBuffer));
1107fa27ce4SDimitry Andric if (std::error_code EC = write(ProfileMap))
1117fa27ce4SDimitry Andric return EC;
1127fa27ce4SDimitry Andric
1137fa27ce4SDimitry Andric TotalSize = StringBuffer.size();
1147fa27ce4SDimitry Andric // On Windows every "\n" is actually written as "\r\n" to disk but not to
1157fa27ce4SDimitry Andric // memory buffer, this difference should be added when considering the total
1167fa27ce4SDimitry Andric // output size.
1177fa27ce4SDimitry Andric #ifdef _WIN32
1187fa27ce4SDimitry Andric if (Format == SPF_Text)
1197fa27ce4SDimitry Andric TotalSize += LineCount;
1207fa27ce4SDimitry Andric #endif
1217fa27ce4SDimitry Andric if (TotalSize <= OutputSizeLimit)
1227fa27ce4SDimitry Andric break;
1237fa27ce4SDimitry Andric
1247fa27ce4SDimitry Andric Strategy->Erase(TotalSize);
1257fa27ce4SDimitry Andric IterationCount++;
1267fa27ce4SDimitry Andric } while (ProfileMap.size() != 0);
1277fa27ce4SDimitry Andric
1287fa27ce4SDimitry Andric if (ProfileMap.size() == 0)
1297fa27ce4SDimitry Andric return sampleprof_error::too_large;
1307fa27ce4SDimitry Andric
1317fa27ce4SDimitry Andric OutputStream.swap(OriginalOutputStream);
1327fa27ce4SDimitry Andric OutputStream->write(StringBuffer.data(), StringBuffer.size());
1337fa27ce4SDimitry Andric LLVM_DEBUG(dbgs() << "Profile originally has " << OriginalFunctionCount
1347fa27ce4SDimitry Andric << " functions, reduced to " << ProfileMap.size() << " in "
1357fa27ce4SDimitry Andric << IterationCount << " iterations\n");
1367fa27ce4SDimitry Andric // Silence warning on Release build.
1377fa27ce4SDimitry Andric (void)OriginalFunctionCount;
1387fa27ce4SDimitry Andric (void)IterationCount;
1397fa27ce4SDimitry Andric return sampleprof_error::success;
1407fa27ce4SDimitry Andric }
1417fa27ce4SDimitry Andric
142c0981da4SDimitry Andric std::error_code
writeFuncProfiles(const SampleProfileMap & ProfileMap)143c0981da4SDimitry Andric SampleProfileWriter::writeFuncProfiles(const SampleProfileMap &ProfileMap) {
1446b3f41edSDimitry Andric std::vector<NameFunctionSamples> V;
145c0981da4SDimitry Andric sortFuncProfiles(ProfileMap, V);
1466b3f41edSDimitry Andric for (const auto &I : V) {
1471d5ae102SDimitry Andric if (std::error_code EC = writeSample(*I.second))
1486b3f41edSDimitry Andric return EC;
1496b3f41edSDimitry Andric }
1506b3f41edSDimitry Andric return sampleprof_error::success;
1516b3f41edSDimitry Andric }
1526b3f41edSDimitry Andric
write(const SampleProfileMap & ProfileMap)153c0981da4SDimitry Andric std::error_code SampleProfileWriter::write(const SampleProfileMap &ProfileMap) {
1541d5ae102SDimitry Andric if (std::error_code EC = writeHeader(ProfileMap))
1551d5ae102SDimitry Andric return EC;
1561d5ae102SDimitry Andric
1571d5ae102SDimitry Andric if (std::error_code EC = writeFuncProfiles(ProfileMap))
1581d5ae102SDimitry Andric return EC;
1591d5ae102SDimitry Andric
1601d5ae102SDimitry Andric return sampleprof_error::success;
1611d5ae102SDimitry Andric }
1621d5ae102SDimitry Andric
1631d5ae102SDimitry Andric /// Return the current position and prepare to use it as the start
164b60736ecSDimitry Andric /// position of a section given the section type \p Type and its position
165b60736ecSDimitry Andric /// \p LayoutIdx in SectionHdrLayout.
166b60736ecSDimitry Andric uint64_t
markSectionStart(SecType Type,uint32_t LayoutIdx)167b60736ecSDimitry Andric SampleProfileWriterExtBinaryBase::markSectionStart(SecType Type,
168b60736ecSDimitry Andric uint32_t LayoutIdx) {
1691d5ae102SDimitry Andric uint64_t SectionStart = OutputStream->tell();
170b60736ecSDimitry Andric assert(LayoutIdx < SectionHdrLayout.size() && "LayoutIdx out of range");
171b60736ecSDimitry Andric const auto &Entry = SectionHdrLayout[LayoutIdx];
172b60736ecSDimitry Andric assert(Entry.Type == Type && "Unexpected section type");
1731d5ae102SDimitry Andric // Use LocalBuf as a temporary output for writting data.
174cfca06d7SDimitry Andric if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress))
1751d5ae102SDimitry Andric LocalBufStream.swap(OutputStream);
1761d5ae102SDimitry Andric return SectionStart;
1771d5ae102SDimitry Andric }
1781d5ae102SDimitry Andric
compressAndOutput()1791d5ae102SDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::compressAndOutput() {
1801f917f69SDimitry Andric if (!llvm::compression::zlib::isAvailable())
1811d5ae102SDimitry Andric return sampleprof_error::zlib_unavailable;
1821d5ae102SDimitry Andric std::string &UncompressedStrings =
1831d5ae102SDimitry Andric static_cast<raw_string_ostream *>(LocalBufStream.get())->str();
1841d5ae102SDimitry Andric if (UncompressedStrings.size() == 0)
1851d5ae102SDimitry Andric return sampleprof_error::success;
1861d5ae102SDimitry Andric auto &OS = *OutputStream;
1871f917f69SDimitry Andric SmallVector<uint8_t, 128> CompressedStrings;
1881f917f69SDimitry Andric compression::zlib::compress(arrayRefFromStringRef(UncompressedStrings),
1891f917f69SDimitry Andric CompressedStrings,
1901f917f69SDimitry Andric compression::zlib::BestSizeCompression);
1911d5ae102SDimitry Andric encodeULEB128(UncompressedStrings.size(), OS);
1921d5ae102SDimitry Andric encodeULEB128(CompressedStrings.size(), OS);
1931f917f69SDimitry Andric OS << toStringRef(CompressedStrings);
1941d5ae102SDimitry Andric UncompressedStrings.clear();
1951d5ae102SDimitry Andric return sampleprof_error::success;
1961d5ae102SDimitry Andric }
1971d5ae102SDimitry Andric
198b60736ecSDimitry Andric /// Add a new section into section header table given the section type
199b60736ecSDimitry Andric /// \p Type, its position \p LayoutIdx in SectionHdrLayout and the
200b60736ecSDimitry Andric /// location \p SectionStart where the section should be written to.
addNewSection(SecType Type,uint32_t LayoutIdx,uint64_t SectionStart)201b60736ecSDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::addNewSection(
202b60736ecSDimitry Andric SecType Type, uint32_t LayoutIdx, uint64_t SectionStart) {
203b60736ecSDimitry Andric assert(LayoutIdx < SectionHdrLayout.size() && "LayoutIdx out of range");
204b60736ecSDimitry Andric const auto &Entry = SectionHdrLayout[LayoutIdx];
205b60736ecSDimitry Andric assert(Entry.Type == Type && "Unexpected section type");
206cfca06d7SDimitry Andric if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress)) {
2071d5ae102SDimitry Andric LocalBufStream.swap(OutputStream);
2081d5ae102SDimitry Andric if (std::error_code EC = compressAndOutput())
2091d5ae102SDimitry Andric return EC;
2101d5ae102SDimitry Andric }
2111d5ae102SDimitry Andric SecHdrTable.push_back({Type, Entry.Flags, SectionStart - FileStart,
212b60736ecSDimitry Andric OutputStream->tell() - SectionStart, LayoutIdx});
2131d5ae102SDimitry Andric return sampleprof_error::success;
2141d5ae102SDimitry Andric }
2151d5ae102SDimitry Andric
216c0981da4SDimitry Andric std::error_code
write(const SampleProfileMap & ProfileMap)217c0981da4SDimitry Andric SampleProfileWriterExtBinaryBase::write(const SampleProfileMap &ProfileMap) {
2187fa27ce4SDimitry Andric // When calling write on a different profile map, existing states should be
2197fa27ce4SDimitry Andric // cleared.
2207fa27ce4SDimitry Andric NameTable.clear();
2217fa27ce4SDimitry Andric CSNameTable.clear();
2227fa27ce4SDimitry Andric SecHdrTable.clear();
2237fa27ce4SDimitry Andric
2241d5ae102SDimitry Andric if (std::error_code EC = writeHeader(ProfileMap))
2251d5ae102SDimitry Andric return EC;
2261d5ae102SDimitry Andric
2271d5ae102SDimitry Andric std::string LocalBuf;
2281d5ae102SDimitry Andric LocalBufStream = std::make_unique<raw_string_ostream>(LocalBuf);
2291d5ae102SDimitry Andric if (std::error_code EC = writeSections(ProfileMap))
2301d5ae102SDimitry Andric return EC;
2311d5ae102SDimitry Andric
2321d5ae102SDimitry Andric if (std::error_code EC = writeSecHdrTable())
2331d5ae102SDimitry Andric return EC;
2341d5ae102SDimitry Andric
2351d5ae102SDimitry Andric return sampleprof_error::success;
2361d5ae102SDimitry Andric }
2371d5ae102SDimitry Andric
writeContextIdx(const SampleContext & Context)238c0981da4SDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeContextIdx(
239c0981da4SDimitry Andric const SampleContext &Context) {
240c0981da4SDimitry Andric if (Context.hasContext())
241c0981da4SDimitry Andric return writeCSNameIdx(Context);
242c0981da4SDimitry Andric else
243b1c73532SDimitry Andric return SampleProfileWriterBinary::writeNameIdx(Context.getFunction());
244c0981da4SDimitry Andric }
245c0981da4SDimitry Andric
246c0981da4SDimitry Andric std::error_code
writeCSNameIdx(const SampleContext & Context)247c0981da4SDimitry Andric SampleProfileWriterExtBinaryBase::writeCSNameIdx(const SampleContext &Context) {
248c0981da4SDimitry Andric const auto &Ret = CSNameTable.find(Context);
249c0981da4SDimitry Andric if (Ret == CSNameTable.end())
250c0981da4SDimitry Andric return sampleprof_error::truncated_name_table;
251c0981da4SDimitry Andric encodeULEB128(Ret->second, *OutputStream);
252c0981da4SDimitry Andric return sampleprof_error::success;
253c0981da4SDimitry Andric }
254c0981da4SDimitry Andric
2551d5ae102SDimitry Andric std::error_code
writeSample(const FunctionSamples & S)256b60736ecSDimitry Andric SampleProfileWriterExtBinaryBase::writeSample(const FunctionSamples &S) {
2571d5ae102SDimitry Andric uint64_t Offset = OutputStream->tell();
258c0981da4SDimitry Andric auto &Context = S.getContext();
259c0981da4SDimitry Andric FuncOffsetTable[Context] = Offset - SecLBRProfileStart;
2601d5ae102SDimitry Andric encodeULEB128(S.getHeadSamples(), *OutputStream);
2611d5ae102SDimitry Andric return writeBody(S);
2621d5ae102SDimitry Andric }
2631d5ae102SDimitry Andric
writeFuncOffsetTable()264b60736ecSDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeFuncOffsetTable() {
2651d5ae102SDimitry Andric auto &OS = *OutputStream;
2661d5ae102SDimitry Andric
2671d5ae102SDimitry Andric // Write out the table size.
2681d5ae102SDimitry Andric encodeULEB128(FuncOffsetTable.size(), OS);
2691d5ae102SDimitry Andric
2701d5ae102SDimitry Andric // Write out FuncOffsetTable.
271c0981da4SDimitry Andric auto WriteItem = [&](const SampleContext &Context, uint64_t Offset) {
272c0981da4SDimitry Andric if (std::error_code EC = writeContextIdx(Context))
273344a3780SDimitry Andric return EC;
274c0981da4SDimitry Andric encodeULEB128(Offset, OS);
275c0981da4SDimitry Andric return (std::error_code)sampleprof_error::success;
276c0981da4SDimitry Andric };
277c0981da4SDimitry Andric
278145449b1SDimitry Andric if (FunctionSamples::ProfileIsCS) {
279c0981da4SDimitry Andric // Sort the contexts before writing them out. This is to help fast load all
280c0981da4SDimitry Andric // context profiles for a function as well as their callee contexts which
281c0981da4SDimitry Andric // can help profile-guided importing for ThinLTO.
282c0981da4SDimitry Andric std::map<SampleContext, uint64_t> OrderedFuncOffsetTable(
283c0981da4SDimitry Andric FuncOffsetTable.begin(), FuncOffsetTable.end());
284c0981da4SDimitry Andric for (const auto &Entry : OrderedFuncOffsetTable) {
285c0981da4SDimitry Andric if (std::error_code EC = WriteItem(Entry.first, Entry.second))
286c0981da4SDimitry Andric return EC;
2871d5ae102SDimitry Andric }
288c0981da4SDimitry Andric addSectionFlag(SecFuncOffsetTable, SecFuncOffsetFlags::SecFlagOrdered);
289c0981da4SDimitry Andric } else {
290c0981da4SDimitry Andric for (const auto &Entry : FuncOffsetTable) {
291c0981da4SDimitry Andric if (std::error_code EC = WriteItem(Entry.first, Entry.second))
292c0981da4SDimitry Andric return EC;
293c0981da4SDimitry Andric }
294c0981da4SDimitry Andric }
295c0981da4SDimitry Andric
296b60736ecSDimitry Andric FuncOffsetTable.clear();
2971d5ae102SDimitry Andric return sampleprof_error::success;
2981d5ae102SDimitry Andric }
2991d5ae102SDimitry Andric
writeFuncMetadata(const FunctionSamples & FunctionProfile)300b60736ecSDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeFuncMetadata(
30177fc4c14SDimitry Andric const FunctionSamples &FunctionProfile) {
302b60736ecSDimitry Andric auto &OS = *OutputStream;
30377fc4c14SDimitry Andric if (std::error_code EC = writeContextIdx(FunctionProfile.getContext()))
304344a3780SDimitry Andric return EC;
30577fc4c14SDimitry Andric
306344a3780SDimitry Andric if (FunctionSamples::ProfileIsProbeBased)
30777fc4c14SDimitry Andric encodeULEB128(FunctionProfile.getFunctionHash(), OS);
308145449b1SDimitry Andric if (FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsPreInlined) {
30977fc4c14SDimitry Andric encodeULEB128(FunctionProfile.getContext().getAllAttributes(), OS);
31077fc4c14SDimitry Andric }
31177fc4c14SDimitry Andric
312145449b1SDimitry Andric if (!FunctionSamples::ProfileIsCS) {
31377fc4c14SDimitry Andric // Recursively emit attributes for all callee samples.
31477fc4c14SDimitry Andric uint64_t NumCallsites = 0;
31577fc4c14SDimitry Andric for (const auto &J : FunctionProfile.getCallsiteSamples())
31677fc4c14SDimitry Andric NumCallsites += J.second.size();
31777fc4c14SDimitry Andric encodeULEB128(NumCallsites, OS);
31877fc4c14SDimitry Andric for (const auto &J : FunctionProfile.getCallsiteSamples()) {
31977fc4c14SDimitry Andric for (const auto &FS : J.second) {
32077fc4c14SDimitry Andric LineLocation Loc = J.first;
32177fc4c14SDimitry Andric encodeULEB128(Loc.LineOffset, OS);
32277fc4c14SDimitry Andric encodeULEB128(Loc.Discriminator, OS);
32377fc4c14SDimitry Andric if (std::error_code EC = writeFuncMetadata(FS.second))
32477fc4c14SDimitry Andric return EC;
32577fc4c14SDimitry Andric }
32677fc4c14SDimitry Andric }
32777fc4c14SDimitry Andric }
32877fc4c14SDimitry Andric
32977fc4c14SDimitry Andric return sampleprof_error::success;
33077fc4c14SDimitry Andric }
33177fc4c14SDimitry Andric
writeFuncMetadata(const SampleProfileMap & Profiles)33277fc4c14SDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeFuncMetadata(
33377fc4c14SDimitry Andric const SampleProfileMap &Profiles) {
334145449b1SDimitry Andric if (!FunctionSamples::ProfileIsProbeBased && !FunctionSamples::ProfileIsCS &&
335145449b1SDimitry Andric !FunctionSamples::ProfileIsPreInlined)
33677fc4c14SDimitry Andric return sampleprof_error::success;
33777fc4c14SDimitry Andric for (const auto &Entry : Profiles) {
33877fc4c14SDimitry Andric if (std::error_code EC = writeFuncMetadata(Entry.second))
33977fc4c14SDimitry Andric return EC;
340b60736ecSDimitry Andric }
341b60736ecSDimitry Andric return sampleprof_error::success;
342b60736ecSDimitry Andric }
343b60736ecSDimitry Andric
writeNameTable()344b60736ecSDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeNameTable() {
345cfca06d7SDimitry Andric if (!UseMD5)
346cfca06d7SDimitry Andric return SampleProfileWriterBinary::writeNameTable();
347cfca06d7SDimitry Andric
348cfca06d7SDimitry Andric auto &OS = *OutputStream;
349b1c73532SDimitry Andric std::set<FunctionId> V;
350c0981da4SDimitry Andric stablizeNameTable(NameTable, V);
351cfca06d7SDimitry Andric
352b60736ecSDimitry Andric // Write out the MD5 name table. We wrote unencoded MD5 so reader can
353b60736ecSDimitry Andric // retrieve the name using the name index without having to read the
354b60736ecSDimitry Andric // whole name table.
355cfca06d7SDimitry Andric encodeULEB128(NameTable.size(), OS);
356b1c73532SDimitry Andric support::endian::Writer Writer(OS, llvm::endianness::little);
357b60736ecSDimitry Andric for (auto N : V)
358b1c73532SDimitry Andric Writer.write(N.getHashCode());
359b60736ecSDimitry Andric return sampleprof_error::success;
360cfca06d7SDimitry Andric }
361b60736ecSDimitry Andric
writeNameTableSection(const SampleProfileMap & ProfileMap)362b60736ecSDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeNameTableSection(
363c0981da4SDimitry Andric const SampleProfileMap &ProfileMap) {
364b60736ecSDimitry Andric for (const auto &I : ProfileMap) {
365c0981da4SDimitry Andric addContext(I.second.getContext());
366b60736ecSDimitry Andric addNames(I.second);
367b60736ecSDimitry Andric }
368344a3780SDimitry Andric
369344a3780SDimitry Andric // If NameTable contains ".__uniq." suffix, set SecFlagUniqSuffix flag
370344a3780SDimitry Andric // so compiler won't strip the suffix during profile matching after
371344a3780SDimitry Andric // seeing the flag in the profile.
372b1c73532SDimitry Andric // Original names are unavailable if using MD5, so this option has no use.
373b1c73532SDimitry Andric if (!UseMD5) {
374344a3780SDimitry Andric for (const auto &I : NameTable) {
375b1c73532SDimitry Andric if (I.first.stringRef().contains(FunctionSamples::UniqSuffix)) {
376344a3780SDimitry Andric addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagUniqSuffix);
377344a3780SDimitry Andric break;
378344a3780SDimitry Andric }
379344a3780SDimitry Andric }
380b1c73532SDimitry Andric }
381344a3780SDimitry Andric
382b60736ecSDimitry Andric if (auto EC = writeNameTable())
383b60736ecSDimitry Andric return EC;
384b60736ecSDimitry Andric return sampleprof_error::success;
385b60736ecSDimitry Andric }
386b60736ecSDimitry Andric
writeCSNameTableSection()387c0981da4SDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeCSNameTableSection() {
388c0981da4SDimitry Andric // Sort the names to make CSNameTable deterministic.
389c0981da4SDimitry Andric std::set<SampleContext> OrderedContexts;
390c0981da4SDimitry Andric for (const auto &I : CSNameTable)
391c0981da4SDimitry Andric OrderedContexts.insert(I.first);
392c0981da4SDimitry Andric assert(OrderedContexts.size() == CSNameTable.size() &&
393c0981da4SDimitry Andric "Unmatched ordered and unordered contexts");
394c0981da4SDimitry Andric uint64_t I = 0;
395c0981da4SDimitry Andric for (auto &Context : OrderedContexts)
396c0981da4SDimitry Andric CSNameTable[Context] = I++;
397c0981da4SDimitry Andric
398c0981da4SDimitry Andric auto &OS = *OutputStream;
399c0981da4SDimitry Andric encodeULEB128(OrderedContexts.size(), OS);
400b1c73532SDimitry Andric support::endian::Writer Writer(OS, llvm::endianness::little);
401c0981da4SDimitry Andric for (auto Context : OrderedContexts) {
402c0981da4SDimitry Andric auto Frames = Context.getContextFrames();
403c0981da4SDimitry Andric encodeULEB128(Frames.size(), OS);
404c0981da4SDimitry Andric for (auto &Callsite : Frames) {
405b1c73532SDimitry Andric if (std::error_code EC = writeNameIdx(Callsite.Func))
406c0981da4SDimitry Andric return EC;
407c0981da4SDimitry Andric encodeULEB128(Callsite.Location.LineOffset, OS);
408c0981da4SDimitry Andric encodeULEB128(Callsite.Location.Discriminator, OS);
409c0981da4SDimitry Andric }
410c0981da4SDimitry Andric }
411c0981da4SDimitry Andric
412c0981da4SDimitry Andric return sampleprof_error::success;
413c0981da4SDimitry Andric }
414c0981da4SDimitry Andric
415b60736ecSDimitry Andric std::error_code
writeProfileSymbolListSection()416b60736ecSDimitry Andric SampleProfileWriterExtBinaryBase::writeProfileSymbolListSection() {
417b60736ecSDimitry Andric if (ProfSymList && ProfSymList->size() > 0)
418b60736ecSDimitry Andric if (std::error_code EC = ProfSymList->write(*OutputStream))
419b60736ecSDimitry Andric return EC;
420b60736ecSDimitry Andric
421b60736ecSDimitry Andric return sampleprof_error::success;
422b60736ecSDimitry Andric }
423b60736ecSDimitry Andric
writeOneSection(SecType Type,uint32_t LayoutIdx,const SampleProfileMap & ProfileMap)424b60736ecSDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeOneSection(
425c0981da4SDimitry Andric SecType Type, uint32_t LayoutIdx, const SampleProfileMap &ProfileMap) {
426b60736ecSDimitry Andric // The setting of SecFlagCompress should happen before markSectionStart.
427b60736ecSDimitry Andric if (Type == SecProfileSymbolList && ProfSymList && ProfSymList->toCompress())
428b60736ecSDimitry Andric setToCompressSection(SecProfileSymbolList);
429b60736ecSDimitry Andric if (Type == SecFuncMetadata && FunctionSamples::ProfileIsProbeBased)
430b60736ecSDimitry Andric addSectionFlag(SecFuncMetadata, SecFuncMetadataFlags::SecFlagIsProbeBased);
43177fc4c14SDimitry Andric if (Type == SecFuncMetadata &&
432145449b1SDimitry Andric (FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsPreInlined))
433344a3780SDimitry Andric addSectionFlag(SecFuncMetadata, SecFuncMetadataFlags::SecFlagHasAttribute);
434145449b1SDimitry Andric if (Type == SecProfSummary && FunctionSamples::ProfileIsCS)
43577fc4c14SDimitry Andric addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagFullContext);
436145449b1SDimitry Andric if (Type == SecProfSummary && FunctionSamples::ProfileIsPreInlined)
437145449b1SDimitry Andric addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagIsPreInlined);
438344a3780SDimitry Andric if (Type == SecProfSummary && FunctionSamples::ProfileIsFS)
439344a3780SDimitry Andric addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagFSDiscriminator);
440b60736ecSDimitry Andric
441b60736ecSDimitry Andric uint64_t SectionStart = markSectionStart(Type, LayoutIdx);
442b60736ecSDimitry Andric switch (Type) {
443b60736ecSDimitry Andric case SecProfSummary:
444b60736ecSDimitry Andric computeSummary(ProfileMap);
445b60736ecSDimitry Andric if (auto EC = writeSummary())
446b60736ecSDimitry Andric return EC;
447b60736ecSDimitry Andric break;
448b60736ecSDimitry Andric case SecNameTable:
449b60736ecSDimitry Andric if (auto EC = writeNameTableSection(ProfileMap))
450b60736ecSDimitry Andric return EC;
451b60736ecSDimitry Andric break;
452c0981da4SDimitry Andric case SecCSNameTable:
453c0981da4SDimitry Andric if (auto EC = writeCSNameTableSection())
454c0981da4SDimitry Andric return EC;
455c0981da4SDimitry Andric break;
456b60736ecSDimitry Andric case SecLBRProfile:
457b60736ecSDimitry Andric SecLBRProfileStart = OutputStream->tell();
458b60736ecSDimitry Andric if (std::error_code EC = writeFuncProfiles(ProfileMap))
459b60736ecSDimitry Andric return EC;
460b60736ecSDimitry Andric break;
461b60736ecSDimitry Andric case SecFuncOffsetTable:
462b60736ecSDimitry Andric if (auto EC = writeFuncOffsetTable())
463b60736ecSDimitry Andric return EC;
464b60736ecSDimitry Andric break;
465b60736ecSDimitry Andric case SecFuncMetadata:
466b60736ecSDimitry Andric if (std::error_code EC = writeFuncMetadata(ProfileMap))
467b60736ecSDimitry Andric return EC;
468b60736ecSDimitry Andric break;
469b60736ecSDimitry Andric case SecProfileSymbolList:
470b60736ecSDimitry Andric if (auto EC = writeProfileSymbolListSection())
471b60736ecSDimitry Andric return EC;
472b60736ecSDimitry Andric break;
473b60736ecSDimitry Andric default:
474b60736ecSDimitry Andric if (auto EC = writeCustomSection(Type))
475b60736ecSDimitry Andric return EC;
476b60736ecSDimitry Andric break;
477b60736ecSDimitry Andric }
478b60736ecSDimitry Andric if (std::error_code EC = addNewSection(Type, LayoutIdx, SectionStart))
479b60736ecSDimitry Andric return EC;
480b60736ecSDimitry Andric return sampleprof_error::success;
481b60736ecSDimitry Andric }
482b60736ecSDimitry Andric
writeDefaultLayout(const SampleProfileMap & ProfileMap)483b60736ecSDimitry Andric std::error_code SampleProfileWriterExtBinary::writeDefaultLayout(
484c0981da4SDimitry Andric const SampleProfileMap &ProfileMap) {
485b60736ecSDimitry Andric // The const indices passed to writeOneSection below are specifying the
486b60736ecSDimitry Andric // positions of the sections in SectionHdrLayout. Look at
487b60736ecSDimitry Andric // initSectionHdrLayout to find out where each section is located in
488b60736ecSDimitry Andric // SectionHdrLayout.
489b60736ecSDimitry Andric if (auto EC = writeOneSection(SecProfSummary, 0, ProfileMap))
490b60736ecSDimitry Andric return EC;
491b60736ecSDimitry Andric if (auto EC = writeOneSection(SecNameTable, 1, ProfileMap))
492b60736ecSDimitry Andric return EC;
493c0981da4SDimitry Andric if (auto EC = writeOneSection(SecCSNameTable, 2, ProfileMap))
494b60736ecSDimitry Andric return EC;
495c0981da4SDimitry Andric if (auto EC = writeOneSection(SecLBRProfile, 4, ProfileMap))
496b60736ecSDimitry Andric return EC;
497c0981da4SDimitry Andric if (auto EC = writeOneSection(SecProfileSymbolList, 5, ProfileMap))
498b60736ecSDimitry Andric return EC;
499c0981da4SDimitry Andric if (auto EC = writeOneSection(SecFuncOffsetTable, 3, ProfileMap))
500c0981da4SDimitry Andric return EC;
501c0981da4SDimitry Andric if (auto EC = writeOneSection(SecFuncMetadata, 6, ProfileMap))
502b60736ecSDimitry Andric return EC;
503b60736ecSDimitry Andric return sampleprof_error::success;
504b60736ecSDimitry Andric }
505b60736ecSDimitry Andric
splitProfileMapToTwo(const SampleProfileMap & ProfileMap,SampleProfileMap & ContextProfileMap,SampleProfileMap & NoContextProfileMap)506c0981da4SDimitry Andric static void splitProfileMapToTwo(const SampleProfileMap &ProfileMap,
507c0981da4SDimitry Andric SampleProfileMap &ContextProfileMap,
508c0981da4SDimitry Andric SampleProfileMap &NoContextProfileMap) {
509b60736ecSDimitry Andric for (const auto &I : ProfileMap) {
510b60736ecSDimitry Andric if (I.second.getCallsiteSamples().size())
511c0981da4SDimitry Andric ContextProfileMap.insert({I.first, I.second});
512b60736ecSDimitry Andric else
513c0981da4SDimitry Andric NoContextProfileMap.insert({I.first, I.second});
514b60736ecSDimitry Andric }
515b60736ecSDimitry Andric }
516b60736ecSDimitry Andric
writeCtxSplitLayout(const SampleProfileMap & ProfileMap)517b60736ecSDimitry Andric std::error_code SampleProfileWriterExtBinary::writeCtxSplitLayout(
518c0981da4SDimitry Andric const SampleProfileMap &ProfileMap) {
519c0981da4SDimitry Andric SampleProfileMap ContextProfileMap, NoContextProfileMap;
520b60736ecSDimitry Andric splitProfileMapToTwo(ProfileMap, ContextProfileMap, NoContextProfileMap);
521b60736ecSDimitry Andric
522b60736ecSDimitry Andric if (auto EC = writeOneSection(SecProfSummary, 0, ProfileMap))
523b60736ecSDimitry Andric return EC;
524b60736ecSDimitry Andric if (auto EC = writeOneSection(SecNameTable, 1, ProfileMap))
525b60736ecSDimitry Andric return EC;
526b60736ecSDimitry Andric if (auto EC = writeOneSection(SecLBRProfile, 3, ContextProfileMap))
527b60736ecSDimitry Andric return EC;
528b60736ecSDimitry Andric if (auto EC = writeOneSection(SecFuncOffsetTable, 2, ContextProfileMap))
529b60736ecSDimitry Andric return EC;
530b60736ecSDimitry Andric // Mark the section to have no context. Note section flag needs to be set
531b60736ecSDimitry Andric // before writing the section.
532b60736ecSDimitry Andric addSectionFlag(5, SecCommonFlags::SecFlagFlat);
533b60736ecSDimitry Andric if (auto EC = writeOneSection(SecLBRProfile, 5, NoContextProfileMap))
534b60736ecSDimitry Andric return EC;
535b60736ecSDimitry Andric // Mark the section to have no context. Note section flag needs to be set
536b60736ecSDimitry Andric // before writing the section.
537b60736ecSDimitry Andric addSectionFlag(4, SecCommonFlags::SecFlagFlat);
538b60736ecSDimitry Andric if (auto EC = writeOneSection(SecFuncOffsetTable, 4, NoContextProfileMap))
539b60736ecSDimitry Andric return EC;
540b60736ecSDimitry Andric if (auto EC = writeOneSection(SecProfileSymbolList, 6, ProfileMap))
541b60736ecSDimitry Andric return EC;
542b60736ecSDimitry Andric if (auto EC = writeOneSection(SecFuncMetadata, 7, ProfileMap))
543b60736ecSDimitry Andric return EC;
544b60736ecSDimitry Andric
545cfca06d7SDimitry Andric return sampleprof_error::success;
546cfca06d7SDimitry Andric }
547cfca06d7SDimitry Andric
writeSections(const SampleProfileMap & ProfileMap)5481d5ae102SDimitry Andric std::error_code SampleProfileWriterExtBinary::writeSections(
549c0981da4SDimitry Andric const SampleProfileMap &ProfileMap) {
550b60736ecSDimitry Andric std::error_code EC;
551b60736ecSDimitry Andric if (SecLayout == DefaultLayout)
552b60736ecSDimitry Andric EC = writeDefaultLayout(ProfileMap);
553b60736ecSDimitry Andric else if (SecLayout == CtxSplitLayout)
554b60736ecSDimitry Andric EC = writeCtxSplitLayout(ProfileMap);
555b60736ecSDimitry Andric else
556b60736ecSDimitry Andric llvm_unreachable("Unsupported layout");
5571d5ae102SDimitry Andric return EC;
5581d5ae102SDimitry Andric }
5591d5ae102SDimitry Andric
560eb11fae6SDimitry Andric /// Write samples to a text file.
561dd58ef01SDimitry Andric ///
562dd58ef01SDimitry Andric /// Note: it may be tempting to implement this in terms of
563dd58ef01SDimitry Andric /// FunctionSamples::print(). Please don't. The dump functionality is intended
564dd58ef01SDimitry Andric /// for debugging and has no specified form.
565dd58ef01SDimitry Andric ///
566dd58ef01SDimitry Andric /// The format used here is more structured and deliberate because
567dd58ef01SDimitry Andric /// it needs to be parsed by the SampleProfileReaderText class.
writeSample(const FunctionSamples & S)5681d5ae102SDimitry Andric std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {
569dd58ef01SDimitry Andric auto &OS = *OutputStream;
570145449b1SDimitry Andric if (FunctionSamples::ProfileIsCS)
571c0981da4SDimitry Andric OS << "[" << S.getContext().toString() << "]:" << S.getTotalSamples();
572b60736ecSDimitry Andric else
573b1c73532SDimitry Andric OS << S.getFunction() << ":" << S.getTotalSamples();
574344a3780SDimitry Andric
575dd58ef01SDimitry Andric if (Indent == 0)
576dd58ef01SDimitry Andric OS << ":" << S.getHeadSamples();
577dd58ef01SDimitry Andric OS << "\n";
5787fa27ce4SDimitry Andric LineCount++;
57967c32a98SDimitry Andric
580dd58ef01SDimitry Andric SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
581dd58ef01SDimitry Andric for (const auto &I : SortedSamples.get()) {
582dd58ef01SDimitry Andric LineLocation Loc = I->first;
583dd58ef01SDimitry Andric const SampleRecord &Sample = I->second;
584dd58ef01SDimitry Andric OS.indent(Indent + 1);
58567c32a98SDimitry Andric if (Loc.Discriminator == 0)
58667c32a98SDimitry Andric OS << Loc.LineOffset << ": ";
58767c32a98SDimitry Andric else
58867c32a98SDimitry Andric OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
58967c32a98SDimitry Andric
59067c32a98SDimitry Andric OS << Sample.getSamples();
59167c32a98SDimitry Andric
5921d5ae102SDimitry Andric for (const auto &J : Sample.getSortedCallTargets())
5931d5ae102SDimitry Andric OS << " " << J.first << ":" << J.second;
59467c32a98SDimitry Andric OS << "\n";
5957fa27ce4SDimitry Andric LineCount++;
59667c32a98SDimitry Andric }
59767c32a98SDimitry Andric
59871d5a254SDimitry Andric SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
599dd58ef01SDimitry Andric S.getCallsiteSamples());
600dd58ef01SDimitry Andric Indent += 1;
60171d5a254SDimitry Andric for (const auto &I : SortedCallsiteSamples.get())
60271d5a254SDimitry Andric for (const auto &FS : I->second) {
60301095a5dSDimitry Andric LineLocation Loc = I->first;
60471d5a254SDimitry Andric const FunctionSamples &CalleeSamples = FS.second;
605dd58ef01SDimitry Andric OS.indent(Indent);
606dd58ef01SDimitry Andric if (Loc.Discriminator == 0)
607dd58ef01SDimitry Andric OS << Loc.LineOffset << ": ";
608dd58ef01SDimitry Andric else
609dd58ef01SDimitry Andric OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
6101d5ae102SDimitry Andric if (std::error_code EC = writeSample(CalleeSamples))
611dd58ef01SDimitry Andric return EC;
612dd58ef01SDimitry Andric }
613dd58ef01SDimitry Andric Indent -= 1;
614dd58ef01SDimitry Andric
615b60736ecSDimitry Andric if (FunctionSamples::ProfileIsProbeBased) {
616b60736ecSDimitry Andric OS.indent(Indent + 1);
617b60736ecSDimitry Andric OS << "!CFGChecksum: " << S.getFunctionHash() << "\n";
6187fa27ce4SDimitry Andric LineCount++;
619b60736ecSDimitry Andric }
62077fc4c14SDimitry Andric
62177fc4c14SDimitry Andric if (S.getContext().getAllAttributes()) {
622344a3780SDimitry Andric OS.indent(Indent + 1);
623344a3780SDimitry Andric OS << "!Attributes: " << S.getContext().getAllAttributes() << "\n";
6247fa27ce4SDimitry Andric LineCount++;
625344a3780SDimitry Andric }
626b60736ecSDimitry Andric
627dd58ef01SDimitry Andric return sampleprof_error::success;
62867c32a98SDimitry Andric }
62967c32a98SDimitry Andric
630c0981da4SDimitry Andric std::error_code
writeContextIdx(const SampleContext & Context)631c0981da4SDimitry Andric SampleProfileWriterBinary::writeContextIdx(const SampleContext &Context) {
632c0981da4SDimitry Andric assert(!Context.hasContext() && "cs profile is not supported");
633b1c73532SDimitry Andric return writeNameIdx(Context.getFunction());
634344a3780SDimitry Andric }
635344a3780SDimitry Andric
writeNameIdx(FunctionId FName)636b1c73532SDimitry Andric std::error_code SampleProfileWriterBinary::writeNameIdx(FunctionId FName) {
637c0981da4SDimitry Andric auto &NTable = getNameTable();
638c0981da4SDimitry Andric const auto &Ret = NTable.find(FName);
639c0981da4SDimitry Andric if (Ret == NTable.end())
640dd58ef01SDimitry Andric return sampleprof_error::truncated_name_table;
641344a3780SDimitry Andric encodeULEB128(Ret->second, *OutputStream);
642dd58ef01SDimitry Andric return sampleprof_error::success;
643dd58ef01SDimitry Andric }
64467c32a98SDimitry Andric
addName(FunctionId FName)645b1c73532SDimitry Andric void SampleProfileWriterBinary::addName(FunctionId FName) {
646c0981da4SDimitry Andric auto &NTable = getNameTable();
647c0981da4SDimitry Andric NTable.insert(std::make_pair(FName, 0));
648344a3780SDimitry Andric }
649c0981da4SDimitry Andric
addContext(const SampleContext & Context)650c0981da4SDimitry Andric void SampleProfileWriterBinary::addContext(const SampleContext &Context) {
651b1c73532SDimitry Andric addName(Context.getFunction());
652dd58ef01SDimitry Andric }
653dd58ef01SDimitry Andric
addNames(const FunctionSamples & S)654dd58ef01SDimitry Andric void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
655dd58ef01SDimitry Andric // Add all the names in indirect call targets.
656dd58ef01SDimitry Andric for (const auto &I : S.getBodySamples()) {
657dd58ef01SDimitry Andric const SampleRecord &Sample = I.second;
658dd58ef01SDimitry Andric for (const auto &J : Sample.getCallTargets())
659b1c73532SDimitry Andric addName(J.first);
660dd58ef01SDimitry Andric }
661dd58ef01SDimitry Andric
662dd58ef01SDimitry Andric // Recursively add all the names for inlined callsites.
66371d5a254SDimitry Andric for (const auto &J : S.getCallsiteSamples())
66471d5a254SDimitry Andric for (const auto &FS : J.second) {
66571d5a254SDimitry Andric const FunctionSamples &CalleeSamples = FS.second;
666b1c73532SDimitry Andric addName(CalleeSamples.getFunction());
667dd58ef01SDimitry Andric addNames(CalleeSamples);
668dd58ef01SDimitry Andric }
669dd58ef01SDimitry Andric }
670dd58ef01SDimitry Andric
addContext(const SampleContext & Context)671c0981da4SDimitry Andric void SampleProfileWriterExtBinaryBase::addContext(
672c0981da4SDimitry Andric const SampleContext &Context) {
673c0981da4SDimitry Andric if (Context.hasContext()) {
674c0981da4SDimitry Andric for (auto &Callsite : Context.getContextFrames())
675b1c73532SDimitry Andric SampleProfileWriterBinary::addName(Callsite.Func);
676c0981da4SDimitry Andric CSNameTable.insert(std::make_pair(Context, 0));
677c0981da4SDimitry Andric } else {
678b1c73532SDimitry Andric SampleProfileWriterBinary::addName(Context.getFunction());
679c0981da4SDimitry Andric }
680c0981da4SDimitry Andric }
681c0981da4SDimitry Andric
stablizeNameTable(MapVector<FunctionId,uint32_t> & NameTable,std::set<FunctionId> & V)682c0981da4SDimitry Andric void SampleProfileWriterBinary::stablizeNameTable(
683b1c73532SDimitry Andric MapVector<FunctionId, uint32_t> &NameTable, std::set<FunctionId> &V) {
684eb11fae6SDimitry Andric // Sort the names to make NameTable deterministic.
685eb11fae6SDimitry Andric for (const auto &I : NameTable)
686eb11fae6SDimitry Andric V.insert(I.first);
687eb11fae6SDimitry Andric int i = 0;
688b1c73532SDimitry Andric for (const FunctionId &N : V)
689eb11fae6SDimitry Andric NameTable[N] = i++;
690eb11fae6SDimitry Andric }
691dd58ef01SDimitry Andric
writeNameTable()6921d5ae102SDimitry Andric std::error_code SampleProfileWriterBinary::writeNameTable() {
693eb11fae6SDimitry Andric auto &OS = *OutputStream;
694b1c73532SDimitry Andric std::set<FunctionId> V;
695c0981da4SDimitry Andric stablizeNameTable(NameTable, V);
696eb11fae6SDimitry Andric
697eb11fae6SDimitry Andric // Write out the name table.
698eb11fae6SDimitry Andric encodeULEB128(NameTable.size(), OS);
699eb11fae6SDimitry Andric for (auto N : V) {
700eb11fae6SDimitry Andric OS << N;
701eb11fae6SDimitry Andric encodeULEB128(0, OS);
702eb11fae6SDimitry Andric }
703eb11fae6SDimitry Andric return sampleprof_error::success;
704eb11fae6SDimitry Andric }
705eb11fae6SDimitry Andric
7061d5ae102SDimitry Andric std::error_code
writeMagicIdent(SampleProfileFormat Format)7071d5ae102SDimitry Andric SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) {
708eb11fae6SDimitry Andric auto &OS = *OutputStream;
709dd58ef01SDimitry Andric // Write file magic identifier.
7101d5ae102SDimitry Andric encodeULEB128(SPMagic(Format), OS);
711eb11fae6SDimitry Andric encodeULEB128(SPVersion(), OS);
712eb11fae6SDimitry Andric return sampleprof_error::success;
713eb11fae6SDimitry Andric }
714eb11fae6SDimitry Andric
715c0981da4SDimitry Andric std::error_code
writeHeader(const SampleProfileMap & ProfileMap)716c0981da4SDimitry Andric SampleProfileWriterBinary::writeHeader(const SampleProfileMap &ProfileMap) {
7177fa27ce4SDimitry Andric // When calling write on a different profile map, existing names should be
7187fa27ce4SDimitry Andric // cleared.
7197fa27ce4SDimitry Andric NameTable.clear();
7207fa27ce4SDimitry Andric
7211d5ae102SDimitry Andric writeMagicIdent(Format);
722dd58ef01SDimitry Andric
72301095a5dSDimitry Andric computeSummary(ProfileMap);
72401095a5dSDimitry Andric if (auto EC = writeSummary())
72501095a5dSDimitry Andric return EC;
72601095a5dSDimitry Andric
727dd58ef01SDimitry Andric // Generate the name table for all the functions referenced in the profile.
728dd58ef01SDimitry Andric for (const auto &I : ProfileMap) {
729b1c73532SDimitry Andric addContext(I.second.getContext());
730dd58ef01SDimitry Andric addNames(I.second);
73167c32a98SDimitry Andric }
73267c32a98SDimitry Andric
733eb11fae6SDimitry Andric writeNameTable();
734dd58ef01SDimitry Andric return sampleprof_error::success;
735dd58ef01SDimitry Andric }
736dd58ef01SDimitry Andric
setToCompressAllSections()7371d5ae102SDimitry Andric void SampleProfileWriterExtBinaryBase::setToCompressAllSections() {
7381d5ae102SDimitry Andric for (auto &Entry : SectionHdrLayout)
739cfca06d7SDimitry Andric addSecFlag(Entry, SecCommonFlags::SecFlagCompress);
7401d5ae102SDimitry Andric }
7411d5ae102SDimitry Andric
setToCompressSection(SecType Type)7421d5ae102SDimitry Andric void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) {
743cfca06d7SDimitry Andric addSectionFlag(Type, SecCommonFlags::SecFlagCompress);
7441d5ae102SDimitry Andric }
7451d5ae102SDimitry Andric
allocSecHdrTable()7461d5ae102SDimitry Andric void SampleProfileWriterExtBinaryBase::allocSecHdrTable() {
747b1c73532SDimitry Andric support::endian::Writer Writer(*OutputStream, llvm::endianness::little);
7481d5ae102SDimitry Andric
7491d5ae102SDimitry Andric Writer.write(static_cast<uint64_t>(SectionHdrLayout.size()));
7501d5ae102SDimitry Andric SecHdrTableOffset = OutputStream->tell();
7511d5ae102SDimitry Andric for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
7521d5ae102SDimitry Andric Writer.write(static_cast<uint64_t>(-1));
7531d5ae102SDimitry Andric Writer.write(static_cast<uint64_t>(-1));
7541d5ae102SDimitry Andric Writer.write(static_cast<uint64_t>(-1));
7551d5ae102SDimitry Andric Writer.write(static_cast<uint64_t>(-1));
7561d5ae102SDimitry Andric }
7571d5ae102SDimitry Andric }
7581d5ae102SDimitry Andric
writeSecHdrTable()7591d5ae102SDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() {
760b60736ecSDimitry Andric assert(SecHdrTable.size() == SectionHdrLayout.size() &&
761b60736ecSDimitry Andric "SecHdrTable entries doesn't match SectionHdrLayout");
762b60736ecSDimitry Andric SmallVector<uint32_t, 16> IndexMap(SecHdrTable.size(), -1);
763b60736ecSDimitry Andric for (uint32_t TableIdx = 0; TableIdx < SecHdrTable.size(); TableIdx++) {
764b60736ecSDimitry Andric IndexMap[SecHdrTable[TableIdx].LayoutIndex] = TableIdx;
7651d5ae102SDimitry Andric }
7661d5ae102SDimitry Andric
7671d5ae102SDimitry Andric // Write the section header table in the order specified in
768b60736ecSDimitry Andric // SectionHdrLayout. SectionHdrLayout specifies the sections
769b60736ecSDimitry Andric // order in which profile reader expect to read, so the section
770b60736ecSDimitry Andric // header table should be written in the order in SectionHdrLayout.
771b60736ecSDimitry Andric // Note that the section order in SecHdrTable may be different
772b60736ecSDimitry Andric // from the order in SectionHdrLayout, for example, SecFuncOffsetTable
773b60736ecSDimitry Andric // needs to be computed after SecLBRProfile (the order in SecHdrTable),
774b60736ecSDimitry Andric // but it needs to be read before SecLBRProfile (the order in
775b60736ecSDimitry Andric // SectionHdrLayout). So we use IndexMap above to switch the order.
7767fa27ce4SDimitry Andric support::endian::SeekableWriter Writer(
777b1c73532SDimitry Andric static_cast<raw_pwrite_stream &>(*OutputStream),
778b1c73532SDimitry Andric llvm::endianness::little);
779b60736ecSDimitry Andric for (uint32_t LayoutIdx = 0; LayoutIdx < SectionHdrLayout.size();
780b60736ecSDimitry Andric LayoutIdx++) {
781b60736ecSDimitry Andric assert(IndexMap[LayoutIdx] < SecHdrTable.size() &&
782b60736ecSDimitry Andric "Incorrect LayoutIdx in SecHdrTable");
783b60736ecSDimitry Andric auto Entry = SecHdrTable[IndexMap[LayoutIdx]];
7847fa27ce4SDimitry Andric Writer.pwrite(static_cast<uint64_t>(Entry.Type),
7857fa27ce4SDimitry Andric SecHdrTableOffset + 4 * LayoutIdx * sizeof(uint64_t));
7867fa27ce4SDimitry Andric Writer.pwrite(static_cast<uint64_t>(Entry.Flags),
7877fa27ce4SDimitry Andric SecHdrTableOffset + (4 * LayoutIdx + 1) * sizeof(uint64_t));
7887fa27ce4SDimitry Andric Writer.pwrite(static_cast<uint64_t>(Entry.Offset),
7897fa27ce4SDimitry Andric SecHdrTableOffset + (4 * LayoutIdx + 2) * sizeof(uint64_t));
7907fa27ce4SDimitry Andric Writer.pwrite(static_cast<uint64_t>(Entry.Size),
7917fa27ce4SDimitry Andric SecHdrTableOffset + (4 * LayoutIdx + 3) * sizeof(uint64_t));
7921d5ae102SDimitry Andric }
7931d5ae102SDimitry Andric
7941d5ae102SDimitry Andric return sampleprof_error::success;
7951d5ae102SDimitry Andric }
7961d5ae102SDimitry Andric
writeHeader(const SampleProfileMap & ProfileMap)7971d5ae102SDimitry Andric std::error_code SampleProfileWriterExtBinaryBase::writeHeader(
798c0981da4SDimitry Andric const SampleProfileMap &ProfileMap) {
7991d5ae102SDimitry Andric auto &OS = *OutputStream;
8001d5ae102SDimitry Andric FileStart = OS.tell();
8011d5ae102SDimitry Andric writeMagicIdent(Format);
8021d5ae102SDimitry Andric
8031d5ae102SDimitry Andric allocSecHdrTable();
8041d5ae102SDimitry Andric return sampleprof_error::success;
8051d5ae102SDimitry Andric }
8061d5ae102SDimitry Andric
writeSummary()80701095a5dSDimitry Andric std::error_code SampleProfileWriterBinary::writeSummary() {
80801095a5dSDimitry Andric auto &OS = *OutputStream;
80901095a5dSDimitry Andric encodeULEB128(Summary->getTotalCount(), OS);
81001095a5dSDimitry Andric encodeULEB128(Summary->getMaxCount(), OS);
81101095a5dSDimitry Andric encodeULEB128(Summary->getMaxFunctionCount(), OS);
81201095a5dSDimitry Andric encodeULEB128(Summary->getNumCounts(), OS);
81301095a5dSDimitry Andric encodeULEB128(Summary->getNumFunctions(), OS);
814ac9a064cSDimitry Andric ArrayRef<ProfileSummaryEntry> Entries = Summary->getDetailedSummary();
81501095a5dSDimitry Andric encodeULEB128(Entries.size(), OS);
81601095a5dSDimitry Andric for (auto Entry : Entries) {
81701095a5dSDimitry Andric encodeULEB128(Entry.Cutoff, OS);
81801095a5dSDimitry Andric encodeULEB128(Entry.MinCount, OS);
81901095a5dSDimitry Andric encodeULEB128(Entry.NumCounts, OS);
82001095a5dSDimitry Andric }
82101095a5dSDimitry Andric return sampleprof_error::success;
82201095a5dSDimitry Andric }
writeBody(const FunctionSamples & S)82301095a5dSDimitry Andric std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
824dd58ef01SDimitry Andric auto &OS = *OutputStream;
825c0981da4SDimitry Andric if (std::error_code EC = writeContextIdx(S.getContext()))
826dd58ef01SDimitry Andric return EC;
827dd58ef01SDimitry Andric
82867c32a98SDimitry Andric encodeULEB128(S.getTotalSamples(), OS);
829dd58ef01SDimitry Andric
830dd58ef01SDimitry Andric // Emit all the body samples.
83167c32a98SDimitry Andric encodeULEB128(S.getBodySamples().size(), OS);
83267c32a98SDimitry Andric for (const auto &I : S.getBodySamples()) {
83367c32a98SDimitry Andric LineLocation Loc = I.first;
83467c32a98SDimitry Andric const SampleRecord &Sample = I.second;
83567c32a98SDimitry Andric encodeULEB128(Loc.LineOffset, OS);
83667c32a98SDimitry Andric encodeULEB128(Loc.Discriminator, OS);
83767c32a98SDimitry Andric encodeULEB128(Sample.getSamples(), OS);
83867c32a98SDimitry Andric encodeULEB128(Sample.getCallTargets().size(), OS);
8391d5ae102SDimitry Andric for (const auto &J : Sample.getSortedCallTargets()) {
840b1c73532SDimitry Andric FunctionId Callee = J.first;
841dd58ef01SDimitry Andric uint64_t CalleeSamples = J.second;
842dd58ef01SDimitry Andric if (std::error_code EC = writeNameIdx(Callee))
843dd58ef01SDimitry Andric return EC;
84467c32a98SDimitry Andric encodeULEB128(CalleeSamples, OS);
84567c32a98SDimitry Andric }
84667c32a98SDimitry Andric }
84767c32a98SDimitry Andric
848dd58ef01SDimitry Andric // Recursively emit all the callsite samples.
849044eb2f6SDimitry Andric uint64_t NumCallsites = 0;
850044eb2f6SDimitry Andric for (const auto &J : S.getCallsiteSamples())
851044eb2f6SDimitry Andric NumCallsites += J.second.size();
852044eb2f6SDimitry Andric encodeULEB128(NumCallsites, OS);
85371d5a254SDimitry Andric for (const auto &J : S.getCallsiteSamples())
85471d5a254SDimitry Andric for (const auto &FS : J.second) {
85501095a5dSDimitry Andric LineLocation Loc = J.first;
85671d5a254SDimitry Andric const FunctionSamples &CalleeSamples = FS.second;
857dd58ef01SDimitry Andric encodeULEB128(Loc.LineOffset, OS);
858dd58ef01SDimitry Andric encodeULEB128(Loc.Discriminator, OS);
85901095a5dSDimitry Andric if (std::error_code EC = writeBody(CalleeSamples))
860dd58ef01SDimitry Andric return EC;
86167c32a98SDimitry Andric }
86267c32a98SDimitry Andric
863dd58ef01SDimitry Andric return sampleprof_error::success;
864dd58ef01SDimitry Andric }
865dd58ef01SDimitry Andric
866eb11fae6SDimitry Andric /// Write samples of a top-level function to a binary file.
867dd58ef01SDimitry Andric ///
868dd58ef01SDimitry Andric /// \returns true if the samples were written successfully, false otherwise.
8691d5ae102SDimitry Andric std::error_code
writeSample(const FunctionSamples & S)8701d5ae102SDimitry Andric SampleProfileWriterBinary::writeSample(const FunctionSamples &S) {
871dd58ef01SDimitry Andric encodeULEB128(S.getHeadSamples(), *OutputStream);
87201095a5dSDimitry Andric return writeBody(S);
873dd58ef01SDimitry Andric }
874dd58ef01SDimitry Andric
875eb11fae6SDimitry Andric /// Create a sample profile file writer based on the specified format.
87667c32a98SDimitry Andric ///
87767c32a98SDimitry Andric /// \param Filename The file to create.
87867c32a98SDimitry Andric ///
87967c32a98SDimitry Andric /// \param Format Encoding format for the profile file.
88067c32a98SDimitry Andric ///
88167c32a98SDimitry Andric /// \returns an error code indicating the status of the created writer.
88267c32a98SDimitry Andric ErrorOr<std::unique_ptr<SampleProfileWriter>>
create(StringRef Filename,SampleProfileFormat Format)88367c32a98SDimitry Andric SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
88467c32a98SDimitry Andric std::error_code EC;
885dd58ef01SDimitry Andric std::unique_ptr<raw_ostream> OS;
8867fa27ce4SDimitry Andric if (Format == SPF_Binary || Format == SPF_Ext_Binary)
8871d5ae102SDimitry Andric OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None));
888dd58ef01SDimitry Andric else
889344a3780SDimitry Andric OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_TextWithCRLF));
890dd58ef01SDimitry Andric if (EC)
891dd58ef01SDimitry Andric return EC;
892dd58ef01SDimitry Andric
893dd58ef01SDimitry Andric return create(OS, Format);
894dd58ef01SDimitry Andric }
895dd58ef01SDimitry Andric
896eb11fae6SDimitry Andric /// Create a sample profile stream writer based on the specified format.
897dd58ef01SDimitry Andric ///
898dd58ef01SDimitry Andric /// \param OS The output stream to store the profile data to.
899dd58ef01SDimitry Andric ///
900dd58ef01SDimitry Andric /// \param Format Encoding format for the profile file.
901dd58ef01SDimitry Andric ///
902dd58ef01SDimitry Andric /// \returns an error code indicating the status of the created writer.
903dd58ef01SDimitry Andric ErrorOr<std::unique_ptr<SampleProfileWriter>>
create(std::unique_ptr<raw_ostream> & OS,SampleProfileFormat Format)904dd58ef01SDimitry Andric SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
905dd58ef01SDimitry Andric SampleProfileFormat Format) {
906dd58ef01SDimitry Andric std::error_code EC;
90767c32a98SDimitry Andric std::unique_ptr<SampleProfileWriter> Writer;
90867c32a98SDimitry Andric
909344a3780SDimitry Andric // Currently only Text and Extended Binary format are supported for CSSPGO.
910145449b1SDimitry Andric if ((FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsProbeBased) &&
9117fa27ce4SDimitry Andric Format == SPF_Binary)
912344a3780SDimitry Andric return sampleprof_error::unsupported_writing_format;
913344a3780SDimitry Andric
91467c32a98SDimitry Andric if (Format == SPF_Binary)
915eb11fae6SDimitry Andric Writer.reset(new SampleProfileWriterRawBinary(OS));
9161d5ae102SDimitry Andric else if (Format == SPF_Ext_Binary)
9171d5ae102SDimitry Andric Writer.reset(new SampleProfileWriterExtBinary(OS));
91867c32a98SDimitry Andric else if (Format == SPF_Text)
919dd58ef01SDimitry Andric Writer.reset(new SampleProfileWriterText(OS));
920dd58ef01SDimitry Andric else if (Format == SPF_GCC)
921dd58ef01SDimitry Andric EC = sampleprof_error::unsupported_writing_format;
92267c32a98SDimitry Andric else
92367c32a98SDimitry Andric EC = sampleprof_error::unrecognized_format;
92467c32a98SDimitry Andric
92567c32a98SDimitry Andric if (EC)
92667c32a98SDimitry Andric return EC;
92767c32a98SDimitry Andric
9281d5ae102SDimitry Andric Writer->Format = Format;
92967c32a98SDimitry Andric return std::move(Writer);
93067c32a98SDimitry Andric }
93101095a5dSDimitry Andric
computeSummary(const SampleProfileMap & ProfileMap)932c0981da4SDimitry Andric void SampleProfileWriter::computeSummary(const SampleProfileMap &ProfileMap) {
93301095a5dSDimitry Andric SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
934344a3780SDimitry Andric Summary = Builder.computeSummaryForProfiles(ProfileMap);
93501095a5dSDimitry Andric }
936