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   std::stable_sort(
54       V.begin(), V.end(),
55       [](const NameFunctionSamples &A, const NameFunctionSamples &B) {
56         if (A.second->getTotalSamples() == B.second->getTotalSamples())
57           return A.first > B.first;
58         return A.second->getTotalSamples() > B.second->getTotalSamples();
59       });
60 
61   for (const auto &I : V) {
62     if (std::error_code EC = write(*I.second))
63       return EC;
64   }
65   return sampleprof_error::success;
66 }
67 
68 std::error_code SampleProfileWriterCompactBinary::write(
69     const StringMap<FunctionSamples> &ProfileMap) {
70   if (std::error_code EC = SampleProfileWriter::write(ProfileMap))
71     return EC;
72   if (std::error_code EC = writeFuncOffsetTable())
73     return EC;
74   return sampleprof_error::success;
75 }
76 
77 /// Write samples to a text file.
78 ///
79 /// Note: it may be tempting to implement this in terms of
80 /// FunctionSamples::print().  Please don't.  The dump functionality is intended
81 /// for debugging and has no specified form.
82 ///
83 /// The format used here is more structured and deliberate because
84 /// it needs to be parsed by the SampleProfileReaderText class.
85 std::error_code SampleProfileWriterText::write(const FunctionSamples &S) {
86   auto &OS = *OutputStream;
87   OS << S.getName() << ":" << S.getTotalSamples();
88   if (Indent == 0)
89     OS << ":" << S.getHeadSamples();
90   OS << "\n";
91 
92   SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples());
93   for (const auto &I : SortedSamples.get()) {
94     LineLocation Loc = I->first;
95     const SampleRecord &Sample = I->second;
96     OS.indent(Indent + 1);
97     if (Loc.Discriminator == 0)
98       OS << Loc.LineOffset << ": ";
99     else
100       OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
101 
102     OS << Sample.getSamples();
103 
104     for (const auto &J : Sample.getCallTargets())
105       OS << " " << J.first() << ":" << J.second;
106     OS << "\n";
107   }
108 
109   SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples(
110       S.getCallsiteSamples());
111   Indent += 1;
112   for (const auto &I : SortedCallsiteSamples.get())
113     for (const auto &FS : I->second) {
114       LineLocation Loc = I->first;
115       const FunctionSamples &CalleeSamples = FS.second;
116       OS.indent(Indent);
117       if (Loc.Discriminator == 0)
118         OS << Loc.LineOffset << ": ";
119       else
120         OS << Loc.LineOffset << "." << Loc.Discriminator << ": ";
121       if (std::error_code EC = write(CalleeSamples))
122         return EC;
123     }
124   Indent -= 1;
125 
126   return sampleprof_error::success;
127 }
128 
129 std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
130   const auto &ret = NameTable.find(FName);
131   if (ret == NameTable.end())
132     return sampleprof_error::truncated_name_table;
133   encodeULEB128(ret->second, *OutputStream);
134   return sampleprof_error::success;
135 }
136 
137 void SampleProfileWriterBinary::addName(StringRef FName) {
138   NameTable.insert(std::make_pair(FName, 0));
139 }
140 
141 void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
142   // Add all the names in indirect call targets.
143   for (const auto &I : S.getBodySamples()) {
144     const SampleRecord &Sample = I.second;
145     for (const auto &J : Sample.getCallTargets())
146       addName(J.first());
147   }
148 
149   // Recursively add all the names for inlined callsites.
150   for (const auto &J : S.getCallsiteSamples())
151     for (const auto &FS : J.second) {
152       const FunctionSamples &CalleeSamples = FS.second;
153       addName(CalleeSamples.getName());
154       addNames(CalleeSamples);
155     }
156 }
157 
158 void SampleProfileWriterBinary::stablizeNameTable(std::set<StringRef> &V) {
159   // Sort the names to make NameTable deterministic.
160   for (const auto &I : NameTable)
161     V.insert(I.first);
162   int i = 0;
163   for (const StringRef &N : V)
164     NameTable[N] = i++;
165 }
166 
167 std::error_code SampleProfileWriterRawBinary::writeNameTable() {
168   auto &OS = *OutputStream;
169   std::set<StringRef> V;
170   stablizeNameTable(V);
171 
172   // Write out the name table.
173   encodeULEB128(NameTable.size(), OS);
174   for (auto N : V) {
175     OS << N;
176     encodeULEB128(0, OS);
177   }
178   return sampleprof_error::success;
179 }
180 
181 std::error_code SampleProfileWriterCompactBinary::writeFuncOffsetTable() {
182   auto &OS = *OutputStream;
183 
184   // Fill the slot remembered by TableOffset with the offset of FuncOffsetTable.
185   auto &OFS = static_cast<raw_fd_ostream &>(OS);
186   uint64_t FuncOffsetTableStart = OS.tell();
187   if (OFS.seek(TableOffset) == (uint64_t)-1)
188     return sampleprof_error::ostream_seek_unsupported;
189   support::endian::Writer Writer(*OutputStream, support::little);
190   Writer.write(FuncOffsetTableStart);
191   if (OFS.seek(FuncOffsetTableStart) == (uint64_t)-1)
192     return sampleprof_error::ostream_seek_unsupported;
193 
194   // Write out the table size.
195   encodeULEB128(FuncOffsetTable.size(), OS);
196 
197   // Write out FuncOffsetTable.
198   for (auto entry : FuncOffsetTable) {
199     writeNameIdx(entry.first);
200     encodeULEB128(entry.second, OS);
201   }
202   return sampleprof_error::success;
203 }
204 
205 std::error_code SampleProfileWriterCompactBinary::writeNameTable() {
206   auto &OS = *OutputStream;
207   std::set<StringRef> V;
208   stablizeNameTable(V);
209 
210   // Write out the name table.
211   encodeULEB128(NameTable.size(), OS);
212   for (auto N : V) {
213     encodeULEB128(MD5Hash(N), OS);
214   }
215   return sampleprof_error::success;
216 }
217 
218 std::error_code SampleProfileWriterRawBinary::writeMagicIdent() {
219   auto &OS = *OutputStream;
220   // Write file magic identifier.
221   encodeULEB128(SPMagic(), OS);
222   encodeULEB128(SPVersion(), OS);
223   return sampleprof_error::success;
224 }
225 
226 std::error_code SampleProfileWriterCompactBinary::writeMagicIdent() {
227   auto &OS = *OutputStream;
228   // Write file magic identifier.
229   encodeULEB128(SPMagic(SPF_Compact_Binary), OS);
230   encodeULEB128(SPVersion(), OS);
231   return sampleprof_error::success;
232 }
233 
234 std::error_code SampleProfileWriterBinary::writeHeader(
235     const StringMap<FunctionSamples> &ProfileMap) {
236   writeMagicIdent();
237 
238   computeSummary(ProfileMap);
239   if (auto EC = writeSummary())
240     return EC;
241 
242   // Generate the name table for all the functions referenced in the profile.
243   for (const auto &I : ProfileMap) {
244     addName(I.first());
245     addNames(I.second);
246   }
247 
248   writeNameTable();
249   return sampleprof_error::success;
250 }
251 
252 std::error_code SampleProfileWriterCompactBinary::writeHeader(
253     const StringMap<FunctionSamples> &ProfileMap) {
254   support::endian::Writer Writer(*OutputStream, support::little);
255   if (auto EC = SampleProfileWriterBinary::writeHeader(ProfileMap))
256     return EC;
257 
258   // Reserve a slot for the offset of function offset table. The slot will
259   // be populated with the offset of FuncOffsetTable later.
260   TableOffset = OutputStream->tell();
261   Writer.write(static_cast<uint64_t>(-2));
262   return sampleprof_error::success;
263 }
264 
265 std::error_code SampleProfileWriterBinary::writeSummary() {
266   auto &OS = *OutputStream;
267   encodeULEB128(Summary->getTotalCount(), OS);
268   encodeULEB128(Summary->getMaxCount(), OS);
269   encodeULEB128(Summary->getMaxFunctionCount(), OS);
270   encodeULEB128(Summary->getNumCounts(), OS);
271   encodeULEB128(Summary->getNumFunctions(), OS);
272   std::vector<ProfileSummaryEntry> &Entries = Summary->getDetailedSummary();
273   encodeULEB128(Entries.size(), OS);
274   for (auto Entry : Entries) {
275     encodeULEB128(Entry.Cutoff, OS);
276     encodeULEB128(Entry.MinCount, OS);
277     encodeULEB128(Entry.NumCounts, OS);
278   }
279   return sampleprof_error::success;
280 }
281 std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
282   auto &OS = *OutputStream;
283 
284   if (std::error_code EC = writeNameIdx(S.getName()))
285     return EC;
286 
287   encodeULEB128(S.getTotalSamples(), OS);
288 
289   // Emit all the body samples.
290   encodeULEB128(S.getBodySamples().size(), OS);
291   for (const auto &I : S.getBodySamples()) {
292     LineLocation Loc = I.first;
293     const SampleRecord &Sample = I.second;
294     encodeULEB128(Loc.LineOffset, OS);
295     encodeULEB128(Loc.Discriminator, OS);
296     encodeULEB128(Sample.getSamples(), OS);
297     encodeULEB128(Sample.getCallTargets().size(), OS);
298     for (const auto &J : Sample.getCallTargets()) {
299       StringRef Callee = J.first();
300       uint64_t CalleeSamples = J.second;
301       if (std::error_code EC = writeNameIdx(Callee))
302         return EC;
303       encodeULEB128(CalleeSamples, OS);
304     }
305   }
306 
307   // Recursively emit all the callsite samples.
308   uint64_t NumCallsites = 0;
309   for (const auto &J : S.getCallsiteSamples())
310     NumCallsites += J.second.size();
311   encodeULEB128(NumCallsites, OS);
312   for (const auto &J : S.getCallsiteSamples())
313     for (const auto &FS : J.second) {
314       LineLocation Loc = J.first;
315       const FunctionSamples &CalleeSamples = FS.second;
316       encodeULEB128(Loc.LineOffset, OS);
317       encodeULEB128(Loc.Discriminator, OS);
318       if (std::error_code EC = writeBody(CalleeSamples))
319         return EC;
320     }
321 
322   return sampleprof_error::success;
323 }
324 
325 /// Write samples of a top-level function to a binary file.
326 ///
327 /// \returns true if the samples were written successfully, false otherwise.
328 std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) {
329   encodeULEB128(S.getHeadSamples(), *OutputStream);
330   return writeBody(S);
331 }
332 
333 std::error_code
334 SampleProfileWriterCompactBinary::write(const FunctionSamples &S) {
335   uint64_t Offset = OutputStream->tell();
336   StringRef Name = S.getName();
337   FuncOffsetTable[Name] = Offset;
338   encodeULEB128(S.getHeadSamples(), *OutputStream);
339   return writeBody(S);
340 }
341 
342 /// Create a sample profile file writer based on the specified format.
343 ///
344 /// \param Filename The file to create.
345 ///
346 /// \param Format Encoding format for the profile file.
347 ///
348 /// \returns an error code indicating the status of the created writer.
349 ErrorOr<std::unique_ptr<SampleProfileWriter>>
350 SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) {
351   std::error_code EC;
352   std::unique_ptr<raw_ostream> OS;
353   if (Format == SPF_Binary || Format == SPF_Compact_Binary)
354     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_None));
355   else
356     OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::F_Text));
357   if (EC)
358     return EC;
359 
360   return create(OS, Format);
361 }
362 
363 /// Create a sample profile stream writer based on the specified format.
364 ///
365 /// \param OS The output stream to store the profile data to.
366 ///
367 /// \param Format Encoding format for the profile file.
368 ///
369 /// \returns an error code indicating the status of the created writer.
370 ErrorOr<std::unique_ptr<SampleProfileWriter>>
371 SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
372                             SampleProfileFormat Format) {
373   std::error_code EC;
374   std::unique_ptr<SampleProfileWriter> Writer;
375 
376   if (Format == SPF_Binary)
377     Writer.reset(new SampleProfileWriterRawBinary(OS));
378   else if (Format == SPF_Compact_Binary)
379     Writer.reset(new SampleProfileWriterCompactBinary(OS));
380   else if (Format == SPF_Text)
381     Writer.reset(new SampleProfileWriterText(OS));
382   else if (Format == SPF_GCC)
383     EC = sampleprof_error::unsupported_writing_format;
384   else
385     EC = sampleprof_error::unrecognized_format;
386 
387   if (EC)
388     return EC;
389 
390   return std::move(Writer);
391 }
392 
393 void SampleProfileWriter::computeSummary(
394     const StringMap<FunctionSamples> &ProfileMap) {
395   SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
396   for (const auto &I : ProfileMap) {
397     const FunctionSamples &Profile = I.second;
398     Builder.addRecord(Profile);
399   }
400   Summary = Builder.getSummary();
401 }
402