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