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