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