Home | History | Annotate | Line # | Download | only in ProfileData
      1 //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // This file implements the class that writes LLVM sample profiles. It
     10 // supports two file formats: text and binary. The textual representation
     11 // is useful for debugging and testing purposes. The binary representation
     12 // is more compact, resulting in smaller file sizes. However, they can
     13 // both be used interchangeably.
     14 //
     15 // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the
     16 // supported formats.
     17 //
     18 //===----------------------------------------------------------------------===//
     19 
     20 #include "llvm/ProfileData/SampleProfWriter.h"
     21 #include "llvm/ADT/StringRef.h"
     22 #include "llvm/ADT/StringSet.h"
     23 #include "llvm/ProfileData/ProfileCommon.h"
     24 #include "llvm/ProfileData/SampleProf.h"
     25 #include "llvm/Support/Compression.h"
     26 #include "llvm/Support/Endian.h"
     27 #include "llvm/Support/EndianStream.h"
     28 #include "llvm/Support/ErrorOr.h"
     29 #include "llvm/Support/FileSystem.h"
     30 #include "llvm/Support/LEB128.h"
     31 #include "llvm/Support/MD5.h"
     32 #include "llvm/Support/raw_ostream.h"
     33 #include <algorithm>
     34 #include <cstdint>
     35 #include <memory>
     36 #include <set>
     37 #include <system_error>
     38 #include <utility>
     39 #include <vector>
     40 
     41 using namespace llvm;
     42 using namespace sampleprof;
     43 
     44 std::error_code SampleProfileWriter::writeFuncProfiles(
     45     const StringMap<FunctionSamples> &ProfileMap) {
     46   // Sort the ProfileMap by total samples.
     47   typedef std::pair<StringRef, const FunctionSamples *> NameFunctionSamples;
     48   std::vector<NameFunctionSamples> V;
     49   for (const auto &I : ProfileMap) {
     50     assert(I.getKey() == I.second.getNameWithContext() &&
     51            "Inconsistent profile map");
     52     V.push_back(std::make_pair(I.second.getNameWithContext(), &I.second));
     53   }
     54   llvm::stable_sort(
     55       V, [](const NameFunctionSamples &A, const NameFunctionSamples &B) {
     56         if (A.second->getTotalSamples() == B.second->getTotalSamples())
     57           return A.first > B.first;
     58         return A.second->getTotalSamples() > B.second->getTotalSamples();
     59       });
     60 
     61   for (const auto &I : V) {
     62     if (std::error_code EC = writeSample(*I.second))
     63       return EC;
     64   }
     65   return sampleprof_error::success;
     66 }
     67 
     68 std::error_code
     69 SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) {
     70   if (std::error_code EC = writeHeader(ProfileMap))
     71     return EC;
     72 
     73   if (std::error_code EC = writeFuncProfiles(ProfileMap))
     74     return EC;
     75 
     76   return sampleprof_error::success;
     77 }
     78 
     79 /// Return the current position and prepare to use it as the start
     80 /// position of a section given the section type \p Type and its position
     81 /// \p LayoutIdx in SectionHdrLayout.
     82 uint64_t
     83 SampleProfileWriterExtBinaryBase::markSectionStart(SecType Type,
     84                                                    uint32_t LayoutIdx) {
     85   uint64_t SectionStart = OutputStream->tell();
     86   assert(LayoutIdx < SectionHdrLayout.size() && "LayoutIdx out of range");
     87   const auto &Entry = SectionHdrLayout[LayoutIdx];
     88   assert(Entry.Type == Type && "Unexpected section type");
     89   // Use LocalBuf as a temporary output for writting data.
     90   if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress))
     91     LocalBufStream.swap(OutputStream);
     92   return SectionStart;
     93 }
     94 
     95 std::error_code SampleProfileWriterExtBinaryBase::compressAndOutput() {
     96   if (!llvm::zlib::isAvailable())
     97     return sampleprof_error::zlib_unavailable;
     98   std::string &UncompressedStrings =
     99       static_cast<raw_string_ostream *>(LocalBufStream.get())->str();
    100   if (UncompressedStrings.size() == 0)
    101     return sampleprof_error::success;
    102   auto &OS = *OutputStream;
    103   SmallString<128> CompressedStrings;
    104   llvm::Error E = zlib::compress(UncompressedStrings, CompressedStrings,
    105                                  zlib::BestSizeCompression);
    106   if (E)
    107     return sampleprof_error::compress_failed;
    108   encodeULEB128(UncompressedStrings.size(), OS);
    109   encodeULEB128(CompressedStrings.size(), OS);
    110   OS << CompressedStrings.str();
    111   UncompressedStrings.clear();
    112   return sampleprof_error::success;
    113 }
    114 
    115 /// Add a new section into section header table given the section type
    116 /// \p Type, its position \p LayoutIdx in SectionHdrLayout and the
    117 /// location \p SectionStart where the section should be written to.
    118 std::error_code SampleProfileWriterExtBinaryBase::addNewSection(
    119     SecType Type, uint32_t LayoutIdx, uint64_t SectionStart) {
    120   assert(LayoutIdx < SectionHdrLayout.size() && "LayoutIdx out of range");
    121   const auto &Entry = SectionHdrLayout[LayoutIdx];
    122   assert(Entry.Type == Type && "Unexpected section type");
    123   if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress)) {
    124     LocalBufStream.swap(OutputStream);
    125     if (std::error_code EC = compressAndOutput())
    126       return EC;
    127   }
    128   SecHdrTable.push_back({Type, Entry.Flags, SectionStart - FileStart,
    129                          OutputStream->tell() - SectionStart, LayoutIdx});
    130   return sampleprof_error::success;
    131 }
    132 
    133 std::error_code SampleProfileWriterExtBinaryBase::write(
    134     const StringMap<FunctionSamples> &ProfileMap) {
    135   if (std::error_code EC = writeHeader(ProfileMap))
    136     return EC;
    137 
    138   std::string LocalBuf;
    139   LocalBufStream = std::make_unique<raw_string_ostream>(LocalBuf);
    140   if (std::error_code EC = writeSections(ProfileMap))
    141     return EC;
    142 
    143   if (std::error_code EC = writeSecHdrTable())
    144     return EC;
    145 
    146   return sampleprof_error::success;
    147 }
    148 
    149 std::error_code
    150 SampleProfileWriterExtBinaryBase::writeSample(const FunctionSamples &S) {
    151   uint64_t Offset = OutputStream->tell();
    152   StringRef Name = S.getNameWithContext();
    153   FuncOffsetTable[Name] = Offset - SecLBRProfileStart;
    154   encodeULEB128(S.getHeadSamples(), *OutputStream);
    155   return writeBody(S);
    156 }
    157 
    158 std::error_code SampleProfileWriterExtBinaryBase::writeFuncOffsetTable() {
    159   auto &OS = *OutputStream;
    160 
    161   // Write out the table size.
    162   encodeULEB128(FuncOffsetTable.size(), OS);
    163 
    164   // Write out FuncOffsetTable.
    165   for (auto Entry : FuncOffsetTable) {
    166     if (std::error_code EC =
    167             writeNameIdx(Entry.first, FunctionSamples::ProfileIsCS))
    168       return EC;
    169     encodeULEB128(Entry.second, OS);
    170   }
    171   FuncOffsetTable.clear();
    172   return sampleprof_error::success;
    173 }
    174 
    175 std::error_code SampleProfileWriterExtBinaryBase::writeFuncMetadata(
    176     const StringMap<FunctionSamples> &Profiles) {
    177   if (!FunctionSamples::ProfileIsProbeBased && !FunctionSamples::ProfileIsCS)
    178     return sampleprof_error::success;
    179   auto &OS = *OutputStream;
    180   for (const auto &Entry : Profiles) {
    181     if (std::error_code EC = writeNameIdx(Entry.second.getNameWithContext(),
    182                                           FunctionSamples::ProfileIsCS))
    183       return EC;
    184     if (FunctionSamples::ProfileIsProbeBased)
    185       encodeULEB128(Entry.second.getFunctionHash(), OS);
    186     if (FunctionSamples::ProfileIsCS)
    187       encodeULEB128(Entry.second.getContext().getAllAttributes(), OS);
    188   }
    189   return sampleprof_error::success;
    190 }
    191 
    192 std::error_code SampleProfileWriterExtBinaryBase::writeNameTable() {
    193   if (!UseMD5)
    194     return SampleProfileWriterBinary::writeNameTable();
    195 
    196   auto &OS = *OutputStream;
    197   std::set<StringRef> V;
    198   stablizeNameTable(V);
    199 
    200   // Write out the MD5 name table. We wrote unencoded MD5 so reader can
    201   // retrieve the name using the name index without having to read the
    202   // whole name table.
    203   encodeULEB128(NameTable.size(), OS);
    204   support::endian::Writer Writer(OS, support::little);
    205   for (auto N : V)
    206     Writer.write(MD5Hash(N));
    207   return sampleprof_error::success;
    208 }
    209 
    210 std::error_code SampleProfileWriterExtBinaryBase::writeNameTableSection(
    211     const StringMap<FunctionSamples> &ProfileMap) {
    212   for (const auto &I : ProfileMap) {
    213     assert(I.first() == I.second.getNameWithContext() &&
    214            "Inconsistent profile map");
    215     addName(I.second.getNameWithContext(), FunctionSamples::ProfileIsCS);
    216     addNames(I.second);
    217   }
    218 
    219   // If NameTable contains ".__uniq." suffix, set SecFlagUniqSuffix flag
    220   // so compiler won't strip the suffix during profile matching after
    221   // seeing the flag in the profile.
    222   for (const auto &I : NameTable) {
    223     if (I.first.find(FunctionSamples::UniqSuffix) != StringRef::npos) {
    224       addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagUniqSuffix);
    225       break;
    226     }
    227   }
    228 
    229   if (auto EC = writeNameTable())
    230     return EC;
    231   return sampleprof_error::success;
    232 }
    233 
    234 std::error_code
    235 SampleProfileWriterExtBinaryBase::writeProfileSymbolListSection() {
    236   if (ProfSymList && ProfSymList->size() > 0)
    237     if (std::error_code EC = ProfSymList->write(*OutputStream))
    238       return EC;
    239 
    240   return sampleprof_error::success;
    241 }
    242 
    243 std::error_code SampleProfileWriterExtBinaryBase::writeOneSection(
    244     SecType Type, uint32_t LayoutIdx,
    245     const StringMap<FunctionSamples> &ProfileMap) {
    246   // The setting of SecFlagCompress should happen before markSectionStart.
    247   if (Type == SecProfileSymbolList && ProfSymList && ProfSymList->toCompress())
    248     setToCompressSection(SecProfileSymbolList);
    249   if (Type == SecFuncMetadata && FunctionSamples::ProfileIsProbeBased)
    250     addSectionFlag(SecFuncMetadata, SecFuncMetadataFlags::SecFlagIsProbeBased);
    251   if (Type == SecProfSummary && FunctionSamples::ProfileIsCS)
    252     addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagFullContext);
    253   if (Type == SecFuncMetadata && FunctionSamples::ProfileIsCS)
    254     addSectionFlag(SecFuncMetadata, SecFuncMetadataFlags::SecFlagHasAttribute);
    255 
    256   uint64_t SectionStart = markSectionStart(Type, LayoutIdx);
    257   switch (Type) {
    258   case SecProfSummary:
    259     computeSummary(ProfileMap);
    260     if (auto EC = writeSummary())
    261       return EC;
    262     break;
    263   case SecNameTable:
    264     if (auto EC = writeNameTableSection(ProfileMap))
    265       return EC;
    266     break;
    267   case SecLBRProfile:
    268     SecLBRProfileStart = OutputStream->tell();
    269     if (std::error_code EC = writeFuncProfiles(ProfileMap))
    270       return EC;
    271     break;
    272   case SecFuncOffsetTable:
    273     if (auto EC = writeFuncOffsetTable())
    274       return EC;
    275     break;
    276   case SecFuncMetadata:
    277     if (std::error_code EC = writeFuncMetadata(ProfileMap))
    278       return EC;
    279     break;
    280   case SecProfileSymbolList:
    281     if (auto EC = writeProfileSymbolListSection())
    282       return EC;
    283     break;
    284   default:
    285     if (auto EC = writeCustomSection(Type))
    286       return EC;
    287     break;
    288   }
    289   if (std::error_code EC = addNewSection(Type, LayoutIdx, SectionStart))
    290     return EC;
    291   return sampleprof_error::success;
    292 }
    293 
    294 std::error_code SampleProfileWriterExtBinary::writeDefaultLayout(
    295     const StringMap<FunctionSamples> &ProfileMap) {
    296   // The const indices passed to writeOneSection below are specifying the
    297   // positions of the sections in SectionHdrLayout. Look at
    298   // initSectionHdrLayout to find out where each section is located in
    299   // SectionHdrLayout.
    300   if (auto EC = writeOneSection(SecProfSummary, 0, ProfileMap))
    301     return EC;
    302   if (auto EC = writeOneSection(SecNameTable, 1, ProfileMap))
    303     return EC;
    304   if (auto EC = writeOneSection(SecLBRProfile, 3, ProfileMap))
    305     return EC;
    306   if (auto EC = writeOneSection(SecProfileSymbolList, 4, ProfileMap))
    307     return EC;
    308   if (auto EC = writeOneSection(SecFuncOffsetTable, 2, ProfileMap))
    309     return EC;
    310   if (auto EC = writeOneSection(SecFuncMetadata, 5, ProfileMap))
    311     return EC;
    312   return sampleprof_error::success;
    313 }
    314 
    315 static void
    316 splitProfileMapToTwo(const StringMap<FunctionSamples> &ProfileMap,
    317                      StringMap<FunctionSamples> &ContextProfileMap,
    318                      StringMap<FunctionSamples> &NoContextProfileMap) {
    319   for (const auto &I : ProfileMap) {
    320     if (I.second.getCallsiteSamples().size())
    321       ContextProfileMap.insert({I.first(), I.second});
    322     else
    323       NoContextProfileMap.insert({I.first(), I.second});
    324   }
    325 }
    326 
    327 std::error_code SampleProfileWriterExtBinary::writeCtxSplitLayout(
    328     const StringMap<FunctionSamples> &ProfileMap) {
    329   StringMap<FunctionSamples> ContextProfileMap, NoContextProfileMap;
    330   splitProfileMapToTwo(ProfileMap, ContextProfileMap, NoContextProfileMap);
    331 
    332   if (auto EC = writeOneSection(SecProfSummary, 0, ProfileMap))
    333     return EC;
    334   if (auto EC = writeOneSection(SecNameTable, 1, ProfileMap))
    335     return EC;
    336   if (auto EC = writeOneSection(SecLBRProfile, 3, ContextProfileMap))
    337     return EC;
    338   if (auto EC = writeOneSection(SecFuncOffsetTable, 2, ContextProfileMap))
    339     return EC;
    340   // Mark the section to have no context. Note section flag needs to be set
    341   // before writing the section.
    342   addSectionFlag(5, SecCommonFlags::SecFlagFlat);
    343   if (auto EC = writeOneSection(SecLBRProfile, 5, NoContextProfileMap))
    344     return EC;
    345   // Mark the section to have no context. Note section flag needs to be set
    346   // before writing the section.
    347   addSectionFlag(4, SecCommonFlags::SecFlagFlat);
    348   if (auto EC = writeOneSection(SecFuncOffsetTable, 4, NoContextProfileMap))
    349     return EC;
    350   if (auto EC = writeOneSection(SecProfileSymbolList, 6, ProfileMap))
    351     return EC;
    352   if (auto EC = writeOneSection(SecFuncMetadata, 7, ProfileMap))
    353     return EC;
    354 
    355   return sampleprof_error::success;
    356 }
    357 
    358 std::error_code SampleProfileWriterExtBinary::writeSections(
    359     const StringMap<FunctionSamples> &ProfileMap) {
    360   std::error_code EC;
    361   if (SecLayout == DefaultLayout)
    362     EC = writeDefaultLayout(ProfileMap);
    363   else if (SecLayout == CtxSplitLayout)
    364     EC = writeCtxSplitLayout(ProfileMap);
    365   else
    366     llvm_unreachable("Unsupported layout");
    367   return EC;
    368 }
    369 
    370 std::error_code SampleProfileWriterCompactBinary::write(
    371     const StringMap<FunctionSamples> &ProfileMap) {
    372   if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
    373     return EC;
    374   if (std::error_code EC = writeFuncOffsetTable())
    375     return EC;
    376   return sampleprof_error::success;
    377 }
    378 
    379 /// Write samples to a text file.
    380 ///
    381 /// Note: it may be tempting to implement this in terms of
    382 /// FunctionSamples::print().  Please don't.  The dump functionality is intended
    383 /// for debugging and has no specified form.
    384 ///
    385 /// The format used here is more structured and deliberate because
    386 /// it needs to be parsed by the SampleProfileReaderText class.
    387 std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {
    388   auto &OS = *OutputStream;
    389   if (FunctionSamples::ProfileIsCS)
    390     OS << "[" << S.getNameWithContext() << "]:" << S.getTotalSamples();
    391   else
    392     OS << S.getName() << ":" << S.getTotalSamples();
    393 
    394   if (Indent == 0)
    395     OS << ":" << S.getHeadSamples();
    396   OS << "\n";
    397 
    398   SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
    399   for (const auto &I : SortedSamples.get()) {
    400     LineLocation Loc = I->first;
    401     const SampleRecord &Sample = I->second;
    402     OS.indent(Indent + 1);
    403     if (Loc.Discriminator == 0)
    404       OS << Loc.LineOffset << ": ";
    405     else
    406       OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
    407 
    408     OS << Sample.getSamples();
    409 
    410     for (const auto &J : Sample.getSortedCallTargets())
    411       OS << " " << J.first << ":" << J.second;
    412     OS << "\n";
    413   }
    414 
    415   SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
    416       S.getCallsiteSamples());
    417   Indent += 1;
    418   for (const auto &I : SortedCallsiteSamples.get())
    419     for (const auto &FS : I->second) {
    420       LineLocation Loc = I->first;
    421       const FunctionSamples &CalleeSamples = FS.second;
    422       OS.indent(Indent);
    423       if (Loc.Discriminator == 0)
    424         OS << Loc.LineOffset << ": ";
    425       else
    426         OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
    427       if (std::error_code EC = writeSample(CalleeSamples))
    428         return EC;
    429     }
    430   Indent -= 1;
    431 
    432   if (Indent == 0) {
    433     if (FunctionSamples::ProfileIsProbeBased) {
    434       OS.indent(Indent + 1);
    435       OS << "!CFGChecksum: " << S.getFunctionHash() << "\n";
    436     }
    437     if (FunctionSamples::ProfileIsCS) {
    438       OS.indent(Indent + 1);
    439       OS << "!Attributes: " << S.getContext().getAllAttributes() << "\n";
    440     }
    441   }
    442 
    443   return sampleprof_error::success;
    444 }
    445 
    446 std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName,
    447                                                         bool IsContextName) {
    448   std::string BracketedName;
    449   if (IsContextName) {
    450     BracketedName = "[" + FName.str() + "]";
    451     FName = StringRef(BracketedName);
    452   }
    453 
    454   const auto &Ret = NameTable.find(FName);
    455   if (Ret == NameTable.end())
    456     return sampleprof_error::truncated_name_table;
    457   encodeULEB128(Ret->second, *OutputStream);
    458   return sampleprof_error::success;
    459 }
    460 
    461 void SampleProfileWriterBinary::addName(StringRef FName, bool IsContextName) {
    462   if (IsContextName) {
    463     auto It = BracketedContextStr.insert("[" + FName.str() + "]");
    464     FName = StringRef(*It.first);
    465   }
    466   NameTable.insert(std::make_pair(FName, 0));
    467 }
    468 
    469 void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
    470   // Add all the names in indirect call targets.
    471   for (const auto &I : S.getBodySamples()) {
    472     const SampleRecord &Sample = I.second;
    473     for (const auto &J : Sample.getCallTargets())
    474       addName(J.first());
    475   }
    476 
    477   // Recursively add all the names for inlined callsites.
    478   for (const auto &J : S.getCallsiteSamples())
    479     for (const auto &FS : J.second) {
    480       const FunctionSamples &CalleeSamples = FS.second;
    481       addName(CalleeSamples.getName());
    482       addNames(CalleeSamples);
    483     }
    484 }
    485 
    486 void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) {
    487   // Sort the names to make NameTable deterministic.
    488   for (const auto &I : NameTable)
    489     V.insert(I.first);
    490   int i = 0;
    491   for (const StringRef &N : V)
    492     NameTable[N] = i++;
    493 }
    494 
    495 std::error_code SampleProfileWriterBinary::writeNameTable() {
    496   auto &OS = *OutputStream;
    497   std::set<StringRef> V;
    498   stablizeNameTable(V);
    499 
    500   // Write out the name table.
    501   encodeULEB128(NameTable.size(), OS);
    502   for (auto N : V) {
    503     OS << N;
    504     encodeULEB128(0, OS);
    505   }
    506   return sampleprof_error::success;
    507 }
    508 
    509 std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
    510   auto &OS = *OutputStream;
    511 
    512   // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
    513   auto &OFS = static_cast<raw_fd_ostream &>(OS);
    514   uint64_t FuncOffsetTableStart = OS.tell();
    515   if (OFS.seek(TableOffset) == (uint64_t)-1)
    516     return sampleprof_error::ostream_seek_unsupported;
    517   support::endian::Writer Writer(*OutputStream, support::little);
    518   Writer.write(FuncOffsetTableStart);
    519   if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1)
    520     return sampleprof_error::ostream_seek_unsupported;
    521 
    522   // Write out the table size.
    523   encodeULEB128(FuncOffsetTable.size(), OS);
    524 
    525   // Write out FuncOffsetTable.
    526   for (auto Entry : FuncOffsetTable) {
    527     if (std::error_code EC =
    528             writeNameIdx(Entry.first, FunctionSamples::ProfileIsCS))
    529       return EC;
    530     encodeULEB128(Entry.second, OS);
    531   }
    532   return sampleprof_error::success;
    533 }
    534 
    535 std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
    536   auto &OS = *OutputStream;
    537   std::set<StringRef> V;
    538   stablizeNameTable(V);
    539 
    540   // Write out the name table.
    541   encodeULEB128(NameTable.size(), OS);
    542   for (auto N : V) {
    543     encodeULEB128(MD5Hash(N), OS);
    544   }
    545   return sampleprof_error::success;
    546 }
    547 
    548 std::error_code
    549 SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) {
    550   auto &OS = *OutputStream;
    551   // Write file magic identifier.
    552   encodeULEB128(SPMagic(Format), OS);
    553   encodeULEB128(SPVersion(), OS);
    554   return sampleprof_error::success;
    555 }
    556 
    557 std::error_code SampleProfileWriterBinary::writeHeader(
    558     const StringMap<FunctionSamples> &ProfileMap) {
    559   writeMagicIdent(Format);
    560 
    561   computeSummary(ProfileMap);
    562   if (auto EC = writeSummary())
    563     return EC;
    564 
    565   // Generate the name table for all the functions referenced in the profile.
    566   for (const auto &I : ProfileMap) {
    567     assert(I.first() == I.second.getNameWithContext() &&
    568            "Inconsistent profile map");
    569     addName(I.first(), FunctionSamples::ProfileIsCS);
    570     addNames(I.second);
    571   }
    572 
    573   writeNameTable();
    574   return sampleprof_error::success;
    575 }
    576 
    577 void SampleProfileWriterExtBinaryBase::setToCompressAllSections() {
    578   for (auto &Entry : SectionHdrLayout)
    579     addSecFlag(Entry, SecCommonFlags::SecFlagCompress);
    580 }
    581 
    582 void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) {
    583   addSectionFlag(Type, SecCommonFlags::SecFlagCompress);
    584 }
    585 
    586 void SampleProfileWriterExtBinaryBase::allocSecHdrTable() {
    587   support::endian::Writer Writer(*OutputStream, support::little);
    588 
    589   Writer.write(static_cast<uint64_t>(SectionHdrLayout.size()));
    590   SecHdrTableOffset = OutputStream->tell();
    591   for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
    592     Writer.write(static_cast<uint64_t>(-1));
    593     Writer.write(static_cast<uint64_t>(-1));
    594     Writer.write(static_cast<uint64_t>(-1));
    595     Writer.write(static_cast<uint64_t>(-1));
    596   }
    597 }
    598 
    599 std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() {
    600   auto &OFS = static_cast<raw_fd_ostream &>(*OutputStream);
    601   uint64_t Saved = OutputStream->tell();
    602 
    603   // Set OutputStream to the location saved in SecHdrTableOffset.
    604   if (OFS.seek(SecHdrTableOffset) == (uint64_t)-1)
    605     return sampleprof_error::ostream_seek_unsupported;
    606   support::endian::Writer Writer(*OutputStream, support::little);
    607 
    608   assert(SecHdrTable.size() == SectionHdrLayout.size() &&
    609          "SecHdrTable entries doesn't match SectionHdrLayout");
    610   SmallVector<uint32_t, 16> IndexMap(SecHdrTable.size(), -1);
    611   for (uint32_t TableIdx = 0; TableIdx < SecHdrTable.size(); TableIdx++) {
    612     IndexMap[SecHdrTable[TableIdx].LayoutIndex] = TableIdx;
    613   }
    614 
    615   // Write the section header table in the order specified in
    616   // SectionHdrLayout. SectionHdrLayout specifies the sections
    617   // order in which profile reader expect to read, so the section
    618   // header table should be written in the order in SectionHdrLayout.
    619   // Note that the section order in SecHdrTable may be different
    620   // from the order in SectionHdrLayout, for example, SecFuncOffsetTable
    621   // needs to be computed after SecLBRProfile (the order in SecHdrTable),
    622   // but it needs to be read before SecLBRProfile (the order in
    623   // SectionHdrLayout). So we use IndexMap above to switch the order.
    624   for (uint32_t LayoutIdx = 0; LayoutIdx < SectionHdrLayout.size();
    625        LayoutIdx++) {
    626     assert(IndexMap[LayoutIdx] < SecHdrTable.size() &&
    627            "Incorrect LayoutIdx in SecHdrTable");
    628     auto Entry = SecHdrTable[IndexMap[LayoutIdx]];
    629     Writer.write(static_cast<uint64_t>(Entry.Type));
    630     Writer.write(static_cast<uint64_t>(Entry.Flags));
    631     Writer.write(static_cast<uint64_t>(Entry.Offset));
    632     Writer.write(static_cast<uint64_t>(Entry.Size));
    633   }
    634 
    635   // Reset OutputStream.
    636   if (OFS.seek(Saved) == (uint64_t)-1)
    637     return sampleprof_error::ostream_seek_unsupported;
    638 
    639   return sampleprof_error::success;
    640 }
    641 
    642 std::error_code SampleProfileWriterExtBinaryBase::writeHeader(
    643     const StringMap<FunctionSamples> &ProfileMap) {
    644   auto &OS = *OutputStream;
    645   FileStart = OS.tell();
    646   writeMagicIdent(Format);
    647 
    648   allocSecHdrTable();
    649   return sampleprof_error::success;
    650 }
    651 
    652 std::error_code SampleProfileWriterCompactBinary::writeHeader(
    653     const StringMap<FunctionSamples> &ProfileMap) {
    654   support::endian::Writer Writer(*OutputStream, support::little);
    655   if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap))
    656     return EC;
    657 
    658   // Reserve a slot for the offset of function offset table. The slot will
    659   // be populated with the offset of FuncOffsetTable later.
    660   TableOffset = OutputStream->tell();
    661   Writer.write(static_cast<uint64_t>(-2));
    662   return sampleprof_error::success;
    663 }
    664 
    665 std::error_code SampleProfileWriterBinary::writeSummary() {
    666   auto &OS = *OutputStream;
    667   encodeULEB128(Summary->getTotalCount(), OS);
    668   encodeULEB128(Summary->getMaxCount(), OS);
    669   encodeULEB128(Summary->getMaxFunctionCount(), OS);
    670   encodeULEB128(Summary->getNumCounts(), OS);
    671   encodeULEB128(Summary->getNumFunctions(), OS);
    672   std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
    673   encodeULEB128(Entries.size(), OS);
    674   for (auto Entry : Entries) {
    675     encodeULEB128(Entry.Cutoff, OS);
    676     encodeULEB128(Entry.MinCount, OS);
    677     encodeULEB128(Entry.NumCounts, OS);
    678   }
    679   return sampleprof_error::success;
    680 }
    681 std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
    682   auto &OS = *OutputStream;
    683 
    684   if (std::error_code EC =
    685           writeNameIdx(S.getNameWithContext(), FunctionSamples::ProfileIsCS))
    686     return EC;
    687 
    688   encodeULEB128(S.getTotalSamples(), OS);
    689 
    690   // Emit all the body samples.
    691   encodeULEB128(S.getBodySamples().size(), OS);
    692   for (const auto &I : S.getBodySamples()) {
    693     LineLocation Loc = I.first;
    694     const SampleRecord &Sample = I.second;
    695     encodeULEB128(Loc.LineOffset, OS);
    696     encodeULEB128(Loc.Discriminator, OS);
    697     encodeULEB128(Sample.getSamples(), OS);
    698     encodeULEB128(Sample.getCallTargets().size(), OS);
    699     for (const auto &J : Sample.getSortedCallTargets()) {
    700       StringRef Callee = J.first;
    701       uint64_t CalleeSamples = J.second;
    702       if (std::error_code EC = writeNameIdx(Callee))
    703         return EC;
    704       encodeULEB128(CalleeSamples, OS);
    705     }
    706   }
    707 
    708   // Recursively emit all the callsite samples.
    709   uint64_t NumCallsites = 0;
    710   for (const auto &J : S.getCallsiteSamples())
    711     NumCallsites += J.second.size();
    712   encodeULEB128(NumCallsites, OS);
    713   for (const auto &J : S.getCallsiteSamples())
    714     for (const auto &FS : J.second) {
    715       LineLocation Loc = J.first;
    716       const FunctionSamples &CalleeSamples = FS.second;
    717       encodeULEB128(Loc.LineOffset, OS);
    718       encodeULEB128(Loc.Discriminator, OS);
    719       if (std::error_code EC = writeBody(CalleeSamples))
    720         return EC;
    721     }
    722 
    723   return sampleprof_error::success;
    724 }
    725 
    726 /// Write samples of a top-level function to a binary file.
    727 ///
    728 /// \returns true if the samples were written successfully, false otherwise.
    729 std::error_code
    730 SampleProfileWriterBinary::writeSample(const FunctionSamples &S) {
    731   encodeULEB128(S.getHeadSamples(), *OutputStream);
    732   return writeBody(S);
    733 }
    734 
    735 std::error_code
    736 SampleProfileWriterCompactBinary::writeSample(const FunctionSamples &S) {
    737   uint64_t Offset = OutputStream->tell();
    738   StringRef Name = S.getName();
    739   FuncOffsetTable[Name] = Offset;
    740   encodeULEB128(S.getHeadSamples(), *OutputStream);
    741   return writeBody(S);
    742 }
    743 
    744 /// Create a sample profile file writer based on the specified format.
    745 ///
    746 /// \param Filename The file to create.
    747 ///
    748 /// \param Format Encoding format for the profile file.
    749 ///
    750 /// \returns an error code indicating the status of the created writer.
    751 ErrorOr<std::unique_ptr<SampleProfileWriter>>
    752 SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
    753   std::error_code EC;
    754   std::unique_ptr<raw_ostream> OS;
    755   if (Format == SPF_Binary || Format == SPF_Ext_Binary ||
    756       Format == SPF_Compact_Binary)
    757     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None));
    758   else
    759     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_TextWithCRLF));
    760   if (EC)
    761     return EC;
    762 
    763   return create(OS, Format);
    764 }
    765 
    766 /// Create a sample profile stream writer based on the specified format.
    767 ///
    768 /// \param OS The output stream to store the profile data to.
    769 ///
    770 /// \param Format Encoding format for the profile file.
    771 ///
    772 /// \returns an error code indicating the status of the created writer.
    773 ErrorOr<std::unique_ptr<SampleProfileWriter>>
    774 SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
    775                             SampleProfileFormat Format) {
    776   std::error_code EC;
    777   std::unique_ptr<SampleProfileWriter> Writer;
    778 
    779   // Currently only Text and Extended Binary format are supported for CSSPGO.
    780   if ((FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsProbeBased) &&
    781       (Format == SPF_Binary || Format == SPF_Compact_Binary))
    782     return sampleprof_error::unsupported_writing_format;
    783 
    784   if (Format == SPF_Binary)
    785     Writer.reset(new SampleProfileWriterRawBinary(OS));
    786   else if (Format == SPF_Ext_Binary)
    787     Writer.reset(new SampleProfileWriterExtBinary(OS));
    788   else if (Format == SPF_Compact_Binary)
    789     Writer.reset(new SampleProfileWriterCompactBinary(OS));
    790   else if (Format == SPF_Text)
    791     Writer.reset(new SampleProfileWriterText(OS));
    792   else if (Format == SPF_GCC)
    793     EC = sampleprof_error::unsupported_writing_format;
    794   else
    795     EC = sampleprof_error::unrecognized_format;
    796 
    797   if (EC)
    798     return EC;
    799 
    800   Writer->Format = Format;
    801   return std::move(Writer);
    802 }
    803 
    804 void SampleProfileWriter::computeSummary(
    805     const StringMap<FunctionSamples> &ProfileMap) {
    806   SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
    807   Summary = Builder.computeSummaryForProfiles(ProfileMap);
    808 }
    809