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/ADT/StringSet.h"
19 #include "llvm/IR/ProfileSummary.h"
20 #include "llvm/ProfileData/SampleProf.h"
21 #include "llvm/Support/ErrorOr.h"
22 #include "llvm/Support/raw_ostream.h"
23 #include <algorithm>
24 #include <cstdint>
25 #include <memory>
26 #include <set>
27 #include <system_error>
28 
29 namespace llvm {
30 namespace sampleprof {
31 
32 enum SectionLayout {
33   DefaultLayout,
34   // The layout splits profile with context information from profile without
35   // context information. When Thinlto is enabled, ThinLTO postlink phase only
36   // has to load profile with context information and can skip the other part.
37   CtxSplitLayout,
38   NumOfLayout,
39 };
40 
41 /// Sample-based profile writer. Base class.
42 class SampleProfileWriter {
43 public:
44   virtual ~SampleProfileWriter() = default;
45 
46   /// Write sample profiles in \p S.
47   ///
48   /// \returns status code of the file update operation.
49   virtual std::error_code writeSample(const FunctionSamples &S) = 0;
50 
51   /// Write all the sample profiles in the given map of samples.
52   ///
53   /// \returns status code of the file update operation.
54   virtual std::error_code write(const StringMap<FunctionSamples> &ProfileMap);
55 
56   raw_ostream &getOutputStream() { return *OutputStream; }
57 
58   /// Profile writer factory.
59   ///
60   /// Create a new file writer based on the value of \p Format.
61   static ErrorOr<std::unique_ptr<SampleProfileWriter>>
62   create(StringRef Filename, SampleProfileFormat Format);
63 
64   /// Create a new stream writer based on the value of \p Format.
65   /// For testing.
66   static ErrorOr<std::unique_ptr<SampleProfileWriter>>
67   create(std::unique_ptr<raw_ostream> &OS, SampleProfileFormat Format);
68 
69   virtual void setProfileSymbolList(ProfileSymbolList *PSL) {}
70   virtual void setToCompressAllSections() {}
71   virtual void setUseMD5() {}
72   virtual void setPartialProfile() {}
73   virtual void resetSecLayout(SectionLayout SL) {}
74 
75 protected:
76   SampleProfileWriter(std::unique_ptr<raw_ostream> &OS)
77       : OutputStream(std::move(OS)) {}
78 
79   /// Write a file header for the profile file.
80   virtual std::error_code
81   writeHeader(const StringMap<FunctionSamples> &ProfileMap) = 0;
82 
83   // Write function profiles to the profile file.
84   virtual std::error_code
85   writeFuncProfiles(const StringMap<FunctionSamples> &ProfileMap);
86 
87   /// Output stream where to emit the profile to.
88   std::unique_ptr<raw_ostream> OutputStream;
89 
90   /// Profile summary.
91   std::unique_ptr<ProfileSummary> Summary;
92 
93   /// Compute summary for this profile.
94   void computeSummary(const StringMap<FunctionSamples> &ProfileMap);
95 
96   /// Profile format.
97   SampleProfileFormat Format = SPF_None;
98 };
99 
100 /// Sample-based profile writer (text format).
101 class SampleProfileWriterText : public SampleProfileWriter {
102 public:
103   std::error_code writeSample(const FunctionSamples &S) override;
104 
105 protected:
106   SampleProfileWriterText(std::unique_ptr<raw_ostream> &OS)
107       : SampleProfileWriter(OS), Indent(0) {}
108 
109   std::error_code
110   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override {
111     return sampleprof_error::success;
112   }
113 
114 private:
115   /// Indent level to use when writing.
116   ///
117   /// This is used when printing inlined callees.
118   unsigned Indent;
119 
120   friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
121   SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
122                               SampleProfileFormat Format);
123 };
124 
125 /// Sample-based profile writer (binary format).
126 class SampleProfileWriterBinary : public SampleProfileWriter {
127 public:
128   SampleProfileWriterBinary(std::unique_ptr<raw_ostream> &OS)
129       : SampleProfileWriter(OS) {}
130 
131   virtual std::error_code writeSample(const FunctionSamples &S) override;
132 
133 protected:
134   virtual std::error_code writeMagicIdent(SampleProfileFormat Format);
135   virtual std::error_code writeNameTable();
136   virtual std::error_code
137   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
138   std::error_code writeSummary();
139   std::error_code writeNameIdx(StringRef FName);
140   std::error_code writeBody(const FunctionSamples &S);
141   inline void stablizeNameTable(std::set<StringRef> &V);
142 
143   MapVector<StringRef, uint32_t> NameTable;
144 
145   void addName(StringRef FName);
146   void addNames(const FunctionSamples &S);
147 
148 private:
149   friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
150   SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
151                               SampleProfileFormat Format);
152 };
153 
154 class SampleProfileWriterRawBinary : public SampleProfileWriterBinary {
155   using SampleProfileWriterBinary::SampleProfileWriterBinary;
156 };
157 
158 const std::array<SmallVector<SecHdrTableEntry, 8>, NumOfLayout>
159     ExtBinaryHdrLayoutTable = {
160         // Note that SecFuncOffsetTable section is written after SecLBRProfile
161         // in the profile, but is put before SecLBRProfile in SectionHdrLayout.
162         // This is because sample reader follows the order in SectionHdrLayout
163         // to read each section. To read function profiles on demand, sample
164         // reader need to get the offset of each function profile first.
165         //
166         // DefaultLayout
167         SmallVector<SecHdrTableEntry, 8>({{SecProfSummary, 0, 0, 0, 0},
168                                           {SecNameTable, 0, 0, 0, 0},
169                                           {SecFuncOffsetTable, 0, 0, 0, 0},
170                                           {SecLBRProfile, 0, 0, 0, 0},
171                                           {SecProfileSymbolList, 0, 0, 0, 0},
172                                           {SecFuncMetadata, 0, 0, 0, 0}}),
173         // CtxSplitLayout
174         SmallVector<SecHdrTableEntry, 8>({{SecProfSummary, 0, 0, 0, 0},
175                                           {SecNameTable, 0, 0, 0, 0},
176                                           // profile with context
177                                           // for next two sections
178                                           {SecFuncOffsetTable, 0, 0, 0, 0},
179                                           {SecLBRProfile, 0, 0, 0, 0},
180                                           // profile without context
181                                           // for next two sections
182                                           {SecFuncOffsetTable, 0, 0, 0, 0},
183                                           {SecLBRProfile, 0, 0, 0, 0},
184                                           {SecProfileSymbolList, 0, 0, 0, 0},
185                                           {SecFuncMetadata, 0, 0, 0, 0}}),
186 };
187 
188 class SampleProfileWriterExtBinaryBase : public SampleProfileWriterBinary {
189   using SampleProfileWriterBinary::SampleProfileWriterBinary;
190 public:
191   virtual std::error_code
192   write(const StringMap<FunctionSamples> &ProfileMap) override;
193 
194   virtual void setToCompressAllSections() override;
195   void setToCompressSection(SecType Type);
196   virtual std::error_code writeSample(const FunctionSamples &S) override;
197 
198   // Set to use MD5 to represent string in NameTable.
199   virtual void setUseMD5() override {
200     UseMD5 = true;
201     addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagMD5Name);
202     // MD5 will be stored as plain uint64_t instead of variable-length
203     // quantity format in NameTable section.
204     addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagFixedLengthMD5);
205   }
206 
207   // Set the profile to be partial. It means the profile is for
208   // common/shared code. The common profile is usually merged from
209   // profiles collected from running other targets.
210   virtual void setPartialProfile() override {
211     addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagPartial);
212   }
213 
214   virtual void setProfileSymbolList(ProfileSymbolList *PSL) override {
215     ProfSymList = PSL;
216   };
217 
218   virtual void resetSecLayout(SectionLayout SL) override {
219     verifySecLayout(SL);
220 #ifndef NDEBUG
221     // Make sure resetSecLayout is called before any flag setting.
222     for (auto &Entry : SectionHdrLayout) {
223       assert(Entry.Flags == 0 &&
224              "resetSecLayout has to be called before any flag setting");
225     }
226 #endif
227     SecLayout = SL;
228     SectionHdrLayout = ExtBinaryHdrLayoutTable[SL];
229   }
230 
231 protected:
232   uint64_t markSectionStart(SecType Type, uint32_t LayoutIdx);
233   std::error_code addNewSection(SecType Sec, uint32_t LayoutIdx,
234                                 uint64_t SectionStart);
235   template <class SecFlagType>
236   void addSectionFlag(SecType Type, SecFlagType Flag) {
237     for (auto &Entry : SectionHdrLayout) {
238       if (Entry.Type == Type)
239         addSecFlag(Entry, Flag);
240     }
241   }
242   template <class SecFlagType>
243   void addSectionFlag(uint32_t SectionIdx, SecFlagType Flag) {
244     addSecFlag(SectionHdrLayout[SectionIdx], Flag);
245   }
246 
247   // placeholder for subclasses to dispatch their own section writers.
248   virtual std::error_code writeCustomSection(SecType Type) = 0;
249   // Verify the SecLayout is supported by the format.
250   virtual void verifySecLayout(SectionLayout SL) = 0;
251 
252   // specify the order to write sections.
253   virtual std::error_code
254   writeSections(const StringMap<FunctionSamples> &ProfileMap) = 0;
255 
256   // Dispatch section writer for each section. \p LayoutIdx is the sequence
257   // number indicating where the section is located in SectionHdrLayout.
258   virtual std::error_code
259   writeOneSection(SecType Type, uint32_t LayoutIdx,
260                   const StringMap<FunctionSamples> &ProfileMap);
261 
262   // Helper function to write name table.
263   virtual std::error_code writeNameTable() override;
264 
265   std::error_code writeFuncMetadata(const StringMap<FunctionSamples> &Profiles);
266 
267   // Functions to write various kinds of sections.
268   std::error_code
269   writeNameTableSection(const StringMap<FunctionSamples> &ProfileMap);
270   std::error_code writeFuncOffsetTable();
271   std::error_code writeProfileSymbolListSection();
272 
273   SectionLayout SecLayout = DefaultLayout;
274   // Specifiy the order of sections in section header table. Note
275   // the order of sections in SecHdrTable may be different that the
276   // order in SectionHdrLayout. sample Reader will follow the order
277   // in SectionHdrLayout to read each section.
278   SmallVector<SecHdrTableEntry, 8> SectionHdrLayout =
279       ExtBinaryHdrLayoutTable[DefaultLayout];
280 
281   // Save the start of SecLBRProfile so we can compute the offset to the
282   // start of SecLBRProfile for each Function's Profile and will keep it
283   // in FuncOffsetTable.
284   uint64_t SecLBRProfileStart = 0;
285 
286 private:
287   void allocSecHdrTable();
288   std::error_code writeSecHdrTable();
289   virtual std::error_code
290   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
291   std::error_code compressAndOutput();
292 
293   // We will swap the raw_ostream held by LocalBufStream and that
294   // held by OutputStream if we try to add a section which needs
295   // compression. After the swap, all the data written to output
296   // will be temporarily buffered into the underlying raw_string_ostream
297   // originally held by LocalBufStream. After the data writing for the
298   // section is completed, compress the data in the local buffer,
299   // swap the raw_ostream back and write the compressed data to the
300   // real output.
301   std::unique_ptr<raw_ostream> LocalBufStream;
302   // The location where the output stream starts.
303   uint64_t FileStart;
304   // The location in the output stream where the SecHdrTable should be
305   // written to.
306   uint64_t SecHdrTableOffset;
307   // The table contains SecHdrTableEntry entries in order of how they are
308   // populated in the writer. It may be different from the order in
309   // SectionHdrLayout which specifies the sequence in which sections will
310   // be read.
311   std::vector<SecHdrTableEntry> SecHdrTable;
312 
313   // FuncOffsetTable maps function name to its profile offset in SecLBRProfile
314   // section. It is used to load function profile on demand.
315   MapVector<StringRef, uint64_t> FuncOffsetTable;
316   // Whether to use MD5 to represent string.
317   bool UseMD5 = false;
318 
319   ProfileSymbolList *ProfSymList = nullptr;
320 };
321 
322 class SampleProfileWriterExtBinary : public SampleProfileWriterExtBinaryBase {
323 public:
324   SampleProfileWriterExtBinary(std::unique_ptr<raw_ostream> &OS)
325       : SampleProfileWriterExtBinaryBase(OS) {}
326 
327 private:
328   std::error_code
329   writeDefaultLayout(const StringMap<FunctionSamples> &ProfileMap);
330   std::error_code
331   writeCtxSplitLayout(const StringMap<FunctionSamples> &ProfileMap);
332 
333   virtual std::error_code
334   writeSections(const StringMap<FunctionSamples> &ProfileMap) override;
335 
336   virtual std::error_code writeCustomSection(SecType Type) override {
337     return sampleprof_error::success;
338   };
339 
340   virtual void verifySecLayout(SectionLayout SL) override {
341     assert((SL == DefaultLayout || SL == CtxSplitLayout) &&
342            "Unsupported layout");
343   }
344 };
345 
346 // CompactBinary is a compact format of binary profile which both reduces
347 // the profile size and the load time needed when compiling. It has two
348 // major difference with Binary format.
349 // 1. It represents all the strings in name table using md5 hash.
350 // 2. It saves a function offset table which maps function name index to
351 // the offset of its function profile to the start of the binary profile,
352 // so by using the function offset table, for those function profiles which
353 // will not be needed when compiling a module, the profile reader does't
354 // have to read them and it saves compile time if the profile size is huge.
355 // The layout of the compact format is shown as follows:
356 //
357 //    Part1: Profile header, the same as binary format, containing magic
358 //           number, version, summary, name table...
359 //    Part2: Function Offset Table Offset, which saves the position of
360 //           Part4.
361 //    Part3: Function profile collection
362 //             function1 profile start
363 //                 ....
364 //             function2 profile start
365 //                 ....
366 //             function3 profile start
367 //                 ....
368 //                ......
369 //    Part4: Function Offset Table
370 //             function1 name index --> function1 profile start
371 //             function2 name index --> function2 profile start
372 //             function3 name index --> function3 profile start
373 //
374 // We need Part2 because profile reader can use it to find out and read
375 // function offset table without reading Part3 first.
376 class SampleProfileWriterCompactBinary : public SampleProfileWriterBinary {
377   using SampleProfileWriterBinary::SampleProfileWriterBinary;
378 
379 public:
380   virtual std::error_code writeSample(const FunctionSamples &S) override;
381   virtual std::error_code
382   write(const StringMap<FunctionSamples> &ProfileMap) override;
383 
384 protected:
385   /// The table mapping from function name to the offset of its FunctionSample
386   /// towards profile start.
387   MapVector<StringRef, uint64_t> FuncOffsetTable;
388   /// The offset of the slot to be filled with the offset of FuncOffsetTable
389   /// towards profile start.
390   uint64_t TableOffset;
391   virtual std::error_code writeNameTable() override;
392   virtual std::error_code
393   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
394   std::error_code writeFuncOffsetTable();
395 };
396 
397 } // end namespace sampleprof
398 } // end namespace llvm
399 
400 #endif // LLVM_PROFILEDATA_SAMPLEPROFWRITER_H
401