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