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