xref: /freebsd/contrib/llvm-project/llvm/lib/ProfileData/SampleProfWriter.cpp (revision 162ae9c834f6d9f9cb443bd62cceb23e0b5fef48)
1 //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
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.
14 //
15 // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the
16 // supported formats.
17 //
18 //===----------------------------------------------------------------------===//
19 
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"
31 #include <algorithm>
32 #include <cstdint>
33 #include <memory>
34 #include <set>
35 #include <system_error>
36 #include <utility>
37 #include <vector>
38 
39 using namespace llvm;
40 using namespace sampleprof;
41 
42 std::error_code
43 SampleProfileWriter::write(const StringMap<FunctionSamples> &ProfileMap) {
44   if (std::error_code EC = writeHeader(ProfileMap))
45     return EC;
46 
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));
52 
53   llvm::stable_sort(
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();
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 std::error_code SampleProfileWriterCompactBinary::write(
68     const StringMap<FunctionSamples> &ProfileMap) {
69   if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
70     return EC;
71   if (std::error_code EC = writeFuncOffsetTable())
72     return EC;
73   return sampleprof_error::success;
74 }
75 
76 /// Write samples to a text file.
77 ///
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.
81 ///
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();
87   if (Indent == 0)
88     OS << ":" << S.getHeadSamples();
89   OS << "\n";
90 
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 << ": ";
98     else
99       OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
100 
101     OS << Sample.getSamples();
102 
103     for (const auto &J : Sample.getCallTargets())
104       OS << " " << J.first() << ":" << J.second;
105     OS << "\n";
106   }
107 
108   SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
109       S.getCallsiteSamples());
110   Indent += 1;
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;
115       OS.indent(Indent);
116       if (Loc.Discriminator == 0)
117         OS << Loc.LineOffset << ": ";
118       else
119         OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
120       if (std::error_code EC = write(CalleeSamples))
121         return EC;
122     }
123   Indent -= 1;
124 
125   return sampleprof_error::success;
126 }
127 
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;
134 }
135 
136 void SampleProfileWriterBinary::addName(StringRef FName) {
137   NameTable.insert(std::make_pair(FName, 0));
138 }
139 
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())
145       addName(J.first());
146   }
147 
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);
154     }
155 }
156 
157 void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) {
158   // Sort the names to make NameTable deterministic.
159   for (const auto &I : NameTable)
160     V.insert(I.first);
161   int i = 0;
162   for (const StringRef &N : V)
163     NameTable[N] = i++;
164 }
165 
166 std::error_code SampleProfileWriterRawBinary::writeNameTable() {
167   auto &OS = *OutputStream;
168   std::set<StringRef> V;
169   stablizeNameTable(V);
170 
171   // Write out the name table.
172   encodeULEB128(NameTable.size(), OS);
173   for (auto N : V) {
174     OS << N;
175     encodeULEB128(0, OS);
176   }
177   return sampleprof_error::success;
178 }
179 
180 std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
181   auto &OS = *OutputStream;
182 
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;
192 
193   // Write out the table size.
194   encodeULEB128(FuncOffsetTable.size(), OS);
195 
196   // Write out FuncOffsetTable.
197   for (auto entry : FuncOffsetTable) {
198     writeNameIdx(entry.first);
199     encodeULEB128(entry.second, OS);
200   }
201   return sampleprof_error::success;
202 }
203 
204 std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
205   auto &OS = *OutputStream;
206   std::set<StringRef> V;
207   stablizeNameTable(V);
208 
209   // Write out the name table.
210   encodeULEB128(NameTable.size(), OS);
211   for (auto N : V) {
212     encodeULEB128(MD5Hash(N), OS);
213   }
214   return sampleprof_error::success;
215 }
216 
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;
223 }
224 
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;
231 }
232 
233 std::error_code SampleProfileWriterBinary::writeHeader(
234     const StringMap<FunctionSamples> &ProfileMap) {
235   writeMagicIdent();
236 
237   computeSummary(ProfileMap);
238   if (auto EC = writeSummary())
239     return EC;
240 
241   // Generate the name table for all the functions referenced in the profile.
242   for (const auto &I : ProfileMap) {
243     addName(I.first());
244     addNames(I.second);
245   }
246 
247   writeNameTable();
248   return sampleprof_error::success;
249 }
250 
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))
255     return EC;
256 
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;
262 }
263 
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);
277   }
278   return sampleprof_error::success;
279 }
280 std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
281   auto &OS = *OutputStream;
282 
283   if (std::error_code EC = writeNameIdx(S.getName()))
284     return EC;
285 
286   encodeULEB128(S.getTotalSamples(), OS);
287 
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))
301         return EC;
302       encodeULEB128(CalleeSamples, OS);
303     }
304   }
305 
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))
318         return EC;
319     }
320 
321   return sampleprof_error::success;
322 }
323 
324 /// Write samples of a top-level function to a binary file.
325 ///
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);
329   return writeBody(S);
330 }
331 
332 std::error_code
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);
338   return writeBody(S);
339 }
340 
341 /// Create a sample profile file writer based on the specified format.
342 ///
343 /// \param Filename The file to create.
344 ///
345 /// \param Format Encoding format for the profile file.
346 ///
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) {
350   std::error_code EC;
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));
354   else
355     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_Text));
356   if (EC)
357     return EC;
358 
359   return create(OS, Format);
360 }
361 
362 /// Create a sample profile stream writer based on the specified format.
363 ///
364 /// \param OS The output stream to store the profile data to.
365 ///
366 /// \param Format Encoding format for the profile file.
367 ///
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) {
372   std::error_code EC;
373   std::unique_ptr<SampleProfileWriter> Writer;
374 
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;
383   else
384     EC = sampleprof_error::unrecognized_format;
385 
386   if (EC)
387     return EC;
388 
389   return std::move(Writer);
390 }
391 
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);
398   }
399   Summary = Builder.getSummary();
400 }
401