1 //===- SampleProfWriter.h - Write LLVM sample profile data ------*- C++ -*-===//
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 contains definitions needed for writing sample profiles.
10 //
11 //===----------------------------------------------------------------------===//
12 #ifndef LLVM_PROFILEDATA_SAMPLEPROFWRITER_H
13 #define LLVM_PROFILEDATA_SAMPLEPROFWRITER_H
14 
15 #include "llvm/ADT/MapVector.h"
16 #include "llvm/ADT/StringMap.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/IR/ProfileSummary.h"
19 #include "llvm/ProfileData/SampleProf.h"
20 #include "llvm/Support/ErrorOr.h"
21 #include "llvm/Support/raw_ostream.h"
22 #include <algorithm>
23 #include <cstdint>
24 #include <memory>
25 #include <set>
26 #include <system_error>
27 
28 namespace llvm {
29 namespace sampleprof {
30 
31 /// Sample-based profile writer. Base class.
32 class SampleProfileWriter {
33 public:
34   virtual ~SampleProfileWriter() = default;
35 
36   /// Write sample profiles in \p S.
37   ///
38   /// \returns status code of the file update operation.
39   virtual std::error_code writeSample(const FunctionSamples &S) = 0;
40 
41   /// Write all the sample profiles in the given map of samples.
42   ///
43   /// \returns status code of the file update operation.
44   virtual std::error_code write(const StringMap<FunctionSamples> &ProfileMap);
45 
46   raw_ostream &getOutputStream() { return *OutputStream; }
47 
48   /// Profile writer factory.
49   ///
50   /// Create a new file writer based on the value of \p Format.
51   static ErrorOr<std::unique_ptr<SampleProfileWriter>>
52   create(StringRef Filename, SampleProfileFormat Format);
53 
54   /// Create a new stream writer based on the value of \p Format.
55   /// For testing.
56   static ErrorOr<std::unique_ptr<SampleProfileWriter>>
57   create(std::unique_ptr<raw_ostream> &OS, SampleProfileFormat Format);
58 
59   virtual void setProfileSymbolList(ProfileSymbolList *PSL) {}
60   virtual void setToCompressAllSections() {}
61   virtual void setUseMD5() {}
62 
63 protected:
64   SampleProfileWriter(std::unique_ptr<raw_ostream> &OS)
65       : OutputStream(std::move(OS)) {}
66 
67   /// Write a file header for the profile file.
68   virtual std::error_code
69   writeHeader(const StringMap<FunctionSamples> &ProfileMap) = 0;
70 
71   // Write function profiles to the profile file.
72   virtual std::error_code
73   writeFuncProfiles(const StringMap<FunctionSamples> &ProfileMap);
74 
75   /// Output stream where to emit the profile to.
76   std::unique_ptr<raw_ostream> OutputStream;
77 
78   /// Profile summary.
79   std::unique_ptr<ProfileSummary> Summary;
80 
81   /// Compute summary for this profile.
82   void computeSummary(const StringMap<FunctionSamples> &ProfileMap);
83 
84   /// Profile format.
85   SampleProfileFormat Format = SPF_None;
86 };
87 
88 /// Sample-based profile writer (text format).
89 class SampleProfileWriterText : public SampleProfileWriter {
90 public:
91   std::error_code writeSample(const FunctionSamples &S) override;
92 
93 protected:
94   SampleProfileWriterText(std::unique_ptr<raw_ostream> &OS)
95       : SampleProfileWriter(OS), Indent(0) {}
96 
97   std::error_code
98   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override {
99     return sampleprof_error::success;
100   }
101 
102 private:
103   /// Indent level to use when writing.
104   ///
105   /// This is used when printing inlined callees.
106   unsigned Indent;
107 
108   friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
109   SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
110                               SampleProfileFormat Format);
111 };
112 
113 /// Sample-based profile writer (binary format).
114 class SampleProfileWriterBinary : public SampleProfileWriter {
115 public:
116   SampleProfileWriterBinary(std::unique_ptr<raw_ostream> &OS)
117       : SampleProfileWriter(OS) {}
118 
119   virtual std::error_code writeSample(const FunctionSamples &S) override;
120 
121 protected:
122   virtual std::error_code writeMagicIdent(SampleProfileFormat Format);
123   virtual std::error_code writeNameTable();
124   virtual std::error_code
125   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
126   std::error_code writeSummary();
127   std::error_code writeNameIdx(StringRef FName);
128   std::error_code writeBody(const FunctionSamples &S);
129   inline void stablizeNameTable(std::set<StringRef> &V);
130 
131   MapVector<StringRef, uint32_t> NameTable;
132 
133   void addName(StringRef FName);
134   void addNames(const FunctionSamples &S);
135 
136 private:
137   friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
138   SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
139                               SampleProfileFormat Format);
140 };
141 
142 class SampleProfileWriterRawBinary : public SampleProfileWriterBinary {
143   using SampleProfileWriterBinary::SampleProfileWriterBinary;
144 };
145 
146 class SampleProfileWriterExtBinaryBase : public SampleProfileWriterBinary {
147   using SampleProfileWriterBinary::SampleProfileWriterBinary;
148 public:
149   virtual std::error_code
150   write(const StringMap<FunctionSamples> &ProfileMap) override;
151 
152   virtual void setToCompressAllSections() override;
153   void setToCompressSection(SecType Type);
154 
155 protected:
156   uint64_t markSectionStart(SecType Type);
157   std::error_code addNewSection(SecType Sec, uint64_t SectionStart);
158   template <class SecFlagType>
159   void addSectionFlag(SecType Type, SecFlagType Flag) {
160     for (auto &Entry : SectionHdrLayout) {
161       if (Entry.Type == Type)
162         addSecFlag(Entry, Flag);
163     }
164   }
165 
166   virtual void initSectionHdrLayout() = 0;
167   virtual std::error_code
168   writeSections(const StringMap<FunctionSamples> &ProfileMap) = 0;
169 
170   // Specifiy the order of sections in section header table. Note
171   // the order of sections in the profile may be different that the
172   // order in SectionHdrLayout. sample Reader will follow the order
173   // in SectionHdrLayout to read each section.
174   SmallVector<SecHdrTableEntry, 8> SectionHdrLayout;
175 
176 private:
177   void allocSecHdrTable();
178   std::error_code writeSecHdrTable();
179   virtual std::error_code
180   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
181   SecHdrTableEntry &getEntryInLayout(SecType Type);
182   std::error_code compressAndOutput();
183 
184   // We will swap the raw_ostream held by LocalBufStream and that
185   // held by OutputStream if we try to add a section which needs
186   // compression. After the swap, all the data written to output
187   // will be temporarily buffered into the underlying raw_string_ostream
188   // originally held by LocalBufStream. After the data writing for the
189   // section is completed, compress the data in the local buffer,
190   // swap the raw_ostream back and write the compressed data to the
191   // real output.
192   std::unique_ptr<raw_ostream> LocalBufStream;
193   // The location where the output stream starts.
194   uint64_t FileStart;
195   // The location in the output stream where the SecHdrTable should be
196   // written to.
197   uint64_t SecHdrTableOffset;
198   // Initial Section Flags setting.
199   std::vector<SecHdrTableEntry> SecHdrTable;
200 };
201 
202 class SampleProfileWriterExtBinary : public SampleProfileWriterExtBinaryBase {
203 public:
204   SampleProfileWriterExtBinary(std::unique_ptr<raw_ostream> &OS)
205       : SampleProfileWriterExtBinaryBase(OS) {
206     initSectionHdrLayout();
207   }
208 
209   virtual std::error_code writeSample(const FunctionSamples &S) override;
210   virtual void setProfileSymbolList(ProfileSymbolList *PSL) override {
211     ProfSymList = PSL;
212   };
213 
214   // Set to use MD5 to represent string in NameTable.
215   virtual void setUseMD5() override {
216     UseMD5 = true;
217     addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagMD5Name);
218   }
219 
220 private:
221   virtual void initSectionHdrLayout() override {
222     // Note that SecFuncOffsetTable section is written after SecLBRProfile
223     // in the profile, but is put before SecLBRProfile in SectionHdrLayout.
224     //
225     // This is because sample reader follows the order of SectionHdrLayout to
226     // read each section, to read function profiles on demand sample reader
227     // need to get the offset of each function profile first.
228     //
229     // SecFuncOffsetTable section is written after SecLBRProfile in the
230     // profile because FuncOffsetTable needs to be populated while section
231     // SecLBRProfile is written.
232     SectionHdrLayout = {{SecProfSummary, 0, 0, 0},
233                         {SecNameTable, 0, 0, 0},
234                         {SecFuncOffsetTable, 0, 0, 0},
235                         {SecLBRProfile, 0, 0, 0},
236                         {SecProfileSymbolList, 0, 0, 0}};
237   };
238   virtual std::error_code
239   writeSections(const StringMap<FunctionSamples> &ProfileMap) override;
240 
241   std::error_code writeFuncOffsetTable();
242   virtual std::error_code writeNameTable() override;
243 
244   ProfileSymbolList *ProfSymList = nullptr;
245 
246   // Save the start of SecLBRProfile so we can compute the offset to the
247   // start of SecLBRProfile for each Function's Profile and will keep it
248   // in FuncOffsetTable.
249   uint64_t SecLBRProfileStart = 0;
250   // FuncOffsetTable maps function name to its profile offset in SecLBRProfile
251   // section. It is used to load function profile on demand.
252   MapVector<StringRef, uint64_t> FuncOffsetTable;
253   // Whether to use MD5 to represent string.
254   bool UseMD5 = false;
255 };
256 
257 // CompactBinary is a compact format of binary profile which both reduces
258 // the profile size and the load time needed when compiling. It has two
259 // major difference with Binary format.
260 // 1. It represents all the strings in name table using md5 hash.
261 // 2. It saves a function offset table which maps function name index to
262 // the offset of its function profile to the start of the binary profile,
263 // so by using the function offset table, for those function profiles which
264 // will not be needed when compiling a module, the profile reader does't
265 // have to read them and it saves compile time if the profile size is huge.
266 // The layout of the compact format is shown as follows:
267 //
268 //    Part1: Profile header, the same as binary format, containing magic
269 //           number, version, summary, name table...
270 //    Part2: Function Offset Table Offset, which saves the position of
271 //           Part4.
272 //    Part3: Function profile collection
273 //             function1 profile start
274 //                 ....
275 //             function2 profile start
276 //                 ....
277 //             function3 profile start
278 //                 ....
279 //                ......
280 //    Part4: Function Offset Table
281 //             function1 name index --> function1 profile start
282 //             function2 name index --> function2 profile start
283 //             function3 name index --> function3 profile start
284 //
285 // We need Part2 because profile reader can use it to find out and read
286 // function offset table without reading Part3 first.
287 class SampleProfileWriterCompactBinary : public SampleProfileWriterBinary {
288   using SampleProfileWriterBinary::SampleProfileWriterBinary;
289 
290 public:
291   virtual std::error_code writeSample(const FunctionSamples &S) override;
292   virtual std::error_code
293   write(const StringMap<FunctionSamples> &ProfileMap) override;
294 
295 protected:
296   /// The table mapping from function name to the offset of its FunctionSample
297   /// towards profile start.
298   MapVector<StringRef, uint64_t> FuncOffsetTable;
299   /// The offset of the slot to be filled with the offset of FuncOffsetTable
300   /// towards profile start.
301   uint64_t TableOffset;
302   virtual std::error_code writeNameTable() override;
303   virtual std::error_code
304   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
305   std::error_code writeFuncOffsetTable();
306 };
307 
308 } // end namespace sampleprof
309 } // end namespace llvm
310 
311 #endif // LLVM_PROFILEDATA_SAMPLEPROFWRITER_H
312