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