1c572e92cSDiego Novillo //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===//
2c572e92cSDiego Novillo //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c572e92cSDiego Novillo //
7c572e92cSDiego Novillo //===----------------------------------------------------------------------===//
8c572e92cSDiego Novillo //
9c572e92cSDiego Novillo // This file implements the class that writes LLVM sample profiles. It
10c572e92cSDiego Novillo // supports two file formats: text and binary. The textual representation
11c572e92cSDiego Novillo // is useful for debugging and testing purposes. The binary representation
12c572e92cSDiego Novillo // is more compact, resulting in smaller file sizes. However, they can
13c572e92cSDiego Novillo // both be used interchangeably.
14c572e92cSDiego Novillo //
15c572e92cSDiego Novillo // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the
16c572e92cSDiego Novillo // supported formats.
17c572e92cSDiego Novillo //
18c572e92cSDiego Novillo //===----------------------------------------------------------------------===//
19c572e92cSDiego Novillo 
206bda14b3SChandler Carruth #include "llvm/ProfileData/SampleProfWriter.h"
21e78d131aSEugene Zelenko #include "llvm/ADT/StringRef.h"
22e78d131aSEugene Zelenko #include "llvm/ProfileData/ProfileCommon.h"
23e78d131aSEugene Zelenko #include "llvm/ProfileData/SampleProf.h"
24b523790aSWei Mi #include "llvm/Support/Compression.h"
256a14325dSWei Mi #include "llvm/Support/Endian.h"
266a14325dSWei Mi #include "llvm/Support/EndianStream.h"
27c572e92cSDiego Novillo #include "llvm/Support/ErrorOr.h"
28e78d131aSEugene Zelenko #include "llvm/Support/FileSystem.h"
29c572e92cSDiego Novillo #include "llvm/Support/LEB128.h"
30a0c0857eSWei Mi #include "llvm/Support/MD5.h"
31e78d131aSEugene Zelenko #include "llvm/Support/raw_ostream.h"
32e78d131aSEugene Zelenko #include <algorithm>
33e78d131aSEugene Zelenko #include <cstdint>
34e78d131aSEugene Zelenko #include <memory>
358d1c983fSDehao Chen #include <set>
36e78d131aSEugene Zelenko #include <system_error>
37e78d131aSEugene Zelenko #include <utility>
38e78d131aSEugene Zelenko #include <vector>
39c572e92cSDiego Novillo 
40c572e92cSDiego Novillo using namespace llvm;
41e78d131aSEugene Zelenko using namespace sampleprof;
42c572e92cSDiego Novillo 
43be907324SWei Mi std::error_code SampleProfileWriter::writeFuncProfiles(
44be907324SWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
458d1c983fSDehao Chen   // Sort the ProfileMap by total samples.
468d1c983fSDehao Chen   typedef std::pair<StringRef, const FunctionSamples *> NameFunctionSamples;
478d1c983fSDehao Chen   std::vector<NameFunctionSamples> V;
488d1c983fSDehao Chen   for (const auto &I : ProfileMap)
498d1c983fSDehao Chen     V.push_back(std::make_pair(I.getKey(), &I.second));
508d1c983fSDehao Chen 
51efd94c56SFangrui Song   llvm::stable_sort(
52efd94c56SFangrui Song       V, [](const NameFunctionSamples &A, const NameFunctionSamples &B) {
538d1c983fSDehao Chen         if (A.second->getTotalSamples() == B.second->getTotalSamples())
548d1c983fSDehao Chen           return A.first > B.first;
558d1c983fSDehao Chen         return A.second->getTotalSamples() > B.second->getTotalSamples();
568d1c983fSDehao Chen       });
578d1c983fSDehao Chen 
588d1c983fSDehao Chen   for (const auto &I : V) {
59be907324SWei Mi     if (std::error_code EC = writeSample(*I.second))
608d1c983fSDehao Chen       return EC;
618d1c983fSDehao Chen   }
628d1c983fSDehao Chen   return sampleprof_error::success;
638d1c983fSDehao Chen }
648d1c983fSDehao Chen 
65be907324SWei Mi std::error_code
66be907324SWei Mi SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) {
67be907324SWei Mi   if (std::error_code EC = writeHeader(ProfileMap))
68be907324SWei Mi     return EC;
69be907324SWei Mi 
70be907324SWei Mi   if (std::error_code EC = writeFuncProfiles(ProfileMap))
71be907324SWei Mi     return EC;
72be907324SWei Mi 
73be907324SWei Mi   return sampleprof_error::success;
74be907324SWei Mi }
75be907324SWei Mi 
76b523790aSWei Mi SecHdrTableEntry &
77b523790aSWei Mi SampleProfileWriterExtBinaryBase::getEntryInLayout(SecType Type) {
78b523790aSWei Mi   auto SecIt = std::find_if(
7909dcfe68SWei Mi       SectionHdrLayout.begin(), SectionHdrLayout.end(),
80b523790aSWei Mi       [=](const auto &Entry) -> bool { return Entry.Type == Type; });
81b523790aSWei Mi   return *SecIt;
82be907324SWei Mi }
83be907324SWei Mi 
84b523790aSWei Mi /// Return the current position and prepare to use it as the start
85b523790aSWei Mi /// position of a section.
86b523790aSWei Mi uint64_t SampleProfileWriterExtBinaryBase::markSectionStart(SecType Type) {
87b523790aSWei Mi   uint64_t SectionStart = OutputStream->tell();
88b523790aSWei Mi   auto &Entry = getEntryInLayout(Type);
89b523790aSWei Mi   // Use LocalBuf as a temporary output for writting data.
90ebad6788SWei Mi   if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress))
91b523790aSWei Mi     LocalBufStream.swap(OutputStream);
92b523790aSWei Mi   return SectionStart;
93b523790aSWei Mi }
94b523790aSWei Mi 
95b523790aSWei Mi std::error_code SampleProfileWriterExtBinaryBase::compressAndOutput() {
96b523790aSWei Mi   if (!llvm::zlib::isAvailable())
97b523790aSWei Mi     return sampleprof_error::zlib_unavailable;
98b523790aSWei Mi   std::string &UncompressedStrings =
99b523790aSWei Mi       static_cast<raw_string_ostream *>(LocalBufStream.get())->str();
100b523790aSWei Mi   if (UncompressedStrings.size() == 0)
101b523790aSWei Mi     return sampleprof_error::success;
102b523790aSWei Mi   auto &OS = *OutputStream;
103b523790aSWei Mi   SmallString<128> CompressedStrings;
104b523790aSWei Mi   llvm::Error E = zlib::compress(UncompressedStrings, CompressedStrings,
105b523790aSWei Mi                                  zlib::BestSizeCompression);
106b523790aSWei Mi   if (E)
107b523790aSWei Mi     return sampleprof_error::compress_failed;
108b523790aSWei Mi   encodeULEB128(UncompressedStrings.size(), OS);
109b523790aSWei Mi   encodeULEB128(CompressedStrings.size(), OS);
110b523790aSWei Mi   OS << CompressedStrings.str();
111b523790aSWei Mi   UncompressedStrings.clear();
112b523790aSWei Mi   return sampleprof_error::success;
113b523790aSWei Mi }
114b523790aSWei Mi 
115b523790aSWei Mi /// Add a new section into section header table.
116b523790aSWei Mi std::error_code
117b523790aSWei Mi SampleProfileWriterExtBinaryBase::addNewSection(SecType Type,
118be907324SWei Mi                                                 uint64_t SectionStart) {
119b523790aSWei Mi   auto Entry = getEntryInLayout(Type);
120ebad6788SWei Mi   if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress)) {
121b523790aSWei Mi     LocalBufStream.swap(OutputStream);
122b523790aSWei Mi     if (std::error_code EC = compressAndOutput())
123b523790aSWei Mi       return EC;
124b523790aSWei Mi   }
125b523790aSWei Mi   SecHdrTable.push_back({Type, Entry.Flags, SectionStart - FileStart,
126b523790aSWei Mi                          OutputStream->tell() - SectionStart});
127b523790aSWei Mi   return sampleprof_error::success;
128be907324SWei Mi }
129be907324SWei Mi 
130be907324SWei Mi std::error_code SampleProfileWriterExtBinaryBase::write(
131be907324SWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
132be907324SWei Mi   if (std::error_code EC = writeHeader(ProfileMap))
133be907324SWei Mi     return EC;
134be907324SWei Mi 
135b523790aSWei Mi   std::string LocalBuf;
136b523790aSWei Mi   LocalBufStream = std::make_unique<raw_string_ostream>(LocalBuf);
137be907324SWei Mi   if (std::error_code EC = writeSections(ProfileMap))
138be907324SWei Mi     return EC;
139be907324SWei Mi 
140be907324SWei Mi   if (std::error_code EC = writeSecHdrTable())
141be907324SWei Mi     return EC;
142be907324SWei Mi 
143be907324SWei Mi   return sampleprof_error::success;
144be907324SWei Mi }
145be907324SWei Mi 
14609dcfe68SWei Mi std::error_code
14793953d41SWei Mi SampleProfileWriterExtBinaryBase::writeSample(const FunctionSamples &S) {
14809dcfe68SWei Mi   uint64_t Offset = OutputStream->tell();
14909dcfe68SWei Mi   StringRef Name = S.getName();
15009dcfe68SWei Mi   FuncOffsetTable[Name] = Offset - SecLBRProfileStart;
15109dcfe68SWei Mi   encodeULEB128(S.getHeadSamples(), *OutputStream);
15209dcfe68SWei Mi   return writeBody(S);
15309dcfe68SWei Mi }
15409dcfe68SWei Mi 
15593953d41SWei Mi std::error_code SampleProfileWriterExtBinaryBase::writeFuncOffsetTable() {
15609dcfe68SWei Mi   auto &OS = *OutputStream;
15709dcfe68SWei Mi 
15809dcfe68SWei Mi   // Write out the table size.
15909dcfe68SWei Mi   encodeULEB128(FuncOffsetTable.size(), OS);
16009dcfe68SWei Mi 
16109dcfe68SWei Mi   // Write out FuncOffsetTable.
16209dcfe68SWei Mi   for (auto entry : FuncOffsetTable) {
16309dcfe68SWei Mi     writeNameIdx(entry.first);
16409dcfe68SWei Mi     encodeULEB128(entry.second, OS);
16509dcfe68SWei Mi   }
16609dcfe68SWei Mi   return sampleprof_error::success;
16709dcfe68SWei Mi }
16809dcfe68SWei Mi 
16993953d41SWei Mi std::error_code SampleProfileWriterExtBinaryBase::writeNameTable() {
170ebad6788SWei Mi   if (!UseMD5)
171ebad6788SWei Mi     return SampleProfileWriterBinary::writeNameTable();
172ebad6788SWei Mi 
173ebad6788SWei Mi   auto &OS = *OutputStream;
174ebad6788SWei Mi   std::set<StringRef> V;
175ebad6788SWei Mi   stablizeNameTable(V);
176ebad6788SWei Mi 
177ebad6788SWei Mi   // Write out the name table.
178ebad6788SWei Mi   encodeULEB128(NameTable.size(), OS);
179ebad6788SWei Mi   for (auto N : V) {
180ebad6788SWei Mi     encodeULEB128(MD5Hash(N), OS);
181ebad6788SWei Mi   }
182ebad6788SWei Mi   return sampleprof_error::success;
183ebad6788SWei Mi }
184ebad6788SWei Mi 
18593953d41SWei Mi std::error_code SampleProfileWriterExtBinaryBase::writeNameTableSection(
186be907324SWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
187be907324SWei Mi   for (const auto &I : ProfileMap) {
188be907324SWei Mi     addName(I.first());
189be907324SWei Mi     addNames(I.second);
190be907324SWei Mi   }
19193953d41SWei Mi   if (auto EC = writeNameTable())
192b523790aSWei Mi     return EC;
19393953d41SWei Mi   return sampleprof_error::success;
19493953d41SWei Mi }
195be907324SWei Mi 
19693953d41SWei Mi std::error_code
19793953d41SWei Mi SampleProfileWriterExtBinaryBase::writeProfileSymbolListSection() {
198798e59b8SWei Mi   if (ProfSymList && ProfSymList->size() > 0)
199798e59b8SWei Mi     if (std::error_code EC = ProfSymList->write(*OutputStream))
200798e59b8SWei Mi       return EC;
201be907324SWei Mi 
20293953d41SWei Mi   return sampleprof_error::success;
20393953d41SWei Mi }
20409dcfe68SWei Mi 
20593953d41SWei Mi std::error_code SampleProfileWriterExtBinaryBase::writeOneSection(
20693953d41SWei Mi     SecType Type, const StringMap<FunctionSamples> &ProfileMap) {
20793953d41SWei Mi   // The setting of SecFlagCompress should happen before markSectionStart.
20893953d41SWei Mi   if (Type == SecProfileSymbolList && ProfSymList && ProfSymList->toCompress())
20993953d41SWei Mi     setToCompressSection(SecProfileSymbolList);
21093953d41SWei Mi 
21193953d41SWei Mi   uint64_t SectionStart = markSectionStart(Type);
21293953d41SWei Mi   switch (Type) {
21393953d41SWei Mi   case SecProfSummary:
21493953d41SWei Mi     computeSummary(ProfileMap);
21593953d41SWei Mi     if (auto EC = writeSummary())
21693953d41SWei Mi       return EC;
21793953d41SWei Mi     break;
21893953d41SWei Mi   case SecNameTable:
21993953d41SWei Mi     if (auto EC = writeNameTableSection(ProfileMap))
22093953d41SWei Mi       return EC;
22193953d41SWei Mi     break;
22293953d41SWei Mi   case SecLBRProfile:
22393953d41SWei Mi     SecLBRProfileStart = OutputStream->tell();
22493953d41SWei Mi     if (std::error_code EC = writeFuncProfiles(ProfileMap))
22593953d41SWei Mi       return EC;
22693953d41SWei Mi     break;
22793953d41SWei Mi   case SecFuncOffsetTable:
22893953d41SWei Mi     if (auto EC = writeFuncOffsetTable())
22993953d41SWei Mi       return EC;
23093953d41SWei Mi     break;
23193953d41SWei Mi   case SecProfileSymbolList:
23293953d41SWei Mi     if (auto EC = writeProfileSymbolListSection())
23393953d41SWei Mi       return EC;
23493953d41SWei Mi     break;
23593953d41SWei Mi   default:
23693953d41SWei Mi     if (auto EC = writeCustomSection(Type))
23793953d41SWei Mi       return EC;
23893953d41SWei Mi     break;
23993953d41SWei Mi   }
24093953d41SWei Mi   if (std::error_code EC = addNewSection(Type, SectionStart))
24193953d41SWei Mi     return EC;
24293953d41SWei Mi   return sampleprof_error::success;
24393953d41SWei Mi }
24493953d41SWei Mi 
24593953d41SWei Mi std::error_code SampleProfileWriterExtBinary::writeSections(
24693953d41SWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
24793953d41SWei Mi   if (auto EC = writeOneSection(SecProfSummary, ProfileMap))
24893953d41SWei Mi     return EC;
24993953d41SWei Mi   if (auto EC = writeOneSection(SecNameTable, ProfileMap))
25093953d41SWei Mi     return EC;
25193953d41SWei Mi   if (auto EC = writeOneSection(SecLBRProfile, ProfileMap))
25293953d41SWei Mi     return EC;
25393953d41SWei Mi   if (auto EC = writeOneSection(SecProfileSymbolList, ProfileMap))
25493953d41SWei Mi     return EC;
25593953d41SWei Mi   if (auto EC = writeOneSection(SecFuncOffsetTable, ProfileMap))
25693953d41SWei Mi     return EC;
257be907324SWei Mi   return sampleprof_error::success;
258be907324SWei Mi }
259be907324SWei Mi 
2606a14325dSWei Mi std::error_code SampleProfileWriterCompactBinary::write(
2616a14325dSWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
2626a14325dSWei Mi   if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
2636a14325dSWei Mi     return EC;
2646a14325dSWei Mi   if (std::error_code EC = writeFuncOffsetTable())
2656a14325dSWei Mi     return EC;
2666a14325dSWei Mi   return sampleprof_error::success;
2676a14325dSWei Mi }
2686a14325dSWei Mi 
2695f8f34e4SAdrian Prantl /// Write samples to a text file.
2708e415a82SDiego Novillo ///
2718e415a82SDiego Novillo /// Note: it may be tempting to implement this in terms of
272ef548d29SDiego Novillo /// FunctionSamples::print().  Please don't.  The dump functionality is intended
2738e415a82SDiego Novillo /// for debugging and has no specified form.
2748e415a82SDiego Novillo ///
2758e415a82SDiego Novillo /// The format used here is more structured and deliberate because
2768e415a82SDiego Novillo /// it needs to be parsed by the SampleProfileReaderText class.
277be907324SWei Mi std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {
27851abea74SNathan Slingerland   auto &OS = *OutputStream;
279*1f05b1a9Swlei   if (FunctionSamples::ProfileIsCS)
280*1f05b1a9Swlei     OS << "[" << S.getNameWithContext() << "]:" << S.getTotalSamples();
281*1f05b1a9Swlei   else
28257d1dda5SDehao Chen     OS << S.getName() << ":" << S.getTotalSamples();
283aae1ed8eSDiego Novillo   if (Indent == 0)
284aae1ed8eSDiego Novillo     OS << ":" << S.getHeadSamples();
285aae1ed8eSDiego Novillo   OS << "\n";
286c572e92cSDiego Novillo 
287ef548d29SDiego Novillo   SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
288ef548d29SDiego Novillo   for (const auto &I : SortedSamples.get()) {
289ef548d29SDiego Novillo     LineLocation Loc = I->first;
290ef548d29SDiego Novillo     const SampleRecord &Sample = I->second;
291aae1ed8eSDiego Novillo     OS.indent(Indent + 1);
292c572e92cSDiego Novillo     if (Loc.Discriminator == 0)
293c572e92cSDiego Novillo       OS << Loc.LineOffset << ": ";
294c572e92cSDiego Novillo     else
295c572e92cSDiego Novillo       OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
296c572e92cSDiego Novillo 
297c572e92cSDiego Novillo     OS << Sample.getSamples();
298c572e92cSDiego Novillo 
2995adace35SWenlei He     for (const auto &J : Sample.getSortedCallTargets())
3005adace35SWenlei He       OS << " " << J.first << ":" << J.second;
301c572e92cSDiego Novillo     OS << "\n";
302c572e92cSDiego Novillo   }
303c572e92cSDiego Novillo 
3042c7ca9b5SDehao Chen   SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
305ef548d29SDiego Novillo       S.getCallsiteSamples());
306aae1ed8eSDiego Novillo   Indent += 1;
3072c7ca9b5SDehao Chen   for (const auto &I : SortedCallsiteSamples.get())
3082c7ca9b5SDehao Chen     for (const auto &FS : I->second) {
30957d1dda5SDehao Chen       LineLocation Loc = I->first;
3102c7ca9b5SDehao Chen       const FunctionSamples &CalleeSamples = FS.second;
311aae1ed8eSDiego Novillo       OS.indent(Indent);
312aae1ed8eSDiego Novillo       if (Loc.Discriminator == 0)
313aae1ed8eSDiego Novillo         OS << Loc.LineOffset << ": ";
314aae1ed8eSDiego Novillo       else
315aae1ed8eSDiego Novillo         OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
316be907324SWei Mi       if (std::error_code EC = writeSample(CalleeSamples))
317760c5a8fSDiego Novillo         return EC;
318aae1ed8eSDiego Novillo     }
319aae1ed8eSDiego Novillo   Indent -= 1;
320aae1ed8eSDiego Novillo 
321760c5a8fSDiego Novillo   return sampleprof_error::success;
322c572e92cSDiego Novillo }
323c572e92cSDiego Novillo 
324760c5a8fSDiego Novillo std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
325760c5a8fSDiego Novillo   const auto &ret = NameTable.find(FName);
326760c5a8fSDiego Novillo   if (ret == NameTable.end())
327760c5a8fSDiego Novillo     return sampleprof_error::truncated_name_table;
32851abea74SNathan Slingerland   encodeULEB128(ret->second, *OutputStream);
329760c5a8fSDiego Novillo   return sampleprof_error::success;
330760c5a8fSDiego Novillo }
331c572e92cSDiego Novillo 
332760c5a8fSDiego Novillo void SampleProfileWriterBinary::addName(StringRef FName) {
3338d1c983fSDehao Chen   NameTable.insert(std::make_pair(FName, 0));
334760c5a8fSDiego Novillo }
335760c5a8fSDiego Novillo 
336760c5a8fSDiego Novillo void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
337760c5a8fSDiego Novillo   // Add all the names in indirect call targets.
338760c5a8fSDiego Novillo   for (const auto &I : S.getBodySamples()) {
339760c5a8fSDiego Novillo     const SampleRecord &Sample = I.second;
340760c5a8fSDiego Novillo     for (const auto &J : Sample.getCallTargets())
341760c5a8fSDiego Novillo       addName(J.first());
342760c5a8fSDiego Novillo   }
343760c5a8fSDiego Novillo 
344760c5a8fSDiego Novillo   // Recursively add all the names for inlined callsites.
3452c7ca9b5SDehao Chen   for (const auto &J : S.getCallsiteSamples())
3462c7ca9b5SDehao Chen     for (const auto &FS : J.second) {
3472c7ca9b5SDehao Chen       const FunctionSamples &CalleeSamples = FS.second;
34857d1dda5SDehao Chen       addName(CalleeSamples.getName());
349760c5a8fSDiego Novillo       addNames(CalleeSamples);
350760c5a8fSDiego Novillo     }
351760c5a8fSDiego Novillo }
352760c5a8fSDiego Novillo 
353a0c0857eSWei Mi void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) {
354a0c0857eSWei Mi   // Sort the names to make NameTable deterministic.
355a0c0857eSWei Mi   for (const auto &I : NameTable)
356a0c0857eSWei Mi     V.insert(I.first);
357a0c0857eSWei Mi   int i = 0;
358a0c0857eSWei Mi   for (const StringRef &N : V)
359a0c0857eSWei Mi     NameTable[N] = i++;
360a0c0857eSWei Mi }
36151abea74SNathan Slingerland 
362be907324SWei Mi std::error_code SampleProfileWriterBinary::writeNameTable() {
363a0c0857eSWei Mi   auto &OS = *OutputStream;
364a0c0857eSWei Mi   std::set<StringRef> V;
365a0c0857eSWei Mi   stablizeNameTable(V);
366a0c0857eSWei Mi 
367a0c0857eSWei Mi   // Write out the name table.
368a0c0857eSWei Mi   encodeULEB128(NameTable.size(), OS);
369a0c0857eSWei Mi   for (auto N : V) {
370a0c0857eSWei Mi     OS << N;
371a0c0857eSWei Mi     encodeULEB128(0, OS);
372a0c0857eSWei Mi   }
373a0c0857eSWei Mi   return sampleprof_error::success;
374a0c0857eSWei Mi }
375a0c0857eSWei Mi 
3766a14325dSWei Mi std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
3776a14325dSWei Mi   auto &OS = *OutputStream;
3786a14325dSWei Mi 
3796a14325dSWei Mi   // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
3806a14325dSWei Mi   auto &OFS = static_cast<raw_fd_ostream &>(OS);
3816a14325dSWei Mi   uint64_t FuncOffsetTableStart = OS.tell();
3826a14325dSWei Mi   if (OFS.seek(TableOffset) == (uint64_t)-1)
3836a14325dSWei Mi     return sampleprof_error::ostream_seek_unsupported;
3846a14325dSWei Mi   support::endian::Writer Writer(*OutputStream, support::little);
3856a14325dSWei Mi   Writer.write(FuncOffsetTableStart);
3866a14325dSWei Mi   if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1)
3876a14325dSWei Mi     return sampleprof_error::ostream_seek_unsupported;
3886a14325dSWei Mi 
3896a14325dSWei Mi   // Write out the table size.
3906a14325dSWei Mi   encodeULEB128(FuncOffsetTable.size(), OS);
3916a14325dSWei Mi 
3926a14325dSWei Mi   // Write out FuncOffsetTable.
3936a14325dSWei Mi   for (auto entry : FuncOffsetTable) {
3946a14325dSWei Mi     writeNameIdx(entry.first);
3956a14325dSWei Mi     encodeULEB128(entry.second, OS);
3966a14325dSWei Mi   }
3976a14325dSWei Mi   return sampleprof_error::success;
3986a14325dSWei Mi }
3996a14325dSWei Mi 
400a0c0857eSWei Mi std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
401a0c0857eSWei Mi   auto &OS = *OutputStream;
402a0c0857eSWei Mi   std::set<StringRef> V;
403a0c0857eSWei Mi   stablizeNameTable(V);
404a0c0857eSWei Mi 
405a0c0857eSWei Mi   // Write out the name table.
406a0c0857eSWei Mi   encodeULEB128(NameTable.size(), OS);
407a0c0857eSWei Mi   for (auto N : V) {
408a0c0857eSWei Mi     encodeULEB128(MD5Hash(N), OS);
409a0c0857eSWei Mi   }
410a0c0857eSWei Mi   return sampleprof_error::success;
411a0c0857eSWei Mi }
412a0c0857eSWei Mi 
413be907324SWei Mi std::error_code
414be907324SWei Mi SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) {
415a0c0857eSWei Mi   auto &OS = *OutputStream;
416760c5a8fSDiego Novillo   // Write file magic identifier.
417be907324SWei Mi   encodeULEB128(SPMagic(Format), OS);
418a0c0857eSWei Mi   encodeULEB128(SPVersion(), OS);
419a0c0857eSWei Mi   return sampleprof_error::success;
420a0c0857eSWei Mi }
421a0c0857eSWei Mi 
422a0c0857eSWei Mi std::error_code SampleProfileWriterBinary::writeHeader(
423a0c0857eSWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
424be907324SWei Mi   writeMagicIdent(Format);
425760c5a8fSDiego Novillo 
42640ee23dbSEaswaran Raman   computeSummary(ProfileMap);
42740ee23dbSEaswaran Raman   if (auto EC = writeSummary())
42840ee23dbSEaswaran Raman     return EC;
42940ee23dbSEaswaran Raman 
430760c5a8fSDiego Novillo   // Generate the name table for all the functions referenced in the profile.
431760c5a8fSDiego Novillo   for (const auto &I : ProfileMap) {
432760c5a8fSDiego Novillo     addName(I.first());
433760c5a8fSDiego Novillo     addNames(I.second);
434760c5a8fSDiego Novillo   }
435760c5a8fSDiego Novillo 
436a0c0857eSWei Mi   writeNameTable();
437760c5a8fSDiego Novillo   return sampleprof_error::success;
438c572e92cSDiego Novillo }
439c572e92cSDiego Novillo 
440b523790aSWei Mi void SampleProfileWriterExtBinaryBase::setToCompressAllSections() {
44109dcfe68SWei Mi   for (auto &Entry : SectionHdrLayout)
442ebad6788SWei Mi     addSecFlag(Entry, SecCommonFlags::SecFlagCompress);
443b523790aSWei Mi }
444b523790aSWei Mi 
445b523790aSWei Mi void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) {
446ebad6788SWei Mi   addSectionFlag(Type, SecCommonFlags::SecFlagCompress);
447b523790aSWei Mi }
448b523790aSWei Mi 
449be907324SWei Mi void SampleProfileWriterExtBinaryBase::allocSecHdrTable() {
450be907324SWei Mi   support::endian::Writer Writer(*OutputStream, support::little);
451be907324SWei Mi 
45209dcfe68SWei Mi   Writer.write(static_cast<uint64_t>(SectionHdrLayout.size()));
453be907324SWei Mi   SecHdrTableOffset = OutputStream->tell();
45409dcfe68SWei Mi   for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
455be907324SWei Mi     Writer.write(static_cast<uint64_t>(-1));
456be907324SWei Mi     Writer.write(static_cast<uint64_t>(-1));
457be907324SWei Mi     Writer.write(static_cast<uint64_t>(-1));
458be907324SWei Mi     Writer.write(static_cast<uint64_t>(-1));
459be907324SWei Mi   }
460be907324SWei Mi }
461be907324SWei Mi 
462be907324SWei Mi std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() {
463be907324SWei Mi   auto &OFS = static_cast<raw_fd_ostream &>(*OutputStream);
464be907324SWei Mi   uint64_t Saved = OutputStream->tell();
465be907324SWei Mi 
466be907324SWei Mi   // Set OutputStream to the location saved in SecHdrTableOffset.
467be907324SWei Mi   if (OFS.seek(SecHdrTableOffset) == (uint64_t)-1)
468be907324SWei Mi     return sampleprof_error::ostream_seek_unsupported;
469be907324SWei Mi   support::endian::Writer Writer(*OutputStream, support::little);
470be907324SWei Mi 
471be907324SWei Mi   DenseMap<uint32_t, uint32_t> IndexMap;
472be907324SWei Mi   for (uint32_t i = 0; i < SecHdrTable.size(); i++) {
473be907324SWei Mi     IndexMap.insert({static_cast<uint32_t>(SecHdrTable[i].Type), i});
474be907324SWei Mi   }
475be907324SWei Mi 
47609dcfe68SWei Mi   // Write the section header table in the order specified in
47709dcfe68SWei Mi   // SectionHdrLayout. That is the sections order Reader will see.
47809dcfe68SWei Mi   // Note that the sections order in which Reader expects to read
47909dcfe68SWei Mi   // may be different from the order in which Writer is able to
48009dcfe68SWei Mi   // write, so we need to adjust the order in SecHdrTable to be
48109dcfe68SWei Mi   // consistent with SectionHdrLayout when we write SecHdrTable
48209dcfe68SWei Mi   // to the memory.
48309dcfe68SWei Mi   for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
48409dcfe68SWei Mi     uint32_t idx = IndexMap[static_cast<uint32_t>(SectionHdrLayout[i].Type)];
485be907324SWei Mi     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Type));
486b523790aSWei Mi     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Flags));
487be907324SWei Mi     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Offset));
488be907324SWei Mi     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Size));
489be907324SWei Mi   }
490be907324SWei Mi 
491be907324SWei Mi   // Reset OutputStream.
492be907324SWei Mi   if (OFS.seek(Saved) == (uint64_t)-1)
493be907324SWei Mi     return sampleprof_error::ostream_seek_unsupported;
494be907324SWei Mi 
495be907324SWei Mi   return sampleprof_error::success;
496be907324SWei Mi }
497be907324SWei Mi 
498be907324SWei Mi std::error_code SampleProfileWriterExtBinaryBase::writeHeader(
499be907324SWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
500be907324SWei Mi   auto &OS = *OutputStream;
501be907324SWei Mi   FileStart = OS.tell();
502be907324SWei Mi   writeMagicIdent(Format);
503be907324SWei Mi 
504be907324SWei Mi   allocSecHdrTable();
505be907324SWei Mi   return sampleprof_error::success;
506be907324SWei Mi }
507be907324SWei Mi 
5086a14325dSWei Mi std::error_code SampleProfileWriterCompactBinary::writeHeader(
5096a14325dSWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
5106a14325dSWei Mi   support::endian::Writer Writer(*OutputStream, support::little);
5116a14325dSWei Mi   if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap))
5126a14325dSWei Mi     return EC;
5136a14325dSWei Mi 
5146a14325dSWei Mi   // Reserve a slot for the offset of function offset table. The slot will
5156a14325dSWei Mi   // be populated with the offset of FuncOffsetTable later.
5166a14325dSWei Mi   TableOffset = OutputStream->tell();
5176a14325dSWei Mi   Writer.write(static_cast<uint64_t>(-2));
5186a14325dSWei Mi   return sampleprof_error::success;
5196a14325dSWei Mi }
5206a14325dSWei Mi 
52140ee23dbSEaswaran Raman std::error_code SampleProfileWriterBinary::writeSummary() {
52240ee23dbSEaswaran Raman   auto &OS = *OutputStream;
5237cefdb81SEaswaran Raman   encodeULEB128(Summary->getTotalCount(), OS);
5247cefdb81SEaswaran Raman   encodeULEB128(Summary->getMaxCount(), OS);
5256f4903d9SEaswaran Raman   encodeULEB128(Summary->getMaxFunctionCount(), OS);
5267cefdb81SEaswaran Raman   encodeULEB128(Summary->getNumCounts(), OS);
52740ee23dbSEaswaran Raman   encodeULEB128(Summary->getNumFunctions(), OS);
52840ee23dbSEaswaran Raman   std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
52940ee23dbSEaswaran Raman   encodeULEB128(Entries.size(), OS);
53040ee23dbSEaswaran Raman   for (auto Entry : Entries) {
53140ee23dbSEaswaran Raman     encodeULEB128(Entry.Cutoff, OS);
53240ee23dbSEaswaran Raman     encodeULEB128(Entry.MinCount, OS);
53340ee23dbSEaswaran Raman     encodeULEB128(Entry.NumCounts, OS);
53440ee23dbSEaswaran Raman   }
53540ee23dbSEaswaran Raman   return sampleprof_error::success;
53640ee23dbSEaswaran Raman }
53757d1dda5SDehao Chen std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
53851abea74SNathan Slingerland   auto &OS = *OutputStream;
53951abea74SNathan Slingerland 
54057d1dda5SDehao Chen   if (std::error_code EC = writeNameIdx(S.getName()))
541760c5a8fSDiego Novillo     return EC;
542760c5a8fSDiego Novillo 
543c572e92cSDiego Novillo   encodeULEB128(S.getTotalSamples(), OS);
544a7f1e8efSDiego Novillo 
545a7f1e8efSDiego Novillo   // Emit all the body samples.
546b93483dbSDiego Novillo   encodeULEB128(S.getBodySamples().size(), OS);
547d5336ae2SDiego Novillo   for (const auto &I : S.getBodySamples()) {
548d5336ae2SDiego Novillo     LineLocation Loc = I.first;
549d5336ae2SDiego Novillo     const SampleRecord &Sample = I.second;
550c572e92cSDiego Novillo     encodeULEB128(Loc.LineOffset, OS);
551c572e92cSDiego Novillo     encodeULEB128(Loc.Discriminator, OS);
552c572e92cSDiego Novillo     encodeULEB128(Sample.getSamples(), OS);
553c572e92cSDiego Novillo     encodeULEB128(Sample.getCallTargets().size(), OS);
5545adace35SWenlei He     for (const auto &J : Sample.getSortedCallTargets()) {
5555adace35SWenlei He       StringRef Callee = J.first;
55638be3330SDiego Novillo       uint64_t CalleeSamples = J.second;
557760c5a8fSDiego Novillo       if (std::error_code EC = writeNameIdx(Callee))
558760c5a8fSDiego Novillo         return EC;
559c572e92cSDiego Novillo       encodeULEB128(CalleeSamples, OS);
560c572e92cSDiego Novillo     }
561c572e92cSDiego Novillo   }
562c572e92cSDiego Novillo 
563a7f1e8efSDiego Novillo   // Recursively emit all the callsite samples.
5642c27daf7SDehao Chen   uint64_t NumCallsites = 0;
5652c27daf7SDehao Chen   for (const auto &J : S.getCallsiteSamples())
5662c27daf7SDehao Chen     NumCallsites += J.second.size();
5672c27daf7SDehao Chen   encodeULEB128(NumCallsites, OS);
5682c7ca9b5SDehao Chen   for (const auto &J : S.getCallsiteSamples())
5692c7ca9b5SDehao Chen     for (const auto &FS : J.second) {
57057d1dda5SDehao Chen       LineLocation Loc = J.first;
5712c7ca9b5SDehao Chen       const FunctionSamples &CalleeSamples = FS.second;
572a7f1e8efSDiego Novillo       encodeULEB128(Loc.LineOffset, OS);
573a7f1e8efSDiego Novillo       encodeULEB128(Loc.Discriminator, OS);
57457d1dda5SDehao Chen       if (std::error_code EC = writeBody(CalleeSamples))
575760c5a8fSDiego Novillo         return EC;
576a7f1e8efSDiego Novillo     }
577a7f1e8efSDiego Novillo 
578760c5a8fSDiego Novillo   return sampleprof_error::success;
579c572e92cSDiego Novillo }
580d5336ae2SDiego Novillo 
5815f8f34e4SAdrian Prantl /// Write samples of a top-level function to a binary file.
582b93483dbSDiego Novillo ///
583b93483dbSDiego Novillo /// \returns true if the samples were written successfully, false otherwise.
584be907324SWei Mi std::error_code
585be907324SWei Mi SampleProfileWriterBinary::writeSample(const FunctionSamples &S) {
58651abea74SNathan Slingerland   encodeULEB128(S.getHeadSamples(), *OutputStream);
58757d1dda5SDehao Chen   return writeBody(S);
588b93483dbSDiego Novillo }
589b93483dbSDiego Novillo 
5906a14325dSWei Mi std::error_code
591be907324SWei Mi SampleProfileWriterCompactBinary::writeSample(const FunctionSamples &S) {
5926a14325dSWei Mi   uint64_t Offset = OutputStream->tell();
5936a14325dSWei Mi   StringRef Name = S.getName();
5946a14325dSWei Mi   FuncOffsetTable[Name] = Offset;
5956a14325dSWei Mi   encodeULEB128(S.getHeadSamples(), *OutputStream);
5966a14325dSWei Mi   return writeBody(S);
5976a14325dSWei Mi }
5986a14325dSWei Mi 
5995f8f34e4SAdrian Prantl /// Create a sample profile file writer based on the specified format.
600d5336ae2SDiego Novillo ///
601d5336ae2SDiego Novillo /// \param Filename The file to create.
602d5336ae2SDiego Novillo ///
603d5336ae2SDiego Novillo /// \param Format Encoding format for the profile file.
604d5336ae2SDiego Novillo ///
605d5336ae2SDiego Novillo /// \returns an error code indicating the status of the created writer.
606fcd55607SDiego Novillo ErrorOr<std::unique_ptr<SampleProfileWriter>>
607fcd55607SDiego Novillo SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
608d5336ae2SDiego Novillo   std::error_code EC;
60951abea74SNathan Slingerland   std::unique_ptr<raw_ostream> OS;
610be907324SWei Mi   if (Format == SPF_Binary || Format == SPF_Ext_Binary ||
611be907324SWei Mi       Format == SPF_Compact_Binary)
612d9b948b6SFangrui Song     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None));
61351abea74SNathan Slingerland   else
614d9b948b6SFangrui Song     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_Text));
61551abea74SNathan Slingerland   if (EC)
61651abea74SNathan Slingerland     return EC;
61751abea74SNathan Slingerland 
61851abea74SNathan Slingerland   return create(OS, Format);
61951abea74SNathan Slingerland }
62051abea74SNathan Slingerland 
6215f8f34e4SAdrian Prantl /// Create a sample profile stream writer based on the specified format.
62251abea74SNathan Slingerland ///
62351abea74SNathan Slingerland /// \param OS The output stream to store the profile data to.
62451abea74SNathan Slingerland ///
62551abea74SNathan Slingerland /// \param Format Encoding format for the profile file.
62651abea74SNathan Slingerland ///
62751abea74SNathan Slingerland /// \returns an error code indicating the status of the created writer.
62851abea74SNathan Slingerland ErrorOr<std::unique_ptr<SampleProfileWriter>>
62951abea74SNathan Slingerland SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
63051abea74SNathan Slingerland                             SampleProfileFormat Format) {
63151abea74SNathan Slingerland   std::error_code EC;
632fcd55607SDiego Novillo   std::unique_ptr<SampleProfileWriter> Writer;
633d5336ae2SDiego Novillo 
634d9be2c7eSWei Mi   if (Format == SPF_Binary)
635a0c0857eSWei Mi     Writer.reset(new SampleProfileWriterRawBinary(OS));
636be907324SWei Mi   else if (Format == SPF_Ext_Binary)
637be907324SWei Mi     Writer.reset(new SampleProfileWriterExtBinary(OS));
638a0c0857eSWei Mi   else if (Format == SPF_Compact_Binary)
639a0c0857eSWei Mi     Writer.reset(new SampleProfileWriterCompactBinary(OS));
640d5336ae2SDiego Novillo   else if (Format == SPF_Text)
64151abea74SNathan Slingerland     Writer.reset(new SampleProfileWriterText(OS));
642760c5a8fSDiego Novillo   else if (Format == SPF_GCC)
643760c5a8fSDiego Novillo     EC = sampleprof_error::unsupported_writing_format;
644d5336ae2SDiego Novillo   else
645d5336ae2SDiego Novillo     EC = sampleprof_error::unrecognized_format;
646d5336ae2SDiego Novillo 
647fcd55607SDiego Novillo   if (EC)
648d5336ae2SDiego Novillo     return EC;
649fcd55607SDiego Novillo 
650be907324SWei Mi   Writer->Format = Format;
651c55cf4afSBill Wendling   return std::move(Writer);
652d5336ae2SDiego Novillo }
65340ee23dbSEaswaran Raman 
65440ee23dbSEaswaran Raman void SampleProfileWriter::computeSummary(
65540ee23dbSEaswaran Raman     const StringMap<FunctionSamples> &ProfileMap) {
656e5a17e3fSEaswaran Raman   SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
65740ee23dbSEaswaran Raman   for (const auto &I : ProfileMap) {
65840ee23dbSEaswaran Raman     const FunctionSamples &Profile = I.second;
659e5a17e3fSEaswaran Raman     Builder.addRecord(Profile);
66040ee23dbSEaswaran Raman   }
66138de59e4SBenjamin Kramer   Summary = Builder.getSummary();
66240ee23dbSEaswaran Raman }
663