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