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 SampleProfileWriterExtBinaryBase::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 SampleProfileWriterExtBinaryBase::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 SampleProfileWriterExtBinaryBase::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 SampleProfileWriterExtBinaryBase::writeNameTableSection(
186     const StringMap<FunctionSamples> &ProfileMap) {
187   for (const auto &I : ProfileMap) {
188     addName(I.first());
189     addNames(I.second);
190   }
191   if (auto EC = writeNameTable())
192     return EC;
193   return sampleprof_error::success;
194 }
195 
196 std::error_code
197 SampleProfileWriterExtBinaryBase::writeProfileSymbolListSection() {
198   if (ProfSymList && ProfSymList->size() > 0)
199     if (std::error_code EC = ProfSymList->write(*OutputStream))
200       return EC;
201 
202   return sampleprof_error::success;
203 }
204 
205 std::error_code SampleProfileWriterExtBinaryBase::writeOneSection(
206     SecType Type, const StringMap<FunctionSamples> &ProfileMap) {
207   // The setting of SecFlagCompress should happen before markSectionStart.
208   if (Type == SecProfileSymbolList && ProfSymList && ProfSymList->toCompress())
209     setToCompressSection(SecProfileSymbolList);
210 
211   uint64_t SectionStart = markSectionStart(Type);
212   switch (Type) {
213   case SecProfSummary:
214     computeSummary(ProfileMap);
215     if (auto EC = writeSummary())
216       return EC;
217     break;
218   case SecNameTable:
219     if (auto EC = writeNameTableSection(ProfileMap))
220       return EC;
221     break;
222   case SecLBRProfile:
223     SecLBRProfileStart = OutputStream->tell();
224     if (std::error_code EC = writeFuncProfiles(ProfileMap))
225       return EC;
226     break;
227   case SecFuncOffsetTable:
228     if (auto EC = writeFuncOffsetTable())
229       return EC;
230     break;
231   case SecProfileSymbolList:
232     if (auto EC = writeProfileSymbolListSection())
233       return EC;
234     break;
235   default:
236     if (auto EC = writeCustomSection(Type))
237       return EC;
238     break;
239   }
240   if (std::error_code EC = addNewSection(Type, SectionStart))
241     return EC;
242   return sampleprof_error::success;
243 }
244 
245 std::error_code SampleProfileWriterExtBinary::writeSections(
246     const StringMap<FunctionSamples> &ProfileMap) {
247   if (auto EC = writeOneSection(SecProfSummary, ProfileMap))
248     return EC;
249   if (auto EC = writeOneSection(SecNameTable, ProfileMap))
250     return EC;
251   if (auto EC = writeOneSection(SecLBRProfile, ProfileMap))
252     return EC;
253   if (auto EC = writeOneSection(SecProfileSymbolList, ProfileMap))
254     return EC;
255   if (auto EC = writeOneSection(SecFuncOffsetTable, ProfileMap))
256     return EC;
257   return sampleprof_error::success;
258 }
259 
260 std::error_code SampleProfileWriterCompactBinary::write(
261     const StringMap<FunctionSamples> &ProfileMap) {
262   if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
263     return EC;
264   if (std::error_code EC = writeFuncOffsetTable())
265     return EC;
266   return sampleprof_error::success;
267 }
268 
269 /// Write samples to a text file.
270 ///
271 /// Note: it may be tempting to implement this in terms of
272 /// FunctionSamples::print().  Please don't.  The dump functionality is intended
273 /// for debugging and has no specified form.
274 ///
275 /// The format used here is more structured and deliberate because
276 /// it needs to be parsed by the SampleProfileReaderText class.
277 std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) {
278   auto &OS = *OutputStream;
279   OS << S.getName() << ":" << S.getTotalSamples();
280   if (Indent == 0)
281     OS << ":" << S.getHeadSamples();
282   OS << "\n";
283 
284   SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
285   for (const auto &I : SortedSamples.get()) {
286     LineLocation Loc = I->first;
287     const SampleRecord &Sample = I->second;
288     OS.indent(Indent + 1);
289     if (Loc.Discriminator == 0)
290       OS << Loc.LineOffset << ": ";
291     else
292       OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
293 
294     OS << Sample.getSamples();
295 
296     for (const auto &J : Sample.getSortedCallTargets())
297       OS << " " << J.first << ":" << J.second;
298     OS << "\n";
299   }
300 
301   SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
302       S.getCallsiteSamples());
303   Indent += 1;
304   for (const auto &I : SortedCallsiteSamples.get())
305     for (const auto &FS : I->second) {
306       LineLocation Loc = I->first;
307       const FunctionSamples &CalleeSamples = FS.second;
308       OS.indent(Indent);
309       if (Loc.Discriminator == 0)
310         OS << Loc.LineOffset << ": ";
311       else
312         OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
313       if (std::error_code EC = writeSample(CalleeSamples))
314         return EC;
315     }
316   Indent -= 1;
317 
318   return sampleprof_error::success;
319 }
320 
321 std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
322   const auto &ret = NameTable.find(FName);
323   if (ret == NameTable.end())
324     return sampleprof_error::truncated_name_table;
325   encodeULEB128(ret->second, *OutputStream);
326   return sampleprof_error::success;
327 }
328 
329 void SampleProfileWriterBinary::addName(StringRef FName) {
330   NameTable.insert(std::make_pair(FName, 0));
331 }
332 
333 void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
334   // Add all the names in indirect call targets.
335   for (const auto &I : S.getBodySamples()) {
336     const SampleRecord &Sample = I.second;
337     for (const auto &J : Sample.getCallTargets())
338       addName(J.first());
339   }
340 
341   // Recursively add all the names for inlined callsites.
342   for (const auto &J : S.getCallsiteSamples())
343     for (const auto &FS : J.second) {
344       const FunctionSamples &CalleeSamples = FS.second;
345       addName(CalleeSamples.getName());
346       addNames(CalleeSamples);
347     }
348 }
349 
350 void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) {
351   // Sort the names to make NameTable deterministic.
352   for (const auto &I : NameTable)
353     V.insert(I.first);
354   int i = 0;
355   for (const StringRef &N : V)
356     NameTable[N] = i++;
357 }
358 
359 std::error_code SampleProfileWriterBinary::writeNameTable() {
360   auto &OS = *OutputStream;
361   std::set<StringRef> V;
362   stablizeNameTable(V);
363 
364   // Write out the name table.
365   encodeULEB128(NameTable.size(), OS);
366   for (auto N : V) {
367     OS << N;
368     encodeULEB128(0, OS);
369   }
370   return sampleprof_error::success;
371 }
372 
373 std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
374   auto &OS = *OutputStream;
375 
376   // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
377   auto &OFS = static_cast<raw_fd_ostream &>(OS);
378   uint64_t FuncOffsetTableStart = OS.tell();
379   if (OFS.seek(TableOffset) == (uint64_t)-1)
380     return sampleprof_error::ostream_seek_unsupported;
381   support::endian::Writer Writer(*OutputStream, support::little);
382   Writer.write(FuncOffsetTableStart);
383   if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1)
384     return sampleprof_error::ostream_seek_unsupported;
385 
386   // Write out the table size.
387   encodeULEB128(FuncOffsetTable.size(), OS);
388 
389   // Write out FuncOffsetTable.
390   for (auto entry : FuncOffsetTable) {
391     writeNameIdx(entry.first);
392     encodeULEB128(entry.second, OS);
393   }
394   return sampleprof_error::success;
395 }
396 
397 std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
398   auto &OS = *OutputStream;
399   std::set<StringRef> V;
400   stablizeNameTable(V);
401 
402   // Write out the name table.
403   encodeULEB128(NameTable.size(), OS);
404   for (auto N : V) {
405     encodeULEB128(MD5Hash(N), OS);
406   }
407   return sampleprof_error::success;
408 }
409 
410 std::error_code
411 SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) {
412   auto &OS = *OutputStream;
413   // Write file magic identifier.
414   encodeULEB128(SPMagic(Format), OS);
415   encodeULEB128(SPVersion(), OS);
416   return sampleprof_error::success;
417 }
418 
419 std::error_code SampleProfileWriterBinary::writeHeader(
420     const StringMap<FunctionSamples> &ProfileMap) {
421   writeMagicIdent(Format);
422 
423   computeSummary(ProfileMap);
424   if (auto EC = writeSummary())
425     return EC;
426 
427   // Generate the name table for all the functions referenced in the profile.
428   for (const auto &I : ProfileMap) {
429     addName(I.first());
430     addNames(I.second);
431   }
432 
433   writeNameTable();
434   return sampleprof_error::success;
435 }
436 
437 void SampleProfileWriterExtBinaryBase::setToCompressAllSections() {
438   for (auto &Entry : SectionHdrLayout)
439     addSecFlag(Entry, SecCommonFlags::SecFlagCompress);
440 }
441 
442 void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) {
443   addSectionFlag(Type, SecCommonFlags::SecFlagCompress);
444 }
445 
446 void SampleProfileWriterExtBinaryBase::allocSecHdrTable() {
447   support::endian::Writer Writer(*OutputStream, support::little);
448 
449   Writer.write(static_cast<uint64_t>(SectionHdrLayout.size()));
450   SecHdrTableOffset = OutputStream->tell();
451   for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
452     Writer.write(static_cast<uint64_t>(-1));
453     Writer.write(static_cast<uint64_t>(-1));
454     Writer.write(static_cast<uint64_t>(-1));
455     Writer.write(static_cast<uint64_t>(-1));
456   }
457 }
458 
459 std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() {
460   auto &OFS = static_cast<raw_fd_ostream &>(*OutputStream);
461   uint64_t Saved = OutputStream->tell();
462 
463   // Set OutputStream to the location saved in SecHdrTableOffset.
464   if (OFS.seek(SecHdrTableOffset) == (uint64_t)-1)
465     return sampleprof_error::ostream_seek_unsupported;
466   support::endian::Writer Writer(*OutputStream, support::little);
467 
468   DenseMap<uint32_t, uint32_t> IndexMap;
469   for (uint32_t i = 0; i < SecHdrTable.size(); i++) {
470     IndexMap.insert({static_cast<uint32_t>(SecHdrTable[i].Type), i});
471   }
472 
473   // Write the section header table in the order specified in
474   // SectionHdrLayout. That is the sections order Reader will see.
475   // Note that the sections order in which Reader expects to read
476   // may be different from the order in which Writer is able to
477   // write, so we need to adjust the order in SecHdrTable to be
478   // consistent with SectionHdrLayout when we write SecHdrTable
479   // to the memory.
480   for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) {
481     uint32_t idx = IndexMap[static_cast<uint32_t>(SectionHdrLayout[i].Type)];
482     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Type));
483     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Flags));
484     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Offset));
485     Writer.write(static_cast<uint64_t>(SecHdrTable[idx].Size));
486   }
487 
488   // Reset OutputStream.
489   if (OFS.seek(Saved) == (uint64_t)-1)
490     return sampleprof_error::ostream_seek_unsupported;
491 
492   return sampleprof_error::success;
493 }
494 
495 std::error_code SampleProfileWriterExtBinaryBase::writeHeader(
496     const StringMap<FunctionSamples> &ProfileMap) {
497   auto &OS = *OutputStream;
498   FileStart = OS.tell();
499   writeMagicIdent(Format);
500 
501   allocSecHdrTable();
502   return sampleprof_error::success;
503 }
504 
505 std::error_code SampleProfileWriterCompactBinary::writeHeader(
506     const StringMap<FunctionSamples> &ProfileMap) {
507   support::endian::Writer Writer(*OutputStream, support::little);
508   if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap))
509     return EC;
510 
511   // Reserve a slot for the offset of function offset table. The slot will
512   // be populated with the offset of FuncOffsetTable later.
513   TableOffset = OutputStream->tell();
514   Writer.write(static_cast<uint64_t>(-2));
515   return sampleprof_error::success;
516 }
517 
518 std::error_code SampleProfileWriterBinary::writeSummary() {
519   auto &OS = *OutputStream;
520   encodeULEB128(Summary->getTotalCount(), OS);
521   encodeULEB128(Summary->getMaxCount(), OS);
522   encodeULEB128(Summary->getMaxFunctionCount(), OS);
523   encodeULEB128(Summary->getNumCounts(), OS);
524   encodeULEB128(Summary->getNumFunctions(), OS);
525   std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
526   encodeULEB128(Entries.size(), OS);
527   for (auto Entry : Entries) {
528     encodeULEB128(Entry.Cutoff, OS);
529     encodeULEB128(Entry.MinCount, OS);
530     encodeULEB128(Entry.NumCounts, OS);
531   }
532   return sampleprof_error::success;
533 }
534 std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
535   auto &OS = *OutputStream;
536 
537   if (std::error_code EC = writeNameIdx(S.getName()))
538     return EC;
539 
540   encodeULEB128(S.getTotalSamples(), OS);
541 
542   // Emit all the body samples.
543   encodeULEB128(S.getBodySamples().size(), OS);
544   for (const auto &I : S.getBodySamples()) {
545     LineLocation Loc = I.first;
546     const SampleRecord &Sample = I.second;
547     encodeULEB128(Loc.LineOffset, OS);
548     encodeULEB128(Loc.Discriminator, OS);
549     encodeULEB128(Sample.getSamples(), OS);
550     encodeULEB128(Sample.getCallTargets().size(), OS);
551     for (const auto &J : Sample.getSortedCallTargets()) {
552       StringRef Callee = J.first;
553       uint64_t CalleeSamples = J.second;
554       if (std::error_code EC = writeNameIdx(Callee))
555         return EC;
556       encodeULEB128(CalleeSamples, OS);
557     }
558   }
559 
560   // Recursively emit all the callsite samples.
561   uint64_t NumCallsites = 0;
562   for (const auto &J : S.getCallsiteSamples())
563     NumCallsites += J.second.size();
564   encodeULEB128(NumCallsites, OS);
565   for (const auto &J : S.getCallsiteSamples())
566     for (const auto &FS : J.second) {
567       LineLocation Loc = J.first;
568       const FunctionSamples &CalleeSamples = FS.second;
569       encodeULEB128(Loc.LineOffset, OS);
570       encodeULEB128(Loc.Discriminator, OS);
571       if (std::error_code EC = writeBody(CalleeSamples))
572         return EC;
573     }
574 
575   return sampleprof_error::success;
576 }
577 
578 /// Write samples of a top-level function to a binary file.
579 ///
580 /// \returns true if the samples were written successfully, false otherwise.
581 std::error_code
582 SampleProfileWriterBinary::writeSample(const FunctionSamples &S) {
583   encodeULEB128(S.getHeadSamples(), *OutputStream);
584   return writeBody(S);
585 }
586 
587 std::error_code
588 SampleProfileWriterCompactBinary::writeSample(const FunctionSamples &S) {
589   uint64_t Offset = OutputStream->tell();
590   StringRef Name = S.getName();
591   FuncOffsetTable[Name] = Offset;
592   encodeULEB128(S.getHeadSamples(), *OutputStream);
593   return writeBody(S);
594 }
595 
596 /// Create a sample profile file writer based on the specified format.
597 ///
598 /// \param Filename The file to create.
599 ///
600 /// \param Format Encoding format for the profile file.
601 ///
602 /// \returns an error code indicating the status of the created writer.
603 ErrorOr<std::unique_ptr<SampleProfileWriter>>
604 SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
605   std::error_code EC;
606   std::unique_ptr<raw_ostream> OS;
607   if (Format == SPF_Binary || Format == SPF_Ext_Binary ||
608       Format == SPF_Compact_Binary)
609     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None));
610   else
611     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_Text));
612   if (EC)
613     return EC;
614 
615   return create(OS, Format);
616 }
617 
618 /// Create a sample profile stream writer based on the specified format.
619 ///
620 /// \param OS The output stream to store the profile data to.
621 ///
622 /// \param Format Encoding format for the profile file.
623 ///
624 /// \returns an error code indicating the status of the created writer.
625 ErrorOr<std::unique_ptr<SampleProfileWriter>>
626 SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
627                             SampleProfileFormat Format) {
628   std::error_code EC;
629   std::unique_ptr<SampleProfileWriter> Writer;
630 
631   if (Format == SPF_Binary)
632     Writer.reset(new SampleProfileWriterRawBinary(OS));
633   else if (Format == SPF_Ext_Binary)
634     Writer.reset(new SampleProfileWriterExtBinary(OS));
635   else if (Format == SPF_Compact_Binary)
636     Writer.reset(new SampleProfileWriterCompactBinary(OS));
637   else if (Format == SPF_Text)
638     Writer.reset(new SampleProfileWriterText(OS));
639   else if (Format == SPF_GCC)
640     EC = sampleprof_error::unsupported_writing_format;
641   else
642     EC = sampleprof_error::unrecognized_format;
643 
644   if (EC)
645     return EC;
646 
647   Writer->Format = Format;
648   return std::move(Writer);
649 }
650 
651 void SampleProfileWriter::computeSummary(
652     const StringMap<FunctionSamples> &ProfileMap) {
653   SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
654   for (const auto &I : ProfileMap) {
655     const FunctionSamples &Profile = I.second;
656     Builder.addRecord(Profile);
657   }
658   Summary = Builder.getSummary();
659 }
660