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 (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 (auto Item : Boundaries) { 310 uint64_t Address = Item.first; 311 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 } else { 387 generateLineNumBasedProfile(); 388 } 389 postProcessProfiles(); 390 } 391 392 void ProfileGenerator::postProcessProfiles() { 393 computeSummaryAndThreshold(); 394 trimColdProfiles(ProfileMap, ColdCountThreshold); 395 calculateAndShowDensity(ProfileMap); 396 } 397 398 void ProfileGenerator::trimColdProfiles(const SampleProfileMap &Profiles, 399 uint64_t ColdCntThreshold) { 400 if (!TrimColdProfile) 401 return; 402 403 // Move cold profiles into a tmp container. 404 std::vector<SampleContext> ColdProfiles; 405 for (const auto &I : ProfileMap) { 406 if (I.second.getTotalSamples() < ColdCntThreshold) 407 ColdProfiles.emplace_back(I.first); 408 } 409 410 // Remove the cold profile from ProfileMap. 411 for (const auto &I : ColdProfiles) 412 ProfileMap.erase(I); 413 } 414 415 void ProfileGenerator::generateLineNumBasedProfile() { 416 assert(SampleCounters.size() == 1 && 417 "Must have one entry for profile generation."); 418 const SampleCounter &SC = SampleCounters.begin()->second; 419 // Fill in function body samples 420 populateBodySamplesForAllFunctions(SC.RangeCounter); 421 // Fill in boundary sample counts as well as call site samples for calls 422 populateBoundarySamplesForAllFunctions(SC.BranchCounter); 423 424 updateTotalSamples(); 425 } 426 427 FunctionSamples &ProfileGenerator::getLeafProfileAndAddTotalSamples( 428 const SampleContextFrameVector &FrameVec, uint64_t Count) { 429 // Get top level profile 430 FunctionSamples *FunctionProfile = 431 &getTopLevelFunctionProfile(FrameVec[0].FuncName); 432 FunctionProfile->addTotalSamples(Count); 433 434 for (size_t I = 1; I < FrameVec.size(); I++) { 435 LineLocation Callsite( 436 FrameVec[I - 1].Location.LineOffset, 437 getBaseDiscriminator(FrameVec[I - 1].Location.Discriminator)); 438 FunctionSamplesMap &SamplesMap = 439 FunctionProfile->functionSamplesAt(Callsite); 440 auto Ret = 441 SamplesMap.emplace(FrameVec[I].FuncName.str(), FunctionSamples()); 442 if (Ret.second) { 443 SampleContext Context(FrameVec[I].FuncName); 444 Ret.first->second.setContext(Context); 445 } 446 FunctionProfile = &Ret.first->second; 447 FunctionProfile->addTotalSamples(Count); 448 } 449 450 return *FunctionProfile; 451 } 452 453 RangeSample 454 ProfileGenerator::preprocessRangeCounter(const RangeSample &RangeCounter) { 455 RangeSample Ranges(RangeCounter.begin(), RangeCounter.end()); 456 if (FillZeroForAllFuncs) { 457 for (auto &FuncI : Binary->getAllBinaryFunctions()) { 458 for (auto &R : FuncI.second.Ranges) { 459 Ranges[{R.first, R.second - 1}] += 0; 460 } 461 } 462 } else { 463 // For each range, we search for all ranges of the function it belongs to 464 // and initialize it with zero count, so it remains zero if doesn't hit any 465 // samples. This is to be consistent with compiler that interpret zero count 466 // as unexecuted(cold). 467 for (auto I : RangeCounter) { 468 uint64_t StartOffset = I.first.first; 469 for (const auto &Range : Binary->getRangesForOffset(StartOffset)) 470 Ranges[{Range.first, Range.second - 1}] += 0; 471 } 472 } 473 RangeSample DisjointRanges; 474 findDisjointRanges(DisjointRanges, Ranges); 475 return DisjointRanges; 476 } 477 478 void ProfileGenerator::populateBodySamplesForAllFunctions( 479 const RangeSample &RangeCounter) { 480 for (auto Range : preprocessRangeCounter(RangeCounter)) { 481 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 482 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 483 uint64_t Count = Range.second; 484 485 InstructionPointer IP(Binary, RangeBegin, true); 486 // Disjoint ranges may have range in the middle of two instr, 487 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 488 // can be Addr1+1 to Addr2-1. We should ignore such range. 489 if (IP.Address > RangeEnd) 490 continue; 491 492 do { 493 uint64_t Offset = Binary->virtualAddrToOffset(IP.Address); 494 const SampleContextFrameVector &FrameVec = 495 Binary->getFrameLocationStack(Offset); 496 if (!FrameVec.empty()) { 497 // FIXME: As accumulating total count per instruction caused some 498 // regression, we changed to accumulate total count per byte as a 499 // workaround. Tuning hotness threshold on the compiler side might be 500 // necessary in the future. 501 FunctionSamples &FunctionProfile = getLeafProfileAndAddTotalSamples( 502 FrameVec, Count * Binary->getInstSize(Offset)); 503 updateBodySamplesforFunctionProfile(FunctionProfile, FrameVec.back(), 504 Count); 505 } 506 } while (IP.advance() && IP.Address <= RangeEnd); 507 } 508 } 509 510 StringRef ProfileGeneratorBase::getCalleeNameForOffset(uint64_t TargetOffset) { 511 // Get the function range by branch target if it's a call branch. 512 auto *FRange = Binary->findFuncRangeForStartOffset(TargetOffset); 513 514 // We won't accumulate sample count for a range whose start is not the real 515 // function entry such as outlined function or inner labels. 516 if (!FRange || !FRange->IsFuncEntry) 517 return StringRef(); 518 519 return FunctionSamples::getCanonicalFnName(FRange->getFuncName()); 520 } 521 522 void ProfileGenerator::populateBoundarySamplesForAllFunctions( 523 const BranchSample &BranchCounters) { 524 for (auto Entry : BranchCounters) { 525 uint64_t SourceOffset = Entry.first.first; 526 uint64_t TargetOffset = Entry.first.second; 527 uint64_t Count = Entry.second; 528 assert(Count != 0 && "Unexpected zero weight branch"); 529 530 StringRef CalleeName = getCalleeNameForOffset(TargetOffset); 531 if (CalleeName.size() == 0) 532 continue; 533 // Record called target sample and its count. 534 const SampleContextFrameVector &FrameVec = 535 Binary->getFrameLocationStack(SourceOffset); 536 if (!FrameVec.empty()) { 537 FunctionSamples &FunctionProfile = 538 getLeafProfileAndAddTotalSamples(FrameVec, 0); 539 FunctionProfile.addCalledTargetSamples( 540 FrameVec.back().Location.LineOffset, 541 getBaseDiscriminator(FrameVec.back().Location.Discriminator), 542 CalleeName, Count); 543 } 544 // Add head samples for callee. 545 FunctionSamples &CalleeProfile = getTopLevelFunctionProfile(CalleeName); 546 CalleeProfile.addHeadSamples(Count); 547 } 548 } 549 550 void ProfileGeneratorBase::calculateAndShowDensity( 551 const SampleProfileMap &Profiles) { 552 double Density = calculateDensity(Profiles, HotCountThreshold); 553 showDensitySuggestion(Density); 554 } 555 556 FunctionSamples &CSProfileGenerator::getFunctionProfileForContext( 557 const SampleContextFrameVector &Context, bool WasLeafInlined) { 558 auto I = ProfileMap.find(SampleContext(Context)); 559 if (I == ProfileMap.end()) { 560 // Save the new context for future references. 561 SampleContextFrames NewContext = *Contexts.insert(Context).first; 562 SampleContext FContext(NewContext, RawContext); 563 auto Ret = ProfileMap.emplace(FContext, FunctionSamples()); 564 if (WasLeafInlined) 565 FContext.setAttribute(ContextWasInlined); 566 FunctionSamples &FProfile = Ret.first->second; 567 FProfile.setContext(FContext); 568 return Ret.first->second; 569 } 570 return I->second; 571 } 572 573 void CSProfileGenerator::generateProfile() { 574 FunctionSamples::ProfileIsCSFlat = true; 575 576 if (Binary->getTrackFuncContextSize()) 577 computeSizeForProfiledFunctions(); 578 579 if (Binary->usePseudoProbes()) { 580 // Enable pseudo probe functionalities in SampleProf 581 FunctionSamples::ProfileIsProbeBased = true; 582 generateProbeBasedProfile(); 583 } else { 584 generateLineNumBasedProfile(); 585 } 586 postProcessProfiles(); 587 } 588 589 void CSProfileGenerator::computeSizeForProfiledFunctions() { 590 // Hash map to deduplicate the function range and the item is a pair of 591 // function start and end offset. 592 std::unordered_map<uint64_t, uint64_t> AggregatedRanges; 593 // Go through all the ranges in the CS counters, use the start of the range to 594 // look up the function it belongs and record the function range. 595 for (const auto &CI : SampleCounters) { 596 for (auto Item : CI.second.RangeCounter) { 597 // FIXME: Filter the bogus crossing function range. 598 uint64_t StartOffset = Item.first.first; 599 // Note that a function can be spilt into multiple ranges, so get all 600 // ranges of the function. 601 for (const auto &Range : Binary->getRangesForOffset(StartOffset)) 602 AggregatedRanges[Range.first] = Range.second; 603 } 604 } 605 606 for (auto I : AggregatedRanges) { 607 uint64_t StartOffset = I.first; 608 uint64_t EndOffset = I.second; 609 Binary->computeInlinedContextSizeForRange(StartOffset, EndOffset); 610 } 611 } 612 613 void CSProfileGenerator::generateLineNumBasedProfile() { 614 for (const auto &CI : SampleCounters) { 615 const StringBasedCtxKey *CtxKey = 616 dyn_cast<StringBasedCtxKey>(CI.first.getPtr()); 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 (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 (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 ProbeBasedCtxKey *CtxKey = 822 dyn_cast<ProbeBasedCtxKey>(CI.first.getPtr()); 823 SampleContextFrameVector ContextStack; 824 extractPrefixContextStack(ContextStack, CtxKey->Probes, Binary); 825 // Fill in function body samples from probes, also infer caller's samples 826 // from callee's probe 827 populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStack); 828 // Fill in boundary samples for a call probe 829 populateBoundarySamplesWithProbes(CI.second.BranchCounter, ContextStack); 830 } 831 } 832 833 void CSProfileGenerator::extractProbesFromRange(const RangeSample &RangeCounter, 834 ProbeCounterMap &ProbeCounter) { 835 RangeSample Ranges; 836 findDisjointRanges(Ranges, RangeCounter); 837 for (const auto &Range : Ranges) { 838 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 839 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 840 uint64_t Count = Range.second; 841 // Disjoint ranges have introduce zero-filled gap that 842 // doesn't belong to current context, filter them out. 843 if (Count == 0) 844 continue; 845 846 InstructionPointer IP(Binary, RangeBegin, true); 847 // Disjoint ranges may have range in the middle of two instr, 848 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 849 // can be Addr1+1 to Addr2-1. We should ignore such range. 850 if (IP.Address > RangeEnd) 851 continue; 852 853 do { 854 const AddressProbesMap &Address2ProbesMap = 855 Binary->getAddress2ProbesMap(); 856 auto It = Address2ProbesMap.find(IP.Address); 857 if (It != Address2ProbesMap.end()) { 858 for (const auto &Probe : It->second) { 859 if (!Probe.isBlock()) 860 continue; 861 ProbeCounter[&Probe] += Count; 862 } 863 } 864 } while (IP.advance() && IP.Address <= RangeEnd); 865 } 866 } 867 868 void CSProfileGenerator::populateBodySamplesWithProbes( 869 const RangeSample &RangeCounter, SampleContextFrames ContextStack) { 870 ProbeCounterMap ProbeCounter; 871 // Extract the top frame probes by looking up each address among the range in 872 // the Address2ProbeMap 873 extractProbesFromRange(RangeCounter, ProbeCounter); 874 std::unordered_map<MCDecodedPseudoProbeInlineTree *, 875 std::unordered_set<FunctionSamples *>> 876 FrameSamples; 877 for (auto PI : ProbeCounter) { 878 const MCDecodedPseudoProbe *Probe = PI.first; 879 uint64_t Count = PI.second; 880 FunctionSamples &FunctionProfile = 881 getFunctionProfileForLeafProbe(ContextStack, Probe); 882 // Record the current frame and FunctionProfile whenever samples are 883 // collected for non-danglie probes. This is for reporting all of the 884 // zero count probes of the frame later. 885 FrameSamples[Probe->getInlineTreeNode()].insert(&FunctionProfile); 886 FunctionProfile.addBodySamplesForProbe(Probe->getIndex(), Count); 887 FunctionProfile.addTotalSamples(Count); 888 if (Probe->isEntry()) { 889 FunctionProfile.addHeadSamples(Count); 890 // Look up for the caller's function profile 891 const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe); 892 SampleContextFrames CalleeContextId = 893 FunctionProfile.getContext().getContextFrames(); 894 if (InlinerDesc != nullptr && CalleeContextId.size() > 1) { 895 // Since the context id will be compressed, we have to use callee's 896 // context id to infer caller's context id to ensure they share the 897 // same context prefix. 898 SampleContextFrameVector CallerContextId; 899 SampleContextFrame &&CallerLeafFrameLoc = 900 getCallerContext(CalleeContextId, CallerContextId); 901 uint64_t CallerIndex = CallerLeafFrameLoc.Location.LineOffset; 902 assert(CallerIndex && 903 "Inferred caller's location index shouldn't be zero!"); 904 FunctionSamples &CallerProfile = 905 getFunctionProfileForContext(CallerContextId); 906 CallerProfile.setFunctionHash(InlinerDesc->FuncHash); 907 CallerProfile.addBodySamples(CallerIndex, 0, Count); 908 CallerProfile.addTotalSamples(Count); 909 CallerProfile.addCalledTargetSamples( 910 CallerIndex, 0, FunctionProfile.getContext().getName(), Count); 911 } 912 } 913 } 914 915 // Assign zero count for remaining probes without sample hits to 916 // differentiate from probes optimized away, of which the counts are unknown 917 // and will be inferred by the compiler. 918 for (auto &I : FrameSamples) { 919 for (auto *FunctionProfile : I.second) { 920 for (auto *Probe : I.first->getProbes()) { 921 FunctionProfile->addBodySamplesForProbe(Probe->getIndex(), 0); 922 } 923 } 924 } 925 } 926 927 void CSProfileGenerator::populateBoundarySamplesWithProbes( 928 const BranchSample &BranchCounter, SampleContextFrames ContextStack) { 929 for (auto BI : BranchCounter) { 930 uint64_t SourceOffset = BI.first.first; 931 uint64_t TargetOffset = BI.first.second; 932 uint64_t Count = BI.second; 933 uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset); 934 const MCDecodedPseudoProbe *CallProbe = 935 Binary->getCallProbeForAddr(SourceAddress); 936 if (CallProbe == nullptr) 937 continue; 938 FunctionSamples &FunctionProfile = 939 getFunctionProfileForLeafProbe(ContextStack, CallProbe); 940 FunctionProfile.addBodySamples(CallProbe->getIndex(), 0, Count); 941 FunctionProfile.addTotalSamples(Count); 942 StringRef CalleeName = getCalleeNameForOffset(TargetOffset); 943 if (CalleeName.size() == 0) 944 continue; 945 FunctionProfile.addCalledTargetSamples(CallProbe->getIndex(), 0, CalleeName, 946 Count); 947 } 948 } 949 950 FunctionSamples &CSProfileGenerator::getFunctionProfileForLeafProbe( 951 SampleContextFrames ContextStack, const MCDecodedPseudoProbe *LeafProbe) { 952 953 // Explicitly copy the context for appending the leaf context 954 SampleContextFrameVector NewContextStack(ContextStack.begin(), 955 ContextStack.end()); 956 Binary->getInlineContextForProbe(LeafProbe, NewContextStack, true); 957 // For leaf inlined context with the top frame, we should strip off the top 958 // frame's probe id, like: 959 // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar" 960 auto LeafFrame = NewContextStack.back(); 961 LeafFrame.Location = LineLocation(0, 0); 962 NewContextStack.pop_back(); 963 // Compress the context string except for the leaf frame 964 CSProfileGenerator::compressRecursionContext(NewContextStack); 965 CSProfileGenerator::trimContext(NewContextStack); 966 NewContextStack.push_back(LeafFrame); 967 968 const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->getGuid()); 969 bool WasLeafInlined = LeafProbe->getInlineTreeNode()->hasInlineSite(); 970 FunctionSamples &FunctionProile = 971 getFunctionProfileForContext(NewContextStack, WasLeafInlined); 972 FunctionProile.setFunctionHash(FuncDesc->FuncHash); 973 return FunctionProile; 974 } 975 976 } // end namespace sampleprof 977 } // end namespace llvm 978