xref: /llvm-project/llvm/include/llvm/ProfileData/SampleProfWriter.h (revision 75e9d191f52b047ea839f75ab2a7a7d9f8c6becd)
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/StringRef.h"
17 #include "llvm/IR/ProfileSummary.h"
18 #include "llvm/ProfileData/SampleProf.h"
19 #include "llvm/Support/ErrorOr.h"
20 #include "llvm/Support/raw_ostream.h"
21 #include <cstdint>
22 #include <memory>
23 #include <set>
24 #include <system_error>
25 
26 namespace llvm {
27 namespace sampleprof {
28 
29 enum SectionLayout {
30   DefaultLayout,
31   // The layout splits profile with inlined functions from profile without
32   // inlined functions. When Thinlto is enabled, ThinLTO postlink phase only
33   // has to load profile with inlined functions and can skip the other part.
34   CtxSplitLayout,
35   NumOfLayout,
36 };
37 
38 /// When writing a profile with size limit, user may want to use a different
39 /// strategy to reduce function count other than dropping functions with fewest
40 /// samples first. In this case a class implementing the same interfaces should
41 /// be provided to SampleProfileWriter::writeWithSizeLimit().
42 class FunctionPruningStrategy {
43 protected:
44   SampleProfileMap &ProfileMap;
45   size_t OutputSizeLimit;
46 
47 public:
48   /// \p ProfileMap A reference to the original profile map. It will be modified
49   /// by Erase().
50   /// \p OutputSizeLimit Size limit in bytes of the output profile. This is
51   /// necessary to estimate how many functions to remove.
52   FunctionPruningStrategy(SampleProfileMap &ProfileMap, size_t OutputSizeLimit)
53       : ProfileMap(ProfileMap), OutputSizeLimit(OutputSizeLimit) {}
54 
55   virtual ~FunctionPruningStrategy() = default;
56 
57   /// SampleProfileWriter::writeWithSizeLimit() calls this after every write
58   /// iteration if the output size still exceeds the limit. This function
59   /// should erase some functions from the profile map so that the writer tries
60   /// to write the profile again with fewer functions. At least 1 entry from the
61   /// profile map must be erased.
62   ///
63   /// \p CurrentOutputSize Number of bytes in the output if current profile map
64   /// is written.
65   virtual void Erase(size_t CurrentOutputSize) = 0;
66 };
67 
68 class DefaultFunctionPruningStrategy : public FunctionPruningStrategy {
69   std::vector<NameFunctionSamples> SortedFunctions;
70 
71 public:
72   DefaultFunctionPruningStrategy(SampleProfileMap &ProfileMap,
73                                  size_t OutputSizeLimit);
74 
75   /// In this default implementation, functions with fewest samples are dropped
76   /// first. Since the exact size of the output cannot be easily calculated due
77   /// to compression, we use a heuristic to remove as many functions as
78   /// necessary but not too many, aiming to minimize the number of write
79   /// iterations.
80   /// Empirically, functions with larger total sample count contain linearly
81   /// more sample entries, meaning it takes linearly more space to write them.
82   /// The cumulative length is therefore quadratic if all functions are sorted
83   /// by total sample count.
84   /// TODO: Find better heuristic.
85   void Erase(size_t CurrentOutputSize) override;
86 };
87 
88 /// Sample-based profile writer. Base class.
89 class SampleProfileWriter {
90 public:
91   virtual ~SampleProfileWriter() = default;
92 
93   /// Write sample profiles in \p S.
94   ///
95   /// \returns status code of the file update operation.
96   virtual std::error_code writeSample(const FunctionSamples &S) = 0;
97 
98   /// Write all the sample profiles in the given map of samples.
99   ///
100   /// \returns status code of the file update operation.
101   virtual std::error_code write(const SampleProfileMap &ProfileMap);
102 
103   /// Write sample profiles up to given size limit, using the pruning strategy
104   /// to drop some functions if necessary.
105   ///
106   /// \returns status code of the file update operation.
107   template <typename FunctionPruningStrategy = DefaultFunctionPruningStrategy>
108   std::error_code writeWithSizeLimit(SampleProfileMap &ProfileMap,
109                                      size_t OutputSizeLimit) {
110     FunctionPruningStrategy Strategy(ProfileMap, OutputSizeLimit);
111     return writeWithSizeLimitInternal(ProfileMap, OutputSizeLimit, &Strategy);
112   }
113 
114   raw_ostream &getOutputStream() { return *OutputStream; }
115 
116   /// Profile writer factory.
117   ///
118   /// Create a new file writer based on the value of \p Format.
119   static ErrorOr<std::unique_ptr<SampleProfileWriter>>
120   create(StringRef Filename, SampleProfileFormat Format);
121 
122   /// Create a new stream writer based on the value of \p Format.
123   /// For testing.
124   static ErrorOr<std::unique_ptr<SampleProfileWriter>>
125   create(std::unique_ptr<raw_ostream> &OS, SampleProfileFormat Format);
126 
127   virtual void setProfileSymbolList(ProfileSymbolList *PSL) {}
128   virtual void setToCompressAllSections() {}
129   virtual void setUseMD5() {}
130   virtual void setPartialProfile() {}
131   virtual void setUseCtxSplitLayout() {}
132 
133 protected:
134   SampleProfileWriter(std::unique_ptr<raw_ostream> &OS)
135       : OutputStream(std::move(OS)) {}
136 
137   /// Write a file header for the profile file.
138   virtual std::error_code writeHeader(const SampleProfileMap &ProfileMap) = 0;
139 
140   // Write function profiles to the profile file.
141   virtual std::error_code writeFuncProfiles(const SampleProfileMap &ProfileMap);
142 
143   std::error_code writeWithSizeLimitInternal(SampleProfileMap &ProfileMap,
144                                              size_t OutputSizeLimit,
145                                              FunctionPruningStrategy *Strategy);
146 
147   /// For writeWithSizeLimit in text mode, each newline takes 1 additional byte
148   /// on Windows when actually written to the file, but not written to a memory
149   /// buffer. This needs to be accounted for when rewriting the profile.
150   size_t LineCount;
151 
152   /// Output stream where to emit the profile to.
153   std::unique_ptr<raw_ostream> OutputStream;
154 
155   /// Profile summary.
156   std::unique_ptr<ProfileSummary> Summary;
157 
158   /// Compute summary for this profile.
159   void computeSummary(const SampleProfileMap &ProfileMap);
160 
161   /// Profile format.
162   SampleProfileFormat Format = SPF_None;
163 };
164 
165 /// Sample-based profile writer (text format).
166 class SampleProfileWriterText : public SampleProfileWriter {
167 public:
168   std::error_code writeSample(const FunctionSamples &S) override;
169 
170 protected:
171   SampleProfileWriterText(std::unique_ptr<raw_ostream> &OS)
172       : SampleProfileWriter(OS) {}
173 
174   std::error_code writeHeader(const SampleProfileMap &ProfileMap) override {
175     LineCount = 0;
176     return sampleprof_error::success;
177   }
178 
179   void setUseCtxSplitLayout() override {
180     MarkFlatProfiles = true;
181   }
182 
183 private:
184   /// Indent level to use when writing.
185   ///
186   /// This is used when printing inlined callees.
187   unsigned Indent = 0;
188 
189   /// If set, writes metadata "!Flat" to functions without inlined functions.
190   /// This flag is for manual inspection only, it has no effect for the profile
191   /// reader because a text sample profile is read sequentially and functions
192   /// cannot be skipped.
193   bool MarkFlatProfiles = false;
194 
195   friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
196   SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
197                               SampleProfileFormat Format);
198 };
199 
200 /// Sample-based profile writer (binary format).
201 class SampleProfileWriterBinary : public SampleProfileWriter {
202 public:
203   SampleProfileWriterBinary(std::unique_ptr<raw_ostream> &OS)
204       : SampleProfileWriter(OS) {}
205 
206   std::error_code writeSample(const FunctionSamples &S) override;
207 
208 protected:
209   virtual MapVector<FunctionId, uint32_t> &getNameTable() { return NameTable; }
210   virtual std::error_code writeMagicIdent(SampleProfileFormat Format);
211   virtual std::error_code writeNameTable();
212   std::error_code writeHeader(const SampleProfileMap &ProfileMap) override;
213   std::error_code writeSummary();
214   virtual std::error_code writeContextIdx(const SampleContext &Context);
215   std::error_code writeNameIdx(FunctionId FName);
216   std::error_code writeBody(const FunctionSamples &S);
217   inline void stablizeNameTable(MapVector<FunctionId, uint32_t> &NameTable,
218                                 std::set<FunctionId> &V);
219 
220   MapVector<FunctionId, uint32_t> NameTable;
221 
222   void addName(FunctionId FName);
223   virtual void addContext(const SampleContext &Context);
224   void addNames(const FunctionSamples &S);
225 
226 private:
227   friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
228   SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
229                               SampleProfileFormat Format);
230 };
231 
232 class SampleProfileWriterRawBinary : public SampleProfileWriterBinary {
233   using SampleProfileWriterBinary::SampleProfileWriterBinary;
234 };
235 
236 const std::array<SmallVector<SecHdrTableEntry, 8>, NumOfLayout>
237     ExtBinaryHdrLayoutTable = {
238         // Note that SecFuncOffsetTable section is written after SecLBRProfile
239         // in the profile, but is put before SecLBRProfile in SectionHdrLayout.
240         // This is because sample reader follows the order in SectionHdrLayout
241         // to read each section. To read function profiles on demand, sample
242         // reader need to get the offset of each function profile first.
243         //
244         // DefaultLayout
245         SmallVector<SecHdrTableEntry, 8>({{SecProfSummary, 0, 0, 0, 0},
246                                           {SecNameTable, 0, 0, 0, 0},
247                                           {SecCSNameTable, 0, 0, 0, 0},
248                                           {SecFuncOffsetTable, 0, 0, 0, 0},
249                                           {SecLBRProfile, 0, 0, 0, 0},
250                                           {SecProfileSymbolList, 0, 0, 0, 0},
251                                           {SecFuncMetadata, 0, 0, 0, 0}}),
252         // CtxSplitLayout
253         SmallVector<SecHdrTableEntry, 8>({{SecProfSummary, 0, 0, 0, 0},
254                                           {SecNameTable, 0, 0, 0, 0},
255                                           // profile with inlined functions
256                                           // for next two sections
257                                           {SecFuncOffsetTable, 0, 0, 0, 0},
258                                           {SecLBRProfile, 0, 0, 0, 0},
259                                           // profile without inlined functions
260                                           // for next two sections
261                                           {SecFuncOffsetTable, 0, 0, 0, 0},
262                                           {SecLBRProfile, 0, 0, 0, 0},
263                                           {SecProfileSymbolList, 0, 0, 0, 0},
264                                           {SecFuncMetadata, 0, 0, 0, 0}}),
265 };
266 
267 class SampleProfileWriterExtBinaryBase : public SampleProfileWriterBinary {
268   using SampleProfileWriterBinary::SampleProfileWriterBinary;
269 public:
270   std::error_code write(const SampleProfileMap &ProfileMap) override;
271 
272   void setToCompressAllSections() override;
273   void setToCompressSection(SecType Type);
274   std::error_code writeSample(const FunctionSamples &S) override;
275 
276   // Set to use MD5 to represent string in NameTable.
277   void setUseMD5() override {
278     UseMD5 = true;
279     addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagMD5Name);
280     // MD5 will be stored as plain uint64_t instead of variable-length
281     // quantity format in NameTable section.
282     addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagFixedLengthMD5);
283   }
284 
285   // Set the profile to be partial. It means the profile is for
286   // common/shared code. The common profile is usually merged from
287   // profiles collected from running other targets.
288   void setPartialProfile() override {
289     addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagPartial);
290   }
291 
292   void setProfileSymbolList(ProfileSymbolList *PSL) override {
293     ProfSymList = PSL;
294   };
295 
296   void setUseCtxSplitLayout() override {
297     resetSecLayout(SectionLayout::CtxSplitLayout);
298   }
299 
300   void resetSecLayout(SectionLayout SL) {
301     verifySecLayout(SL);
302 #ifndef NDEBUG
303     // Make sure resetSecLayout is called before any flag setting.
304     for (auto &Entry : SectionHdrLayout) {
305       assert(Entry.Flags == 0 &&
306              "resetSecLayout has to be called before any flag setting");
307     }
308 #endif
309     SecLayout = SL;
310     SectionHdrLayout = ExtBinaryHdrLayoutTable[SL];
311   }
312 
313 protected:
314   uint64_t markSectionStart(SecType Type, uint32_t LayoutIdx);
315   std::error_code addNewSection(SecType Sec, uint32_t LayoutIdx,
316                                 uint64_t SectionStart);
317   template <class SecFlagType>
318   void addSectionFlag(SecType Type, SecFlagType Flag) {
319     for (auto &Entry : SectionHdrLayout) {
320       if (Entry.Type == Type)
321         addSecFlag(Entry, Flag);
322     }
323   }
324   template <class SecFlagType>
325   void addSectionFlag(uint32_t SectionIdx, SecFlagType Flag) {
326     addSecFlag(SectionHdrLayout[SectionIdx], Flag);
327   }
328 
329   void addContext(const SampleContext &Context) override;
330 
331   // placeholder for subclasses to dispatch their own section writers.
332   virtual std::error_code writeCustomSection(SecType Type) = 0;
333   // Verify the SecLayout is supported by the format.
334   virtual void verifySecLayout(SectionLayout SL) = 0;
335 
336   // specify the order to write sections.
337   virtual std::error_code writeSections(const SampleProfileMap &ProfileMap) = 0;
338 
339   // Dispatch section writer for each section. \p LayoutIdx is the sequence
340   // number indicating where the section is located in SectionHdrLayout.
341   virtual std::error_code writeOneSection(SecType Type, uint32_t LayoutIdx,
342                                           const SampleProfileMap &ProfileMap);
343 
344   // Helper function to write name table.
345   std::error_code writeNameTable() override;
346   std::error_code writeContextIdx(const SampleContext &Context) override;
347   std::error_code writeCSNameIdx(const SampleContext &Context);
348   std::error_code writeCSNameTableSection();
349 
350   std::error_code writeFuncMetadata(const SampleProfileMap &Profiles);
351   std::error_code writeFuncMetadata(const FunctionSamples &Profile);
352 
353   // Functions to write various kinds of sections.
354   std::error_code writeNameTableSection(const SampleProfileMap &ProfileMap);
355   std::error_code writeFuncOffsetTable();
356   std::error_code writeProfileSymbolListSection();
357 
358   SectionLayout SecLayout = DefaultLayout;
359   // Specifiy the order of sections in section header table. Note
360   // the order of sections in SecHdrTable may be different that the
361   // order in SectionHdrLayout. sample Reader will follow the order
362   // in SectionHdrLayout to read each section.
363   SmallVector<SecHdrTableEntry, 8> SectionHdrLayout =
364       ExtBinaryHdrLayoutTable[DefaultLayout];
365 
366   // Save the start of SecLBRProfile so we can compute the offset to the
367   // start of SecLBRProfile for each Function's Profile and will keep it
368   // in FuncOffsetTable.
369   uint64_t SecLBRProfileStart = 0;
370 
371 private:
372   void allocSecHdrTable();
373   std::error_code writeSecHdrTable();
374   std::error_code writeHeader(const SampleProfileMap &ProfileMap) override;
375   std::error_code compressAndOutput();
376 
377   // We will swap the raw_ostream held by LocalBufStream and that
378   // held by OutputStream if we try to add a section which needs
379   // compression. After the swap, all the data written to output
380   // will be temporarily buffered into the underlying raw_string_ostream
381   // originally held by LocalBufStream. After the data writing for the
382   // section is completed, compress the data in the local buffer,
383   // swap the raw_ostream back and write the compressed data to the
384   // real output.
385   std::unique_ptr<raw_ostream> LocalBufStream;
386   // The location where the output stream starts.
387   uint64_t FileStart;
388   // The location in the output stream where the SecHdrTable should be
389   // written to.
390   uint64_t SecHdrTableOffset;
391   // The table contains SecHdrTableEntry entries in order of how they are
392   // populated in the writer. It may be different from the order in
393   // SectionHdrLayout which specifies the sequence in which sections will
394   // be read.
395   std::vector<SecHdrTableEntry> SecHdrTable;
396 
397   // FuncOffsetTable maps function context to its profile offset in
398   // SecLBRProfile section. It is used to load function profile on demand.
399   MapVector<SampleContext, uint64_t> FuncOffsetTable;
400   // Whether to use MD5 to represent string.
401   bool UseMD5 = false;
402 
403   /// CSNameTable maps function context to its offset in SecCSNameTable section.
404   /// The offset will be used everywhere where the context is referenced.
405   MapVector<SampleContext, uint32_t> CSNameTable;
406 
407   ProfileSymbolList *ProfSymList = nullptr;
408 };
409 
410 class SampleProfileWriterExtBinary : public SampleProfileWriterExtBinaryBase {
411 public:
412   SampleProfileWriterExtBinary(std::unique_ptr<raw_ostream> &OS)
413       : SampleProfileWriterExtBinaryBase(OS) {}
414 
415 private:
416   std::error_code writeDefaultLayout(const SampleProfileMap &ProfileMap);
417   std::error_code writeCtxSplitLayout(const SampleProfileMap &ProfileMap);
418 
419   std::error_code writeSections(const SampleProfileMap &ProfileMap) override;
420 
421   std::error_code writeCustomSection(SecType Type) override {
422     return sampleprof_error::success;
423   };
424 
425   void verifySecLayout(SectionLayout SL) override {
426     assert((SL == DefaultLayout || SL == CtxSplitLayout) &&
427            "Unsupported layout");
428   }
429 };
430 
431 } // end namespace sampleprof
432 } // end namespace llvm
433 
434 #endif // LLVM_PROFILEDATA_SAMPLEPROFWRITER_H
435