1 //===- SampleProfWriter.cpp - Write LLVM sample profile data --------------===// 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 implements the class that writes LLVM sample profiles. It 10 // supports two file formats: text and binary. The textual representation 11 // is useful for debugging and testing purposes. The binary representation 12 // is more compact, resulting in smaller file sizes. However, they can 13 // both be used interchangeably. 14 // 15 // See lib/ProfileData/SampleProfReader.cpp for documentation on each of the 16 // supported formats. 17 // 18 //===----------------------------------------------------------------------===// 19 20 #include "llvm/ProfileData/SampleProfWriter.h" 21 #include "llvm/ADT/StringRef.h" 22 #include "llvm/ProfileData/ProfileCommon.h" 23 #include "llvm/ProfileData/SampleProf.h" 24 #include "llvm/Support/Compression.h" 25 #include "llvm/Support/EndianStream.h" 26 #include "llvm/Support/ErrorOr.h" 27 #include "llvm/Support/FileSystem.h" 28 #include "llvm/Support/LEB128.h" 29 #include "llvm/Support/MD5.h" 30 #include "llvm/Support/raw_ostream.h" 31 #include <cmath> 32 #include <cstdint> 33 #include <memory> 34 #include <set> 35 #include <system_error> 36 #include <utility> 37 #include <vector> 38 39 #define DEBUG_TYPE "llvm-profdata" 40 41 using namespace llvm; 42 using namespace sampleprof; 43 44 namespace llvm { 45 namespace support { 46 namespace endian { 47 namespace { 48 49 // Adapter class to llvm::support::endian::Writer for pwrite(). 50 struct SeekableWriter { 51 raw_pwrite_stream &OS; 52 endianness Endian; 53 SeekableWriter(raw_pwrite_stream &OS, endianness Endian) 54 : OS(OS), Endian(Endian) {} 55 56 template <typename ValueType> 57 void pwrite(ValueType Val, size_t Offset) { 58 std::string StringBuf; 59 raw_string_ostream SStream(StringBuf); 60 Writer(SStream, Endian).write(Val); 61 OS.pwrite(StringBuf.data(), StringBuf.size(), Offset); 62 } 63 }; 64 65 } // namespace 66 } // namespace endian 67 } // namespace support 68 } // namespace llvm 69 70 DefaultFunctionPruningStrategy::DefaultFunctionPruningStrategy( 71 SampleProfileMap &ProfileMap, size_t OutputSizeLimit) 72 : FunctionPruningStrategy(ProfileMap, OutputSizeLimit) { 73 sortFuncProfiles(ProfileMap, SortedFunctions); 74 } 75 76 void DefaultFunctionPruningStrategy::Erase(size_t CurrentOutputSize) { 77 double D = (double)OutputSizeLimit / CurrentOutputSize; 78 size_t NewSize = (size_t)round(ProfileMap.size() * D * D); 79 size_t NumToRemove = ProfileMap.size() - NewSize; 80 if (NumToRemove < 1) 81 NumToRemove = 1; 82 83 assert(NumToRemove <= SortedFunctions.size()); 84 for (const NameFunctionSamples &E : 85 llvm::drop_begin(SortedFunctions, SortedFunctions.size() - NumToRemove)) 86 ProfileMap.erase(E.first); 87 SortedFunctions.resize(SortedFunctions.size() - NumToRemove); 88 } 89 90 std::error_code SampleProfileWriter::writeWithSizeLimitInternal( 91 SampleProfileMap &ProfileMap, size_t OutputSizeLimit, 92 FunctionPruningStrategy *Strategy) { 93 if (OutputSizeLimit == 0) 94 return write(ProfileMap); 95 96 size_t OriginalFunctionCount = ProfileMap.size(); 97 98 std::unique_ptr<raw_ostream> OriginalOutputStream; 99 OutputStream.swap(OriginalOutputStream); 100 101 size_t IterationCount = 0; 102 size_t TotalSize; 103 104 SmallVector<char> StringBuffer; 105 do { 106 StringBuffer.clear(); 107 OutputStream.reset(new raw_svector_ostream(StringBuffer)); 108 if (std::error_code EC = write(ProfileMap)) 109 return EC; 110 111 TotalSize = StringBuffer.size(); 112 // On Windows every "\n" is actually written as "\r\n" to disk but not to 113 // memory buffer, this difference should be added when considering the total 114 // output size. 115 #ifdef _WIN32 116 if (Format == SPF_Text) 117 TotalSize += LineCount; 118 #endif 119 if (TotalSize <= OutputSizeLimit) 120 break; 121 122 Strategy->Erase(TotalSize); 123 IterationCount++; 124 } while (ProfileMap.size() != 0); 125 126 if (ProfileMap.size() == 0) 127 return sampleprof_error::too_large; 128 129 OutputStream.swap(OriginalOutputStream); 130 OutputStream->write(StringBuffer.data(), StringBuffer.size()); 131 LLVM_DEBUG(dbgs() << "Profile originally has " << OriginalFunctionCount 132 << " functions, reduced to " << ProfileMap.size() << " in " 133 << IterationCount << " iterations\n"); 134 // Silence warning on Release build. 135 (void)OriginalFunctionCount; 136 (void)IterationCount; 137 return sampleprof_error::success; 138 } 139 140 std::error_code 141 SampleProfileWriter::writeFuncProfiles(const SampleProfileMap &ProfileMap) { 142 std::vector<NameFunctionSamples> V; 143 sortFuncProfiles(ProfileMap, V); 144 for (const auto &I : V) { 145 if (std::error_code EC = writeSample(*I.second)) 146 return EC; 147 } 148 return sampleprof_error::success; 149 } 150 151 std::error_code SampleProfileWriter::write(const SampleProfileMap &ProfileMap) { 152 if (std::error_code EC = writeHeader(ProfileMap)) 153 return EC; 154 155 if (std::error_code EC = writeFuncProfiles(ProfileMap)) 156 return EC; 157 158 return sampleprof_error::success; 159 } 160 161 /// Return the current position and prepare to use it as the start 162 /// position of a section given the section type \p Type and its position 163 /// \p LayoutIdx in SectionHdrLayout. 164 uint64_t 165 SampleProfileWriterExtBinaryBase::markSectionStart(SecType Type, 166 uint32_t LayoutIdx) { 167 uint64_t SectionStart = OutputStream->tell(); 168 assert(LayoutIdx < SectionHdrLayout.size() && "LayoutIdx out of range"); 169 const auto &Entry = SectionHdrLayout[LayoutIdx]; 170 assert(Entry.Type == Type && "Unexpected section type"); 171 // Use LocalBuf as a temporary output for writting data. 172 if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress)) 173 LocalBufStream.swap(OutputStream); 174 return SectionStart; 175 } 176 177 std::error_code SampleProfileWriterExtBinaryBase::compressAndOutput() { 178 if (!llvm::compression::zlib::isAvailable()) 179 return sampleprof_error::zlib_unavailable; 180 std::string &UncompressedStrings = 181 static_cast<raw_string_ostream *>(LocalBufStream.get())->str(); 182 if (UncompressedStrings.size() == 0) 183 return sampleprof_error::success; 184 auto &OS = *OutputStream; 185 SmallVector<uint8_t, 128> CompressedStrings; 186 compression::zlib::compress(arrayRefFromStringRef(UncompressedStrings), 187 CompressedStrings, 188 compression::zlib::BestSizeCompression); 189 encodeULEB128(UncompressedStrings.size(), OS); 190 encodeULEB128(CompressedStrings.size(), OS); 191 OS << toStringRef(CompressedStrings); 192 UncompressedStrings.clear(); 193 return sampleprof_error::success; 194 } 195 196 /// Add a new section into section header table given the section type 197 /// \p Type, its position \p LayoutIdx in SectionHdrLayout and the 198 /// location \p SectionStart where the section should be written to. 199 std::error_code SampleProfileWriterExtBinaryBase::addNewSection( 200 SecType Type, uint32_t LayoutIdx, uint64_t SectionStart) { 201 assert(LayoutIdx < SectionHdrLayout.size() && "LayoutIdx out of range"); 202 const auto &Entry = SectionHdrLayout[LayoutIdx]; 203 assert(Entry.Type == Type && "Unexpected section type"); 204 if (hasSecFlag(Entry, SecCommonFlags::SecFlagCompress)) { 205 LocalBufStream.swap(OutputStream); 206 if (std::error_code EC = compressAndOutput()) 207 return EC; 208 } 209 SecHdrTable.push_back({Type, Entry.Flags, SectionStart - FileStart, 210 OutputStream->tell() - SectionStart, LayoutIdx}); 211 return sampleprof_error::success; 212 } 213 214 std::error_code 215 SampleProfileWriterExtBinaryBase::write(const SampleProfileMap &ProfileMap) { 216 // When calling write on a different profile map, existing states should be 217 // cleared. 218 NameTable.clear(); 219 CSNameTable.clear(); 220 SecHdrTable.clear(); 221 222 if (std::error_code EC = writeHeader(ProfileMap)) 223 return EC; 224 225 std::string LocalBuf; 226 LocalBufStream = std::make_unique<raw_string_ostream>(LocalBuf); 227 if (std::error_code EC = writeSections(ProfileMap)) 228 return EC; 229 230 if (std::error_code EC = writeSecHdrTable()) 231 return EC; 232 233 return sampleprof_error::success; 234 } 235 236 std::error_code SampleProfileWriterExtBinaryBase::writeContextIdx( 237 const SampleContext &Context) { 238 if (Context.hasContext()) 239 return writeCSNameIdx(Context); 240 else 241 return SampleProfileWriterBinary::writeNameIdx(Context.getFunction()); 242 } 243 244 std::error_code 245 SampleProfileWriterExtBinaryBase::writeCSNameIdx(const SampleContext &Context) { 246 const auto &Ret = CSNameTable.find(Context); 247 if (Ret == CSNameTable.end()) 248 return sampleprof_error::truncated_name_table; 249 encodeULEB128(Ret->second, *OutputStream); 250 return sampleprof_error::success; 251 } 252 253 std::error_code 254 SampleProfileWriterExtBinaryBase::writeSample(const FunctionSamples &S) { 255 uint64_t Offset = OutputStream->tell(); 256 auto &Context = S.getContext(); 257 FuncOffsetTable[Context] = Offset - SecLBRProfileStart; 258 encodeULEB128(S.getHeadSamples(), *OutputStream); 259 return writeBody(S); 260 } 261 262 std::error_code SampleProfileWriterExtBinaryBase::writeFuncOffsetTable() { 263 auto &OS = *OutputStream; 264 265 // Write out the table size. 266 encodeULEB128(FuncOffsetTable.size(), OS); 267 268 // Write out FuncOffsetTable. 269 auto WriteItem = [&](const SampleContext &Context, uint64_t Offset) { 270 if (std::error_code EC = writeContextIdx(Context)) 271 return EC; 272 encodeULEB128(Offset, OS); 273 return (std::error_code)sampleprof_error::success; 274 }; 275 276 if (FunctionSamples::ProfileIsCS) { 277 // Sort the contexts before writing them out. This is to help fast load all 278 // context profiles for a function as well as their callee contexts which 279 // can help profile-guided importing for ThinLTO. 280 std::map<SampleContext, uint64_t> OrderedFuncOffsetTable( 281 FuncOffsetTable.begin(), FuncOffsetTable.end()); 282 for (const auto &Entry : OrderedFuncOffsetTable) { 283 if (std::error_code EC = WriteItem(Entry.first, Entry.second)) 284 return EC; 285 } 286 addSectionFlag(SecFuncOffsetTable, SecFuncOffsetFlags::SecFlagOrdered); 287 } else { 288 for (const auto &Entry : FuncOffsetTable) { 289 if (std::error_code EC = WriteItem(Entry.first, Entry.second)) 290 return EC; 291 } 292 } 293 294 FuncOffsetTable.clear(); 295 return sampleprof_error::success; 296 } 297 298 std::error_code SampleProfileWriterExtBinaryBase::writeFuncMetadata( 299 const FunctionSamples &FunctionProfile) { 300 auto &OS = *OutputStream; 301 if (std::error_code EC = writeContextIdx(FunctionProfile.getContext())) 302 return EC; 303 304 if (FunctionSamples::ProfileIsProbeBased) 305 encodeULEB128(FunctionProfile.getFunctionHash(), OS); 306 if (FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsPreInlined) { 307 encodeULEB128(FunctionProfile.getContext().getAllAttributes(), OS); 308 } 309 310 if (!FunctionSamples::ProfileIsCS) { 311 // Recursively emit attributes for all callee samples. 312 uint64_t NumCallsites = 0; 313 for (const auto &J : FunctionProfile.getCallsiteSamples()) 314 NumCallsites += J.second.size(); 315 encodeULEB128(NumCallsites, OS); 316 for (const auto &J : FunctionProfile.getCallsiteSamples()) { 317 for (const auto &FS : J.second) { 318 LineLocation Loc = J.first; 319 encodeULEB128(Loc.LineOffset, OS); 320 encodeULEB128(Loc.Discriminator, OS); 321 if (std::error_code EC = writeFuncMetadata(FS.second)) 322 return EC; 323 } 324 } 325 } 326 327 return sampleprof_error::success; 328 } 329 330 std::error_code SampleProfileWriterExtBinaryBase::writeFuncMetadata( 331 const SampleProfileMap &Profiles) { 332 if (!FunctionSamples::ProfileIsProbeBased && !FunctionSamples::ProfileIsCS && 333 !FunctionSamples::ProfileIsPreInlined) 334 return sampleprof_error::success; 335 for (const auto &Entry : Profiles) { 336 if (std::error_code EC = writeFuncMetadata(Entry.second)) 337 return EC; 338 } 339 return sampleprof_error::success; 340 } 341 342 std::error_code SampleProfileWriterExtBinaryBase::writeNameTable() { 343 if (!UseMD5) 344 return SampleProfileWriterBinary::writeNameTable(); 345 346 auto &OS = *OutputStream; 347 std::set<FunctionId> V; 348 stablizeNameTable(NameTable, V); 349 350 // Write out the MD5 name table. We wrote unencoded MD5 so reader can 351 // retrieve the name using the name index without having to read the 352 // whole name table. 353 encodeULEB128(NameTable.size(), OS); 354 support::endian::Writer Writer(OS, llvm::endianness::little); 355 for (auto N : V) 356 Writer.write(N.getHashCode()); 357 return sampleprof_error::success; 358 } 359 360 std::error_code SampleProfileWriterExtBinaryBase::writeNameTableSection( 361 const SampleProfileMap &ProfileMap) { 362 for (const auto &I : ProfileMap) { 363 addContext(I.second.getContext()); 364 addNames(I.second); 365 } 366 367 // If NameTable contains ".__uniq." suffix, set SecFlagUniqSuffix flag 368 // so compiler won't strip the suffix during profile matching after 369 // seeing the flag in the profile. 370 // Original names are unavailable if using MD5, so this option has no use. 371 if (!UseMD5) { 372 for (const auto &I : NameTable) { 373 if (I.first.stringRef().contains(FunctionSamples::UniqSuffix)) { 374 addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagUniqSuffix); 375 break; 376 } 377 } 378 } 379 380 if (auto EC = writeNameTable()) 381 return EC; 382 return sampleprof_error::success; 383 } 384 385 std::error_code SampleProfileWriterExtBinaryBase::writeCSNameTableSection() { 386 // Sort the names to make CSNameTable deterministic. 387 std::set<SampleContext> OrderedContexts; 388 for (const auto &I : CSNameTable) 389 OrderedContexts.insert(I.first); 390 assert(OrderedContexts.size() == CSNameTable.size() && 391 "Unmatched ordered and unordered contexts"); 392 uint64_t I = 0; 393 for (auto &Context : OrderedContexts) 394 CSNameTable[Context] = I++; 395 396 auto &OS = *OutputStream; 397 encodeULEB128(OrderedContexts.size(), OS); 398 support::endian::Writer Writer(OS, llvm::endianness::little); 399 for (auto Context : OrderedContexts) { 400 auto Frames = Context.getContextFrames(); 401 encodeULEB128(Frames.size(), OS); 402 for (auto &Callsite : Frames) { 403 if (std::error_code EC = writeNameIdx(Callsite.Func)) 404 return EC; 405 encodeULEB128(Callsite.Location.LineOffset, OS); 406 encodeULEB128(Callsite.Location.Discriminator, OS); 407 } 408 } 409 410 return sampleprof_error::success; 411 } 412 413 std::error_code 414 SampleProfileWriterExtBinaryBase::writeProfileSymbolListSection() { 415 if (ProfSymList && ProfSymList->size() > 0) 416 if (std::error_code EC = ProfSymList->write(*OutputStream)) 417 return EC; 418 419 return sampleprof_error::success; 420 } 421 422 std::error_code SampleProfileWriterExtBinaryBase::writeOneSection( 423 SecType Type, uint32_t LayoutIdx, const SampleProfileMap &ProfileMap) { 424 // The setting of SecFlagCompress should happen before markSectionStart. 425 if (Type == SecProfileSymbolList && ProfSymList && ProfSymList->toCompress()) 426 setToCompressSection(SecProfileSymbolList); 427 if (Type == SecFuncMetadata && FunctionSamples::ProfileIsProbeBased) 428 addSectionFlag(SecFuncMetadata, SecFuncMetadataFlags::SecFlagIsProbeBased); 429 if (Type == SecFuncMetadata && 430 (FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsPreInlined)) 431 addSectionFlag(SecFuncMetadata, SecFuncMetadataFlags::SecFlagHasAttribute); 432 if (Type == SecProfSummary && FunctionSamples::ProfileIsCS) 433 addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagFullContext); 434 if (Type == SecProfSummary && FunctionSamples::ProfileIsPreInlined) 435 addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagIsPreInlined); 436 if (Type == SecProfSummary && FunctionSamples::ProfileIsFS) 437 addSectionFlag(SecProfSummary, SecProfSummaryFlags::SecFlagFSDiscriminator); 438 439 uint64_t SectionStart = markSectionStart(Type, LayoutIdx); 440 switch (Type) { 441 case SecProfSummary: 442 computeSummary(ProfileMap); 443 if (auto EC = writeSummary()) 444 return EC; 445 break; 446 case SecNameTable: 447 if (auto EC = writeNameTableSection(ProfileMap)) 448 return EC; 449 break; 450 case SecCSNameTable: 451 if (auto EC = writeCSNameTableSection()) 452 return EC; 453 break; 454 case SecLBRProfile: 455 SecLBRProfileStart = OutputStream->tell(); 456 if (std::error_code EC = writeFuncProfiles(ProfileMap)) 457 return EC; 458 break; 459 case SecFuncOffsetTable: 460 if (auto EC = writeFuncOffsetTable()) 461 return EC; 462 break; 463 case SecFuncMetadata: 464 if (std::error_code EC = writeFuncMetadata(ProfileMap)) 465 return EC; 466 break; 467 case SecProfileSymbolList: 468 if (auto EC = writeProfileSymbolListSection()) 469 return EC; 470 break; 471 default: 472 if (auto EC = writeCustomSection(Type)) 473 return EC; 474 break; 475 } 476 if (std::error_code EC = addNewSection(Type, LayoutIdx, SectionStart)) 477 return EC; 478 return sampleprof_error::success; 479 } 480 481 std::error_code SampleProfileWriterExtBinary::writeDefaultLayout( 482 const SampleProfileMap &ProfileMap) { 483 // The const indices passed to writeOneSection below are specifying the 484 // positions of the sections in SectionHdrLayout. Look at 485 // initSectionHdrLayout to find out where each section is located in 486 // SectionHdrLayout. 487 if (auto EC = writeOneSection(SecProfSummary, 0, ProfileMap)) 488 return EC; 489 if (auto EC = writeOneSection(SecNameTable, 1, ProfileMap)) 490 return EC; 491 if (auto EC = writeOneSection(SecCSNameTable, 2, ProfileMap)) 492 return EC; 493 if (auto EC = writeOneSection(SecLBRProfile, 4, ProfileMap)) 494 return EC; 495 if (auto EC = writeOneSection(SecProfileSymbolList, 5, ProfileMap)) 496 return EC; 497 if (auto EC = writeOneSection(SecFuncOffsetTable, 3, ProfileMap)) 498 return EC; 499 if (auto EC = writeOneSection(SecFuncMetadata, 6, ProfileMap)) 500 return EC; 501 return sampleprof_error::success; 502 } 503 504 static void splitProfileMapToTwo(const SampleProfileMap &ProfileMap, 505 SampleProfileMap &ContextProfileMap, 506 SampleProfileMap &NoContextProfileMap) { 507 for (const auto &I : ProfileMap) { 508 if (I.second.getCallsiteSamples().size()) 509 ContextProfileMap.insert({I.first, I.second}); 510 else 511 NoContextProfileMap.insert({I.first, I.second}); 512 } 513 } 514 515 std::error_code SampleProfileWriterExtBinary::writeCtxSplitLayout( 516 const SampleProfileMap &ProfileMap) { 517 SampleProfileMap ContextProfileMap, NoContextProfileMap; 518 splitProfileMapToTwo(ProfileMap, ContextProfileMap, NoContextProfileMap); 519 520 if (auto EC = writeOneSection(SecProfSummary, 0, ProfileMap)) 521 return EC; 522 if (auto EC = writeOneSection(SecNameTable, 1, ProfileMap)) 523 return EC; 524 if (auto EC = writeOneSection(SecLBRProfile, 3, ContextProfileMap)) 525 return EC; 526 if (auto EC = writeOneSection(SecFuncOffsetTable, 2, ContextProfileMap)) 527 return EC; 528 // Mark the section to have no context. Note section flag needs to be set 529 // before writing the section. 530 addSectionFlag(5, SecCommonFlags::SecFlagFlat); 531 if (auto EC = writeOneSection(SecLBRProfile, 5, NoContextProfileMap)) 532 return EC; 533 // Mark the section to have no context. Note section flag needs to be set 534 // before writing the section. 535 addSectionFlag(4, SecCommonFlags::SecFlagFlat); 536 if (auto EC = writeOneSection(SecFuncOffsetTable, 4, NoContextProfileMap)) 537 return EC; 538 if (auto EC = writeOneSection(SecProfileSymbolList, 6, ProfileMap)) 539 return EC; 540 if (auto EC = writeOneSection(SecFuncMetadata, 7, ProfileMap)) 541 return EC; 542 543 return sampleprof_error::success; 544 } 545 546 std::error_code SampleProfileWriterExtBinary::writeSections( 547 const SampleProfileMap &ProfileMap) { 548 std::error_code EC; 549 if (SecLayout == DefaultLayout) 550 EC = writeDefaultLayout(ProfileMap); 551 else if (SecLayout == CtxSplitLayout) 552 EC = writeCtxSplitLayout(ProfileMap); 553 else 554 llvm_unreachable("Unsupported layout"); 555 return EC; 556 } 557 558 /// Write samples to a text file. 559 /// 560 /// Note: it may be tempting to implement this in terms of 561 /// FunctionSamples::print(). Please don't. The dump functionality is intended 562 /// for debugging and has no specified form. 563 /// 564 /// The format used here is more structured and deliberate because 565 /// it needs to be parsed by the SampleProfileReaderText class. 566 std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) { 567 auto &OS = *OutputStream; 568 if (FunctionSamples::ProfileIsCS) 569 OS << "[" << S.getContext().toString() << "]:" << S.getTotalSamples(); 570 else 571 OS << S.getFunction() << ":" << S.getTotalSamples(); 572 573 if (Indent == 0) 574 OS << ":" << S.getHeadSamples(); 575 OS << "\n"; 576 LineCount++; 577 578 SampleSorter<LineLocation, SampleRecord> SortedSamples(S.getBodySamples()); 579 for (const auto &I : SortedSamples.get()) { 580 LineLocation Loc = I->first; 581 const SampleRecord &Sample = I->second; 582 OS.indent(Indent + 1); 583 Loc.print(OS); 584 OS << ": " << Sample.getSamples(); 585 586 for (const auto &J : Sample.getSortedCallTargets()) 587 OS << " " << J.first << ":" << J.second; 588 OS << "\n"; 589 LineCount++; 590 } 591 592 SampleSorter<LineLocation, FunctionSamplesMap> SortedCallsiteSamples( 593 S.getCallsiteSamples()); 594 Indent += 1; 595 for (const auto *Element : SortedCallsiteSamples.get()) { 596 // Element is a pointer to a pair of LineLocation and FunctionSamplesMap. 597 const auto &[Loc, FunctionSamplesMap] = *Element; 598 for (const FunctionSamples &CalleeSamples : 599 make_second_range(FunctionSamplesMap)) { 600 OS.indent(Indent); 601 Loc.print(OS); 602 OS << ": "; 603 if (std::error_code EC = writeSample(CalleeSamples)) 604 return EC; 605 } 606 } 607 Indent -= 1; 608 609 if (FunctionSamples::ProfileIsProbeBased) { 610 OS.indent(Indent + 1); 611 OS << "!CFGChecksum: " << S.getFunctionHash() << "\n"; 612 LineCount++; 613 } 614 615 if (S.getContext().getAllAttributes()) { 616 OS.indent(Indent + 1); 617 OS << "!Attributes: " << S.getContext().getAllAttributes() << "\n"; 618 LineCount++; 619 } 620 621 if (Indent == 0 && MarkFlatProfiles && S.getCallsiteSamples().size() == 0) 622 OS << " !Flat\n"; 623 624 return sampleprof_error::success; 625 } 626 627 std::error_code 628 SampleProfileWriterBinary::writeContextIdx(const SampleContext &Context) { 629 assert(!Context.hasContext() && "cs profile is not supported"); 630 return writeNameIdx(Context.getFunction()); 631 } 632 633 std::error_code SampleProfileWriterBinary::writeNameIdx(FunctionId FName) { 634 auto &NTable = getNameTable(); 635 const auto &Ret = NTable.find(FName); 636 if (Ret == NTable.end()) 637 return sampleprof_error::truncated_name_table; 638 encodeULEB128(Ret->second, *OutputStream); 639 return sampleprof_error::success; 640 } 641 642 void SampleProfileWriterBinary::addName(FunctionId FName) { 643 auto &NTable = getNameTable(); 644 NTable.insert(std::make_pair(FName, 0)); 645 } 646 647 void SampleProfileWriterBinary::addContext(const SampleContext &Context) { 648 addName(Context.getFunction()); 649 } 650 651 void SampleProfileWriterBinary::addNames(const FunctionSamples &S) { 652 // Add all the names in indirect call targets. 653 for (const auto &I : S.getBodySamples()) { 654 const SampleRecord &Sample = I.second; 655 for (const auto &J : Sample.getCallTargets()) 656 addName(J.first); 657 } 658 659 // Recursively add all the names for inlined callsites. 660 for (const auto &J : S.getCallsiteSamples()) 661 for (const auto &FS : J.second) { 662 const FunctionSamples &CalleeSamples = FS.second; 663 addName(CalleeSamples.getFunction()); 664 addNames(CalleeSamples); 665 } 666 } 667 668 void SampleProfileWriterExtBinaryBase::addContext( 669 const SampleContext &Context) { 670 if (Context.hasContext()) { 671 for (auto &Callsite : Context.getContextFrames()) 672 SampleProfileWriterBinary::addName(Callsite.Func); 673 CSNameTable.insert(std::make_pair(Context, 0)); 674 } else { 675 SampleProfileWriterBinary::addName(Context.getFunction()); 676 } 677 } 678 679 void SampleProfileWriterBinary::stablizeNameTable( 680 MapVector<FunctionId, uint32_t> &NameTable, std::set<FunctionId> &V) { 681 // Sort the names to make NameTable deterministic. 682 for (const auto &I : NameTable) 683 V.insert(I.first); 684 int i = 0; 685 for (const FunctionId &N : V) 686 NameTable[N] = i++; 687 } 688 689 std::error_code SampleProfileWriterBinary::writeNameTable() { 690 auto &OS = *OutputStream; 691 std::set<FunctionId> V; 692 stablizeNameTable(NameTable, V); 693 694 // Write out the name table. 695 encodeULEB128(NameTable.size(), OS); 696 for (auto N : V) { 697 OS << N; 698 encodeULEB128(0, OS); 699 } 700 return sampleprof_error::success; 701 } 702 703 std::error_code 704 SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) { 705 auto &OS = *OutputStream; 706 // Write file magic identifier. 707 encodeULEB128(SPMagic(Format), OS); 708 encodeULEB128(SPVersion(), OS); 709 return sampleprof_error::success; 710 } 711 712 std::error_code 713 SampleProfileWriterBinary::writeHeader(const SampleProfileMap &ProfileMap) { 714 // When calling write on a different profile map, existing names should be 715 // cleared. 716 NameTable.clear(); 717 718 writeMagicIdent(Format); 719 720 computeSummary(ProfileMap); 721 if (auto EC = writeSummary()) 722 return EC; 723 724 // Generate the name table for all the functions referenced in the profile. 725 for (const auto &I : ProfileMap) { 726 addContext(I.second.getContext()); 727 addNames(I.second); 728 } 729 730 writeNameTable(); 731 return sampleprof_error::success; 732 } 733 734 void SampleProfileWriterExtBinaryBase::setToCompressAllSections() { 735 for (auto &Entry : SectionHdrLayout) 736 addSecFlag(Entry, SecCommonFlags::SecFlagCompress); 737 } 738 739 void SampleProfileWriterExtBinaryBase::setToCompressSection(SecType Type) { 740 addSectionFlag(Type, SecCommonFlags::SecFlagCompress); 741 } 742 743 void SampleProfileWriterExtBinaryBase::allocSecHdrTable() { 744 support::endian::Writer Writer(*OutputStream, llvm::endianness::little); 745 746 Writer.write(static_cast<uint64_t>(SectionHdrLayout.size())); 747 SecHdrTableOffset = OutputStream->tell(); 748 for (uint32_t i = 0; i < SectionHdrLayout.size(); i++) { 749 Writer.write(static_cast<uint64_t>(-1)); 750 Writer.write(static_cast<uint64_t>(-1)); 751 Writer.write(static_cast<uint64_t>(-1)); 752 Writer.write(static_cast<uint64_t>(-1)); 753 } 754 } 755 756 std::error_code SampleProfileWriterExtBinaryBase::writeSecHdrTable() { 757 assert(SecHdrTable.size() == SectionHdrLayout.size() && 758 "SecHdrTable entries doesn't match SectionHdrLayout"); 759 SmallVector<uint32_t, 16> IndexMap(SecHdrTable.size(), -1); 760 for (uint32_t TableIdx = 0; TableIdx < SecHdrTable.size(); TableIdx++) { 761 IndexMap[SecHdrTable[TableIdx].LayoutIndex] = TableIdx; 762 } 763 764 // Write the section header table in the order specified in 765 // SectionHdrLayout. SectionHdrLayout specifies the sections 766 // order in which profile reader expect to read, so the section 767 // header table should be written in the order in SectionHdrLayout. 768 // Note that the section order in SecHdrTable may be different 769 // from the order in SectionHdrLayout, for example, SecFuncOffsetTable 770 // needs to be computed after SecLBRProfile (the order in SecHdrTable), 771 // but it needs to be read before SecLBRProfile (the order in 772 // SectionHdrLayout). So we use IndexMap above to switch the order. 773 support::endian::SeekableWriter Writer( 774 static_cast<raw_pwrite_stream &>(*OutputStream), 775 llvm::endianness::little); 776 for (uint32_t LayoutIdx = 0; LayoutIdx < SectionHdrLayout.size(); 777 LayoutIdx++) { 778 assert(IndexMap[LayoutIdx] < SecHdrTable.size() && 779 "Incorrect LayoutIdx in SecHdrTable"); 780 auto Entry = SecHdrTable[IndexMap[LayoutIdx]]; 781 Writer.pwrite(static_cast<uint64_t>(Entry.Type), 782 SecHdrTableOffset + 4 * LayoutIdx * sizeof(uint64_t)); 783 Writer.pwrite(static_cast<uint64_t>(Entry.Flags), 784 SecHdrTableOffset + (4 * LayoutIdx + 1) * sizeof(uint64_t)); 785 Writer.pwrite(static_cast<uint64_t>(Entry.Offset), 786 SecHdrTableOffset + (4 * LayoutIdx + 2) * sizeof(uint64_t)); 787 Writer.pwrite(static_cast<uint64_t>(Entry.Size), 788 SecHdrTableOffset + (4 * LayoutIdx + 3) * sizeof(uint64_t)); 789 } 790 791 return sampleprof_error::success; 792 } 793 794 std::error_code SampleProfileWriterExtBinaryBase::writeHeader( 795 const SampleProfileMap &ProfileMap) { 796 auto &OS = *OutputStream; 797 FileStart = OS.tell(); 798 writeMagicIdent(Format); 799 800 allocSecHdrTable(); 801 return sampleprof_error::success; 802 } 803 804 std::error_code SampleProfileWriterBinary::writeSummary() { 805 auto &OS = *OutputStream; 806 encodeULEB128(Summary->getTotalCount(), OS); 807 encodeULEB128(Summary->getMaxCount(), OS); 808 encodeULEB128(Summary->getMaxFunctionCount(), OS); 809 encodeULEB128(Summary->getNumCounts(), OS); 810 encodeULEB128(Summary->getNumFunctions(), OS); 811 ArrayRef<ProfileSummaryEntry> Entries = Summary->getDetailedSummary(); 812 encodeULEB128(Entries.size(), OS); 813 for (auto Entry : Entries) { 814 encodeULEB128(Entry.Cutoff, OS); 815 encodeULEB128(Entry.MinCount, OS); 816 encodeULEB128(Entry.NumCounts, OS); 817 } 818 return sampleprof_error::success; 819 } 820 std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) { 821 auto &OS = *OutputStream; 822 if (std::error_code EC = writeContextIdx(S.getContext())) 823 return EC; 824 825 encodeULEB128(S.getTotalSamples(), OS); 826 827 // Emit all the body samples. 828 encodeULEB128(S.getBodySamples().size(), OS); 829 for (const auto &I : S.getBodySamples()) { 830 LineLocation Loc = I.first; 831 const SampleRecord &Sample = I.second; 832 Loc.serialize(OS); 833 Sample.serialize(OS, getNameTable()); 834 } 835 836 // Recursively emit all the callsite samples. 837 uint64_t NumCallsites = 0; 838 for (const auto &J : S.getCallsiteSamples()) 839 NumCallsites += J.second.size(); 840 encodeULEB128(NumCallsites, OS); 841 for (const auto &[Loc, CalleeFunctionSampleMap] : S.getCallsiteSamples()) 842 for (const auto &FunctionSample : 843 llvm::make_second_range(CalleeFunctionSampleMap)) { 844 Loc.serialize(OS); 845 if (std::error_code EC = writeBody(FunctionSample)) 846 return EC; 847 } 848 849 return sampleprof_error::success; 850 } 851 852 /// Write samples of a top-level function to a binary file. 853 /// 854 /// \returns true if the samples were written successfully, false otherwise. 855 std::error_code 856 SampleProfileWriterBinary::writeSample(const FunctionSamples &S) { 857 encodeULEB128(S.getHeadSamples(), *OutputStream); 858 return writeBody(S); 859 } 860 861 /// Create a sample profile file writer based on the specified format. 862 /// 863 /// \param Filename The file to create. 864 /// 865 /// \param Format Encoding format for the profile file. 866 /// 867 /// \returns an error code indicating the status of the created writer. 868 ErrorOr<std::unique_ptr<SampleProfileWriter>> 869 SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) { 870 std::error_code EC; 871 std::unique_ptr<raw_ostream> OS; 872 if (Format == SPF_Binary || Format == SPF_Ext_Binary) 873 OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None)); 874 else 875 OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_TextWithCRLF)); 876 if (EC) 877 return EC; 878 879 return create(OS, Format); 880 } 881 882 /// Create a sample profile stream writer based on the specified format. 883 /// 884 /// \param OS The output stream to store the profile data to. 885 /// 886 /// \param Format Encoding format for the profile file. 887 /// 888 /// \returns an error code indicating the status of the created writer. 889 ErrorOr<std::unique_ptr<SampleProfileWriter>> 890 SampleProfileWriter::create(std::unique_ptr<raw_ostream> &OS, 891 SampleProfileFormat Format) { 892 std::error_code EC; 893 std::unique_ptr<SampleProfileWriter> Writer; 894 895 // Currently only Text and Extended Binary format are supported for CSSPGO. 896 if ((FunctionSamples::ProfileIsCS || FunctionSamples::ProfileIsProbeBased) && 897 Format == SPF_Binary) 898 return sampleprof_error::unsupported_writing_format; 899 900 if (Format == SPF_Binary) 901 Writer.reset(new SampleProfileWriterRawBinary(OS)); 902 else if (Format == SPF_Ext_Binary) 903 Writer.reset(new SampleProfileWriterExtBinary(OS)); 904 else if (Format == SPF_Text) 905 Writer.reset(new SampleProfileWriterText(OS)); 906 else if (Format == SPF_GCC) 907 EC = sampleprof_error::unsupported_writing_format; 908 else 909 EC = sampleprof_error::unrecognized_format; 910 911 if (EC) 912 return EC; 913 914 Writer->Format = Format; 915 return std::move(Writer); 916 } 917 918 void SampleProfileWriter::computeSummary(const SampleProfileMap &ProfileMap) { 919 SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); 920 Summary = Builder.computeSummaryForProfiles(ProfileMap); 921 } 922