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