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