xref: /freebsd/contrib/llvm-project/llvm/lib/ProfileData/SampleProfWriter.cpp (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
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/ProfileData/ProfileCommon.h"
23 #include "llvm/ProfileData/SampleProf.h"
24 #include "llvm/Support/Compression.h"
25 #include "llvm/Support/Endian.h"
26 #include "llvm/Support/EndianStream.h"
27 #include "llvm/Support/ErrorOr.h"
28 #include "llvm/Support/FileSystem.h"
29 #include "llvm/Support/LEB128.h"
30 #include "llvm/Support/MD5.h"
31 #include "llvm/Support/raw_ostream.h"
32 #include <algorithm>
33 #include <cstdint>
34 #include <memory>
35 #include <set>
36 #include <system_error>
37 #include <utility>
38 #include <vector>
39 
40 using namespace llvm;
41 using namespace sampleprof;
42 
43 std::error_code SampleProfileWriter::writeFuncProfiles(
44     const StringMap<FunctionSamples> &ProfileMap) {
45   // Sort the ProfileMap by total samples.
46   typedef std::pair<StringRef, const FunctionSamples *> NameFunctionSamples;
47   std::vector<NameFunctionSamples> V;
48   for (const auto &I : ProfileMap)
49     V.push_back(std::make_pair(I.getKey(), &I.second));
50 
51   llvm::stable_sort(
52       V, [](const NameFunctionSamples &A, const NameFunctionSamples &B) {
53         if (A.second->getTotalSamples() == B.second->getTotalSamples())
54           return A.first > B.first;
55         return A.second->getTotalSamples() > B.second->getTotalSamples();
56       });
57 
58   for (const auto &I : V) {
59     if (std::error_code EC = writeSample(*I.second))
60       return EC;
61   }
62   return sampleprof_error::success;
63 }
64 
65 std::error_code
66 SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) {
67   if (std::error_code EC = writeHeader(ProfileMap))
68     return EC;
69 
70   if (std::error_code EC = writeFuncProfiles(ProfileMap))
71     return EC;
72 
73   return sampleprof_error::success;
74 }
75 
76 SecHdrTableEntry &
77 SampleProfileWriterExtBinaryBase::getEntryInLayout(SecType Type) {
78   auto SecIt = std::find_if(
79       SectionHdrLayout.begin(), SectionHdrLayout.end(),
80       [=](const auto &Entry) -> bool { return Entry.Type == Type; });
81   return *SecIt;
82 }
83 
84 /// Return the current position and prepare to use it as the start
85 /// position of a section.
86 uint64_t SampleProfileWriterExtBinaryBase::markSectionStart(SecType Type) {
87   uint64_t SectionStart = OutputStream->tell();
88   auto &Entry = getEntryInLayout(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.
116 std::error_code
117 SampleProfileWriterExtBinaryBase::addNewSection(SecType Type,
118                                                 uint64_t SectionStart) {
119   auto Entry = getEntryInLayout(Type);
120   if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress)) {
121     LocalBufStream.swap(OutputStream);
122     if (std::error_code EC = compressAndOutput())
123       return EC;
124   }
125   SecHdrTable.push_back({Type, Entry.Flags, SectionStart - FileStart,
126                          OutputStream->tell() - SectionStart});
127   return sampleprof_error::success;
128 }
129 
130 std::error_code SampleProfileWriterExtBinaryBase::write(
131     const StringMap<FunctionSamples> &ProfileMap) {
132   if (std::error_code EC = writeHeader(ProfileMap))
133     return EC;
134 
135   std::string LocalBuf;
136   LocalBufStream = std::make_unique<raw_string_ostream>(LocalBuf);
137   if (std::error_code EC = writeSections(ProfileMap))
138     return EC;
139 
140   if (std::error_code EC = writeSecHdrTable())
141     return EC;
142 
143   return sampleprof_error::success;
144 }
145 
146 std::error_code
147 SampleProfileWriterExtBinary::writeSample(const FunctionSamples &S) {
148   uint64_t Offset = OutputStream->tell();
149   StringRef Name = S.getName();
150   FuncOffsetTable[Name] = Offset - SecLBRProfileStart;
151   encodeULEB128(S.getHeadSamples(), *OutputStream);
152   return writeBody(S);
153 }
154 
155 std::error_code SampleProfileWriterExtBinary::writeFuncOffsetTable() {
156   auto &OS = *OutputStream;
157 
158   // Write out the table size.
159   encodeULEB128(FuncOffsetTable.size(), OS);
160 
161   // Write out FuncOffsetTable.
162   for (auto entry : FuncOffsetTable) {
163     writeNameIdx(entry.first);
164     encodeULEB128(entry.second, OS);
165   }
166   return sampleprof_error::success;
167 }
168 
169 std::error_code SampleProfileWriterExtBinary::writeNameTable() {
170   if (!UseMD5)
171     return SampleProfileWriterBinary::writeNameTable();
172 
173   auto &OS = *OutputStream;
174   std::set<StringRef> V;
175   stablizeNameTable(V);
176 
177   // Write out the name table.
178   encodeULEB128(NameTable.size(), OS);
179   for (auto N : V) {
180     encodeULEB128(MD5Hash(N), OS);
181   }
182   return sampleprof_error::success;
183 }
184 
185 std::error_code SampleProfileWriterExtBinary::writeSections(
186     const StringMap<FunctionSamples> &ProfileMap) {
187   uint64_t SectionStart = markSectionStart(SecProfSummary);
188   computeSummary(ProfileMap);
189   if (auto EC = writeSummary())
190     return EC;
191   if (std::error_code EC = addNewSection(SecProfSummary, SectionStart))
192     return EC;
193 
194   // Generate the name table for all the functions referenced in the profile.
195   SectionStart = markSectionStart(SecNameTable);
196   for (const auto &I : ProfileMap) {
197     addName(I.first());
198     addNames(I.second);
199   }
200   writeNameTable();
201   if (std::error_code EC = addNewSection(SecNameTable, SectionStart))
202     return EC;
203 
204   SectionStart = markSectionStart(SecLBRProfile);
205   SecLBRProfileStart = OutputStream->tell();
206   if (std::error_code EC = writeFuncProfiles(ProfileMap))
207     return EC;
208   if (std::error_code EC = addNewSection(SecLBRProfile, SectionStart))
209     return EC;
210 
211   if (ProfSymList && ProfSymList->toCompress())
212     setToCompressSection(SecProfileSymbolList);
213 
214   SectionStart = markSectionStart(SecProfileSymbolList);
215   if (ProfSymList && ProfSymList->size() > 0)
216     if (std::error_code EC = ProfSymList->write(*OutputStream))
217       return EC;
218   if (std::error_code EC = addNewSection(SecProfileSymbolList, SectionStart))
219     return EC;
220 
221   SectionStart = markSectionStart(SecFuncOffsetTable);
222   if (std::error_code EC = writeFuncOffsetTable())
223     return EC;
224   if (std::error_code EC = addNewSection(SecFuncOffsetTable, SectionStart))
225     return EC;
226 
227   return sampleprof_error::success;
228 }
229 
230 std::error_code SampleProfileWriterCompactBinary::write(
231     const StringMap<FunctionSamples> &ProfileMap) {
232   if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
233     return EC;
234   if (std::error_code EC = writeFuncOffsetTable())
235     return EC;
236   return sampleprof_error::success;
237 }
238 
239 /// Write samples to a text file.
240 ///
241 /// Note: it may be tempting to implement this in terms of
242 /// FunctionSamples::print().  Please don't.  The dump functionality is intended
243 /// for debugging and has no specified form.
244 ///
245 /// The format used here is more structured and deliberate because
246 /// it needs to be parsed by the SampleProfileReaderText class.
247 std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {
248   auto &OS = *OutputStream;
249   OS << S.getName() << ":" << S.getTotalSamples();
250   if (Indent == 0)
251     OS << ":" << S.getHeadSamples();
252   OS << "\n";
253 
254   SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
255   for (const auto &I : SortedSamples.get()) {
256     LineLocation Loc = I->first;
257     const SampleRecord &Sample = I->second;
258     OS.indent(Indent + 1);
259     if (Loc.Discriminator == 0)
260       OS << Loc.LineOffset << ": ";
261     else
262       OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
263 
264     OS << Sample.getSamples();
265 
266     for (const auto &J : Sample.getSortedCallTargets())
267       OS << " " << J.first << ":" << J.second;
268     OS << "\n";
269   }
270 
271   SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
272       S.getCallsiteSamples());
273   Indent += 1;
274   for (const auto &I : SortedCallsiteSamples.get())
275     for (const auto &FS : I->second) {
276       LineLocation Loc = I->first;
277       const FunctionSamples &CalleeSamples = FS.second;
278       OS.indent(Indent);
279       if (Loc.Discriminator == 0)
280         OS << Loc.LineOffset << ": ";
281       else
282         OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
283       if (std::error_code EC = writeSample(CalleeSamples))
284         return EC;
285     }
286   Indent -= 1;
287 
288   return sampleprof_error::success;
289 }
290 
291 std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
292   const auto &ret = NameTable.find(FName);
293   if (ret == NameTable.end())
294     return sampleprof_error::truncated_name_table;
295   encodeULEB128(ret->second, *OutputStream);
296   return sampleprof_error::success;
297 }
298 
299 void SampleProfileWriterBinary::addName(StringRef FName) {
300   NameTable.insert(std::make_pair(FName, 0));
301 }
302 
303 void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
304   // Add all the names in indirect call targets.
305   for (const auto &I : S.getBodySamples()) {
306     const SampleRecord &Sample = I.second;
307     for (const auto &J : Sample.getCallTargets())
308       addName(J.first());
309   }
310 
311   // Recursively add all the names for inlined callsites.
312   for (const auto &J : S.getCallsiteSamples())
313     for (const auto &FS : J.second) {
314       const FunctionSamples &CalleeSamples = FS.second;
315       addName(CalleeSamples.getName());
316       addNames(CalleeSamples);
317     }
318 }
319 
320 void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) {
321   // Sort the names to make NameTable deterministic.
322   for (const auto &I : NameTable)
323     V.insert(I.first);
324   int i = 0;
325   for (const StringRef &N : V)
326     NameTable[N] = i++;
327 }
328 
329 std::error_code SampleProfileWriterBinary::writeNameTable() {
330   auto &OS = *OutputStream;
331   std::set<StringRef> V;
332   stablizeNameTable(V);
333 
334   // Write out the name table.
335   encodeULEB128(NameTable.size(), OS);
336   for (auto N : V) {
337     OS << N;
338     encodeULEB128(0, OS);
339   }
340   return sampleprof_error::success;
341 }
342 
343 std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
344   auto &OS = *OutputStream;
345 
346   // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
347   auto &OFS = static_cast<raw_fd_ostream &>(OS);
348   uint64_t FuncOffsetTableStart = OS.tell();
349   if (OFS.seek(TableOffset) == (uint64_t)-1)
350     return sampleprof_error::ostream_seek_unsupported;
351   support::endian::Writer Writer(*OutputStream, support::little);
352   Writer.write(FuncOffsetTableStart);
353   if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1)
354     return sampleprof_error::ostream_seek_unsupported;
355 
356   // Write out the table size.
357   encodeULEB128(FuncOffsetTable.size(), OS);
358 
359   // Write out FuncOffsetTable.
360   for (auto entry : FuncOffsetTable) {
361     writeNameIdx(entry.first);
362     encodeULEB128(entry.second, OS);
363   }
364   return sampleprof_error::success;
365 }
366 
367 std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
368   auto &OS = *OutputStream;
369   std::set<StringRef> V;
370   stablizeNameTable(V);
371 
372   // Write out the name table.
373   encodeULEB128(NameTable.size(), OS);
374   for (auto N : V) {
375     encodeULEB128(MD5Hash(N), OS);
376   }
377   return sampleprof_error::success;
378 }
379 
380 std::error_code
381 SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) {
382   auto &OS = *OutputStream;
383   // Write file magic identifier.
384   encodeULEB128(SPMagic(Format), OS);
385   encodeULEB128(SPVersion(), OS);
386   return sampleprof_error::success;
387 }
388 
389 std::error_code SampleProfileWriterBinary::writeHeader(
390     const StringMap<FunctionSamples> &ProfileMap) {
391   writeMagicIdent(Format);
392 
393   computeSummary(ProfileMap);
394   if (auto EC = writeSummary())
395     return EC;
396 
397   // Generate the name table for all the functions referenced in the profile.
398   for (const auto &I : ProfileMap) {
399     addName(I.first());
400     addNames(I.second);
401   }
402 
403   writeNameTable();
404   return sampleprof_error::success;
405 }
406 
407 void SampleProfileWriterExtBinaryBase::setToCompressAllSections() {
408   for (auto &Entry : SectionHdrLayout)
409     addSecFlag(Entry, SecCommonFlags::SecFlagCompress);
410 }
411 
412 void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) {
413   addSectionFlag(Type, SecCommonFlags::SecFlagCompress);
414 }
415 
416 void SampleProfileWriterExtBinaryBase::allocSecHdrTable() {
417   support::endian::Writer Writer(*OutputStream, support::little);
418 
419   Writer.write(static_cast<uint64_t>(SectionHdrLayout.size()));
420   SecHdrTableOffset = OutputStream->tell();
421   for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
422     Writer.write(static_cast<uint64_t>(-1));
423     Writer.write(static_cast<uint64_t>(-1));
424     Writer.write(static_cast<uint64_t>(-1));
425     Writer.write(static_cast<uint64_t>(-1));
426   }
427 }
428 
429 std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() {
430   auto &OFS = static_cast<raw_fd_ostream &>(*OutputStream);
431   uint64_t Saved = OutputStream->tell();
432 
433   // Set OutputStream to the location saved in SecHdrTableOffset.
434   if (OFS.seek(SecHdrTableOffset) == (uint64_t)-1)
435     return sampleprof_error::ostream_seek_unsupported;
436   support::endian::Writer Writer(*OutputStream, support::little);
437 
438   DenseMap<uint32_t, uint32_t> IndexMap;
439   for (uint32_t i = 0; i < SecHdrTable.size(); i++) {
440     IndexMap.insert({static_cast<uint32_t>(SecHdrTable[i].Type), i});
441   }
442 
443   // Write the section header table in the order specified in
444   // SectionHdrLayout. That is the sections order Reader will see.
445   // Note that the sections order in which Reader expects to read
446   // may be different from the order in which Writer is able to
447   // write, so we need to adjust the order in SecHdrTable to be
448   // consistent with SectionHdrLayout when we write SecHdrTable
449   // to the memory.
450   for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
451     uint32_t idx = IndexMap[static_cast<uint32_t>(SectionHdrLayout[i].Type)];
452     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Type));
453     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Flags));
454     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Offset));
455     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Size));
456   }
457 
458   // Reset OutputStream.
459   if (OFS.seek(Saved) == (uint64_t)-1)
460     return sampleprof_error::ostream_seek_unsupported;
461 
462   return sampleprof_error::success;
463 }
464 
465 std::error_code SampleProfileWriterExtBinaryBase::writeHeader(
466     const StringMap<FunctionSamples> &ProfileMap) {
467   auto &OS = *OutputStream;
468   FileStart = OS.tell();
469   writeMagicIdent(Format);
470 
471   allocSecHdrTable();
472   return sampleprof_error::success;
473 }
474 
475 std::error_code SampleProfileWriterCompactBinary::writeHeader(
476     const StringMap<FunctionSamples> &ProfileMap) {
477   support::endian::Writer Writer(*OutputStream, support::little);
478   if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap))
479     return EC;
480 
481   // Reserve a slot for the offset of function offset table. The slot will
482   // be populated with the offset of FuncOffsetTable later.
483   TableOffset = OutputStream->tell();
484   Writer.write(static_cast<uint64_t>(-2));
485   return sampleprof_error::success;
486 }
487 
488 std::error_code SampleProfileWriterBinary::writeSummary() {
489   auto &OS = *OutputStream;
490   encodeULEB128(Summary->getTotalCount(), OS);
491   encodeULEB128(Summary->getMaxCount(), OS);
492   encodeULEB128(Summary->getMaxFunctionCount(), OS);
493   encodeULEB128(Summary->getNumCounts(), OS);
494   encodeULEB128(Summary->getNumFunctions(), OS);
495   std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
496   encodeULEB128(Entries.size(), OS);
497   for (auto Entry : Entries) {
498     encodeULEB128(Entry.Cutoff, OS);
499     encodeULEB128(Entry.MinCount, OS);
500     encodeULEB128(Entry.NumCounts, OS);
501   }
502   return sampleprof_error::success;
503 }
504 std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
505   auto &OS = *OutputStream;
506 
507   if (std::error_code EC = writeNameIdx(S.getName()))
508     return EC;
509 
510   encodeULEB128(S.getTotalSamples(), OS);
511 
512   // Emit all the body samples.
513   encodeULEB128(S.getBodySamples().size(), OS);
514   for (const auto &I : S.getBodySamples()) {
515     LineLocation Loc = I.first;
516     const SampleRecord &Sample = I.second;
517     encodeULEB128(Loc.LineOffset, OS);
518     encodeULEB128(Loc.Discriminator, OS);
519     encodeULEB128(Sample.getSamples(), OS);
520     encodeULEB128(Sample.getCallTargets().size(), OS);
521     for (const auto &J : Sample.getSortedCallTargets()) {
522       StringRef Callee = J.first;
523       uint64_t CalleeSamples = J.second;
524       if (std::error_code EC = writeNameIdx(Callee))
525         return EC;
526       encodeULEB128(CalleeSamples, OS);
527     }
528   }
529 
530   // Recursively emit all the callsite samples.
531   uint64_t NumCallsites = 0;
532   for (const auto &J : S.getCallsiteSamples())
533     NumCallsites += J.second.size();
534   encodeULEB128(NumCallsites, OS);
535   for (const auto &J : S.getCallsiteSamples())
536     for (const auto &FS : J.second) {
537       LineLocation Loc = J.first;
538       const FunctionSamples &CalleeSamples = FS.second;
539       encodeULEB128(Loc.LineOffset, OS);
540       encodeULEB128(Loc.Discriminator, OS);
541       if (std::error_code EC = writeBody(CalleeSamples))
542         return EC;
543     }
544 
545   return sampleprof_error::success;
546 }
547 
548 /// Write samples of a top-level function to a binary file.
549 ///
550 /// \returns true if the samples were written successfully, false otherwise.
551 std::error_code
552 SampleProfileWriterBinary::writeSample(const FunctionSamples &S) {
553   encodeULEB128(S.getHeadSamples(), *OutputStream);
554   return writeBody(S);
555 }
556 
557 std::error_code
558 SampleProfileWriterCompactBinary::writeSample(const FunctionSamples &S) {
559   uint64_t Offset = OutputStream->tell();
560   StringRef Name = S.getName();
561   FuncOffsetTable[Name] = Offset;
562   encodeULEB128(S.getHeadSamples(), *OutputStream);
563   return writeBody(S);
564 }
565 
566 /// Create a sample profile file writer based on the specified format.
567 ///
568 /// \param Filename The file to create.
569 ///
570 /// \param Format Encoding format for the profile file.
571 ///
572 /// \returns an error code indicating the status of the created writer.
573 ErrorOr<std::unique_ptr<SampleProfileWriter>>
574 SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
575   std::error_code EC;
576   std::unique_ptr<raw_ostream> OS;
577   if (Format == SPF_Binary || Format == SPF_Ext_Binary ||
578       Format == SPF_Compact_Binary)
579     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None));
580   else
581     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_Text));
582   if (EC)
583     return EC;
584 
585   return create(OS, Format);
586 }
587 
588 /// Create a sample profile stream writer based on the specified format.
589 ///
590 /// \param OS The output stream to store the profile data to.
591 ///
592 /// \param Format Encoding format for the profile file.
593 ///
594 /// \returns an error code indicating the status of the created writer.
595 ErrorOr<std::unique_ptr<SampleProfileWriter>>
596 SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
597                             SampleProfileFormat Format) {
598   std::error_code EC;
599   std::unique_ptr<SampleProfileWriter> Writer;
600 
601   if (Format == SPF_Binary)
602     Writer.reset(new SampleProfileWriterRawBinary(OS));
603   else if (Format == SPF_Ext_Binary)
604     Writer.reset(new SampleProfileWriterExtBinary(OS));
605   else if (Format == SPF_Compact_Binary)
606     Writer.reset(new SampleProfileWriterCompactBinary(OS));
607   else if (Format == SPF_Text)
608     Writer.reset(new SampleProfileWriterText(OS));
609   else if (Format == SPF_GCC)
610     EC = sampleprof_error::unsupported_writing_format;
611   else
612     EC = sampleprof_error::unrecognized_format;
613 
614   if (EC)
615     return EC;
616 
617   Writer->Format = Format;
618   return std::move(Writer);
619 }
620 
621 void SampleProfileWriter::computeSummary(
622     const StringMap<FunctionSamples> &ProfileMap) {
623   SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
624   for (const auto &I : ProfileMap) {
625     const FunctionSamples &Profile = I.second;
626     Builder.addRecord(Profile);
627   }
628   Summary = Builder.getSummary();
629 }
630