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