Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:44:25

0001 //===- SampleProfWriter.h - Write LLVM sample profile data ------*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // This file contains definitions needed for writing sample profiles.
0010 //
0011 //===----------------------------------------------------------------------===//
0012 #ifndef LLVM_PROFILEDATA_SAMPLEPROFWRITER_H
0013 #define LLVM_PROFILEDATA_SAMPLEPROFWRITER_H
0014 
0015 #include "llvm/ADT/MapVector.h"
0016 #include "llvm/ADT/StringRef.h"
0017 #include "llvm/IR/ProfileSummary.h"
0018 #include "llvm/ProfileData/SampleProf.h"
0019 #include "llvm/Support/ErrorOr.h"
0020 #include "llvm/Support/raw_ostream.h"
0021 #include <cstdint>
0022 #include <memory>
0023 #include <set>
0024 #include <system_error>
0025 
0026 namespace llvm {
0027 namespace sampleprof {
0028 
0029 enum SectionLayout {
0030   DefaultLayout,
0031   // The layout splits profile with inlined functions from profile without
0032   // inlined functions. When Thinlto is enabled, ThinLTO postlink phase only
0033   // has to load profile with inlined functions and can skip the other part.
0034   CtxSplitLayout,
0035   NumOfLayout,
0036 };
0037 
0038 /// When writing a profile with size limit, user may want to use a different
0039 /// strategy to reduce function count other than dropping functions with fewest
0040 /// samples first. In this case a class implementing the same interfaces should
0041 /// be provided to SampleProfileWriter::writeWithSizeLimit().
0042 class FunctionPruningStrategy {
0043 protected:
0044   SampleProfileMap &ProfileMap;
0045   size_t OutputSizeLimit;
0046 
0047 public:
0048   /// \p ProfileMap A reference to the original profile map. It will be modified
0049   /// by Erase().
0050   /// \p OutputSizeLimit Size limit in bytes of the output profile. This is
0051   /// necessary to estimate how many functions to remove.
0052   FunctionPruningStrategy(SampleProfileMap &ProfileMap, size_t OutputSizeLimit)
0053       : ProfileMap(ProfileMap), OutputSizeLimit(OutputSizeLimit) {}
0054 
0055   virtual ~FunctionPruningStrategy() = default;
0056 
0057   /// SampleProfileWriter::writeWithSizeLimit() calls this after every write
0058   /// iteration if the output size still exceeds the limit. This function
0059   /// should erase some functions from the profile map so that the writer tries
0060   /// to write the profile again with fewer functions. At least 1 entry from the
0061   /// profile map must be erased.
0062   ///
0063   /// \p CurrentOutputSize Number of bytes in the output if current profile map
0064   /// is written.
0065   virtual void Erase(size_t CurrentOutputSize) = 0;
0066 };
0067 
0068 class DefaultFunctionPruningStrategy : public FunctionPruningStrategy {
0069   std::vector<NameFunctionSamples> SortedFunctions;
0070 
0071 public:
0072   DefaultFunctionPruningStrategy(SampleProfileMap &ProfileMap,
0073                                  size_t OutputSizeLimit);
0074 
0075   /// In this default implementation, functions with fewest samples are dropped
0076   /// first. Since the exact size of the output cannot be easily calculated due
0077   /// to compression, we use a heuristic to remove as many functions as
0078   /// necessary but not too many, aiming to minimize the number of write
0079   /// iterations.
0080   /// Empirically, functions with larger total sample count contain linearly
0081   /// more sample entries, meaning it takes linearly more space to write them.
0082   /// The cumulative length is therefore quadratic if all functions are sorted
0083   /// by total sample count.
0084   /// TODO: Find better heuristic.
0085   void Erase(size_t CurrentOutputSize) override;
0086 };
0087 
0088 /// Sample-based profile writer. Base class.
0089 class SampleProfileWriter {
0090 public:
0091   virtual ~SampleProfileWriter() = default;
0092 
0093   /// Write sample profiles in \p S.
0094   ///
0095   /// \returns status code of the file update operation.
0096   virtual std::error_code writeSample(const FunctionSamples &S) = 0;
0097 
0098   /// Write all the sample profiles in the given map of samples.
0099   ///
0100   /// \returns status code of the file update operation.
0101   virtual std::error_code write(const SampleProfileMap &ProfileMap);
0102 
0103   /// Write sample profiles up to given size limit, using the pruning strategy
0104   /// to drop some functions if necessary.
0105   ///
0106   /// \returns status code of the file update operation.
0107   template <typename FunctionPruningStrategy = DefaultFunctionPruningStrategy>
0108   std::error_code writeWithSizeLimit(SampleProfileMap &ProfileMap,
0109                                      size_t OutputSizeLimit) {
0110     FunctionPruningStrategy Strategy(ProfileMap, OutputSizeLimit);
0111     return writeWithSizeLimitInternal(ProfileMap, OutputSizeLimit, &Strategy);
0112   }
0113 
0114   raw_ostream &getOutputStream() { return *OutputStream; }
0115 
0116   /// Profile writer factory.
0117   ///
0118   /// Create a new file writer based on the value of \p Format.
0119   static ErrorOr<std::unique_ptr<SampleProfileWriter>>
0120   create(StringRef Filename, SampleProfileFormat Format);
0121 
0122   /// Create a new stream writer based on the value of \p Format.
0123   /// For testing.
0124   static ErrorOr<std::unique_ptr<SampleProfileWriter>>
0125   create(std::unique_ptr<raw_ostream> &OS, SampleProfileFormat Format);
0126 
0127   virtual void setProfileSymbolList(ProfileSymbolList *PSL) {}
0128   virtual void setToCompressAllSections() {}
0129   virtual void setUseMD5() {}
0130   virtual void setPartialProfile() {}
0131   virtual void setUseCtxSplitLayout() {}
0132 
0133 protected:
0134   SampleProfileWriter(std::unique_ptr<raw_ostream> &OS)
0135       : OutputStream(std::move(OS)) {}
0136 
0137   /// Write a file header for the profile file.
0138   virtual std::error_code writeHeader(const SampleProfileMap &ProfileMap) = 0;
0139 
0140   // Write function profiles to the profile file.
0141   virtual std::error_code writeFuncProfiles(const SampleProfileMap &ProfileMap);
0142 
0143   std::error_code writeWithSizeLimitInternal(SampleProfileMap &ProfileMap,
0144                                              size_t OutputSizeLimit,
0145                                              FunctionPruningStrategy *Strategy);
0146 
0147   /// For writeWithSizeLimit in text mode, each newline takes 1 additional byte
0148   /// on Windows when actually written to the file, but not written to a memory
0149   /// buffer. This needs to be accounted for when rewriting the profile.
0150   size_t LineCount;
0151 
0152   /// Output stream where to emit the profile to.
0153   std::unique_ptr<raw_ostream> OutputStream;
0154 
0155   /// Profile summary.
0156   std::unique_ptr<ProfileSummary> Summary;
0157 
0158   /// Compute summary for this profile.
0159   void computeSummary(const SampleProfileMap &ProfileMap);
0160 
0161   /// Profile format.
0162   SampleProfileFormat Format = SPF_None;
0163 };
0164 
0165 /// Sample-based profile writer (text format).
0166 class SampleProfileWriterText : public SampleProfileWriter {
0167 public:
0168   std::error_code writeSample(const FunctionSamples &S) override;
0169 
0170 protected:
0171   SampleProfileWriterText(std::unique_ptr<raw_ostream> &OS)
0172       : SampleProfileWriter(OS) {}
0173 
0174   std::error_code writeHeader(const SampleProfileMap &ProfileMap) override {
0175     LineCount = 0;
0176     return sampleprof_error::success;
0177   }
0178 
0179   void setUseCtxSplitLayout() override {
0180     MarkFlatProfiles = true;
0181   }
0182 
0183 private:
0184   /// Indent level to use when writing.
0185   ///
0186   /// This is used when printing inlined callees.
0187   unsigned Indent = 0;
0188 
0189   /// If set, writes metadata "!Flat" to functions without inlined functions.
0190   /// This flag is for manual inspection only, it has no effect for the profile
0191   /// reader because a text sample profile is read sequentially and functions
0192   /// cannot be skipped.
0193   bool MarkFlatProfiles = false;
0194 
0195   friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
0196   SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
0197                               SampleProfileFormat Format);
0198 };
0199 
0200 /// Sample-based profile writer (binary format).
0201 class SampleProfileWriterBinary : public SampleProfileWriter {
0202 public:
0203   SampleProfileWriterBinary(std::unique_ptr<raw_ostream> &OS)
0204       : SampleProfileWriter(OS) {}
0205 
0206   std::error_code writeSample(const FunctionSamples &S) override;
0207 
0208 protected:
0209   virtual MapVector<FunctionId, uint32_t> &getNameTable() { return NameTable; }
0210   virtual std::error_code writeMagicIdent(SampleProfileFormat Format);
0211   virtual std::error_code writeNameTable();
0212   std::error_code writeHeader(const SampleProfileMap &ProfileMap) override;
0213   std::error_code writeSummary();
0214   virtual std::error_code writeContextIdx(const SampleContext &Context);
0215   std::error_code writeNameIdx(FunctionId FName);
0216   std::error_code writeBody(const FunctionSamples &S);
0217   inline void stablizeNameTable(MapVector<FunctionId, uint32_t> &NameTable,
0218                                 std::set<FunctionId> &V);
0219   
0220   MapVector<FunctionId, uint32_t> NameTable;
0221   
0222   void addName(FunctionId FName);
0223   virtual void addContext(const SampleContext &Context);
0224   void addNames(const FunctionSamples &S);
0225 
0226 private:
0227   friend ErrorOr<std::unique_ptr<SampleProfileWriter>>
0228   SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS,
0229                               SampleProfileFormat Format);
0230 };
0231 
0232 class SampleProfileWriterRawBinary : public SampleProfileWriterBinary {
0233   using SampleProfileWriterBinary::SampleProfileWriterBinary;
0234 };
0235 
0236 const std::array<SmallVector<SecHdrTableEntry, 8>, NumOfLayout>
0237     ExtBinaryHdrLayoutTable = {
0238         // Note that SecFuncOffsetTable section is written after SecLBRProfile
0239         // in the profile, but is put before SecLBRProfile in SectionHdrLayout.
0240         // This is because sample reader follows the order in SectionHdrLayout
0241         // to read each section. To read function profiles on demand, sample
0242         // reader need to get the offset of each function profile first.
0243         //
0244         // DefaultLayout
0245         SmallVector<SecHdrTableEntry, 8>({{SecProfSummary, 0, 0, 0, 0},
0246                                           {SecNameTable, 0, 0, 0, 0},
0247                                           {SecCSNameTable, 0, 0, 0, 0},
0248                                           {SecFuncOffsetTable, 0, 0, 0, 0},
0249                                           {SecLBRProfile, 0, 0, 0, 0},
0250                                           {SecProfileSymbolList, 0, 0, 0, 0},
0251                                           {SecFuncMetadata, 0, 0, 0, 0}}),
0252         // CtxSplitLayout
0253         SmallVector<SecHdrTableEntry, 8>({{SecProfSummary, 0, 0, 0, 0},
0254                                           {SecNameTable, 0, 0, 0, 0},
0255                                           // profile with inlined functions
0256                                           // for next two sections
0257                                           {SecFuncOffsetTable, 0, 0, 0, 0},
0258                                           {SecLBRProfile, 0, 0, 0, 0},
0259                                           // profile without inlined functions
0260                                           // for next two sections
0261                                           {SecFuncOffsetTable, 0, 0, 0, 0},
0262                                           {SecLBRProfile, 0, 0, 0, 0},
0263                                           {SecProfileSymbolList, 0, 0, 0, 0},
0264                                           {SecFuncMetadata, 0, 0, 0, 0}}),
0265 };
0266 
0267 class SampleProfileWriterExtBinaryBase : public SampleProfileWriterBinary {
0268   using SampleProfileWriterBinary::SampleProfileWriterBinary;
0269 public:
0270   std::error_code write(const SampleProfileMap &ProfileMap) override;
0271 
0272   void setToCompressAllSections() override;
0273   void setToCompressSection(SecType Type);
0274   std::error_code writeSample(const FunctionSamples &S) override;
0275 
0276   // Set to use MD5 to represent string in NameTable.
0277   void setUseMD5() override {
0278     UseMD5 = true;
0279     addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagMD5Name);
0280     // MD5 will be stored as plain uint64_t instead of variable-length
0281     // quantity format in NameTable section.
0282     addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagFixedLengthMD5);
0283   }
0284 
0285   // Set the profile to be partial. It means the profile is for
0286   // common/shared code. The common profile is usually merged from
0287   // profiles collected from running other targets.
0288   void setPartialProfile() override {
0289     addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagPartial);
0290   }
0291 
0292   void setProfileSymbolList(ProfileSymbolList *PSL) override {
0293     ProfSymList = PSL;
0294   };
0295 
0296   void setUseCtxSplitLayout() override {
0297     resetSecLayout(SectionLayout::CtxSplitLayout);
0298   }
0299 
0300   void resetSecLayout(SectionLayout SL) {
0301     verifySecLayout(SL);
0302 #ifndef NDEBUG
0303     // Make sure resetSecLayout is called before any flag setting.
0304     for (auto &Entry : SectionHdrLayout) {
0305       assert(Entry.Flags == 0 &&
0306              "resetSecLayout has to be called before any flag setting");
0307     }
0308 #endif
0309     SecLayout = SL;
0310     SectionHdrLayout = ExtBinaryHdrLayoutTable[SL];
0311   }
0312 
0313 protected:
0314   uint64_t markSectionStart(SecType Type, uint32_t LayoutIdx);
0315   std::error_code addNewSection(SecType Sec, uint32_t LayoutIdx,
0316                                 uint64_t SectionStart);
0317   template <class SecFlagType>
0318   void addSectionFlag(SecType Type, SecFlagType Flag) {
0319     for (auto &Entry : SectionHdrLayout) {
0320       if (Entry.Type == Type)
0321         addSecFlag(Entry, Flag);
0322     }
0323   }
0324   template <class SecFlagType>
0325   void addSectionFlag(uint32_t SectionIdx, SecFlagType Flag) {
0326     addSecFlag(SectionHdrLayout[SectionIdx], Flag);
0327   }
0328 
0329   void addContext(const SampleContext &Context) override;
0330 
0331   // placeholder for subclasses to dispatch their own section writers.
0332   virtual std::error_code writeCustomSection(SecType Type) = 0;
0333   // Verify the SecLayout is supported by the format.
0334   virtual void verifySecLayout(SectionLayout SL) = 0;
0335 
0336   // specify the order to write sections.
0337   virtual std::error_code writeSections(const SampleProfileMap &ProfileMap) = 0;
0338 
0339   // Dispatch section writer for each section. \p LayoutIdx is the sequence
0340   // number indicating where the section is located in SectionHdrLayout.
0341   virtual std::error_code writeOneSection(SecType Type, uint32_t LayoutIdx,
0342                                           const SampleProfileMap &ProfileMap);
0343 
0344   // Helper function to write name table.
0345   std::error_code writeNameTable() override;
0346   std::error_code writeContextIdx(const SampleContext &Context) override;
0347   std::error_code writeCSNameIdx(const SampleContext &Context);
0348   std::error_code writeCSNameTableSection();
0349 
0350   std::error_code writeFuncMetadata(const SampleProfileMap &Profiles);
0351   std::error_code writeFuncMetadata(const FunctionSamples &Profile);
0352 
0353   // Functions to write various kinds of sections.
0354   std::error_code writeNameTableSection(const SampleProfileMap &ProfileMap);
0355   std::error_code writeFuncOffsetTable();
0356   std::error_code writeProfileSymbolListSection();
0357 
0358   SectionLayout SecLayout = DefaultLayout;
0359   // Specifiy the order of sections in section header table. Note
0360   // the order of sections in SecHdrTable may be different that the
0361   // order in SectionHdrLayout. sample Reader will follow the order
0362   // in SectionHdrLayout to read each section.
0363   SmallVector<SecHdrTableEntry, 8> SectionHdrLayout =
0364       ExtBinaryHdrLayoutTable[DefaultLayout];
0365 
0366   // Save the start of SecLBRProfile so we can compute the offset to the
0367   // start of SecLBRProfile for each Function's Profile and will keep it
0368   // in FuncOffsetTable.
0369   uint64_t SecLBRProfileStart = 0;
0370 
0371 private:
0372   void allocSecHdrTable();
0373   std::error_code writeSecHdrTable();
0374   std::error_code writeHeader(const SampleProfileMap &ProfileMap) override;
0375   std::error_code compressAndOutput();
0376 
0377   // We will swap the raw_ostream held by LocalBufStream and that
0378   // held by OutputStream if we try to add a section which needs
0379   // compression. After the swap, all the data written to output
0380   // will be temporarily buffered into the underlying raw_string_ostream
0381   // originally held by LocalBufStream. After the data writing for the
0382   // section is completed, compress the data in the local buffer,
0383   // swap the raw_ostream back and write the compressed data to the
0384   // real output.
0385   std::unique_ptr<raw_ostream> LocalBufStream;
0386   // The location where the output stream starts.
0387   uint64_t FileStart;
0388   // The location in the output stream where the SecHdrTable should be
0389   // written to.
0390   uint64_t SecHdrTableOffset;
0391   // The table contains SecHdrTableEntry entries in order of how they are
0392   // populated in the writer. It may be different from the order in
0393   // SectionHdrLayout which specifies the sequence in which sections will
0394   // be read.
0395   std::vector<SecHdrTableEntry> SecHdrTable;
0396 
0397   // FuncOffsetTable maps function context to its profile offset in
0398   // SecLBRProfile section. It is used to load function profile on demand.
0399   MapVector<SampleContext, uint64_t> FuncOffsetTable;
0400   // Whether to use MD5 to represent string.
0401   bool UseMD5 = false;
0402 
0403   /// CSNameTable maps function context to its offset in SecCSNameTable section.
0404   /// The offset will be used everywhere where the context is referenced.
0405   MapVector<SampleContext, uint32_t> CSNameTable;
0406 
0407   ProfileSymbolList *ProfSymList = nullptr;
0408 };
0409 
0410 class SampleProfileWriterExtBinary : public SampleProfileWriterExtBinaryBase {
0411 public:
0412   SampleProfileWriterExtBinary(std::unique_ptr<raw_ostream> &OS)
0413       : SampleProfileWriterExtBinaryBase(OS) {}
0414 
0415 private:
0416   std::error_code writeDefaultLayout(const SampleProfileMap &ProfileMap);
0417   std::error_code writeCtxSplitLayout(const SampleProfileMap &ProfileMap);
0418 
0419   std::error_code writeSections(const SampleProfileMap &ProfileMap) override;
0420 
0421   std::error_code writeCustomSection(SecType Type) override {
0422     return sampleprof_error::success;
0423   };
0424 
0425   void verifySecLayout(SectionLayout SL) override {
0426     assert((SL == DefaultLayout || SL == CtxSplitLayout) &&
0427            "Unsupported layout");
0428   }
0429 };
0430 
0431 } // end namespace sampleprof
0432 } // end namespace llvm
0433 
0434 #endif // LLVM_PROFILEDATA_SAMPLEPROFWRITER_H