1 //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===//
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
7 //===----------------------------------------------------------------------===//
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.
15 // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the
18 //===----------------------------------------------------------------------===//
20 #include "llvm/ProfileData/SampleProfWriter.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/ADT/StringSet.h"
23 #include "llvm/ProfileData/ProfileCommon.h"
24 #include "llvm/ProfileData/SampleProf.h"
25 #include "llvm/Support/Compression.h"
26 #include "llvm/Support/Endian.h"
27 #include "llvm/Support/EndianStream.h"
28 #include "llvm/Support/ErrorOr.h"
29 #include "llvm/Support/FileSystem.h"
30 #include "llvm/Support/LEB128.h"
31 #include "llvm/Support/MD5.h"
32 #include "llvm/Support/raw_ostream.h"
37 #include <system_error>
42 using namespace sampleprof
;
44 std::error_code
SampleProfileWriter::writeFuncProfiles(
45 const StringMap
<FunctionSamples
> &ProfileMap
) {
46 std::vector
<NameFunctionSamples
> V
;
47 sortFuncProfiles(ProfileMap
, V
);
48 for (const auto &I
: V
) {
49 if (std::error_code EC
= writeSample(*I
.second
))
52 return sampleprof_error::success
;
56 SampleProfileWriter::write(const StringMap
<FunctionSamples
> &ProfileMap
) {
57 if (std::error_code EC
= writeHeader(ProfileMap
))
60 if (std::error_code EC
= writeFuncProfiles(ProfileMap
))
63 return sampleprof_error::success
;
66 /// Return the current position and prepare to use it as the start
67 /// position of a section given the section type \p Type and its position
68 /// \p LayoutIdx in SectionHdrLayout.
70 SampleProfileWriterExtBinaryBase::markSectionStart(SecType Type
,
72 uint64_t SectionStart
= OutputStream
->tell();
73 assert(LayoutIdx
< SectionHdrLayout
.size() && "LayoutIdx out of range");
74 const auto &Entry
= SectionHdrLayout
[LayoutIdx
];
75 assert(Entry
.Type
== Type
&& "Unexpected section type");
76 // Use LocalBuf as a temporary output for writting data.
77 if (hasSecFlag(Entry
, SecCommonFlags::SecFlagCompress
))
78 LocalBufStream
.swap(OutputStream
);
82 std::error_code
SampleProfileWriterExtBinaryBase::compressAndOutput() {
83 if (!llvm::zlib::isAvailable())
84 return sampleprof_error::zlib_unavailable
;
85 std::string
&UncompressedStrings
=
86 static_cast<raw_string_ostream
*>(LocalBufStream
.get())->str();
87 if (UncompressedStrings
.size() == 0)
88 return sampleprof_error::success
;
89 auto &OS
= *OutputStream
;
90 SmallString
<128> CompressedStrings
;
91 llvm::Error E
= zlib::compress(UncompressedStrings
, CompressedStrings
,
92 zlib::BestSizeCompression
);
94 return sampleprof_error::compress_failed
;
95 encodeULEB128(UncompressedStrings
.size(), OS
);
96 encodeULEB128(CompressedStrings
.size(), OS
);
97 OS
<< CompressedStrings
.str();
98 UncompressedStrings
.clear();
99 return sampleprof_error::success
;
102 /// Add a new section into section header table given the section type
103 /// \p Type, its position \p LayoutIdx in SectionHdrLayout and the
104 /// location \p SectionStart where the section should be written to.
105 std::error_code
SampleProfileWriterExtBinaryBase::addNewSection(
106 SecType Type
, uint32_t LayoutIdx
, uint64_t SectionStart
) {
107 assert(LayoutIdx
< SectionHdrLayout
.size() && "LayoutIdx out of range");
108 const auto &Entry
= SectionHdrLayout
[LayoutIdx
];
109 assert(Entry
.Type
== Type
&& "Unexpected section type");
110 if (hasSecFlag(Entry
, SecCommonFlags::SecFlagCompress
)) {
111 LocalBufStream
.swap(OutputStream
);
112 if (std::error_code EC
= compressAndOutput())
115 SecHdrTable
.push_back({Type
, Entry
.Flags
, SectionStart
- FileStart
,
116 OutputStream
->tell() - SectionStart
, LayoutIdx
});
117 return sampleprof_error::success
;
120 std::error_code
SampleProfileWriterExtBinaryBase::write(
121 const StringMap
<FunctionSamples
> &ProfileMap
) {
122 if (std::error_code EC
= writeHeader(ProfileMap
))
125 std::string LocalBuf
;
126 LocalBufStream
= std::make_unique
<raw_string_ostream
>(LocalBuf
);
127 if (std::error_code EC
= writeSections(ProfileMap
))
130 if (std::error_code EC
= writeSecHdrTable())
133 return sampleprof_error::success
;
137 SampleProfileWriterExtBinaryBase::writeSample(const FunctionSamples
&S
) {
138 uint64_t Offset
= OutputStream
->tell();
139 StringRef Name
= S
.getNameWithContext();
140 FuncOffsetTable
[Name
] = Offset
- SecLBRProfileStart
;
141 encodeULEB128(S
.getHeadSamples(), *OutputStream
);
145 std::error_code
SampleProfileWriterExtBinaryBase::writeFuncOffsetTable() {
146 auto &OS
= *OutputStream
;
148 // Write out the table size.
149 encodeULEB128(FuncOffsetTable
.size(), OS
);
151 // Write out FuncOffsetTable.
152 for (auto Entry
: FuncOffsetTable
) {
153 if (std::error_code EC
=
154 writeNameIdx(Entry
.first
, FunctionSamples::ProfileIsCS
))
156 encodeULEB128(Entry
.second
, OS
);
158 FuncOffsetTable
.clear();
159 return sampleprof_error::success
;
162 std::error_code
SampleProfileWriterExtBinaryBase::writeFuncMetadata(
163 const StringMap
<FunctionSamples
> &Profiles
) {
164 if (!FunctionSamples::ProfileIsProbeBased
&& !FunctionSamples::ProfileIsCS
)
165 return sampleprof_error::success
;
166 auto &OS
= *OutputStream
;
167 for (const auto &Entry
: Profiles
) {
168 if (std::error_code EC
= writeNameIdx(Entry
.second
.getNameWithContext(),
169 FunctionSamples::ProfileIsCS
))
171 if (FunctionSamples::ProfileIsProbeBased
)
172 encodeULEB128(Entry
.second
.getFunctionHash(), OS
);
173 if (FunctionSamples::ProfileIsCS
)
174 encodeULEB128(Entry
.second
.getContext().getAllAttributes(), OS
);
176 return sampleprof_error::success
;
179 std::error_code
SampleProfileWriterExtBinaryBase::writeNameTable() {
181 return SampleProfileWriterBinary::writeNameTable();
183 auto &OS
= *OutputStream
;
184 std::set
<StringRef
> V
;
185 stablizeNameTable(V
);
187 // Write out the MD5 name table. We wrote unencoded MD5 so reader can
188 // retrieve the name using the name index without having to read the
190 encodeULEB128(NameTable
.size(), OS
);
191 support::endian::Writer
Writer(OS
, support::little
);
193 Writer
.write(MD5Hash(N
));
194 return sampleprof_error::success
;
197 std::error_code
SampleProfileWriterExtBinaryBase::writeNameTableSection(
198 const StringMap
<FunctionSamples
> &ProfileMap
) {
199 for (const auto &I
: ProfileMap
) {
200 assert(I
.first() == I
.second
.getNameWithContext() &&
201 "Inconsistent profile map");
202 addName(I
.second
.getNameWithContext(), FunctionSamples::ProfileIsCS
);
206 // If NameTable contains ".__uniq." suffix, set SecFlagUniqSuffix flag
207 // so compiler won't strip the suffix during profile matching after
208 // seeing the flag in the profile.
209 for (const auto &I
: NameTable
) {
210 if (I
.first
.find(FunctionSamples::UniqSuffix
) != StringRef::npos
) {
211 addSectionFlag(SecNameTable
, SecNameTableFlags::SecFlagUniqSuffix
);
216 if (auto EC
= writeNameTable())
218 return sampleprof_error::success
;
222 SampleProfileWriterExtBinaryBase::writeProfileSymbolListSection() {
223 if (ProfSymList
&& ProfSymList
->size() > 0)
224 if (std::error_code EC
= ProfSymList
->write(*OutputStream
))
227 return sampleprof_error::success
;
230 std::error_code
SampleProfileWriterExtBinaryBase::writeOneSection(
231 SecType Type
, uint32_t LayoutIdx
,
232 const StringMap
<FunctionSamples
> &ProfileMap
) {
233 // The setting of SecFlagCompress should happen before markSectionStart.
234 if (Type
== SecProfileSymbolList
&& ProfSymList
&& ProfSymList
->toCompress())
235 setToCompressSection(SecProfileSymbolList
);
236 if (Type
== SecFuncMetadata
&& FunctionSamples::ProfileIsProbeBased
)
237 addSectionFlag(SecFuncMetadata
, SecFuncMetadataFlags::SecFlagIsProbeBased
);
238 if (Type
== SecProfSummary
&& FunctionSamples::ProfileIsCS
)
239 addSectionFlag(SecProfSummary
, SecProfSummaryFlags::SecFlagFullContext
);
240 if (Type
== SecFuncMetadata
&& FunctionSamples::ProfileIsCS
)
241 addSectionFlag(SecFuncMetadata
, SecFuncMetadataFlags::SecFlagHasAttribute
);
242 if (Type
== SecProfSummary
&& FunctionSamples::ProfileIsFS
)
243 addSectionFlag(SecProfSummary
, SecProfSummaryFlags::SecFlagFSDiscriminator
);
245 uint64_t SectionStart
= markSectionStart(Type
, LayoutIdx
);
248 computeSummary(ProfileMap
);
249 if (auto EC
= writeSummary())
253 if (auto EC
= writeNameTableSection(ProfileMap
))
257 SecLBRProfileStart
= OutputStream
->tell();
258 if (std::error_code EC
= writeFuncProfiles(ProfileMap
))
261 case SecFuncOffsetTable
:
262 if (auto EC
= writeFuncOffsetTable())
265 case SecFuncMetadata
:
266 if (std::error_code EC
= writeFuncMetadata(ProfileMap
))
269 case SecProfileSymbolList
:
270 if (auto EC
= writeProfileSymbolListSection())
274 if (auto EC
= writeCustomSection(Type
))
278 if (std::error_code EC
= addNewSection(Type
, LayoutIdx
, SectionStart
))
280 return sampleprof_error::success
;
283 std::error_code
SampleProfileWriterExtBinary::writeDefaultLayout(
284 const StringMap
<FunctionSamples
> &ProfileMap
) {
285 // The const indices passed to writeOneSection below are specifying the
286 // positions of the sections in SectionHdrLayout. Look at
287 // initSectionHdrLayout to find out where each section is located in
289 if (auto EC
= writeOneSection(SecProfSummary
, 0, ProfileMap
))
291 if (auto EC
= writeOneSection(SecNameTable
, 1, ProfileMap
))
293 if (auto EC
= writeOneSection(SecLBRProfile
, 3, ProfileMap
))
295 if (auto EC
= writeOneSection(SecProfileSymbolList
, 4, ProfileMap
))
297 if (auto EC
= writeOneSection(SecFuncOffsetTable
, 2, ProfileMap
))
299 if (auto EC
= writeOneSection(SecFuncMetadata
, 5, ProfileMap
))
301 return sampleprof_error::success
;
305 splitProfileMapToTwo(const StringMap
<FunctionSamples
> &ProfileMap
,
306 StringMap
<FunctionSamples
> &ContextProfileMap
,
307 StringMap
<FunctionSamples
> &NoContextProfileMap
) {
308 for (const auto &I
: ProfileMap
) {
309 if (I
.second
.getCallsiteSamples().size())
310 ContextProfileMap
.insert({I
.first(), I
.second
});
312 NoContextProfileMap
.insert({I
.first(), I
.second
});
316 std::error_code
SampleProfileWriterExtBinary::writeCtxSplitLayout(
317 const StringMap
<FunctionSamples
> &ProfileMap
) {
318 StringMap
<FunctionSamples
> ContextProfileMap
, NoContextProfileMap
;
319 splitProfileMapToTwo(ProfileMap
, ContextProfileMap
, NoContextProfileMap
);
321 if (auto EC
= writeOneSection(SecProfSummary
, 0, ProfileMap
))
323 if (auto EC
= writeOneSection(SecNameTable
, 1, ProfileMap
))
325 if (auto EC
= writeOneSection(SecLBRProfile
, 3, ContextProfileMap
))
327 if (auto EC
= writeOneSection(SecFuncOffsetTable
, 2, ContextProfileMap
))
329 // Mark the section to have no context. Note section flag needs to be set
330 // before writing the section.
331 addSectionFlag(5, SecCommonFlags::SecFlagFlat
);
332 if (auto EC
= writeOneSection(SecLBRProfile
, 5, NoContextProfileMap
))
334 // Mark the section to have no context. Note section flag needs to be set
335 // before writing the section.
336 addSectionFlag(4, SecCommonFlags::SecFlagFlat
);
337 if (auto EC
= writeOneSection(SecFuncOffsetTable
, 4, NoContextProfileMap
))
339 if (auto EC
= writeOneSection(SecProfileSymbolList
, 6, ProfileMap
))
341 if (auto EC
= writeOneSection(SecFuncMetadata
, 7, ProfileMap
))
344 return sampleprof_error::success
;
347 std::error_code
SampleProfileWriterExtBinary::writeSections(
348 const StringMap
<FunctionSamples
> &ProfileMap
) {
350 if (SecLayout
== DefaultLayout
)
351 EC
= writeDefaultLayout(ProfileMap
);
352 else if (SecLayout
== CtxSplitLayout
)
353 EC
= writeCtxSplitLayout(ProfileMap
);
355 llvm_unreachable("Unsupported layout");
359 std::error_code
SampleProfileWriterCompactBinary::write(
360 const StringMap
<FunctionSamples
> &ProfileMap
) {
361 if (std::error_code EC
= SampleProfileWriter::write(ProfileMap
))
363 if (std::error_code EC
= writeFuncOffsetTable())
365 return sampleprof_error::success
;
368 /// Write samples to a text file.
370 /// Note: it may be tempting to implement this in terms of
371 /// FunctionSamples::print(). Please don't. The dump functionality is intended
372 /// for debugging and has no specified form.
374 /// The format used here is more structured and deliberate because
375 /// it needs to be parsed by the SampleProfileReaderText class.
376 std::error_code
SampleProfileWriterText::writeSample(const FunctionSamples
&S
) {
377 auto &OS
= *OutputStream
;
378 if (FunctionSamples::ProfileIsCS
)
379 OS
<< "[" << S
.getNameWithContext() << "]:" << S
.getTotalSamples();
381 OS
<< S
.getName() << ":" << S
.getTotalSamples();
384 OS
<< ":" << S
.getHeadSamples();
387 SampleSorter
<LineLocation
, SampleRecord
> SortedSamples(S
.getBodySamples());
388 for (const auto &I
: SortedSamples
.get()) {
389 LineLocation Loc
= I
->first
;
390 const SampleRecord
&Sample
= I
->second
;
391 OS
.indent(Indent
+ 1);
392 if (Loc
.Discriminator
== 0)
393 OS
<< Loc
.LineOffset
<< ": ";
395 OS
<< Loc
.LineOffset
<< "." << Loc
.Discriminator
<< ": ";
397 OS
<< Sample
.getSamples();
399 for (const auto &J
: Sample
.getSortedCallTargets())
400 OS
<< " " << J
.first
<< ":" << J
.second
;
404 SampleSorter
<LineLocation
, FunctionSamplesMap
> SortedCallsiteSamples(
405 S
.getCallsiteSamples());
407 for (const auto &I
: SortedCallsiteSamples
.get())
408 for (const auto &FS
: I
->second
) {
409 LineLocation Loc
= I
->first
;
410 const FunctionSamples
&CalleeSamples
= FS
.second
;
412 if (Loc
.Discriminator
== 0)
413 OS
<< Loc
.LineOffset
<< ": ";
415 OS
<< Loc
.LineOffset
<< "." << Loc
.Discriminator
<< ": ";
416 if (std::error_code EC
= writeSample(CalleeSamples
))
422 if (FunctionSamples::ProfileIsProbeBased
) {
423 OS
.indent(Indent
+ 1);
424 OS
<< "!CFGChecksum: " << S
.getFunctionHash() << "\n";
426 if (FunctionSamples::ProfileIsCS
) {
427 OS
.indent(Indent
+ 1);
428 OS
<< "!Attributes: " << S
.getContext().getAllAttributes() << "\n";
432 return sampleprof_error::success
;
435 std::error_code
SampleProfileWriterBinary::writeNameIdx(StringRef FName
,
436 bool IsContextName
) {
437 std::string BracketedName
;
439 BracketedName
= "[" + FName
.str() + "]";
440 FName
= StringRef(BracketedName
);
443 const auto &Ret
= NameTable
.find(FName
);
444 if (Ret
== NameTable
.end())
445 return sampleprof_error::truncated_name_table
;
446 encodeULEB128(Ret
->second
, *OutputStream
);
447 return sampleprof_error::success
;
450 void SampleProfileWriterBinary::addName(StringRef FName
, bool IsContextName
) {
452 auto It
= BracketedContextStr
.insert("[" + FName
.str() + "]");
453 FName
= StringRef(*It
.first
);
455 NameTable
.insert(std::make_pair(FName
, 0));
458 void SampleProfileWriterBinary::addNames(const FunctionSamples
&S
) {
459 // Add all the names in indirect call targets.
460 for (const auto &I
: S
.getBodySamples()) {
461 const SampleRecord
&Sample
= I
.second
;
462 for (const auto &J
: Sample
.getCallTargets())
466 // Recursively add all the names for inlined callsites.
467 for (const auto &J
: S
.getCallsiteSamples())
468 for (const auto &FS
: J
.second
) {
469 const FunctionSamples
&CalleeSamples
= FS
.second
;
470 addName(CalleeSamples
.getName());
471 addNames(CalleeSamples
);
475 void SampleProfileWriterBinary::stablizeNameTable(std::set
<StringRef
> &V
) {
476 // Sort the names to make NameTable deterministic.
477 for (const auto &I
: NameTable
)
480 for (const StringRef
&N
: V
)
484 std::error_code
SampleProfileWriterBinary::writeNameTable() {
485 auto &OS
= *OutputStream
;
486 std::set
<StringRef
> V
;
487 stablizeNameTable(V
);
489 // Write out the name table.
490 encodeULEB128(NameTable
.size(), OS
);
493 encodeULEB128(0, OS
);
495 return sampleprof_error::success
;
498 std::error_code
SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
499 auto &OS
= *OutputStream
;
501 // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
502 auto &OFS
= static_cast<raw_fd_ostream
&>(OS
);
503 uint64_t FuncOffsetTableStart
= OS
.tell();
504 if (OFS
.seek(TableOffset
) == (uint64_t)-1)
505 return sampleprof_error::ostream_seek_unsupported
;
506 support::endian::Writer
Writer(*OutputStream
, support::little
);
507 Writer
.write(FuncOffsetTableStart
);
508 if (OFS
.seek(FuncOffsetTableStart
) == (uint64_t)-1)
509 return sampleprof_error::ostream_seek_unsupported
;
511 // Write out the table size.
512 encodeULEB128(FuncOffsetTable
.size(), OS
);
514 // Write out FuncOffsetTable.
515 for (auto Entry
: FuncOffsetTable
) {
516 if (std::error_code EC
=
517 writeNameIdx(Entry
.first
, FunctionSamples::ProfileIsCS
))
519 encodeULEB128(Entry
.second
, OS
);
521 return sampleprof_error::success
;
524 std::error_code
SampleProfileWriterCompactBinary::writeNameTable() {
525 auto &OS
= *OutputStream
;
526 std::set
<StringRef
> V
;
527 stablizeNameTable(V
);
529 // Write out the name table.
530 encodeULEB128(NameTable
.size(), OS
);
532 encodeULEB128(MD5Hash(N
), OS
);
534 return sampleprof_error::success
;
538 SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format
) {
539 auto &OS
= *OutputStream
;
540 // Write file magic identifier.
541 encodeULEB128(SPMagic(Format
), OS
);
542 encodeULEB128(SPVersion(), OS
);
543 return sampleprof_error::success
;
546 std::error_code
SampleProfileWriterBinary::writeHeader(
547 const StringMap
<FunctionSamples
> &ProfileMap
) {
548 writeMagicIdent(Format
);
550 computeSummary(ProfileMap
);
551 if (auto EC
= writeSummary())
554 // Generate the name table for all the functions referenced in the profile.
555 for (const auto &I
: ProfileMap
) {
556 assert(I
.first() == I
.second
.getNameWithContext() &&
557 "Inconsistent profile map");
558 addName(I
.first(), FunctionSamples::ProfileIsCS
);
563 return sampleprof_error::success
;
566 void SampleProfileWriterExtBinaryBase::setToCompressAllSections() {
567 for (auto &Entry
: SectionHdrLayout
)
568 addSecFlag(Entry
, SecCommonFlags::SecFlagCompress
);
571 void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type
) {
572 addSectionFlag(Type
, SecCommonFlags::SecFlagCompress
);
575 void SampleProfileWriterExtBinaryBase::allocSecHdrTable() {
576 support::endian::Writer
Writer(*OutputStream
, support::little
);
578 Writer
.write(static_cast<uint64_t>(SectionHdrLayout
.size()));
579 SecHdrTableOffset
= OutputStream
->tell();
580 for (uint32_t i
= 0; i
< SectionHdrLayout
.size(); i
++) {
581 Writer
.write(static_cast<uint64_t>(-1));
582 Writer
.write(static_cast<uint64_t>(-1));
583 Writer
.write(static_cast<uint64_t>(-1));
584 Writer
.write(static_cast<uint64_t>(-1));
588 std::error_code
SampleProfileWriterExtBinaryBase::writeSecHdrTable() {
589 auto &OFS
= static_cast<raw_fd_ostream
&>(*OutputStream
);
590 uint64_t Saved
= OutputStream
->tell();
592 // Set OutputStream to the location saved in SecHdrTableOffset.
593 if (OFS
.seek(SecHdrTableOffset
) == (uint64_t)-1)
594 return sampleprof_error::ostream_seek_unsupported
;
595 support::endian::Writer
Writer(*OutputStream
, support::little
);
597 assert(SecHdrTable
.size() == SectionHdrLayout
.size() &&
598 "SecHdrTable entries doesn't match SectionHdrLayout");
599 SmallVector
<uint32_t, 16> IndexMap(SecHdrTable
.size(), -1);
600 for (uint32_t TableIdx
= 0; TableIdx
< SecHdrTable
.size(); TableIdx
++) {
601 IndexMap
[SecHdrTable
[TableIdx
].LayoutIndex
] = TableIdx
;
604 // Write the section header table in the order specified in
605 // SectionHdrLayout. SectionHdrLayout specifies the sections
606 // order in which profile reader expect to read, so the section
607 // header table should be written in the order in SectionHdrLayout.
608 // Note that the section order in SecHdrTable may be different
609 // from the order in SectionHdrLayout, for example, SecFuncOffsetTable
610 // needs to be computed after SecLBRProfile (the order in SecHdrTable),
611 // but it needs to be read before SecLBRProfile (the order in
612 // SectionHdrLayout). So we use IndexMap above to switch the order.
613 for (uint32_t LayoutIdx
= 0; LayoutIdx
< SectionHdrLayout
.size();
615 assert(IndexMap
[LayoutIdx
] < SecHdrTable
.size() &&
616 "Incorrect LayoutIdx in SecHdrTable");
617 auto Entry
= SecHdrTable
[IndexMap
[LayoutIdx
]];
618 Writer
.write(static_cast<uint64_t>(Entry
.Type
));
619 Writer
.write(static_cast<uint64_t>(Entry
.Flags
));
620 Writer
.write(static_cast<uint64_t>(Entry
.Offset
));
621 Writer
.write(static_cast<uint64_t>(Entry
.Size
));
624 // Reset OutputStream.
625 if (OFS
.seek(Saved
) == (uint64_t)-1)
626 return sampleprof_error::ostream_seek_unsupported
;
628 return sampleprof_error::success
;
631 std::error_code
SampleProfileWriterExtBinaryBase::writeHeader(
632 const StringMap
<FunctionSamples
> &ProfileMap
) {
633 auto &OS
= *OutputStream
;
634 FileStart
= OS
.tell();
635 writeMagicIdent(Format
);
638 return sampleprof_error::success
;
641 std::error_code
SampleProfileWriterCompactBinary::writeHeader(
642 const StringMap
<FunctionSamples
> &ProfileMap
) {
643 support::endian::Writer
Writer(*OutputStream
, support::little
);
644 if (auto EC
= SampleProfileWriterBinary::writeHeader(ProfileMap
))
647 // Reserve a slot for the offset of function offset table. The slot will
648 // be populated with the offset of FuncOffsetTable later.
649 TableOffset
= OutputStream
->tell();
650 Writer
.write(static_cast<uint64_t>(-2));
651 return sampleprof_error::success
;
654 std::error_code
SampleProfileWriterBinary::writeSummary() {
655 auto &OS
= *OutputStream
;
656 encodeULEB128(Summary
->getTotalCount(), OS
);
657 encodeULEB128(Summary
->getMaxCount(), OS
);
658 encodeULEB128(Summary
->getMaxFunctionCount(), OS
);
659 encodeULEB128(Summary
->getNumCounts(), OS
);
660 encodeULEB128(Summary
->getNumFunctions(), OS
);
661 std::vector
<ProfileSummaryEntry
> &Entries
= Summary
->getDetailedSummary();
662 encodeULEB128(Entries
.size(), OS
);
663 for (auto Entry
: Entries
) {
664 encodeULEB128(Entry
.Cutoff
, OS
);
665 encodeULEB128(Entry
.MinCount
, OS
);
666 encodeULEB128(Entry
.NumCounts
, OS
);
668 return sampleprof_error::success
;
670 std::error_code
SampleProfileWriterBinary::writeBody(const FunctionSamples
&S
) {
671 auto &OS
= *OutputStream
;
673 if (std::error_code EC
=
674 writeNameIdx(S
.getNameWithContext(), FunctionSamples::ProfileIsCS
))
677 encodeULEB128(S
.getTotalSamples(), OS
);
679 // Emit all the body samples.
680 encodeULEB128(S
.getBodySamples().size(), OS
);
681 for (const auto &I
: S
.getBodySamples()) {
682 LineLocation Loc
= I
.first
;
683 const SampleRecord
&Sample
= I
.second
;
684 encodeULEB128(Loc
.LineOffset
, OS
);
685 encodeULEB128(Loc
.Discriminator
, OS
);
686 encodeULEB128(Sample
.getSamples(), OS
);
687 encodeULEB128(Sample
.getCallTargets().size(), OS
);
688 for (const auto &J
: Sample
.getSortedCallTargets()) {
689 StringRef Callee
= J
.first
;
690 uint64_t CalleeSamples
= J
.second
;
691 if (std::error_code EC
= writeNameIdx(Callee
))
693 encodeULEB128(CalleeSamples
, OS
);
697 // Recursively emit all the callsite samples.
698 uint64_t NumCallsites
= 0;
699 for (const auto &J
: S
.getCallsiteSamples())
700 NumCallsites
+= J
.second
.size();
701 encodeULEB128(NumCallsites
, OS
);
702 for (const auto &J
: S
.getCallsiteSamples())
703 for (const auto &FS
: J
.second
) {
704 LineLocation Loc
= J
.first
;
705 const FunctionSamples
&CalleeSamples
= FS
.second
;
706 encodeULEB128(Loc
.LineOffset
, OS
);
707 encodeULEB128(Loc
.Discriminator
, OS
);
708 if (std::error_code EC
= writeBody(CalleeSamples
))
712 return sampleprof_error::success
;
715 /// Write samples of a top-level function to a binary file.
717 /// \returns true if the samples were written successfully, false otherwise.
719 SampleProfileWriterBinary::writeSample(const FunctionSamples
&S
) {
720 encodeULEB128(S
.getHeadSamples(), *OutputStream
);
725 SampleProfileWriterCompactBinary::writeSample(const FunctionSamples
&S
) {
726 uint64_t Offset
= OutputStream
->tell();
727 StringRef Name
= S
.getName();
728 FuncOffsetTable
[Name
] = Offset
;
729 encodeULEB128(S
.getHeadSamples(), *OutputStream
);
733 /// Create a sample profile file writer based on the specified format.
735 /// \param Filename The file to create.
737 /// \param Format Encoding format for the profile file.
739 /// \returns an error code indicating the status of the created writer.
740 ErrorOr
<std::unique_ptr
<SampleProfileWriter
>>
741 SampleProfileWriter::create(StringRef Filename
, SampleProfileFormat Format
) {
743 std::unique_ptr
<raw_ostream
> OS
;
744 if (Format
== SPF_Binary
|| Format
== SPF_Ext_Binary
||
745 Format
== SPF_Compact_Binary
)
746 OS
.reset(new raw_fd_ostream(Filename
, EC
, sys::fs::OF_None
));
748 OS
.reset(new raw_fd_ostream(Filename
, EC
, sys::fs::OF_TextWithCRLF
));
752 return create(OS
, Format
);
755 /// Create a sample profile stream writer based on the specified format.
757 /// \param OS The output stream to store the profile data to.
759 /// \param Format Encoding format for the profile file.
761 /// \returns an error code indicating the status of the created writer.
762 ErrorOr
<std::unique_ptr
<SampleProfileWriter
>>
763 SampleProfileWriter::create(std::unique_ptr
<raw_ostream
> &OS
,
764 SampleProfileFormat Format
) {
766 std::unique_ptr
<SampleProfileWriter
> Writer
;
768 // Currently only Text and Extended Binary format are supported for CSSPGO.
769 if ((FunctionSamples::ProfileIsCS
|| FunctionSamples::ProfileIsProbeBased
) &&
770 (Format
== SPF_Binary
|| Format
== SPF_Compact_Binary
))
771 return sampleprof_error::unsupported_writing_format
;
773 if (Format
== SPF_Binary
)
774 Writer
.reset(new SampleProfileWriterRawBinary(OS
));
775 else if (Format
== SPF_Ext_Binary
)
776 Writer
.reset(new SampleProfileWriterExtBinary(OS
));
777 else if (Format
== SPF_Compact_Binary
)
778 Writer
.reset(new SampleProfileWriterCompactBinary(OS
));
779 else if (Format
== SPF_Text
)
780 Writer
.reset(new SampleProfileWriterText(OS
));
781 else if (Format
== SPF_GCC
)
782 EC
= sampleprof_error::unsupported_writing_format
;
784 EC
= sampleprof_error::unrecognized_format
;
789 Writer
->Format
= Format
;
790 return std::move(Writer
);
793 void SampleProfileWriter::computeSummary(
794 const StringMap
<FunctionSamples
> &ProfileMap
) {
795 SampleProfileSummaryBuilder
Builder(ProfileSummaryBuilder::DefaultCutoffs
);
796 Summary
= Builder
.computeSummaryForProfiles(ProfileMap
);