• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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