1 //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file implements the class that writes LLVM sample profiles. It
11 // supports two file formats: text and binary. The textual representation
12 // is useful for debugging and testing purposes. The binary representation
13 // is more compact, resulting in smaller file sizes. However, they can
14 // both be used interchangeably.
15 //
16 // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the
17 // supported formats.
18 //
19 //===----------------------------------------------------------------------===//
20
21 #include "llvm/ProfileData/SampleProfWriter.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/ProfileData/ProfileCommon.h"
24 #include "llvm/ProfileData/SampleProf.h"
25 #include "llvm/Support/ErrorOr.h"
26 #include "llvm/Support/FileSystem.h"
27 #include "llvm/Support/LEB128.h"
28 #include "llvm/Support/MD5.h"
29 #include "llvm/Support/raw_ostream.h"
30 #include <algorithm>
31 #include <cstdint>
32 #include <memory>
33 #include <set>
34 #include <system_error>
35 #include <utility>
36 #include <vector>
37
38 using namespace llvm;
39 using namespace sampleprof;
40
41 std::error_code
write(const StringMap<FunctionSamples> & ProfileMap)42 SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) {
43 if (std::error_code EC = writeHeader(ProfileMap))
44 return EC;
45
46 // Sort the ProfileMap by total samples.
47 typedef std::pair<StringRef, const FunctionSamples *> NameFunctionSamples;
48 std::vector<NameFunctionSamples> V;
49 for (const auto &I : ProfileMap)
50 V.push_back(std::make_pair(I.getKey(), &I.second));
51
52 std::stable_sort(
53 V.begin(), V.end(),
54 [](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();
58 });
59
60 for (const auto &I : V) {
61 if (std::error_code EC = write(*I.second))
62 return EC;
63 }
64 return sampleprof_error::success;
65 }
66
67 /// Write samples to a text file.
68 ///
69 /// Note: it may be tempting to implement this in terms of
70 /// FunctionSamples::print(). Please don't. The dump functionality is intended
71 /// for debugging and has no specified form.
72 ///
73 /// The format used here is more structured and deliberate because
74 /// it needs to be parsed by the SampleProfileReaderText class.
write(const FunctionSamples & S)75 std::error_code SampleProfileWriterText::write(const FunctionSamples &S) {
76 auto &OS = *OutputStream;
77 OS << S.getName() << ":" << S.getTotalSamples();
78 if (Indent == 0)
79 OS << ":" << S.getHeadSamples();
80 OS << "\n";
81
82 SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
83 for (const auto &I : SortedSamples.get()) {
84 LineLocation Loc = I->first;
85 const SampleRecord &Sample = I->second;
86 OS.indent(Indent + 1);
87 if (Loc.Discriminator == 0)
88 OS << Loc.LineOffset << ": ";
89 else
90 OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
91
92 OS << Sample.getSamples();
93
94 for (const auto &J : Sample.getCallTargets())
95 OS << " " << J.first() << ":" << J.second;
96 OS << "\n";
97 }
98
99 SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
100 S.getCallsiteSamples());
101 Indent += 1;
102 for (const auto &I : SortedCallsiteSamples.get())
103 for (const auto &FS : I->second) {
104 LineLocation Loc = I->first;
105 const FunctionSamples &CalleeSamples = FS.second;
106 OS.indent(Indent);
107 if (Loc.Discriminator == 0)
108 OS << Loc.LineOffset << ": ";
109 else
110 OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
111 if (std::error_code EC = write(CalleeSamples))
112 return EC;
113 }
114 Indent -= 1;
115
116 return sampleprof_error::success;
117 }
118
writeNameIdx(StringRef FName)119 std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
120 const auto &ret = NameTable.find(FName);
121 if (ret == NameTable.end())
122 return sampleprof_error::truncated_name_table;
123 encodeULEB128(ret->second, *OutputStream);
124 return sampleprof_error::success;
125 }
126
addName(StringRef FName)127 void SampleProfileWriterBinary::addName(StringRef FName) {
128 NameTable.insert(std::make_pair(FName, 0));
129 }
130
addNames(const FunctionSamples & S)131 void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
132 // Add all the names in indirect call targets.
133 for (const auto &I : S.getBodySamples()) {
134 const SampleRecord &Sample = I.second;
135 for (const auto &J : Sample.getCallTargets())
136 addName(J.first());
137 }
138
139 // Recursively add all the names for inlined callsites.
140 for (const auto &J : S.getCallsiteSamples())
141 for (const auto &FS : J.second) {
142 const FunctionSamples &CalleeSamples = FS.second;
143 addName(CalleeSamples.getName());
144 addNames(CalleeSamples);
145 }
146 }
147
stablizeNameTable(std::set<StringRef> & V)148 void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) {
149 // Sort the names to make NameTable deterministic.
150 for (const auto &I : NameTable)
151 V.insert(I.first);
152 int i = 0;
153 for (const StringRef &N : V)
154 NameTable[N] = i++;
155 }
156
writeNameTable()157 std::error_code SampleProfileWriterRawBinary::writeNameTable() {
158 auto &OS = *OutputStream;
159 std::set<StringRef> V;
160 stablizeNameTable(V);
161
162 // Write out the name table.
163 encodeULEB128(NameTable.size(), OS);
164 for (auto N : V) {
165 OS << N;
166 encodeULEB128(0, OS);
167 }
168 return sampleprof_error::success;
169 }
170
writeNameTable()171 std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
172 auto &OS = *OutputStream;
173 std::set<StringRef> V;
174 stablizeNameTable(V);
175
176 // Write out the name table.
177 encodeULEB128(NameTable.size(), OS);
178 for (auto N : V) {
179 encodeULEB128(MD5Hash(N), OS);
180 }
181 return sampleprof_error::success;
182 }
183
writeMagicIdent()184 std::error_code SampleProfileWriterRawBinary::writeMagicIdent() {
185 auto &OS = *OutputStream;
186 // Write file magic identifier.
187 encodeULEB128(SPMagic(), OS);
188 encodeULEB128(SPVersion(), OS);
189 return sampleprof_error::success;
190 }
191
writeMagicIdent()192 std::error_code SampleProfileWriterCompactBinary::writeMagicIdent() {
193 auto &OS = *OutputStream;
194 // Write file magic identifier.
195 encodeULEB128(SPMagic(SPF_Compact_Binary), OS);
196 encodeULEB128(SPVersion(), OS);
197 return sampleprof_error::success;
198 }
199
writeHeader(const StringMap<FunctionSamples> & ProfileMap)200 std::error_code SampleProfileWriterBinary::writeHeader(
201 const StringMap<FunctionSamples> &ProfileMap) {
202 writeMagicIdent();
203
204 computeSummary(ProfileMap);
205 if (auto EC = writeSummary())
206 return EC;
207
208 // Generate the name table for all the functions referenced in the profile.
209 for (const auto &I : ProfileMap) {
210 addName(I.first());
211 addNames(I.second);
212 }
213
214 writeNameTable();
215 return sampleprof_error::success;
216 }
217
writeSummary()218 std::error_code SampleProfileWriterBinary::writeSummary() {
219 auto &OS = *OutputStream;
220 encodeULEB128(Summary->getTotalCount(), OS);
221 encodeULEB128(Summary->getMaxCount(), OS);
222 encodeULEB128(Summary->getMaxFunctionCount(), OS);
223 encodeULEB128(Summary->getNumCounts(), OS);
224 encodeULEB128(Summary->getNumFunctions(), OS);
225 std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
226 encodeULEB128(Entries.size(), OS);
227 for (auto Entry : Entries) {
228 encodeULEB128(Entry.Cutoff, OS);
229 encodeULEB128(Entry.MinCount, OS);
230 encodeULEB128(Entry.NumCounts, OS);
231 }
232 return sampleprof_error::success;
233 }
writeBody(const FunctionSamples & S)234 std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
235 auto &OS = *OutputStream;
236
237 if (std::error_code EC = writeNameIdx(S.getName()))
238 return EC;
239
240 encodeULEB128(S.getTotalSamples(), OS);
241
242 // Emit all the body samples.
243 encodeULEB128(S.getBodySamples().size(), OS);
244 for (const auto &I : S.getBodySamples()) {
245 LineLocation Loc = I.first;
246 const SampleRecord &Sample = I.second;
247 encodeULEB128(Loc.LineOffset, OS);
248 encodeULEB128(Loc.Discriminator, OS);
249 encodeULEB128(Sample.getSamples(), OS);
250 encodeULEB128(Sample.getCallTargets().size(), OS);
251 for (const auto &J : Sample.getCallTargets()) {
252 StringRef Callee = J.first();
253 uint64_t CalleeSamples = J.second;
254 if (std::error_code EC = writeNameIdx(Callee))
255 return EC;
256 encodeULEB128(CalleeSamples, OS);
257 }
258 }
259
260 // Recursively emit all the callsite samples.
261 uint64_t NumCallsites = 0;
262 for (const auto &J : S.getCallsiteSamples())
263 NumCallsites += J.second.size();
264 encodeULEB128(NumCallsites, OS);
265 for (const auto &J : S.getCallsiteSamples())
266 for (const auto &FS : J.second) {
267 LineLocation Loc = J.first;
268 const FunctionSamples &CalleeSamples = FS.second;
269 encodeULEB128(Loc.LineOffset, OS);
270 encodeULEB128(Loc.Discriminator, OS);
271 if (std::error_code EC = writeBody(CalleeSamples))
272 return EC;
273 }
274
275 return sampleprof_error::success;
276 }
277
278 /// Write samples of a top-level function to a binary file.
279 ///
280 /// \returns true if the samples were written successfully, false otherwise.
write(const FunctionSamples & S)281 std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) {
282 encodeULEB128(S.getHeadSamples(), *OutputStream);
283 return writeBody(S);
284 }
285
286 /// Create a sample profile file writer based on the specified format.
287 ///
288 /// \param Filename The file to create.
289 ///
290 /// \param Format Encoding format for the profile file.
291 ///
292 /// \returns an error code indicating the status of the created writer.
293 ErrorOr<std::unique_ptr<SampleProfileWriter>>
create(StringRef Filename,SampleProfileFormat Format)294 SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
295 std::error_code EC;
296 std::unique_ptr<raw_ostream> OS;
297 if (Format == SPF_Binary || Format == SPF_Compact_Binary)
298 OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_None));
299 else
300 OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_Text));
301 if (EC)
302 return EC;
303
304 return create(OS, Format);
305 }
306
307 /// Create a sample profile stream writer based on the specified format.
308 ///
309 /// \param OS The output stream to store the profile data to.
310 ///
311 /// \param Format Encoding format for the profile file.
312 ///
313 /// \returns an error code indicating the status of the created writer.
314 ErrorOr<std::unique_ptr<SampleProfileWriter>>
create(std::unique_ptr<raw_ostream> & OS,SampleProfileFormat Format)315 SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
316 SampleProfileFormat Format) {
317 std::error_code EC;
318 std::unique_ptr<SampleProfileWriter> Writer;
319
320 if (Format == SPF_Binary)
321 Writer.reset(new SampleProfileWriterRawBinary(OS));
322 else if (Format == SPF_Compact_Binary)
323 Writer.reset(new SampleProfileWriterCompactBinary(OS));
324 else if (Format == SPF_Text)
325 Writer.reset(new SampleProfileWriterText(OS));
326 else if (Format == SPF_GCC)
327 EC = sampleprof_error::unsupported_writing_format;
328 else
329 EC = sampleprof_error::unrecognized_format;
330
331 if (EC)
332 return EC;
333
334 return std::move(Writer);
335 }
336
computeSummary(const StringMap<FunctionSamples> & ProfileMap)337 void SampleProfileWriter::computeSummary(
338 const StringMap<FunctionSamples> &ProfileMap) {
339 SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
340 for (const auto &I : ProfileMap) {
341 const FunctionSamples &Profile = I.second;
342 Builder.addRecord(Profile);
343 }
344 Summary = Builder.getSummary();
345 }
346