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