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   virtual void setPartialProfile() {}
63 
64 protected:
65   SampleProfileWriter(std::unique_ptr<raw_ostream> &OS)
66       : OutputStream(std::move(OS)) {}
67 
68   /// Write a file header for the profile file.
69   virtual std::error_code
70   writeHeader(const StringMap<FunctionSamples> &ProfileMap) = 0;
71 
72   // Write function profiles to the profile file.
73   virtual std::error_code
74   writeFuncProfiles(const StringMap<FunctionSamples> &ProfileMap);
75 
76   /// Output stream where to emit the profile to.
77   std::unique_ptr<raw_ostream> OutputStream;
78 
79   /// Profile summary.
80   std::unique_ptr<ProfileSummary> Summary;
81 
82   /// Compute summary for this profile.
83   void computeSummary(const StringMap<FunctionSamples> &ProfileMap);
84 
85   /// Profile format.
86   SampleProfileFormat Format = SPF_None;
87 };
88 
89 /// Sample-based profile writer (text format).
90 class SampleProfileWriterText : public SampleProfileWriter {
91 public:
92   std::error_code writeSample(const FunctionSamples &S) override;
93 
94 protected:
95   SampleProfileWriterText(std::unique_ptr<raw_ostream> &OS)
96       : SampleProfileWriter(OS), Indent(0) {}
97 
98   std::error_code
99   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override {
100     return sampleprof_error::success;
101   }
102 
103 private:
104   /// Indent level to use when writing.
105   ///
106   /// This is used when printing inlined callees.
107   unsigned Indent;
108 
109   friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
110   SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
111                               SampleProfileFormat Format);
112 };
113 
114 /// Sample-based profile writer (binary format).
115 class SampleProfileWriterBinary : public SampleProfileWriter {
116 public:
117   SampleProfileWriterBinary(std::unique_ptr<raw_ostream> &OS)
118       : SampleProfileWriter(OS) {}
119 
120   virtual std::error_code writeSample(const FunctionSamples &S) override;
121 
122 protected:
123   virtual std::error_code writeMagicIdent(SampleProfileFormat Format);
124   virtual std::error_code writeNameTable();
125   virtual std::error_code
126   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
127   std::error_code writeSummary();
128   std::error_code writeNameIdx(StringRef FName);
129   std::error_code writeBody(const FunctionSamples &S);
130   inline void stablizeNameTable(std::set<StringRef> &V);
131 
132   MapVector<StringRef, uint32_t> NameTable;
133 
134   void addName(StringRef FName);
135   void addNames(const FunctionSamples &S);
136 
137 private:
138   friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
139   SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
140                               SampleProfileFormat Format);
141 };
142 
143 class SampleProfileWriterRawBinary : public SampleProfileWriterBinary {
144   using SampleProfileWriterBinary::SampleProfileWriterBinary;
145 };
146 
147 class SampleProfileWriterExtBinaryBase : public SampleProfileWriterBinary {
148   using SampleProfileWriterBinary::SampleProfileWriterBinary;
149 public:
150   virtual std::error_code
151   write(const StringMap<FunctionSamples> &ProfileMap) override;
152 
153   virtual void setToCompressAllSections() override;
154   void setToCompressSection(SecType Type);
155   virtual std::error_code writeSample(const FunctionSamples &S) override;
156 
157   // Set to use MD5 to represent string in NameTable.
158   virtual void setUseMD5() override {
159     UseMD5 = true;
160     addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagMD5Name);
161     // MD5 will be stored as plain uint64_t instead of variable-length
162     // quantity format in NameTable section.
163     addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagFixedLengthMD5);
164   }
165 
166   // Set the profile to be partial. It means the profile is for
167   // common/shared code. The common profile is usually merged from
168   // profiles collected from running other targets.
169   virtual void setPartialProfile() override {
170     addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagPartial);
171   }
172 
173   virtual void setProfileSymbolList(ProfileSymbolList *PSL) override {
174     ProfSymList = PSL;
175   };
176 
177 protected:
178   uint64_t markSectionStart(SecType Type, uint32_t LayoutIdx);
179   std::error_code addNewSection(SecType Sec, uint32_t LayoutIdx,
180                                 uint64_t SectionStart);
181   template <class SecFlagType>
182   void addSectionFlag(SecType Type, SecFlagType Flag) {
183     for (auto &Entry : SectionHdrLayout) {
184       if (Entry.Type == Type)
185         addSecFlag(Entry, Flag);
186     }
187   }
188 
189   // placeholder for subclasses to dispatch their own section writers.
190   virtual std::error_code writeCustomSection(SecType Type) = 0;
191 
192   virtual void initSectionHdrLayout() = 0;
193   // specify the order to write sections.
194   virtual std::error_code
195   writeSections(const StringMap<FunctionSamples> &ProfileMap) = 0;
196 
197   // Dispatch section writer for each section. \p LayoutIdx is the sequence
198   // number indicating where the section is located in SectionHdrLayout.
199   virtual std::error_code
200   writeOneSection(SecType Type, uint32_t LayoutIdx,
201                   const StringMap<FunctionSamples> &ProfileMap);
202 
203   // Helper function to write name table.
204   virtual std::error_code writeNameTable() override;
205 
206   std::error_code writeFuncMetadata(const StringMap<FunctionSamples> &Profiles);
207 
208   // Functions to write various kinds of sections.
209   std::error_code
210   writeNameTableSection(const StringMap<FunctionSamples> &ProfileMap);
211   std::error_code writeFuncOffsetTable();
212   std::error_code writeProfileSymbolListSection();
213 
214   // Specifiy the order of sections in section header table. Note
215   // the order of sections in SecHdrTable may be different that the
216   // order in SectionHdrLayout. sample Reader will follow the order
217   // in SectionHdrLayout to read each section.
218   SmallVector<SecHdrTableEntry, 8> SectionHdrLayout;
219 
220   // Save the start of SecLBRProfile so we can compute the offset to the
221   // start of SecLBRProfile for each Function's Profile and will keep it
222   // in FuncOffsetTable.
223   uint64_t SecLBRProfileStart = 0;
224 
225 private:
226   void allocSecHdrTable();
227   std::error_code writeSecHdrTable();
228   virtual std::error_code
229   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
230   std::error_code compressAndOutput();
231 
232   // We will swap the raw_ostream held by LocalBufStream and that
233   // held by OutputStream if we try to add a section which needs
234   // compression. After the swap, all the data written to output
235   // will be temporarily buffered into the underlying raw_string_ostream
236   // originally held by LocalBufStream. After the data writing for the
237   // section is completed, compress the data in the local buffer,
238   // swap the raw_ostream back and write the compressed data to the
239   // real output.
240   std::unique_ptr<raw_ostream> LocalBufStream;
241   // The location where the output stream starts.
242   uint64_t FileStart;
243   // The location in the output stream where the SecHdrTable should be
244   // written to.
245   uint64_t SecHdrTableOffset;
246   // The table contains SecHdrTableEntry entries in order of how they are
247   // populated in the writer. It may be different from the order in
248   // SectionHdrLayout which specifies the sequence in which sections will
249   // be read.
250   std::vector<SecHdrTableEntry> SecHdrTable;
251 
252   // FuncOffsetTable maps function name to its profile offset in SecLBRProfile
253   // section. It is used to load function profile on demand.
254   MapVector<StringRef, uint64_t> FuncOffsetTable;
255   // Whether to use MD5 to represent string.
256   bool UseMD5 = false;
257 
258   ProfileSymbolList *ProfSymList = nullptr;
259 };
260 
261 class SampleProfileWriterExtBinary : public SampleProfileWriterExtBinaryBase {
262 public:
263   SampleProfileWriterExtBinary(std::unique_ptr<raw_ostream> &OS)
264       : SampleProfileWriterExtBinaryBase(OS) {
265     initSectionHdrLayout();
266   }
267 
268 private:
269   virtual void initSectionHdrLayout() override {
270     // Note that SecFuncOffsetTable section is written after SecLBRProfile
271     // in the profile, but is put before SecLBRProfile in SectionHdrLayout.
272     //
273     // This is because sample reader follows the order of SectionHdrLayout to
274     // read each section, to read function profiles on demand sample reader
275     // need to get the offset of each function profile first.
276     //
277     // SecFuncOffsetTable section is written after SecLBRProfile in the
278     // profile because FuncOffsetTable needs to be populated while section
279     // SecLBRProfile is written.
280     SectionHdrLayout = {
281         {SecProfSummary, 0, 0, 0, 0},       {SecNameTable, 0, 0, 0, 0},
282         {SecFuncOffsetTable, 0, 0, 0, 0},   {SecLBRProfile, 0, 0, 0, 0},
283         {SecProfileSymbolList, 0, 0, 0, 0}, {SecFuncMetadata, 0, 0, 0, 0}};
284   };
285   virtual std::error_code
286   writeSections(const StringMap<FunctionSamples> &ProfileMap) override;
287 
288   virtual std::error_code writeCustomSection(SecType Type) override {
289     return sampleprof_error::success;
290   };
291 };
292 
293 // CompactBinary is a compact format of binary profile which both reduces
294 // the profile size and the load time needed when compiling. It has two
295 // major difference with Binary format.
296 // 1. It represents all the strings in name table using md5 hash.
297 // 2. It saves a function offset table which maps function name index to
298 // the offset of its function profile to the start of the binary profile,
299 // so by using the function offset table, for those function profiles which
300 // will not be needed when compiling a module, the profile reader does't
301 // have to read them and it saves compile time if the profile size is huge.
302 // The layout of the compact format is shown as follows:
303 //
304 //    Part1: Profile header, the same as binary format, containing magic
305 //           number, version, summary, name table...
306 //    Part2: Function Offset Table Offset, which saves the position of
307 //           Part4.
308 //    Part3: Function profile collection
309 //             function1 profile start
310 //                 ....
311 //             function2 profile start
312 //                 ....
313 //             function3 profile start
314 //                 ....
315 //                ......
316 //    Part4: Function Offset Table
317 //             function1 name index --> function1 profile start
318 //             function2 name index --> function2 profile start
319 //             function3 name index --> function3 profile start
320 //
321 // We need Part2 because profile reader can use it to find out and read
322 // function offset table without reading Part3 first.
323 class SampleProfileWriterCompactBinary : public SampleProfileWriterBinary {
324   using SampleProfileWriterBinary::SampleProfileWriterBinary;
325 
326 public:
327   virtual std::error_code writeSample(const FunctionSamples &S) override;
328   virtual std::error_code
329   write(const StringMap<FunctionSamples> &ProfileMap) override;
330 
331 protected:
332   /// The table mapping from function name to the offset of its FunctionSample
333   /// towards profile start.
334   MapVector<StringRef, uint64_t> FuncOffsetTable;
335   /// The offset of the slot to be filled with the offset of FuncOffsetTable
336   /// towards profile start.
337   uint64_t TableOffset;
338   virtual std::error_code writeNameTable() override;
339   virtual std::error_code
340   writeHeader(const StringMap<FunctionSamples> &ProfileMap) override;
341   std::error_code writeFuncOffsetTable();
342 };
343 
344 } // end namespace sampleprof
345 } // end namespace llvm
346 
347 #endif // LLVM_PROFILEDATA_SAMPLEPROFWRITER_H
348