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/ProfileData/ProfileCommon.h"
23 #include "llvm/ProfileData/SampleProf.h"
24 #include "llvm/Support/Endian.h"
25 #include "llvm/Support/EndianStream.h"
26 #include "llvm/Support/ErrorOr.h"
27 #include "llvm/Support/FileSystem.h"
28 #include "llvm/Support/LEB128.h"
29 #include "llvm/Support/MD5.h"
30 #include "llvm/Support/raw_ostream.h"
35 #include <system_error>
40 using namespace sampleprof
;
43 SampleProfileWriter::write(const StringMap
<FunctionSamples
> &ProfileMap
) {
44 if (std::error_code EC
= writeHeader(ProfileMap
))
47 // Sort the ProfileMap by total samples.
48 typedef std::pair
<StringRef
, const FunctionSamples
*> NameFunctionSamples
;
49 std::vector
<NameFunctionSamples
> V
;
50 for (const auto &I
: ProfileMap
)
51 V
.push_back(std::make_pair(I
.getKey(), &I
.second
));
54 V
, [](const NameFunctionSamples
&A
, const NameFunctionSamples
&B
) {
55 if (A
.second
->getTotalSamples() == B
.second
->getTotalSamples())
56 return A
.first
> B
.first
;
57 return A
.second
->getTotalSamples() > B
.second
->getTotalSamples();
60 for (const auto &I
: V
) {
61 if (std::error_code EC
= write(*I
.second
))
64 return sampleprof_error::success
;
67 std::error_code
SampleProfileWriterCompactBinary::write(
68 const StringMap
<FunctionSamples
> &ProfileMap
) {
69 if (std::error_code EC
= SampleProfileWriter::write(ProfileMap
))
71 if (std::error_code EC
= writeFuncOffsetTable())
73 return sampleprof_error::success
;
76 /// Write samples to a text file.
78 /// Note: it may be tempting to implement this in terms of
79 /// FunctionSamples::print(). Please don't. The dump functionality is intended
80 /// for debugging and has no specified form.
82 /// The format used here is more structured and deliberate because
83 /// it needs to be parsed by the SampleProfileReaderText class.
84 std::error_code
SampleProfileWriterText::write(const FunctionSamples
&S
) {
85 auto &OS
= *OutputStream
;
86 OS
<< S
.getName() << ":" << S
.getTotalSamples();
88 OS
<< ":" << S
.getHeadSamples();
91 SampleSorter
<LineLocation
, SampleRecord
> SortedSamples(S
.getBodySamples());
92 for (const auto &I
: SortedSamples
.get()) {
93 LineLocation Loc
= I
->first
;
94 const SampleRecord
&Sample
= I
->second
;
95 OS
.indent(Indent
+ 1);
96 if (Loc
.Discriminator
== 0)
97 OS
<< Loc
.LineOffset
<< ": ";
99 OS
<< Loc
.LineOffset
<< "." << Loc
.Discriminator
<< ": ";
101 OS
<< Sample
.getSamples();
103 for (const auto &J
: Sample
.getCallTargets())
104 OS
<< " " << J
.first() << ":" << J
.second
;
108 SampleSorter
<LineLocation
, FunctionSamplesMap
> SortedCallsiteSamples(
109 S
.getCallsiteSamples());
111 for (const auto &I
: SortedCallsiteSamples
.get())
112 for (const auto &FS
: I
->second
) {
113 LineLocation Loc
= I
->first
;
114 const FunctionSamples
&CalleeSamples
= FS
.second
;
116 if (Loc
.Discriminator
== 0)
117 OS
<< Loc
.LineOffset
<< ": ";
119 OS
<< Loc
.LineOffset
<< "." << Loc
.Discriminator
<< ": ";
120 if (std::error_code EC
= write(CalleeSamples
))
125 return sampleprof_error::success
;
128 std::error_code
SampleProfileWriterBinary::writeNameIdx(StringRef FName
) {
129 const auto &ret
= NameTable
.find(FName
);
130 if (ret
== NameTable
.end())
131 return sampleprof_error::truncated_name_table
;
132 encodeULEB128(ret
->second
, *OutputStream
);
133 return sampleprof_error::success
;
136 void SampleProfileWriterBinary::addName(StringRef FName
) {
137 NameTable
.insert(std::make_pair(FName
, 0));
140 void SampleProfileWriterBinary::addNames(const FunctionSamples
&S
) {
141 // Add all the names in indirect call targets.
142 for (const auto &I
: S
.getBodySamples()) {
143 const SampleRecord
&Sample
= I
.second
;
144 for (const auto &J
: Sample
.getCallTargets())
148 // Recursively add all the names for inlined callsites.
149 for (const auto &J
: S
.getCallsiteSamples())
150 for (const auto &FS
: J
.second
) {
151 const FunctionSamples
&CalleeSamples
= FS
.second
;
152 addName(CalleeSamples
.getName());
153 addNames(CalleeSamples
);
157 void SampleProfileWriterBinary::stablizeNameTable(std::set
<StringRef
> &V
) {
158 // Sort the names to make NameTable deterministic.
159 for (const auto &I
: NameTable
)
162 for (const StringRef
&N
: V
)
166 std::error_code
SampleProfileWriterRawBinary::writeNameTable() {
167 auto &OS
= *OutputStream
;
168 std::set
<StringRef
> V
;
169 stablizeNameTable(V
);
171 // Write out the name table.
172 encodeULEB128(NameTable
.size(), OS
);
175 encodeULEB128(0, OS
);
177 return sampleprof_error::success
;
180 std::error_code
SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
181 auto &OS
= *OutputStream
;
183 // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
184 auto &OFS
= static_cast<raw_fd_ostream
&>(OS
);
185 uint64_t FuncOffsetTableStart
= OS
.tell();
186 if (OFS
.seek(TableOffset
) == (uint64_t)-1)
187 return sampleprof_error::ostream_seek_unsupported
;
188 support::endian::Writer
Writer(*OutputStream
, support::little
);
189 Writer
.write(FuncOffsetTableStart
);
190 if (OFS
.seek(FuncOffsetTableStart
) == (uint64_t)-1)
191 return sampleprof_error::ostream_seek_unsupported
;
193 // Write out the table size.
194 encodeULEB128(FuncOffsetTable
.size(), OS
);
196 // Write out FuncOffsetTable.
197 for (auto entry
: FuncOffsetTable
) {
198 writeNameIdx(entry
.first
);
199 encodeULEB128(entry
.second
, OS
);
201 return sampleprof_error::success
;
204 std::error_code
SampleProfileWriterCompactBinary::writeNameTable() {
205 auto &OS
= *OutputStream
;
206 std::set
<StringRef
> V
;
207 stablizeNameTable(V
);
209 // Write out the name table.
210 encodeULEB128(NameTable
.size(), OS
);
212 encodeULEB128(MD5Hash(N
), OS
);
214 return sampleprof_error::success
;
217 std::error_code
SampleProfileWriterRawBinary::writeMagicIdent() {
218 auto &OS
= *OutputStream
;
219 // Write file magic identifier.
220 encodeULEB128(SPMagic(), OS
);
221 encodeULEB128(SPVersion(), OS
);
222 return sampleprof_error::success
;
225 std::error_code
SampleProfileWriterCompactBinary::writeMagicIdent() {
226 auto &OS
= *OutputStream
;
227 // Write file magic identifier.
228 encodeULEB128(SPMagic(SPF_Compact_Binary
), OS
);
229 encodeULEB128(SPVersion(), OS
);
230 return sampleprof_error::success
;
233 std::error_code
SampleProfileWriterBinary::writeHeader(
234 const StringMap
<FunctionSamples
> &ProfileMap
) {
237 computeSummary(ProfileMap
);
238 if (auto EC
= writeSummary())
241 // Generate the name table for all the functions referenced in the profile.
242 for (const auto &I
: ProfileMap
) {
248 return sampleprof_error::success
;
251 std::error_code
SampleProfileWriterCompactBinary::writeHeader(
252 const StringMap
<FunctionSamples
> &ProfileMap
) {
253 support::endian::Writer
Writer(*OutputStream
, support::little
);
254 if (auto EC
= SampleProfileWriterBinary::writeHeader(ProfileMap
))
257 // Reserve a slot for the offset of function offset table. The slot will
258 // be populated with the offset of FuncOffsetTable later.
259 TableOffset
= OutputStream
->tell();
260 Writer
.write(static_cast<uint64_t>(-2));
261 return sampleprof_error::success
;
264 std::error_code
SampleProfileWriterBinary::writeSummary() {
265 auto &OS
= *OutputStream
;
266 encodeULEB128(Summary
->getTotalCount(), OS
);
267 encodeULEB128(Summary
->getMaxCount(), OS
);
268 encodeULEB128(Summary
->getMaxFunctionCount(), OS
);
269 encodeULEB128(Summary
->getNumCounts(), OS
);
270 encodeULEB128(Summary
->getNumFunctions(), OS
);
271 std::vector
<ProfileSummaryEntry
> &Entries
= Summary
->getDetailedSummary();
272 encodeULEB128(Entries
.size(), OS
);
273 for (auto Entry
: Entries
) {
274 encodeULEB128(Entry
.Cutoff
, OS
);
275 encodeULEB128(Entry
.MinCount
, OS
);
276 encodeULEB128(Entry
.NumCounts
, OS
);
278 return sampleprof_error::success
;
280 std::error_code
SampleProfileWriterBinary::writeBody(const FunctionSamples
&S
) {
281 auto &OS
= *OutputStream
;
283 if (std::error_code EC
= writeNameIdx(S
.getName()))
286 encodeULEB128(S
.getTotalSamples(), OS
);
288 // Emit all the body samples.
289 encodeULEB128(S
.getBodySamples().size(), OS
);
290 for (const auto &I
: S
.getBodySamples()) {
291 LineLocation Loc
= I
.first
;
292 const SampleRecord
&Sample
= I
.second
;
293 encodeULEB128(Loc
.LineOffset
, OS
);
294 encodeULEB128(Loc
.Discriminator
, OS
);
295 encodeULEB128(Sample
.getSamples(), OS
);
296 encodeULEB128(Sample
.getCallTargets().size(), OS
);
297 for (const auto &J
: Sample
.getCallTargets()) {
298 StringRef Callee
= J
.first();
299 uint64_t CalleeSamples
= J
.second
;
300 if (std::error_code EC
= writeNameIdx(Callee
))
302 encodeULEB128(CalleeSamples
, OS
);
306 // Recursively emit all the callsite samples.
307 uint64_t NumCallsites
= 0;
308 for (const auto &J
: S
.getCallsiteSamples())
309 NumCallsites
+= J
.second
.size();
310 encodeULEB128(NumCallsites
, OS
);
311 for (const auto &J
: S
.getCallsiteSamples())
312 for (const auto &FS
: J
.second
) {
313 LineLocation Loc
= J
.first
;
314 const FunctionSamples
&CalleeSamples
= FS
.second
;
315 encodeULEB128(Loc
.LineOffset
, OS
);
316 encodeULEB128(Loc
.Discriminator
, OS
);
317 if (std::error_code EC
= writeBody(CalleeSamples
))
321 return sampleprof_error::success
;
324 /// Write samples of a top-level function to a binary file.
326 /// \returns true if the samples were written successfully, false otherwise.
327 std::error_code
SampleProfileWriterBinary::write(const FunctionSamples
&S
) {
328 encodeULEB128(S
.getHeadSamples(), *OutputStream
);
333 SampleProfileWriterCompactBinary::write(const FunctionSamples
&S
) {
334 uint64_t Offset
= OutputStream
->tell();
335 StringRef Name
= S
.getName();
336 FuncOffsetTable
[Name
] = Offset
;
337 encodeULEB128(S
.getHeadSamples(), *OutputStream
);
341 /// Create a sample profile file writer based on the specified format.
343 /// \param Filename The file to create.
345 /// \param Format Encoding format for the profile file.
347 /// \returns an error code indicating the status of the created writer.
348 ErrorOr
<std::unique_ptr
<SampleProfileWriter
>>
349 SampleProfileWriter::create(StringRef Filename
, SampleProfileFormat Format
) {
351 std::unique_ptr
<raw_ostream
> OS
;
352 if (Format
== SPF_Binary
|| Format
== SPF_Compact_Binary
)
353 OS
.reset(new raw_fd_ostream(Filename
, EC
, sys::fs::F_None
));
355 OS
.reset(new raw_fd_ostream(Filename
, EC
, sys::fs::F_Text
));
359 return create(OS
, Format
);
362 /// Create a sample profile stream writer based on the specified format.
364 /// \param OS The output stream to store the profile data to.
366 /// \param Format Encoding format for the profile file.
368 /// \returns an error code indicating the status of the created writer.
369 ErrorOr
<std::unique_ptr
<SampleProfileWriter
>>
370 SampleProfileWriter::create(std::unique_ptr
<raw_ostream
> &OS
,
371 SampleProfileFormat Format
) {
373 std::unique_ptr
<SampleProfileWriter
> Writer
;
375 if (Format
== SPF_Binary
)
376 Writer
.reset(new SampleProfileWriterRawBinary(OS
));
377 else if (Format
== SPF_Compact_Binary
)
378 Writer
.reset(new SampleProfileWriterCompactBinary(OS
));
379 else if (Format
== SPF_Text
)
380 Writer
.reset(new SampleProfileWriterText(OS
));
381 else if (Format
== SPF_GCC
)
382 EC
= sampleprof_error::unsupported_writing_format
;
384 EC
= sampleprof_error::unrecognized_format
;
389 return std::move(Writer
);
392 void SampleProfileWriter::computeSummary(
393 const StringMap
<FunctionSamples
> &ProfileMap
) {
394 SampleProfileSummaryBuilder
Builder(ProfileSummaryBuilder::DefaultCutoffs
);
395 for (const auto &I
: ProfileMap
) {
396 const FunctionSamples
&Profile
= I
.second
;
397 Builder
.addRecord(Profile
);
399 Summary
= Builder
.getSummary();