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