1 //===-- ProfileGenerator.cpp - Profile Generator ---------------*- C++ -*-===// 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 #include "ProfileGenerator.h" 10 #include "ErrorHandling.h" 11 #include "ProfiledBinary.h" 12 #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" 13 #include "llvm/ProfileData/ProfileCommon.h" 14 #include <float.h> 15 #include <unordered_set> 16 17 cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), 18 cl::Required, 19 cl::desc("Output profile file")); 20 static cl::alias OutputA("o", cl::desc("Alias for --output"), 21 cl::aliasopt(OutputFilename)); 22 23 static cl::opt<SampleProfileFormat> OutputFormat( 24 "format", cl::desc("Format of output profile"), cl::init(SPF_Ext_Binary), 25 cl::values( 26 clEnumValN(SPF_Binary, "binary", "Binary encoding (default)"), 27 clEnumValN(SPF_Compact_Binary, "compbinary", "Compact binary encoding"), 28 clEnumValN(SPF_Ext_Binary, "extbinary", "Extensible binary encoding"), 29 clEnumValN(SPF_Text, "text", "Text encoding"), 30 clEnumValN(SPF_GCC, "gcc", 31 "GCC encoding (only meaningful for -sample)"))); 32 33 cl::opt<bool> UseMD5( 34 "use-md5", cl::init(false), cl::Hidden, 35 cl::desc("Use md5 to represent function names in the output profile (only " 36 "meaningful for -extbinary)")); 37 38 static cl::opt<bool> PopulateProfileSymbolList( 39 "populate-profile-symbol-list", cl::init(false), cl::Hidden, 40 cl::desc("Populate profile symbol list (only meaningful for -extbinary)")); 41 42 static cl::opt<bool> FillZeroForAllFuncs( 43 "fill-zero-for-all-funcs", cl::init(false), cl::Hidden, 44 cl::desc("Attribute all functions' range with zero count " 45 "even it's not hit by any samples.")); 46 47 static cl::opt<int32_t, true> RecursionCompression( 48 "compress-recursion", 49 cl::desc("Compressing recursion by deduplicating adjacent frame " 50 "sequences up to the specified size. -1 means no size limit."), 51 cl::Hidden, 52 cl::location(llvm::sampleprof::CSProfileGenerator::MaxCompressionSize)); 53 54 static cl::opt<bool> 55 TrimColdProfile("trim-cold-profile", cl::init(false), cl::ZeroOrMore, 56 cl::desc("If the total count of the profile is smaller " 57 "than threshold, it will be trimmed.")); 58 59 static cl::opt<bool> CSProfMergeColdContext( 60 "csprof-merge-cold-context", cl::init(true), cl::ZeroOrMore, 61 cl::desc("If the total count of context profile is smaller than " 62 "the threshold, it will be merged into context-less base " 63 "profile.")); 64 65 static cl::opt<uint32_t> CSProfMaxColdContextDepth( 66 "csprof-max-cold-context-depth", cl::init(1), cl::ZeroOrMore, 67 cl::desc("Keep the last K contexts while merging cold profile. 1 means the " 68 "context-less base profile")); 69 70 static cl::opt<int, true> CSProfMaxContextDepth( 71 "csprof-max-context-depth", cl::ZeroOrMore, 72 cl::desc("Keep the last K contexts while merging profile. -1 means no " 73 "depth limit."), 74 cl::location(llvm::sampleprof::CSProfileGenerator::MaxContextDepth)); 75 76 static cl::opt<double> HotFunctionDensityThreshold( 77 "hot-function-density-threshold", llvm::cl::init(1000), 78 llvm::cl::desc( 79 "specify density threshold for hot functions (default: 1000)"), 80 llvm::cl::Optional); 81 static cl::opt<bool> ShowDensity("show-density", llvm::cl::init(false), 82 llvm::cl::desc("show profile density details"), 83 llvm::cl::Optional); 84 85 static cl::opt<bool> UpdateTotalSamples( 86 "update-total-samples", llvm::cl::init(false), 87 llvm::cl::desc( 88 "Update total samples by accumulating all its body samples."), 89 llvm::cl::Optional); 90 91 extern cl::opt<int> ProfileSummaryCutoffHot; 92 93 static cl::opt<bool> GenCSNestedProfile( 94 "gen-cs-nested-profile", cl::Hidden, cl::init(false), 95 cl::desc("Generate nested function profiles for CSSPGO")); 96 97 using namespace llvm; 98 using namespace sampleprof; 99 100 namespace llvm { 101 namespace sampleprof { 102 103 // Initialize the MaxCompressionSize to -1 which means no size limit 104 int32_t CSProfileGenerator::MaxCompressionSize = -1; 105 106 int CSProfileGenerator::MaxContextDepth = -1; 107 108 bool ProfileGeneratorBase::UseFSDiscriminator = false; 109 110 std::unique_ptr<ProfileGeneratorBase> 111 ProfileGeneratorBase::create(ProfiledBinary *Binary, 112 const ContextSampleCounterMap &SampleCounters, 113 bool ProfileIsCSFlat) { 114 std::unique_ptr<ProfileGeneratorBase> Generator; 115 if (ProfileIsCSFlat) { 116 if (Binary->useFSDiscriminator()) 117 exitWithError("FS discriminator is not supported in CS profile."); 118 Generator.reset(new CSProfileGenerator(Binary, SampleCounters)); 119 } else { 120 Generator.reset(new ProfileGenerator(Binary, SampleCounters)); 121 } 122 ProfileGeneratorBase::UseFSDiscriminator = Binary->useFSDiscriminator(); 123 FunctionSamples::ProfileIsFS = Binary->useFSDiscriminator(); 124 125 return Generator; 126 } 127 128 void ProfileGeneratorBase::write(std::unique_ptr<SampleProfileWriter> Writer, 129 SampleProfileMap &ProfileMap) { 130 // Populate profile symbol list if extended binary format is used. 131 ProfileSymbolList SymbolList; 132 133 if (PopulateProfileSymbolList && OutputFormat == SPF_Ext_Binary) { 134 Binary->populateSymbolListFromDWARF(SymbolList); 135 Writer->setProfileSymbolList(&SymbolList); 136 } 137 138 if (std::error_code EC = Writer->write(ProfileMap)) 139 exitWithError(std::move(EC)); 140 } 141 142 void ProfileGeneratorBase::write() { 143 auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); 144 if (std::error_code EC = WriterOrErr.getError()) 145 exitWithError(EC, OutputFilename); 146 147 if (UseMD5) { 148 if (OutputFormat != SPF_Ext_Binary) 149 WithColor::warning() << "-use-md5 is ignored. Specify " 150 "--format=extbinary to enable it\n"; 151 else 152 WriterOrErr.get()->setUseMD5(); 153 } 154 155 write(std::move(WriterOrErr.get()), ProfileMap); 156 } 157 158 void ProfileGeneratorBase::showDensitySuggestion(double Density) { 159 if (Density == 0.0) 160 WithColor::warning() << "The --profile-summary-cutoff-hot option may be " 161 "set too low. Please check your command.\n"; 162 else if (Density < HotFunctionDensityThreshold) 163 WithColor::warning() 164 << "AutoFDO is estimated to optimize better with " 165 << format("%.1f", HotFunctionDensityThreshold / Density) 166 << "x more samples. Please consider increasing sampling rate or " 167 "profiling for longer duration to get more samples.\n"; 168 169 if (ShowDensity) 170 outs() << "Minimum profile density for hot functions with top " 171 << format("%.2f", 172 static_cast<double>(ProfileSummaryCutoffHot.getValue()) / 173 10000) 174 << "% total samples: " << format("%.1f", Density) << "\n"; 175 } 176 177 double ProfileGeneratorBase::calculateDensity(const SampleProfileMap &Profiles, 178 uint64_t HotCntThreshold) { 179 double Density = DBL_MAX; 180 std::vector<const FunctionSamples *> HotFuncs; 181 for (auto &I : Profiles) { 182 auto &FuncSamples = I.second; 183 if (FuncSamples.getTotalSamples() < HotCntThreshold) 184 continue; 185 HotFuncs.emplace_back(&FuncSamples); 186 } 187 188 for (auto *FuncSamples : HotFuncs) { 189 auto *Func = Binary->getBinaryFunction(FuncSamples->getName()); 190 if (!Func) 191 continue; 192 uint64_t FuncSize = Func->getFuncSize(); 193 if (FuncSize == 0) 194 continue; 195 Density = 196 std::min(Density, static_cast<double>(FuncSamples->getTotalSamples()) / 197 FuncSize); 198 } 199 200 return Density == DBL_MAX ? 0.0 : Density; 201 } 202 203 void ProfileGeneratorBase::findDisjointRanges(RangeSample &DisjointRanges, 204 const RangeSample &Ranges) { 205 206 /* 207 Regions may overlap with each other. Using the boundary info, find all 208 disjoint ranges and their sample count. BoundaryPoint contains the count 209 multiple samples begin/end at this points. 210 211 |<--100-->| Sample1 212 |<------200------>| Sample2 213 A B C 214 215 In the example above, 216 Sample1 begins at A, ends at B, its value is 100. 217 Sample2 beings at A, ends at C, its value is 200. 218 For A, BeginCount is the sum of sample begins at A, which is 300 and no 219 samples ends at A, so EndCount is 0. 220 Then boundary points A, B, and C with begin/end counts are: 221 A: (300, 0) 222 B: (0, 100) 223 C: (0, 200) 224 */ 225 struct BoundaryPoint { 226 // Sum of sample counts beginning at this point 227 uint64_t BeginCount = UINT64_MAX; 228 // Sum of sample counts ending at this point 229 uint64_t EndCount = UINT64_MAX; 230 // Is the begin point of a zero range. 231 bool IsZeroRangeBegin = false; 232 // Is the end point of a zero range. 233 bool IsZeroRangeEnd = false; 234 235 void addBeginCount(uint64_t Count) { 236 if (BeginCount == UINT64_MAX) 237 BeginCount = 0; 238 BeginCount += Count; 239 } 240 241 void addEndCount(uint64_t Count) { 242 if (EndCount == UINT64_MAX) 243 EndCount = 0; 244 EndCount += Count; 245 } 246 }; 247 248 /* 249 For the above example. With boundary points, follwing logic finds two 250 disjoint region of 251 252 [A,B]: 300 253 [B+1,C]: 200 254 255 If there is a boundary point that both begin and end, the point itself 256 becomes a separate disjoint region. For example, if we have original 257 ranges of 258 259 |<--- 100 --->| 260 |<--- 200 --->| 261 A B C 262 263 there are three boundary points with their begin/end counts of 264 265 A: (100, 0) 266 B: (200, 100) 267 C: (0, 200) 268 269 the disjoint ranges would be 270 271 [A, B-1]: 100 272 [B, B]: 300 273 [B+1, C]: 200. 274 275 Example for zero value range: 276 277 |<--- 100 --->| 278 |<--- 200 --->| 279 |<--------------- 0 ----------------->| 280 A B C D E F 281 282 [A, B-1] : 0 283 [B, C] : 100 284 [C+1, D-1]: 0 285 [D, E] : 200 286 [E+1, F] : 0 287 */ 288 std::map<uint64_t, BoundaryPoint> Boundaries; 289 290 for (const auto &Item : Ranges) { 291 assert(Item.first.first <= Item.first.second && 292 "Invalid instruction range"); 293 auto &BeginPoint = Boundaries[Item.first.first]; 294 auto &EndPoint = Boundaries[Item.first.second]; 295 uint64_t Count = Item.second; 296 297 BeginPoint.addBeginCount(Count); 298 EndPoint.addEndCount(Count); 299 if (Count == 0) { 300 BeginPoint.IsZeroRangeBegin = true; 301 EndPoint.IsZeroRangeEnd = true; 302 } 303 } 304 305 // Use UINT64_MAX to indicate there is no existing range between BeginAddress 306 // and the next valid address 307 uint64_t BeginAddress = UINT64_MAX; 308 int ZeroRangeDepth = 0; 309 uint64_t Count = 0; 310 for (const auto &Item : Boundaries) { 311 uint64_t Address = Item.first; 312 const BoundaryPoint &Point = Item.second; 313 if (Point.BeginCount != UINT64_MAX) { 314 if (BeginAddress != UINT64_MAX) 315 DisjointRanges[{BeginAddress, Address - 1}] = Count; 316 Count += Point.BeginCount; 317 BeginAddress = Address; 318 ZeroRangeDepth += Point.IsZeroRangeBegin; 319 } 320 if (Point.EndCount != UINT64_MAX) { 321 assert((BeginAddress != UINT64_MAX) && 322 "First boundary point cannot be 'end' point"); 323 DisjointRanges[{BeginAddress, Address}] = Count; 324 assert(Count >= Point.EndCount && "Mismatched live ranges"); 325 Count -= Point.EndCount; 326 BeginAddress = Address + 1; 327 ZeroRangeDepth -= Point.IsZeroRangeEnd; 328 // If the remaining count is zero and it's no longer in a zero range, this 329 // means we consume all the ranges before, thus mark BeginAddress as 330 // UINT64_MAX. e.g. supposing we have two non-overlapping ranges: 331 // [<---- 10 ---->] 332 // [<---- 20 ---->] 333 // A B C D 334 // The BeginAddress(B+1) will reset to invalid(UINT64_MAX), so we won't 335 // have the [B+1, C-1] zero range. 336 if (Count == 0 && ZeroRangeDepth == 0) 337 BeginAddress = UINT64_MAX; 338 } 339 } 340 } 341 342 void ProfileGeneratorBase::updateBodySamplesforFunctionProfile( 343 FunctionSamples &FunctionProfile, const SampleContextFrame &LeafLoc, 344 uint64_t Count) { 345 // Use the maximum count of samples with same line location 346 uint32_t Discriminator = getBaseDiscriminator(LeafLoc.Location.Discriminator); 347 348 // Use duplication factor to compensated for loop unroll/vectorization. 349 // Note that this is only needed when we're taking MAX of the counts at 350 // the location instead of SUM. 351 Count *= getDuplicationFactor(LeafLoc.Location.Discriminator); 352 353 ErrorOr<uint64_t> R = 354 FunctionProfile.findSamplesAt(LeafLoc.Location.LineOffset, Discriminator); 355 356 uint64_t PreviousCount = R ? R.get() : 0; 357 if (PreviousCount <= Count) { 358 FunctionProfile.addBodySamples(LeafLoc.Location.LineOffset, Discriminator, 359 Count - PreviousCount); 360 } 361 } 362 363 void ProfileGeneratorBase::updateTotalSamples() { 364 if (!UpdateTotalSamples) 365 return; 366 367 for (auto &Item : ProfileMap) { 368 FunctionSamples &FunctionProfile = Item.second; 369 FunctionProfile.updateTotalSamples(); 370 } 371 } 372 373 FunctionSamples & 374 ProfileGenerator::getTopLevelFunctionProfile(StringRef FuncName) { 375 SampleContext Context(FuncName); 376 auto Ret = ProfileMap.emplace(Context, FunctionSamples()); 377 if (Ret.second) { 378 FunctionSamples &FProfile = Ret.first->second; 379 FProfile.setContext(Context); 380 } 381 return Ret.first->second; 382 } 383 384 void ProfileGenerator::generateProfile() { 385 if (Binary->usePseudoProbes()) { 386 // TODO: Support probe based profile generation 387 exitWithError("Probe based profile generation not supported for AutoFDO, " 388 "consider dropping `--ignore-stack-samples` or adding `--use-dwarf-correlation`."); 389 } else { 390 generateLineNumBasedProfile(); 391 } 392 postProcessProfiles(); 393 } 394 395 void ProfileGenerator::postProcessProfiles() { 396 computeSummaryAndThreshold(); 397 trimColdProfiles(ProfileMap, ColdCountThreshold); 398 calculateAndShowDensity(ProfileMap); 399 } 400 401 void ProfileGenerator::trimColdProfiles(const SampleProfileMap &Profiles, 402 uint64_t ColdCntThreshold) { 403 if (!TrimColdProfile) 404 return; 405 406 // Move cold profiles into a tmp container. 407 std::vector<SampleContext> ColdProfiles; 408 for (const auto &I : ProfileMap) { 409 if (I.second.getTotalSamples() < ColdCntThreshold) 410 ColdProfiles.emplace_back(I.first); 411 } 412 413 // Remove the cold profile from ProfileMap. 414 for (const auto &I : ColdProfiles) 415 ProfileMap.erase(I); 416 } 417 418 void ProfileGenerator::generateLineNumBasedProfile() { 419 assert(SampleCounters.size() == 1 && 420 "Must have one entry for profile generation."); 421 const SampleCounter &SC = SampleCounters.begin()->second; 422 // Fill in function body samples 423 populateBodySamplesForAllFunctions(SC.RangeCounter); 424 // Fill in boundary sample counts as well as call site samples for calls 425 populateBoundarySamplesForAllFunctions(SC.BranchCounter); 426 427 updateTotalSamples(); 428 } 429 430 FunctionSamples &ProfileGenerator::getLeafProfileAndAddTotalSamples( 431 const SampleContextFrameVector &FrameVec, uint64_t Count) { 432 // Get top level profile 433 FunctionSamples *FunctionProfile = 434 &getTopLevelFunctionProfile(FrameVec[0].FuncName); 435 FunctionProfile->addTotalSamples(Count); 436 437 for (size_t I = 1; I < FrameVec.size(); I++) { 438 LineLocation Callsite( 439 FrameVec[I - 1].Location.LineOffset, 440 getBaseDiscriminator(FrameVec[I - 1].Location.Discriminator)); 441 FunctionSamplesMap &SamplesMap = 442 FunctionProfile->functionSamplesAt(Callsite); 443 auto Ret = 444 SamplesMap.emplace(FrameVec[I].FuncName.str(), FunctionSamples()); 445 if (Ret.second) { 446 SampleContext Context(FrameVec[I].FuncName); 447 Ret.first->second.setContext(Context); 448 } 449 FunctionProfile = &Ret.first->second; 450 FunctionProfile->addTotalSamples(Count); 451 } 452 453 return *FunctionProfile; 454 } 455 456 RangeSample 457 ProfileGenerator::preprocessRangeCounter(const RangeSample &RangeCounter) { 458 RangeSample Ranges(RangeCounter.begin(), RangeCounter.end()); 459 if (FillZeroForAllFuncs) { 460 for (auto &FuncI : Binary->getAllBinaryFunctions()) { 461 for (auto &R : FuncI.second.Ranges) { 462 Ranges[{R.first, R.second - 1}] += 0; 463 } 464 } 465 } else { 466 // For each range, we search for all ranges of the function it belongs to 467 // and initialize it with zero count, so it remains zero if doesn't hit any 468 // samples. This is to be consistent with compiler that interpret zero count 469 // as unexecuted(cold). 470 for (const auto &I : RangeCounter) { 471 uint64_t StartOffset = I.first.first; 472 for (const auto &Range : Binary->getRangesForOffset(StartOffset)) 473 Ranges[{Range.first, Range.second - 1}] += 0; 474 } 475 } 476 RangeSample DisjointRanges; 477 findDisjointRanges(DisjointRanges, Ranges); 478 return DisjointRanges; 479 } 480 481 void ProfileGenerator::populateBodySamplesForAllFunctions( 482 const RangeSample &RangeCounter) { 483 for (const auto &Range : preprocessRangeCounter(RangeCounter)) { 484 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 485 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 486 uint64_t Count = Range.second; 487 488 InstructionPointer IP(Binary, RangeBegin, true); 489 // Disjoint ranges may have range in the middle of two instr, 490 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 491 // can be Addr1+1 to Addr2-1. We should ignore such range. 492 if (IP.Address > RangeEnd) 493 continue; 494 495 do { 496 uint64_t Offset = Binary->virtualAddrToOffset(IP.Address); 497 const SampleContextFrameVector &FrameVec = 498 Binary->getFrameLocationStack(Offset); 499 if (!FrameVec.empty()) { 500 // FIXME: As accumulating total count per instruction caused some 501 // regression, we changed to accumulate total count per byte as a 502 // workaround. Tuning hotness threshold on the compiler side might be 503 // necessary in the future. 504 FunctionSamples &FunctionProfile = getLeafProfileAndAddTotalSamples( 505 FrameVec, Count * Binary->getInstSize(Offset)); 506 updateBodySamplesforFunctionProfile(FunctionProfile, FrameVec.back(), 507 Count); 508 } 509 } while (IP.advance() && IP.Address <= RangeEnd); 510 } 511 } 512 513 StringRef ProfileGeneratorBase::getCalleeNameForOffset(uint64_t TargetOffset) { 514 // Get the function range by branch target if it's a call branch. 515 auto *FRange = Binary->findFuncRangeForStartOffset(TargetOffset); 516 517 // We won't accumulate sample count for a range whose start is not the real 518 // function entry such as outlined function or inner labels. 519 if (!FRange || !FRange->IsFuncEntry) 520 return StringRef(); 521 522 return FunctionSamples::getCanonicalFnName(FRange->getFuncName()); 523 } 524 525 void ProfileGenerator::populateBoundarySamplesForAllFunctions( 526 const BranchSample &BranchCounters) { 527 for (const auto &Entry : BranchCounters) { 528 uint64_t SourceOffset = Entry.first.first; 529 uint64_t TargetOffset = Entry.first.second; 530 uint64_t Count = Entry.second; 531 assert(Count != 0 && "Unexpected zero weight branch"); 532 533 StringRef CalleeName = getCalleeNameForOffset(TargetOffset); 534 if (CalleeName.size() == 0) 535 continue; 536 // Record called target sample and its count. 537 const SampleContextFrameVector &FrameVec = 538 Binary->getFrameLocationStack(SourceOffset); 539 if (!FrameVec.empty()) { 540 FunctionSamples &FunctionProfile = 541 getLeafProfileAndAddTotalSamples(FrameVec, 0); 542 FunctionProfile.addCalledTargetSamples( 543 FrameVec.back().Location.LineOffset, 544 getBaseDiscriminator(FrameVec.back().Location.Discriminator), 545 CalleeName, Count); 546 } 547 // Add head samples for callee. 548 FunctionSamples &CalleeProfile = getTopLevelFunctionProfile(CalleeName); 549 CalleeProfile.addHeadSamples(Count); 550 } 551 } 552 553 void ProfileGeneratorBase::calculateAndShowDensity( 554 const SampleProfileMap &Profiles) { 555 double Density = calculateDensity(Profiles, HotCountThreshold); 556 showDensitySuggestion(Density); 557 } 558 559 FunctionSamples &CSProfileGenerator::getFunctionProfileForContext( 560 const SampleContextFrameVector &Context, bool WasLeafInlined) { 561 auto I = ProfileMap.find(SampleContext(Context)); 562 if (I == ProfileMap.end()) { 563 // Save the new context for future references. 564 SampleContextFrames NewContext = *Contexts.insert(Context).first; 565 SampleContext FContext(NewContext, RawContext); 566 auto Ret = ProfileMap.emplace(FContext, FunctionSamples()); 567 if (WasLeafInlined) 568 FContext.setAttribute(ContextWasInlined); 569 FunctionSamples &FProfile = Ret.first->second; 570 FProfile.setContext(FContext); 571 return Ret.first->second; 572 } 573 return I->second; 574 } 575 576 void CSProfileGenerator::generateProfile() { 577 FunctionSamples::ProfileIsCSFlat = true; 578 579 if (Binary->getTrackFuncContextSize()) 580 computeSizeForProfiledFunctions(); 581 582 if (Binary->usePseudoProbes()) { 583 // Enable pseudo probe functionalities in SampleProf 584 FunctionSamples::ProfileIsProbeBased = true; 585 generateProbeBasedProfile(); 586 } else { 587 generateLineNumBasedProfile(); 588 } 589 postProcessProfiles(); 590 } 591 592 void CSProfileGenerator::computeSizeForProfiledFunctions() { 593 std::unordered_set<const BinaryFunction *> ProfiledFunctions; 594 595 // Go through all the ranges in the CS counters, use the start of the range to 596 // look up the function it belongs and record the function. 597 for (const auto &CI : SampleCounters) { 598 for (const auto &Item : CI.second.RangeCounter) { 599 // FIXME: Filter the bogus crossing function range. 600 uint64_t StartOffset = Item.first.first; 601 if (FuncRange *FRange = Binary->findFuncRangeForOffset(StartOffset)) 602 ProfiledFunctions.insert(FRange->Func); 603 } 604 } 605 606 for (auto *Func : ProfiledFunctions) 607 Binary->computeInlinedContextSizeForFunc(Func); 608 609 // Flush the symbolizer to save memory. 610 Binary->flushSymbolizer(); 611 } 612 613 void CSProfileGenerator::generateLineNumBasedProfile() { 614 for (const auto &CI : SampleCounters) { 615 const auto *CtxKey = cast<StringBasedCtxKey>(CI.first.getPtr()); 616 617 // Get or create function profile for the range 618 FunctionSamples &FunctionProfile = 619 getFunctionProfileForContext(CtxKey->Context, CtxKey->WasLeafInlined); 620 621 // Fill in function body samples 622 populateBodySamplesForFunction(FunctionProfile, CI.second.RangeCounter); 623 // Fill in boundary sample counts as well as call site samples for calls 624 populateBoundarySamplesForFunction(CtxKey->Context, FunctionProfile, 625 CI.second.BranchCounter); 626 } 627 // Fill in call site value sample for inlined calls and also use context to 628 // infer missing samples. Since we don't have call count for inlined 629 // functions, we estimate it from inlinee's profile using the entry of the 630 // body sample. 631 populateInferredFunctionSamples(); 632 633 updateTotalSamples(); 634 } 635 636 void CSProfileGenerator::populateBodySamplesForFunction( 637 FunctionSamples &FunctionProfile, const RangeSample &RangeCounter) { 638 // Compute disjoint ranges first, so we can use MAX 639 // for calculating count for each location. 640 RangeSample Ranges; 641 findDisjointRanges(Ranges, RangeCounter); 642 for (const auto &Range : Ranges) { 643 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 644 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 645 uint64_t Count = Range.second; 646 // Disjoint ranges have introduce zero-filled gap that 647 // doesn't belong to current context, filter them out. 648 if (Count == 0) 649 continue; 650 651 InstructionPointer IP(Binary, RangeBegin, true); 652 // Disjoint ranges may have range in the middle of two instr, 653 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 654 // can be Addr1+1 to Addr2-1. We should ignore such range. 655 if (IP.Address > RangeEnd) 656 continue; 657 658 do { 659 uint64_t Offset = Binary->virtualAddrToOffset(IP.Address); 660 auto LeafLoc = Binary->getInlineLeafFrameLoc(Offset); 661 if (LeafLoc.hasValue()) { 662 // Recording body sample for this specific context 663 updateBodySamplesforFunctionProfile(FunctionProfile, *LeafLoc, Count); 664 FunctionProfile.addTotalSamples(Count); 665 } 666 } while (IP.advance() && IP.Address <= RangeEnd); 667 } 668 } 669 670 void CSProfileGenerator::populateBoundarySamplesForFunction( 671 SampleContextFrames ContextId, FunctionSamples &FunctionProfile, 672 const BranchSample &BranchCounters) { 673 674 for (const auto &Entry : BranchCounters) { 675 uint64_t SourceOffset = Entry.first.first; 676 uint64_t TargetOffset = Entry.first.second; 677 uint64_t Count = Entry.second; 678 assert(Count != 0 && "Unexpected zero weight branch"); 679 680 StringRef CalleeName = getCalleeNameForOffset(TargetOffset); 681 if (CalleeName.size() == 0) 682 continue; 683 684 // Record called target sample and its count 685 auto LeafLoc = Binary->getInlineLeafFrameLoc(SourceOffset); 686 if (!LeafLoc.hasValue()) 687 continue; 688 FunctionProfile.addCalledTargetSamples( 689 LeafLoc->Location.LineOffset, 690 getBaseDiscriminator(LeafLoc->Location.Discriminator), CalleeName, 691 Count); 692 693 // Record head sample for called target(callee) 694 SampleContextFrameVector CalleeCtx(ContextId.begin(), ContextId.end()); 695 assert(CalleeCtx.back().FuncName == LeafLoc->FuncName && 696 "Leaf function name doesn't match"); 697 CalleeCtx.back() = *LeafLoc; 698 CalleeCtx.emplace_back(CalleeName, LineLocation(0, 0)); 699 FunctionSamples &CalleeProfile = getFunctionProfileForContext(CalleeCtx); 700 CalleeProfile.addHeadSamples(Count); 701 } 702 } 703 704 static SampleContextFrame 705 getCallerContext(SampleContextFrames CalleeContext, 706 SampleContextFrameVector &CallerContext) { 707 assert(CalleeContext.size() > 1 && "Unexpected empty context"); 708 CalleeContext = CalleeContext.drop_back(); 709 CallerContext.assign(CalleeContext.begin(), CalleeContext.end()); 710 SampleContextFrame CallerFrame = CallerContext.back(); 711 CallerContext.back().Location = LineLocation(0, 0); 712 return CallerFrame; 713 } 714 715 void CSProfileGenerator::populateInferredFunctionSamples() { 716 for (const auto &Item : ProfileMap) { 717 const auto &CalleeContext = Item.first; 718 const FunctionSamples &CalleeProfile = Item.second; 719 720 // If we already have head sample counts, we must have value profile 721 // for call sites added already. Skip to avoid double counting. 722 if (CalleeProfile.getHeadSamples()) 723 continue; 724 // If we don't have context, nothing to do for caller's call site. 725 // This could happen for entry point function. 726 if (CalleeContext.isBaseContext()) 727 continue; 728 729 // Infer Caller's frame loc and context ID through string splitting 730 SampleContextFrameVector CallerContextId; 731 SampleContextFrame &&CallerLeafFrameLoc = 732 getCallerContext(CalleeContext.getContextFrames(), CallerContextId); 733 SampleContextFrames CallerContext(CallerContextId); 734 735 // It's possible that we haven't seen any sample directly in the caller, 736 // in which case CallerProfile will not exist. But we can't modify 737 // ProfileMap while iterating it. 738 // TODO: created function profile for those callers too 739 if (ProfileMap.find(CallerContext) == ProfileMap.end()) 740 continue; 741 FunctionSamples &CallerProfile = ProfileMap[CallerContext]; 742 743 // Since we don't have call count for inlined functions, we 744 // estimate it from inlinee's profile using entry body sample. 745 uint64_t EstimatedCallCount = CalleeProfile.getEntrySamples(); 746 // If we don't have samples with location, use 1 to indicate live. 747 if (!EstimatedCallCount && !CalleeProfile.getBodySamples().size()) 748 EstimatedCallCount = 1; 749 CallerProfile.addCalledTargetSamples( 750 CallerLeafFrameLoc.Location.LineOffset, 751 CallerLeafFrameLoc.Location.Discriminator, 752 CalleeProfile.getContext().getName(), EstimatedCallCount); 753 CallerProfile.addBodySamples(CallerLeafFrameLoc.Location.LineOffset, 754 CallerLeafFrameLoc.Location.Discriminator, 755 EstimatedCallCount); 756 CallerProfile.addTotalSamples(EstimatedCallCount); 757 } 758 } 759 760 void CSProfileGenerator::postProcessProfiles() { 761 // Compute hot/cold threshold based on profile. This will be used for cold 762 // context profile merging/trimming. 763 computeSummaryAndThreshold(); 764 765 // Run global pre-inliner to adjust/merge context profile based on estimated 766 // inline decisions. 767 if (EnableCSPreInliner) { 768 CSPreInliner(ProfileMap, *Binary, HotCountThreshold, ColdCountThreshold) 769 .run(); 770 // Turn off the profile merger by default unless it is explicitly enabled. 771 if (!CSProfMergeColdContext.getNumOccurrences()) 772 CSProfMergeColdContext = false; 773 } 774 775 // Trim and merge cold context profile using cold threshold above. 776 if (TrimColdProfile || CSProfMergeColdContext) { 777 SampleContextTrimmer(ProfileMap) 778 .trimAndMergeColdContextProfiles( 779 HotCountThreshold, TrimColdProfile, CSProfMergeColdContext, 780 CSProfMaxColdContextDepth, EnableCSPreInliner); 781 } 782 783 // Merge function samples of CS profile to calculate profile density. 784 sampleprof::SampleProfileMap ContextLessProfiles; 785 for (const auto &I : ProfileMap) { 786 ContextLessProfiles[I.second.getName()].merge(I.second); 787 } 788 789 calculateAndShowDensity(ContextLessProfiles); 790 if (GenCSNestedProfile) { 791 CSProfileConverter CSConverter(ProfileMap); 792 CSConverter.convertProfiles(); 793 FunctionSamples::ProfileIsCSFlat = false; 794 FunctionSamples::ProfileIsCSNested = EnableCSPreInliner; 795 } 796 } 797 798 void ProfileGeneratorBase::computeSummaryAndThreshold() { 799 SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); 800 auto Summary = Builder.computeSummaryForProfiles(ProfileMap); 801 HotCountThreshold = ProfileSummaryBuilder::getHotCountThreshold( 802 (Summary->getDetailedSummary())); 803 ColdCountThreshold = ProfileSummaryBuilder::getColdCountThreshold( 804 (Summary->getDetailedSummary())); 805 } 806 807 // Helper function to extract context prefix string stack 808 // Extract context stack for reusing, leaf context stack will 809 // be added compressed while looking up function profile 810 static void extractPrefixContextStack( 811 SampleContextFrameVector &ContextStack, 812 const SmallVectorImpl<const MCDecodedPseudoProbe *> &Probes, 813 ProfiledBinary *Binary) { 814 for (const auto *P : Probes) { 815 Binary->getInlineContextForProbe(P, ContextStack, true); 816 } 817 } 818 819 void CSProfileGenerator::generateProbeBasedProfile() { 820 for (const auto &CI : SampleCounters) { 821 const auto *CtxKey = cast<ProbeBasedCtxKey>(CI.first.getPtr()); 822 SampleContextFrameVector ContextStack; 823 extractPrefixContextStack(ContextStack, CtxKey->Probes, Binary); 824 // Fill in function body samples from probes, also infer caller's samples 825 // from callee's probe 826 populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStack); 827 // Fill in boundary samples for a call probe 828 populateBoundarySamplesWithProbes(CI.second.BranchCounter, ContextStack); 829 } 830 } 831 832 void CSProfileGenerator::extractProbesFromRange(const RangeSample &RangeCounter, 833 ProbeCounterMap &ProbeCounter) { 834 RangeSample Ranges; 835 findDisjointRanges(Ranges, RangeCounter); 836 for (const auto &Range : Ranges) { 837 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 838 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 839 uint64_t Count = Range.second; 840 // Disjoint ranges have introduce zero-filled gap that 841 // doesn't belong to current context, filter them out. 842 if (Count == 0) 843 continue; 844 845 InstructionPointer IP(Binary, RangeBegin, true); 846 // Disjoint ranges may have range in the middle of two instr, 847 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 848 // can be Addr1+1 to Addr2-1. We should ignore such range. 849 if (IP.Address > RangeEnd) 850 continue; 851 852 do { 853 const AddressProbesMap &Address2ProbesMap = 854 Binary->getAddress2ProbesMap(); 855 auto It = Address2ProbesMap.find(IP.Address); 856 if (It != Address2ProbesMap.end()) { 857 for (const auto &Probe : It->second) { 858 if (!Probe.isBlock()) 859 continue; 860 ProbeCounter[&Probe] += Count; 861 } 862 } 863 } while (IP.advance() && IP.Address <= RangeEnd); 864 } 865 } 866 867 void CSProfileGenerator::populateBodySamplesWithProbes( 868 const RangeSample &RangeCounter, SampleContextFrames ContextStack) { 869 ProbeCounterMap ProbeCounter; 870 // Extract the top frame probes by looking up each address among the range in 871 // the Address2ProbeMap 872 extractProbesFromRange(RangeCounter, ProbeCounter); 873 std::unordered_map<MCDecodedPseudoProbeInlineTree *, 874 std::unordered_set<FunctionSamples *>> 875 FrameSamples; 876 for (const auto &PI : ProbeCounter) { 877 const MCDecodedPseudoProbe *Probe = PI.first; 878 uint64_t Count = PI.second; 879 FunctionSamples &FunctionProfile = 880 getFunctionProfileForLeafProbe(ContextStack, Probe); 881 // Record the current frame and FunctionProfile whenever samples are 882 // collected for non-danglie probes. This is for reporting all of the 883 // zero count probes of the frame later. 884 FrameSamples[Probe->getInlineTreeNode()].insert(&FunctionProfile); 885 FunctionProfile.addBodySamplesForProbe(Probe->getIndex(), Count); 886 FunctionProfile.addTotalSamples(Count); 887 if (Probe->isEntry()) { 888 FunctionProfile.addHeadSamples(Count); 889 // Look up for the caller's function profile 890 const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe); 891 SampleContextFrames CalleeContextId = 892 FunctionProfile.getContext().getContextFrames(); 893 if (InlinerDesc != nullptr && CalleeContextId.size() > 1) { 894 // Since the context id will be compressed, we have to use callee's 895 // context id to infer caller's context id to ensure they share the 896 // same context prefix. 897 SampleContextFrameVector CallerContextId; 898 SampleContextFrame &&CallerLeafFrameLoc = 899 getCallerContext(CalleeContextId, CallerContextId); 900 uint64_t CallerIndex = CallerLeafFrameLoc.Location.LineOffset; 901 assert(CallerIndex && 902 "Inferred caller's location index shouldn't be zero!"); 903 FunctionSamples &CallerProfile = 904 getFunctionProfileForContext(CallerContextId); 905 CallerProfile.setFunctionHash(InlinerDesc->FuncHash); 906 CallerProfile.addBodySamples(CallerIndex, 0, Count); 907 CallerProfile.addTotalSamples(Count); 908 CallerProfile.addCalledTargetSamples( 909 CallerIndex, 0, FunctionProfile.getContext().getName(), Count); 910 } 911 } 912 } 913 914 // Assign zero count for remaining probes without sample hits to 915 // differentiate from probes optimized away, of which the counts are unknown 916 // and will be inferred by the compiler. 917 for (auto &I : FrameSamples) { 918 for (auto *FunctionProfile : I.second) { 919 for (auto *Probe : I.first->getProbes()) { 920 FunctionProfile->addBodySamplesForProbe(Probe->getIndex(), 0); 921 } 922 } 923 } 924 } 925 926 void CSProfileGenerator::populateBoundarySamplesWithProbes( 927 const BranchSample &BranchCounter, SampleContextFrames ContextStack) { 928 for (const auto &BI : BranchCounter) { 929 uint64_t SourceOffset = BI.first.first; 930 uint64_t TargetOffset = BI.first.second; 931 uint64_t Count = BI.second; 932 uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset); 933 const MCDecodedPseudoProbe *CallProbe = 934 Binary->getCallProbeForAddr(SourceAddress); 935 if (CallProbe == nullptr) 936 continue; 937 FunctionSamples &FunctionProfile = 938 getFunctionProfileForLeafProbe(ContextStack, CallProbe); 939 FunctionProfile.addBodySamples(CallProbe->getIndex(), 0, Count); 940 FunctionProfile.addTotalSamples(Count); 941 StringRef CalleeName = getCalleeNameForOffset(TargetOffset); 942 if (CalleeName.size() == 0) 943 continue; 944 FunctionProfile.addCalledTargetSamples(CallProbe->getIndex(), 0, CalleeName, 945 Count); 946 } 947 } 948 949 FunctionSamples &CSProfileGenerator::getFunctionProfileForLeafProbe( 950 SampleContextFrames ContextStack, const MCDecodedPseudoProbe *LeafProbe) { 951 952 // Explicitly copy the context for appending the leaf context 953 SampleContextFrameVector NewContextStack(ContextStack.begin(), 954 ContextStack.end()); 955 Binary->getInlineContextForProbe(LeafProbe, NewContextStack, true); 956 // For leaf inlined context with the top frame, we should strip off the top 957 // frame's probe id, like: 958 // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar" 959 auto LeafFrame = NewContextStack.back(); 960 LeafFrame.Location = LineLocation(0, 0); 961 NewContextStack.pop_back(); 962 // Compress the context string except for the leaf frame 963 CSProfileGenerator::compressRecursionContext(NewContextStack); 964 CSProfileGenerator::trimContext(NewContextStack); 965 NewContextStack.push_back(LeafFrame); 966 967 const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->getGuid()); 968 bool WasLeafInlined = LeafProbe->getInlineTreeNode()->hasInlineSite(); 969 FunctionSamples &FunctionProile = 970 getFunctionProfileForContext(NewContextStack, WasLeafInlined); 971 FunctionProile.setFunctionHash(FuncDesc->FuncHash); 972 return FunctionProile; 973 } 974 975 } // end namespace sampleprof 976 } // end namespace llvm 977