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(
79*09dcfe68SWei 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.
90b523790aSWei Mi   if (hasSecFlag(Entry, 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);
120b523790aSWei Mi   if (hasSecFlag(Entry, 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 
146*09dcfe68SWei Mi std::error_code
147*09dcfe68SWei Mi SampleProfileWriterExtBinary::writeSample(const FunctionSamples &S) {
148*09dcfe68SWei Mi   uint64_t Offset = OutputStream->tell();
149*09dcfe68SWei Mi   StringRef Name = S.getName();
150*09dcfe68SWei Mi   FuncOffsetTable[Name] = Offset - SecLBRProfileStart;
151*09dcfe68SWei Mi   encodeULEB128(S.getHeadSamples(), *OutputStream);
152*09dcfe68SWei Mi   return writeBody(S);
153*09dcfe68SWei Mi }
154*09dcfe68SWei Mi 
155*09dcfe68SWei Mi std::error_code SampleProfileWriterExtBinary::writeFuncOffsetTable() {
156*09dcfe68SWei Mi   auto &OS = *OutputStream;
157*09dcfe68SWei Mi 
158*09dcfe68SWei Mi   // Write out the table size.
159*09dcfe68SWei Mi   encodeULEB128(FuncOffsetTable.size(), OS);
160*09dcfe68SWei Mi 
161*09dcfe68SWei Mi   // Write out FuncOffsetTable.
162*09dcfe68SWei Mi   for (auto entry : FuncOffsetTable) {
163*09dcfe68SWei Mi     writeNameIdx(entry.first);
164*09dcfe68SWei Mi     encodeULEB128(entry.second, OS);
165*09dcfe68SWei Mi   }
166*09dcfe68SWei Mi   return sampleprof_error::success;
167*09dcfe68SWei Mi }
168*09dcfe68SWei Mi 
169be907324SWei Mi std::error_code SampleProfileWriterExtBinary::writeSections(
170be907324SWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
171b523790aSWei Mi   uint64_t SectionStart = markSectionStart(SecProfSummary);
172be907324SWei Mi   computeSummary(ProfileMap);
173be907324SWei Mi   if (auto EC = writeSummary())
174be907324SWei Mi     return EC;
175b523790aSWei Mi   if (std::error_code EC = addNewSection(SecProfSummary, SectionStart))
176b523790aSWei Mi     return EC;
177be907324SWei Mi 
178be907324SWei Mi   // Generate the name table for all the functions referenced in the profile.
179b523790aSWei Mi   SectionStart = markSectionStart(SecNameTable);
180be907324SWei Mi   for (const auto &I : ProfileMap) {
181be907324SWei Mi     addName(I.first());
182be907324SWei Mi     addNames(I.second);
183be907324SWei Mi   }
184be907324SWei Mi   writeNameTable();
185b523790aSWei Mi   if (std::error_code EC = addNewSection(SecNameTable, SectionStart))
186b523790aSWei Mi     return EC;
187be907324SWei Mi 
188b523790aSWei Mi   SectionStart = markSectionStart(SecLBRProfile);
189*09dcfe68SWei Mi   SecLBRProfileStart = OutputStream->tell();
190be907324SWei Mi   if (std::error_code EC = writeFuncProfiles(ProfileMap))
191be907324SWei Mi     return EC;
192b523790aSWei Mi   if (std::error_code EC = addNewSection(SecLBRProfile, SectionStart))
193b523790aSWei Mi     return EC;
194798e59b8SWei Mi 
195b523790aSWei Mi   if (ProfSymList && ProfSymList->toCompress())
196b523790aSWei Mi     setToCompressSection(SecProfileSymbolList);
197b523790aSWei Mi 
198b523790aSWei Mi   SectionStart = markSectionStart(SecProfileSymbolList);
199798e59b8SWei Mi   if (ProfSymList && ProfSymList->size() > 0)
200798e59b8SWei Mi     if (std::error_code EC = ProfSymList->write(*OutputStream))
201798e59b8SWei Mi       return EC;
202b523790aSWei Mi   if (std::error_code EC = addNewSection(SecProfileSymbolList, SectionStart))
203b523790aSWei Mi     return EC;
204be907324SWei Mi 
205*09dcfe68SWei Mi   SectionStart = markSectionStart(SecFuncOffsetTable);
206*09dcfe68SWei Mi   if (std::error_code EC = writeFuncOffsetTable())
207*09dcfe68SWei Mi     return EC;
208*09dcfe68SWei Mi   if (std::error_code EC = addNewSection(SecFuncOffsetTable, SectionStart))
209*09dcfe68SWei Mi     return EC;
210*09dcfe68SWei Mi 
211be907324SWei Mi   return sampleprof_error::success;
212be907324SWei Mi }
213be907324SWei Mi 
2146a14325dSWei Mi std::error_code SampleProfileWriterCompactBinary::write(
2156a14325dSWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
2166a14325dSWei Mi   if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
2176a14325dSWei Mi     return EC;
2186a14325dSWei Mi   if (std::error_code EC = writeFuncOffsetTable())
2196a14325dSWei Mi     return EC;
2206a14325dSWei Mi   return sampleprof_error::success;
2216a14325dSWei Mi }
2226a14325dSWei Mi 
2235f8f34e4SAdrian Prantl /// Write samples to a text file.
2248e415a82SDiego Novillo ///
2258e415a82SDiego Novillo /// Note: it may be tempting to implement this in terms of
226ef548d29SDiego Novillo /// FunctionSamples::print().  Please don't.  The dump functionality is intended
2278e415a82SDiego Novillo /// for debugging and has no specified form.
2288e415a82SDiego Novillo ///
2298e415a82SDiego Novillo /// The format used here is more structured and deliberate because
2308e415a82SDiego Novillo /// it needs to be parsed by the SampleProfileReaderText class.
231be907324SWei Mi std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {
23251abea74SNathan Slingerland   auto &OS = *OutputStream;
23357d1dda5SDehao Chen   OS << S.getName() << ":" << S.getTotalSamples();
234aae1ed8eSDiego Novillo   if (Indent == 0)
235aae1ed8eSDiego Novillo     OS << ":" << S.getHeadSamples();
236aae1ed8eSDiego Novillo   OS << "\n";
237c572e92cSDiego Novillo 
238ef548d29SDiego Novillo   SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
239ef548d29SDiego Novillo   for (const auto &I : SortedSamples.get()) {
240ef548d29SDiego Novillo     LineLocation Loc = I->first;
241ef548d29SDiego Novillo     const SampleRecord &Sample = I->second;
242aae1ed8eSDiego Novillo     OS.indent(Indent + 1);
243c572e92cSDiego Novillo     if (Loc.Discriminator == 0)
244c572e92cSDiego Novillo       OS << Loc.LineOffset << ": ";
245c572e92cSDiego Novillo     else
246c572e92cSDiego Novillo       OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
247c572e92cSDiego Novillo 
248c572e92cSDiego Novillo     OS << Sample.getSamples();
249c572e92cSDiego Novillo 
2505adace35SWenlei He     for (const auto &J : Sample.getSortedCallTargets())
2515adace35SWenlei He       OS << " " << J.first << ":" << J.second;
252c572e92cSDiego Novillo     OS << "\n";
253c572e92cSDiego Novillo   }
254c572e92cSDiego Novillo 
2552c7ca9b5SDehao Chen   SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
256ef548d29SDiego Novillo       S.getCallsiteSamples());
257aae1ed8eSDiego Novillo   Indent += 1;
2582c7ca9b5SDehao Chen   for (const auto &I : SortedCallsiteSamples.get())
2592c7ca9b5SDehao Chen     for (const auto &FS : I->second) {
26057d1dda5SDehao Chen       LineLocation Loc = I->first;
2612c7ca9b5SDehao Chen       const FunctionSamples &CalleeSamples = FS.second;
262aae1ed8eSDiego Novillo       OS.indent(Indent);
263aae1ed8eSDiego Novillo       if (Loc.Discriminator == 0)
264aae1ed8eSDiego Novillo         OS << Loc.LineOffset << ": ";
265aae1ed8eSDiego Novillo       else
266aae1ed8eSDiego Novillo         OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
267be907324SWei Mi       if (std::error_code EC = writeSample(CalleeSamples))
268760c5a8fSDiego Novillo         return EC;
269aae1ed8eSDiego Novillo     }
270aae1ed8eSDiego Novillo   Indent -= 1;
271aae1ed8eSDiego Novillo 
272760c5a8fSDiego Novillo   return sampleprof_error::success;
273c572e92cSDiego Novillo }
274c572e92cSDiego Novillo 
275760c5a8fSDiego Novillo std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
276760c5a8fSDiego Novillo   const auto &ret = NameTable.find(FName);
277760c5a8fSDiego Novillo   if (ret == NameTable.end())
278760c5a8fSDiego Novillo     return sampleprof_error::truncated_name_table;
27951abea74SNathan Slingerland   encodeULEB128(ret->second, *OutputStream);
280760c5a8fSDiego Novillo   return sampleprof_error::success;
281760c5a8fSDiego Novillo }
282c572e92cSDiego Novillo 
283760c5a8fSDiego Novillo void SampleProfileWriterBinary::addName(StringRef FName) {
2848d1c983fSDehao Chen   NameTable.insert(std::make_pair(FName, 0));
285760c5a8fSDiego Novillo }
286760c5a8fSDiego Novillo 
287760c5a8fSDiego Novillo void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
288760c5a8fSDiego Novillo   // Add all the names in indirect call targets.
289760c5a8fSDiego Novillo   for (const auto &I : S.getBodySamples()) {
290760c5a8fSDiego Novillo     const SampleRecord &Sample = I.second;
291760c5a8fSDiego Novillo     for (const auto &J : Sample.getCallTargets())
292760c5a8fSDiego Novillo       addName(J.first());
293760c5a8fSDiego Novillo   }
294760c5a8fSDiego Novillo 
295760c5a8fSDiego Novillo   // Recursively add all the names for inlined callsites.
2962c7ca9b5SDehao Chen   for (const auto &J : S.getCallsiteSamples())
2972c7ca9b5SDehao Chen     for (const auto &FS : J.second) {
2982c7ca9b5SDehao Chen       const FunctionSamples &CalleeSamples = FS.second;
29957d1dda5SDehao Chen       addName(CalleeSamples.getName());
300760c5a8fSDiego Novillo       addNames(CalleeSamples);
301760c5a8fSDiego Novillo     }
302760c5a8fSDiego Novillo }
303760c5a8fSDiego Novillo 
304a0c0857eSWei Mi void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) {
305a0c0857eSWei Mi   // Sort the names to make NameTable deterministic.
306a0c0857eSWei Mi   for (const auto &I : NameTable)
307a0c0857eSWei Mi     V.insert(I.first);
308a0c0857eSWei Mi   int i = 0;
309a0c0857eSWei Mi   for (const StringRef &N : V)
310a0c0857eSWei Mi     NameTable[N] = i++;
311a0c0857eSWei Mi }
31251abea74SNathan Slingerland 
313be907324SWei Mi std::error_code SampleProfileWriterBinary::writeNameTable() {
314a0c0857eSWei Mi   auto &OS = *OutputStream;
315a0c0857eSWei Mi   std::set<StringRef> V;
316a0c0857eSWei Mi   stablizeNameTable(V);
317a0c0857eSWei Mi 
318a0c0857eSWei Mi   // Write out the name table.
319a0c0857eSWei Mi   encodeULEB128(NameTable.size(), OS);
320a0c0857eSWei Mi   for (auto N : V) {
321a0c0857eSWei Mi     OS << N;
322a0c0857eSWei Mi     encodeULEB128(0, OS);
323a0c0857eSWei Mi   }
324a0c0857eSWei Mi   return sampleprof_error::success;
325a0c0857eSWei Mi }
326a0c0857eSWei Mi 
3276a14325dSWei Mi std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
3286a14325dSWei Mi   auto &OS = *OutputStream;
3296a14325dSWei Mi 
3306a14325dSWei Mi   // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
3316a14325dSWei Mi   auto &OFS = static_cast<raw_fd_ostream &>(OS);
3326a14325dSWei Mi   uint64_t FuncOffsetTableStart = OS.tell();
3336a14325dSWei Mi   if (OFS.seek(TableOffset) == (uint64_t)-1)
3346a14325dSWei Mi     return sampleprof_error::ostream_seek_unsupported;
3356a14325dSWei Mi   support::endian::Writer Writer(*OutputStream, support::little);
3366a14325dSWei Mi   Writer.write(FuncOffsetTableStart);
3376a14325dSWei Mi   if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1)
3386a14325dSWei Mi     return sampleprof_error::ostream_seek_unsupported;
3396a14325dSWei Mi 
3406a14325dSWei Mi   // Write out the table size.
3416a14325dSWei Mi   encodeULEB128(FuncOffsetTable.size(), OS);
3426a14325dSWei Mi 
3436a14325dSWei Mi   // Write out FuncOffsetTable.
3446a14325dSWei Mi   for (auto entry : FuncOffsetTable) {
3456a14325dSWei Mi     writeNameIdx(entry.first);
3466a14325dSWei Mi     encodeULEB128(entry.second, OS);
3476a14325dSWei Mi   }
3486a14325dSWei Mi   return sampleprof_error::success;
3496a14325dSWei Mi }
3506a14325dSWei Mi 
351a0c0857eSWei Mi std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
352a0c0857eSWei Mi   auto &OS = *OutputStream;
353a0c0857eSWei Mi   std::set<StringRef> V;
354a0c0857eSWei Mi   stablizeNameTable(V);
355a0c0857eSWei Mi 
356a0c0857eSWei Mi   // Write out the name table.
357a0c0857eSWei Mi   encodeULEB128(NameTable.size(), OS);
358a0c0857eSWei Mi   for (auto N : V) {
359a0c0857eSWei Mi     encodeULEB128(MD5Hash(N), OS);
360a0c0857eSWei Mi   }
361a0c0857eSWei Mi   return sampleprof_error::success;
362a0c0857eSWei Mi }
363a0c0857eSWei Mi 
364be907324SWei Mi std::error_code
365be907324SWei Mi SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) {
366a0c0857eSWei Mi   auto &OS = *OutputStream;
367760c5a8fSDiego Novillo   // Write file magic identifier.
368be907324SWei Mi   encodeULEB128(SPMagic(Format), OS);
369a0c0857eSWei Mi   encodeULEB128(SPVersion(), OS);
370a0c0857eSWei Mi   return sampleprof_error::success;
371a0c0857eSWei Mi }
372a0c0857eSWei Mi 
373a0c0857eSWei Mi std::error_code SampleProfileWriterBinary::writeHeader(
374a0c0857eSWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
375be907324SWei Mi   writeMagicIdent(Format);
376760c5a8fSDiego Novillo 
37740ee23dbSEaswaran Raman   computeSummary(ProfileMap);
37840ee23dbSEaswaran Raman   if (auto EC = writeSummary())
37940ee23dbSEaswaran Raman     return EC;
38040ee23dbSEaswaran Raman 
381760c5a8fSDiego Novillo   // Generate the name table for all the functions referenced in the profile.
382760c5a8fSDiego Novillo   for (const auto &I : ProfileMap) {
383760c5a8fSDiego Novillo     addName(I.first());
384760c5a8fSDiego Novillo     addNames(I.second);
385760c5a8fSDiego Novillo   }
386760c5a8fSDiego Novillo 
387a0c0857eSWei Mi   writeNameTable();
388760c5a8fSDiego Novillo   return sampleprof_error::success;
389c572e92cSDiego Novillo }
390c572e92cSDiego Novillo 
391b523790aSWei Mi void SampleProfileWriterExtBinaryBase::setToCompressAllSections() {
392*09dcfe68SWei Mi   for (auto &Entry : SectionHdrLayout)
393b523790aSWei Mi     addSecFlags(Entry, SecFlagCompress);
394b523790aSWei Mi }
395b523790aSWei Mi 
396b523790aSWei Mi void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) {
397b523790aSWei Mi   addSectionFlags(Type, SecFlagCompress);
398b523790aSWei Mi }
399b523790aSWei Mi 
400b523790aSWei Mi void SampleProfileWriterExtBinaryBase::addSectionFlags(SecType Type,
401b523790aSWei Mi                                                        SecFlags Flags) {
402*09dcfe68SWei Mi   for (auto &Entry : SectionHdrLayout) {
403b523790aSWei Mi     if (Entry.Type == Type)
404b523790aSWei Mi       addSecFlags(Entry, Flags);
405b523790aSWei Mi   }
406b523790aSWei Mi }
407b523790aSWei Mi 
408be907324SWei Mi void SampleProfileWriterExtBinaryBase::allocSecHdrTable() {
409be907324SWei Mi   support::endian::Writer Writer(*OutputStream, support::little);
410be907324SWei Mi 
411*09dcfe68SWei Mi   Writer.write(static_cast<uint64_t>(SectionHdrLayout.size()));
412be907324SWei Mi   SecHdrTableOffset = OutputStream->tell();
413*09dcfe68SWei Mi   for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
414be907324SWei Mi     Writer.write(static_cast<uint64_t>(-1));
415be907324SWei Mi     Writer.write(static_cast<uint64_t>(-1));
416be907324SWei Mi     Writer.write(static_cast<uint64_t>(-1));
417be907324SWei Mi     Writer.write(static_cast<uint64_t>(-1));
418be907324SWei Mi   }
419be907324SWei Mi }
420be907324SWei Mi 
421be907324SWei Mi std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() {
422be907324SWei Mi   auto &OFS = static_cast<raw_fd_ostream &>(*OutputStream);
423be907324SWei Mi   uint64_t Saved = OutputStream->tell();
424be907324SWei Mi 
425be907324SWei Mi   // Set OutputStream to the location saved in SecHdrTableOffset.
426be907324SWei Mi   if (OFS.seek(SecHdrTableOffset) == (uint64_t)-1)
427be907324SWei Mi     return sampleprof_error::ostream_seek_unsupported;
428be907324SWei Mi   support::endian::Writer Writer(*OutputStream, support::little);
429be907324SWei Mi 
430be907324SWei Mi   DenseMap<uint32_t, uint32_t> IndexMap;
431be907324SWei Mi   for (uint32_t i = 0; i < SecHdrTable.size(); i++) {
432be907324SWei Mi     IndexMap.insert({static_cast<uint32_t>(SecHdrTable[i].Type), i});
433be907324SWei Mi   }
434be907324SWei Mi 
435*09dcfe68SWei Mi   // Write the section header table in the order specified in
436*09dcfe68SWei Mi   // SectionHdrLayout. That is the sections order Reader will see.
437*09dcfe68SWei Mi   // Note that the sections order in which Reader expects to read
438*09dcfe68SWei Mi   // may be different from the order in which Writer is able to
439*09dcfe68SWei Mi   // write, so we need to adjust the order in SecHdrTable to be
440*09dcfe68SWei Mi   // consistent with SectionHdrLayout when we write SecHdrTable
441*09dcfe68SWei Mi   // to the memory.
442*09dcfe68SWei Mi   for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
443*09dcfe68SWei Mi     uint32_t idx = IndexMap[static_cast<uint32_t>(SectionHdrLayout[i].Type)];
444be907324SWei Mi     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Type));
445b523790aSWei Mi     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Flags));
446be907324SWei Mi     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Offset));
447be907324SWei Mi     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Size));
448be907324SWei Mi   }
449be907324SWei Mi 
450be907324SWei Mi   // Reset OutputStream.
451be907324SWei Mi   if (OFS.seek(Saved) == (uint64_t)-1)
452be907324SWei Mi     return sampleprof_error::ostream_seek_unsupported;
453be907324SWei Mi 
454be907324SWei Mi   return sampleprof_error::success;
455be907324SWei Mi }
456be907324SWei Mi 
457be907324SWei Mi std::error_code SampleProfileWriterExtBinaryBase::writeHeader(
458be907324SWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
459be907324SWei Mi   auto &OS = *OutputStream;
460be907324SWei Mi   FileStart = OS.tell();
461be907324SWei Mi   writeMagicIdent(Format);
462be907324SWei Mi 
463be907324SWei Mi   allocSecHdrTable();
464be907324SWei Mi   return sampleprof_error::success;
465be907324SWei Mi }
466be907324SWei Mi 
4676a14325dSWei Mi std::error_code SampleProfileWriterCompactBinary::writeHeader(
4686a14325dSWei Mi     const StringMap<FunctionSamples> &ProfileMap) {
4696a14325dSWei Mi   support::endian::Writer Writer(*OutputStream, support::little);
4706a14325dSWei Mi   if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap))
4716a14325dSWei Mi     return EC;
4726a14325dSWei Mi 
4736a14325dSWei Mi   // Reserve a slot for the offset of function offset table. The slot will
4746a14325dSWei Mi   // be populated with the offset of FuncOffsetTable later.
4756a14325dSWei Mi   TableOffset = OutputStream->tell();
4766a14325dSWei Mi   Writer.write(static_cast<uint64_t>(-2));
4776a14325dSWei Mi   return sampleprof_error::success;
4786a14325dSWei Mi }
4796a14325dSWei Mi 
48040ee23dbSEaswaran Raman std::error_code SampleProfileWriterBinary::writeSummary() {
48140ee23dbSEaswaran Raman   auto &OS = *OutputStream;
4827cefdb81SEaswaran Raman   encodeULEB128(Summary->getTotalCount(), OS);
4837cefdb81SEaswaran Raman   encodeULEB128(Summary->getMaxCount(), OS);
4846f4903d9SEaswaran Raman   encodeULEB128(Summary->getMaxFunctionCount(), OS);
4857cefdb81SEaswaran Raman   encodeULEB128(Summary->getNumCounts(), OS);
48640ee23dbSEaswaran Raman   encodeULEB128(Summary->getNumFunctions(), OS);
48740ee23dbSEaswaran Raman   std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
48840ee23dbSEaswaran Raman   encodeULEB128(Entries.size(), OS);
48940ee23dbSEaswaran Raman   for (auto Entry : Entries) {
49040ee23dbSEaswaran Raman     encodeULEB128(Entry.Cutoff, OS);
49140ee23dbSEaswaran Raman     encodeULEB128(Entry.MinCount, OS);
49240ee23dbSEaswaran Raman     encodeULEB128(Entry.NumCounts, OS);
49340ee23dbSEaswaran Raman   }
49440ee23dbSEaswaran Raman   return sampleprof_error::success;
49540ee23dbSEaswaran Raman }
49657d1dda5SDehao Chen std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
49751abea74SNathan Slingerland   auto &OS = *OutputStream;
49851abea74SNathan Slingerland 
49957d1dda5SDehao Chen   if (std::error_code EC = writeNameIdx(S.getName()))
500760c5a8fSDiego Novillo     return EC;
501760c5a8fSDiego Novillo 
502c572e92cSDiego Novillo   encodeULEB128(S.getTotalSamples(), OS);
503a7f1e8efSDiego Novillo 
504a7f1e8efSDiego Novillo   // Emit all the body samples.
505b93483dbSDiego Novillo   encodeULEB128(S.getBodySamples().size(), OS);
506d5336ae2SDiego Novillo   for (const auto &I : S.getBodySamples()) {
507d5336ae2SDiego Novillo     LineLocation Loc = I.first;
508d5336ae2SDiego Novillo     const SampleRecord &Sample = I.second;
509c572e92cSDiego Novillo     encodeULEB128(Loc.LineOffset, OS);
510c572e92cSDiego Novillo     encodeULEB128(Loc.Discriminator, OS);
511c572e92cSDiego Novillo     encodeULEB128(Sample.getSamples(), OS);
512c572e92cSDiego Novillo     encodeULEB128(Sample.getCallTargets().size(), OS);
5135adace35SWenlei He     for (const auto &J : Sample.getSortedCallTargets()) {
5145adace35SWenlei He       StringRef Callee = J.first;
51538be3330SDiego Novillo       uint64_t CalleeSamples = J.second;
516760c5a8fSDiego Novillo       if (std::error_code EC = writeNameIdx(Callee))
517760c5a8fSDiego Novillo         return EC;
518c572e92cSDiego Novillo       encodeULEB128(CalleeSamples, OS);
519c572e92cSDiego Novillo     }
520c572e92cSDiego Novillo   }
521c572e92cSDiego Novillo 
522a7f1e8efSDiego Novillo   // Recursively emit all the callsite samples.
5232c27daf7SDehao Chen   uint64_t NumCallsites = 0;
5242c27daf7SDehao Chen   for (const auto &J : S.getCallsiteSamples())
5252c27daf7SDehao Chen     NumCallsites += J.second.size();
5262c27daf7SDehao Chen   encodeULEB128(NumCallsites, OS);
5272c7ca9b5SDehao Chen   for (const auto &J : S.getCallsiteSamples())
5282c7ca9b5SDehao Chen     for (const auto &FS : J.second) {
52957d1dda5SDehao Chen       LineLocation Loc = J.first;
5302c7ca9b5SDehao Chen       const FunctionSamples &CalleeSamples = FS.second;
531a7f1e8efSDiego Novillo       encodeULEB128(Loc.LineOffset, OS);
532a7f1e8efSDiego Novillo       encodeULEB128(Loc.Discriminator, OS);
53357d1dda5SDehao Chen       if (std::error_code EC = writeBody(CalleeSamples))
534760c5a8fSDiego Novillo         return EC;
535a7f1e8efSDiego Novillo     }
536a7f1e8efSDiego Novillo 
537760c5a8fSDiego Novillo   return sampleprof_error::success;
538c572e92cSDiego Novillo }
539d5336ae2SDiego Novillo 
5405f8f34e4SAdrian Prantl /// Write samples of a top-level function to a binary file.
541b93483dbSDiego Novillo ///
542b93483dbSDiego Novillo /// \returns true if the samples were written successfully, false otherwise.
543be907324SWei Mi std::error_code
544be907324SWei Mi SampleProfileWriterBinary::writeSample(const FunctionSamples &S) {
54551abea74SNathan Slingerland   encodeULEB128(S.getHeadSamples(), *OutputStream);
54657d1dda5SDehao Chen   return writeBody(S);
547b93483dbSDiego Novillo }
548b93483dbSDiego Novillo 
5496a14325dSWei Mi std::error_code
550be907324SWei Mi SampleProfileWriterCompactBinary::writeSample(const FunctionSamples &S) {
5516a14325dSWei Mi   uint64_t Offset = OutputStream->tell();
5526a14325dSWei Mi   StringRef Name = S.getName();
5536a14325dSWei Mi   FuncOffsetTable[Name] = Offset;
5546a14325dSWei Mi   encodeULEB128(S.getHeadSamples(), *OutputStream);
5556a14325dSWei Mi   return writeBody(S);
5566a14325dSWei Mi }
5576a14325dSWei Mi 
5585f8f34e4SAdrian Prantl /// Create a sample profile file writer based on the specified format.
559d5336ae2SDiego Novillo ///
560d5336ae2SDiego Novillo /// \param Filename The file to create.
561d5336ae2SDiego Novillo ///
562d5336ae2SDiego Novillo /// \param Format Encoding format for the profile file.
563d5336ae2SDiego Novillo ///
564d5336ae2SDiego Novillo /// \returns an error code indicating the status of the created writer.
565fcd55607SDiego Novillo ErrorOr<std::unique_ptr<SampleProfileWriter>>
566fcd55607SDiego Novillo SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
567d5336ae2SDiego Novillo   std::error_code EC;
56851abea74SNathan Slingerland   std::unique_ptr<raw_ostream> OS;
569be907324SWei Mi   if (Format == SPF_Binary || Format == SPF_Ext_Binary ||
570be907324SWei Mi       Format == SPF_Compact_Binary)
571d9b948b6SFangrui Song     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None));
57251abea74SNathan Slingerland   else
573d9b948b6SFangrui Song     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_Text));
57451abea74SNathan Slingerland   if (EC)
57551abea74SNathan Slingerland     return EC;
57651abea74SNathan Slingerland 
57751abea74SNathan Slingerland   return create(OS, Format);
57851abea74SNathan Slingerland }
57951abea74SNathan Slingerland 
5805f8f34e4SAdrian Prantl /// Create a sample profile stream writer based on the specified format.
58151abea74SNathan Slingerland ///
58251abea74SNathan Slingerland /// \param OS The output stream to store the profile data to.
58351abea74SNathan Slingerland ///
58451abea74SNathan Slingerland /// \param Format Encoding format for the profile file.
58551abea74SNathan Slingerland ///
58651abea74SNathan Slingerland /// \returns an error code indicating the status of the created writer.
58751abea74SNathan Slingerland ErrorOr<std::unique_ptr<SampleProfileWriter>>
58851abea74SNathan Slingerland SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
58951abea74SNathan Slingerland                             SampleProfileFormat Format) {
59051abea74SNathan Slingerland   std::error_code EC;
591fcd55607SDiego Novillo   std::unique_ptr<SampleProfileWriter> Writer;
592d5336ae2SDiego Novillo 
593d9be2c7eSWei Mi   if (Format == SPF_Binary)
594a0c0857eSWei Mi     Writer.reset(new SampleProfileWriterRawBinary(OS));
595be907324SWei Mi   else if (Format == SPF_Ext_Binary)
596be907324SWei Mi     Writer.reset(new SampleProfileWriterExtBinary(OS));
597a0c0857eSWei Mi   else if (Format == SPF_Compact_Binary)
598a0c0857eSWei Mi     Writer.reset(new SampleProfileWriterCompactBinary(OS));
599d5336ae2SDiego Novillo   else if (Format == SPF_Text)
60051abea74SNathan Slingerland     Writer.reset(new SampleProfileWriterText(OS));
601760c5a8fSDiego Novillo   else if (Format == SPF_GCC)
602760c5a8fSDiego Novillo     EC = sampleprof_error::unsupported_writing_format;
603d5336ae2SDiego Novillo   else
604d5336ae2SDiego Novillo     EC = sampleprof_error::unrecognized_format;
605d5336ae2SDiego Novillo 
606fcd55607SDiego Novillo   if (EC)
607d5336ae2SDiego Novillo     return EC;
608fcd55607SDiego Novillo 
609be907324SWei Mi   Writer->Format = Format;
610fcd55607SDiego Novillo   return std::move(Writer);
611d5336ae2SDiego Novillo }
61240ee23dbSEaswaran Raman 
61340ee23dbSEaswaran Raman void SampleProfileWriter::computeSummary(
61440ee23dbSEaswaran Raman     const StringMap<FunctionSamples> &ProfileMap) {
615e5a17e3fSEaswaran Raman   SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
61640ee23dbSEaswaran Raman   for (const auto &I : ProfileMap) {
61740ee23dbSEaswaran Raman     const FunctionSamples &Profile = I.second;
618e5a17e3fSEaswaran Raman     Builder.addRecord(Profile);
61940ee23dbSEaswaran Raman   }
62038de59e4SBenjamin Kramer   Summary = Builder.getSummary();
62140ee23dbSEaswaran Raman }
622