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