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 ErrorOr<uint64_t> R = 278 FunctionProfile.findSamplesAt(LeafLoc.Location.LineOffset, Discriminator); 279 280 uint64_t PreviousCount = R ? R.get() : 0; 281 if (PreviousCount <= Count) { 282 FunctionProfile.addBodySamples(LeafLoc.Location.LineOffset, Discriminator, 283 Count - PreviousCount); 284 } 285 } 286 287 FunctionSamples & 288 ProfileGenerator::getTopLevelFunctionProfile(StringRef FuncName) { 289 SampleContext Context(FuncName); 290 auto Ret = ProfileMap.emplace(Context, FunctionSamples()); 291 if (Ret.second) { 292 FunctionSamples &FProfile = Ret.first->second; 293 FProfile.setContext(Context); 294 } 295 return Ret.first->second; 296 } 297 298 void ProfileGenerator::generateProfile() { 299 if (Binary->usePseudoProbes()) { 300 // TODO: Support probe based profile generation 301 } else { 302 generateLineNumBasedProfile(); 303 } 304 } 305 306 void ProfileGenerator::generateLineNumBasedProfile() { 307 assert(SampleCounters.size() == 1 && 308 "Must have one entry for profile generation."); 309 const SampleCounter &SC = SampleCounters.begin()->second; 310 // Fill in function body samples 311 populateBodySamplesForAllFunctions(SC.RangeCounter); 312 // Fill in boundary sample counts as well as call site samples for calls 313 populateBoundarySamplesForAllFunctions(SC.BranchCounter); 314 } 315 316 FunctionSamples &ProfileGenerator::getLeafProfileAndAddTotalSamples( 317 const SampleContextFrameVector &FrameVec, uint64_t Count) { 318 // Get top level profile 319 FunctionSamples *FunctionProfile = 320 &getTopLevelFunctionProfile(FrameVec[0].FuncName); 321 FunctionProfile->addTotalSamples(Count); 322 323 for (size_t I = 1; I < FrameVec.size(); I++) { 324 LineLocation Callsite( 325 FrameVec[I - 1].Location.LineOffset, 326 getBaseDiscriminator(FrameVec[I - 1].Location.Discriminator)); 327 FunctionSamplesMap &SamplesMap = 328 FunctionProfile->functionSamplesAt(Callsite); 329 auto Ret = 330 SamplesMap.emplace(FrameVec[I].FuncName.str(), FunctionSamples()); 331 if (Ret.second) { 332 SampleContext Context(FrameVec[I].FuncName); 333 Ret.first->second.setContext(Context); 334 } 335 FunctionProfile = &Ret.first->second; 336 FunctionProfile->addTotalSamples(Count); 337 } 338 339 return *FunctionProfile; 340 } 341 342 RangeSample 343 ProfileGenerator::preprocessRangeCounter(const RangeSample &RangeCounter) { 344 RangeSample Ranges(RangeCounter.begin(), RangeCounter.end()); 345 // For each range, we search for the range of the function it belongs to and 346 // initialize it with zero count, so it remains zero if doesn't hit any 347 // samples. This is to be consistent with compiler that interpret zero count 348 // as unexecuted(cold). 349 for (auto I : RangeCounter) { 350 uint64_t RangeBegin = I.first.first; 351 uint64_t RangeEnd = I.first.second; 352 // Find the function offset range the current range begin belongs to. 353 auto FuncRange = Binary->findFuncOffsetRange(RangeBegin); 354 if (FuncRange.second == 0) 355 WithColor::warning() 356 << "[" << format("%8" PRIx64, RangeBegin) << " - " 357 << format("%8" PRIx64, RangeEnd) 358 << "]: Invalid range or disassembling error in profiled binary.\n"; 359 else if (RangeEnd > FuncRange.second) 360 WithColor::warning() << "[" << format("%8" PRIx64, RangeBegin) << " - " 361 << format("%8" PRIx64, RangeEnd) 362 << "]: Range is across different functions.\n"; 363 else 364 Ranges[FuncRange] += 0; 365 } 366 RangeSample DisjointRanges; 367 findDisjointRanges(DisjointRanges, Ranges); 368 return DisjointRanges; 369 } 370 371 void ProfileGenerator::populateBodySamplesForAllFunctions( 372 const RangeSample &RangeCounter) { 373 for (auto Range : preprocessRangeCounter(RangeCounter)) { 374 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 375 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 376 uint64_t Count = Range.second; 377 378 InstructionPointer IP(Binary, RangeBegin, true); 379 // Disjoint ranges may have range in the middle of two instr, 380 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 381 // can be Addr1+1 to Addr2-1. We should ignore such range. 382 while (IP.Address <= RangeEnd) { 383 uint64_t Offset = Binary->virtualAddrToOffset(IP.Address); 384 const SampleContextFrameVector &FrameVec = 385 Binary->getFrameLocationStack(Offset); 386 if (!FrameVec.empty()) { 387 uint64_t DC = Count * getDuplicationFactor( 388 FrameVec.back().Location.Discriminator); 389 FunctionSamples &FunctionProfile = 390 getLeafProfileAndAddTotalSamples(FrameVec, DC); 391 updateBodySamplesforFunctionProfile(FunctionProfile, FrameVec.back(), 392 DC); 393 } 394 // Move to next IP within the range. 395 IP.advance(); 396 } 397 } 398 } 399 400 void ProfileGenerator::populateBoundarySamplesForAllFunctions( 401 const BranchSample &BranchCounters) { 402 for (auto Entry : BranchCounters) { 403 uint64_t SourceOffset = Entry.first.first; 404 uint64_t TargetOffset = Entry.first.second; 405 uint64_t Count = Entry.second; 406 assert(Count != 0 && "Unexpected zero weight branch"); 407 408 // Get the callee name by branch target if it's a call branch. 409 StringRef CalleeName = FunctionSamples::getCanonicalFnName( 410 Binary->getFuncFromStartOffset(TargetOffset)); 411 if (CalleeName.size() == 0) 412 continue; 413 // Record called target sample and its count. 414 const SampleContextFrameVector &FrameVec = 415 Binary->getFrameLocationStack(SourceOffset); 416 if (!FrameVec.empty()) { 417 Count *= getDuplicationFactor(FrameVec.back().Location.Discriminator); 418 FunctionSamples &FunctionProfile = 419 getLeafProfileAndAddTotalSamples(FrameVec, Count); 420 FunctionProfile.addCalledTargetSamples( 421 FrameVec.back().Location.LineOffset, 422 getBaseDiscriminator(FrameVec.back().Location.Discriminator), 423 CalleeName, Count); 424 } 425 // Add head samples for callee. 426 FunctionSamples &CalleeProfile = getTopLevelFunctionProfile(CalleeName); 427 CalleeProfile.addHeadSamples(Count); 428 } 429 } 430 431 FunctionSamples &CSProfileGenerator::getFunctionProfileForContext( 432 const SampleContextFrameVector &Context, bool WasLeafInlined) { 433 auto I = ProfileMap.find(SampleContext(Context)); 434 if (I == ProfileMap.end()) { 435 // Save the new context for future references. 436 SampleContextFrames NewContext = *Contexts.insert(Context).first; 437 SampleContext FContext(NewContext, RawContext); 438 auto Ret = ProfileMap.emplace(FContext, FunctionSamples()); 439 if (WasLeafInlined) 440 FContext.setAttribute(ContextWasInlined); 441 FunctionSamples &FProfile = Ret.first->second; 442 FProfile.setContext(FContext); 443 return Ret.first->second; 444 } 445 return I->second; 446 } 447 448 void CSProfileGenerator::generateProfile() { 449 FunctionSamples::ProfileIsCS = true; 450 451 if (Binary->getTrackFuncContextSize()) 452 computeSizeForProfiledFunctions(); 453 454 if (Binary->usePseudoProbes()) { 455 // Enable pseudo probe functionalities in SampleProf 456 FunctionSamples::ProfileIsProbeBased = true; 457 generateProbeBasedProfile(); 458 } else { 459 generateLineNumBasedProfile(); 460 } 461 postProcessProfiles(); 462 } 463 464 void CSProfileGenerator::computeSizeForProfiledFunctions() { 465 // Hash map to deduplicate the function range and the item is a pair of 466 // function start and end offset. 467 std::unordered_map<uint64_t, uint64_t> FuncRanges; 468 // Go through all the ranges in the CS counters, use the start of the range to 469 // look up the function it belongs and record the function range. 470 for (const auto &CI : SampleCounters) { 471 for (auto Item : CI.second.RangeCounter) { 472 // FIXME: Filter the bogus crossing function range. 473 uint64_t RangeStartOffset = Item.first.first; 474 auto FuncRange = Binary->findFuncOffsetRange(RangeStartOffset); 475 if (FuncRange.second != 0) 476 FuncRanges[FuncRange.first] = FuncRange.second; 477 } 478 } 479 480 for (auto I : FuncRanges) { 481 uint64_t StartOffset = I.first; 482 uint64_t EndOffset = I.second; 483 Binary->computeInlinedContextSizeForRange(StartOffset, EndOffset); 484 } 485 } 486 487 void CSProfileGenerator::generateLineNumBasedProfile() { 488 for (const auto &CI : SampleCounters) { 489 const StringBasedCtxKey *CtxKey = 490 dyn_cast<StringBasedCtxKey>(CI.first.getPtr()); 491 // Get or create function profile for the range 492 FunctionSamples &FunctionProfile = 493 getFunctionProfileForContext(CtxKey->Context, CtxKey->WasLeafInlined); 494 495 // Fill in function body samples 496 populateBodySamplesForFunction(FunctionProfile, CI.second.RangeCounter); 497 // Fill in boundary sample counts as well as call site samples for calls 498 populateBoundarySamplesForFunction(CtxKey->Context, FunctionProfile, 499 CI.second.BranchCounter); 500 } 501 // Fill in call site value sample for inlined calls and also use context to 502 // infer missing samples. Since we don't have call count for inlined 503 // functions, we estimate it from inlinee's profile using the entry of the 504 // body sample. 505 populateInferredFunctionSamples(); 506 } 507 508 void CSProfileGenerator::populateBodySamplesForFunction( 509 FunctionSamples &FunctionProfile, const RangeSample &RangeCounter) { 510 // Compute disjoint ranges first, so we can use MAX 511 // for calculating count for each location. 512 RangeSample Ranges; 513 findDisjointRanges(Ranges, RangeCounter); 514 for (auto Range : Ranges) { 515 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 516 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 517 uint64_t Count = Range.second; 518 // Disjoint ranges have introduce zero-filled gap that 519 // doesn't belong to current context, filter them out. 520 if (Count == 0) 521 continue; 522 523 InstructionPointer IP(Binary, RangeBegin, true); 524 // Disjoint ranges may have range in the middle of two instr, 525 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 526 // can be Addr1+1 to Addr2-1. We should ignore such range. 527 while (IP.Address <= RangeEnd) { 528 uint64_t Offset = Binary->virtualAddrToOffset(IP.Address); 529 auto LeafLoc = Binary->getInlineLeafFrameLoc(Offset); 530 if (LeafLoc.hasValue()) { 531 // Recording body sample for this specific context 532 uint64_t DC = 533 Count * getDuplicationFactor(LeafLoc->Location.Discriminator); 534 updateBodySamplesforFunctionProfile(FunctionProfile, *LeafLoc, DC); 535 FunctionProfile.addTotalSamples(DC); 536 } 537 538 // Move to next IP within the range 539 IP.advance(); 540 } 541 } 542 } 543 544 void CSProfileGenerator::populateBoundarySamplesForFunction( 545 SampleContextFrames ContextId, FunctionSamples &FunctionProfile, 546 const BranchSample &BranchCounters) { 547 548 for (auto Entry : BranchCounters) { 549 uint64_t SourceOffset = Entry.first.first; 550 uint64_t TargetOffset = Entry.first.second; 551 uint64_t Count = Entry.second; 552 assert(Count != 0 && "Unexpected zero weight branch"); 553 554 // Get the callee name by branch target if it's a call branch 555 StringRef CalleeName = FunctionSamples::getCanonicalFnName( 556 Binary->getFuncFromStartOffset(TargetOffset)); 557 if (CalleeName.size() == 0) 558 continue; 559 560 // Record called target sample and its count 561 auto LeafLoc = Binary->getInlineLeafFrameLoc(SourceOffset); 562 if (!LeafLoc.hasValue()) 563 continue; 564 Count *= getDuplicationFactor(LeafLoc->Location.Discriminator); 565 FunctionProfile.addCalledTargetSamples( 566 LeafLoc->Location.LineOffset, 567 getBaseDiscriminator(LeafLoc->Location.Discriminator), CalleeName, 568 Count); 569 570 // Record head sample for called target(callee) 571 SampleContextFrameVector CalleeCtx(ContextId.begin(), ContextId.end()); 572 assert(CalleeCtx.back().FuncName == LeafLoc->FuncName && 573 "Leaf function name doesn't match"); 574 CalleeCtx.back() = *LeafLoc; 575 CalleeCtx.emplace_back(CalleeName, LineLocation(0, 0)); 576 FunctionSamples &CalleeProfile = getFunctionProfileForContext(CalleeCtx); 577 CalleeProfile.addHeadSamples(Count); 578 } 579 } 580 581 static SampleContextFrame 582 getCallerContext(SampleContextFrames CalleeContext, 583 SampleContextFrameVector &CallerContext) { 584 assert(CalleeContext.size() > 1 && "Unexpected empty context"); 585 CalleeContext = CalleeContext.drop_back(); 586 CallerContext.assign(CalleeContext.begin(), CalleeContext.end()); 587 SampleContextFrame CallerFrame = CallerContext.back(); 588 CallerContext.back().Location = LineLocation(0, 0); 589 return CallerFrame; 590 } 591 592 void CSProfileGenerator::populateInferredFunctionSamples() { 593 for (const auto &Item : ProfileMap) { 594 const auto &CalleeContext = Item.first; 595 const FunctionSamples &CalleeProfile = Item.second; 596 597 // If we already have head sample counts, we must have value profile 598 // for call sites added already. Skip to avoid double counting. 599 if (CalleeProfile.getHeadSamples()) 600 continue; 601 // If we don't have context, nothing to do for caller's call site. 602 // This could happen for entry point function. 603 if (CalleeContext.isBaseContext()) 604 continue; 605 606 // Infer Caller's frame loc and context ID through string splitting 607 SampleContextFrameVector CallerContextId; 608 SampleContextFrame &&CallerLeafFrameLoc = 609 getCallerContext(CalleeContext.getContextFrames(), CallerContextId); 610 SampleContextFrames CallerContext(CallerContextId); 611 612 // It's possible that we haven't seen any sample directly in the caller, 613 // in which case CallerProfile will not exist. But we can't modify 614 // ProfileMap while iterating it. 615 // TODO: created function profile for those callers too 616 if (ProfileMap.find(CallerContext) == ProfileMap.end()) 617 continue; 618 FunctionSamples &CallerProfile = ProfileMap[CallerContext]; 619 620 // Since we don't have call count for inlined functions, we 621 // estimate it from inlinee's profile using entry body sample. 622 uint64_t EstimatedCallCount = CalleeProfile.getEntrySamples(); 623 // If we don't have samples with location, use 1 to indicate live. 624 if (!EstimatedCallCount && !CalleeProfile.getBodySamples().size()) 625 EstimatedCallCount = 1; 626 CallerProfile.addCalledTargetSamples( 627 CallerLeafFrameLoc.Location.LineOffset, 628 CallerLeafFrameLoc.Location.Discriminator, 629 CalleeProfile.getContext().getName(), EstimatedCallCount); 630 CallerProfile.addBodySamples(CallerLeafFrameLoc.Location.LineOffset, 631 CallerLeafFrameLoc.Location.Discriminator, 632 EstimatedCallCount); 633 CallerProfile.addTotalSamples(EstimatedCallCount); 634 } 635 } 636 637 void CSProfileGenerator::postProcessProfiles() { 638 // Compute hot/cold threshold based on profile. This will be used for cold 639 // context profile merging/trimming. 640 computeSummaryAndThreshold(); 641 642 // Run global pre-inliner to adjust/merge context profile based on estimated 643 // inline decisions. 644 if (EnableCSPreInliner) { 645 CSPreInliner(ProfileMap, *Binary, HotCountThreshold, ColdCountThreshold) 646 .run(); 647 } 648 649 // Trim and merge cold context profile using cold threshold above. By default, 650 // we skip such merging and trimming when preinliner is on. 651 if (!EnableCSPreInliner || CSProfTrimColdContext.getNumOccurrences() || 652 CSProfMergeColdContext.getNumOccurrences()) { 653 SampleContextTrimmer(ProfileMap) 654 .trimAndMergeColdContextProfiles( 655 HotCountThreshold, CSProfTrimColdContext, CSProfMergeColdContext, 656 CSProfMaxColdContextDepth); 657 } 658 } 659 660 void CSProfileGenerator::computeSummaryAndThreshold() { 661 SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); 662 auto Summary = Builder.computeSummaryForProfiles(ProfileMap); 663 HotCountThreshold = ProfileSummaryBuilder::getHotCountThreshold( 664 (Summary->getDetailedSummary())); 665 ColdCountThreshold = ProfileSummaryBuilder::getColdCountThreshold( 666 (Summary->getDetailedSummary())); 667 } 668 669 // Helper function to extract context prefix string stack 670 // Extract context stack for reusing, leaf context stack will 671 // be added compressed while looking up function profile 672 static void extractPrefixContextStack( 673 SampleContextFrameVector &ContextStack, 674 const SmallVectorImpl<const MCDecodedPseudoProbe *> &Probes, 675 ProfiledBinary *Binary) { 676 for (const auto *P : Probes) { 677 Binary->getInlineContextForProbe(P, ContextStack, true); 678 } 679 } 680 681 void CSProfileGenerator::generateProbeBasedProfile() { 682 for (const auto &CI : SampleCounters) { 683 const ProbeBasedCtxKey *CtxKey = 684 dyn_cast<ProbeBasedCtxKey>(CI.first.getPtr()); 685 SampleContextFrameVector ContextStack; 686 extractPrefixContextStack(ContextStack, CtxKey->Probes, Binary); 687 // Fill in function body samples from probes, also infer caller's samples 688 // from callee's probe 689 populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStack); 690 // Fill in boundary samples for a call probe 691 populateBoundarySamplesWithProbes(CI.second.BranchCounter, ContextStack); 692 } 693 } 694 695 void CSProfileGenerator::extractProbesFromRange(const RangeSample &RangeCounter, 696 ProbeCounterMap &ProbeCounter) { 697 RangeSample Ranges; 698 findDisjointRanges(Ranges, RangeCounter); 699 for (const auto &Range : Ranges) { 700 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 701 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 702 uint64_t Count = Range.second; 703 // Disjoint ranges have introduce zero-filled gap that 704 // doesn't belong to current context, filter them out. 705 if (Count == 0) 706 continue; 707 708 InstructionPointer IP(Binary, RangeBegin, true); 709 710 // Disjoint ranges may have range in the middle of two instr, 711 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 712 // can be Addr1+1 to Addr2-1. We should ignore such range. 713 if (IP.Address > RangeEnd) 714 continue; 715 716 while (IP.Address <= RangeEnd) { 717 const AddressProbesMap &Address2ProbesMap = 718 Binary->getAddress2ProbesMap(); 719 auto It = Address2ProbesMap.find(IP.Address); 720 if (It != Address2ProbesMap.end()) { 721 for (const auto &Probe : It->second) { 722 if (!Probe.isBlock()) 723 continue; 724 ProbeCounter[&Probe] += Count; 725 } 726 } 727 728 IP.advance(); 729 } 730 } 731 } 732 733 void CSProfileGenerator::populateBodySamplesWithProbes( 734 const RangeSample &RangeCounter, SampleContextFrames ContextStack) { 735 ProbeCounterMap ProbeCounter; 736 // Extract the top frame probes by looking up each address among the range in 737 // the Address2ProbeMap 738 extractProbesFromRange(RangeCounter, ProbeCounter); 739 std::unordered_map<MCDecodedPseudoProbeInlineTree *, 740 std::unordered_set<FunctionSamples *>> 741 FrameSamples; 742 for (auto PI : ProbeCounter) { 743 const MCDecodedPseudoProbe *Probe = PI.first; 744 uint64_t Count = PI.second; 745 FunctionSamples &FunctionProfile = 746 getFunctionProfileForLeafProbe(ContextStack, Probe); 747 // Record the current frame and FunctionProfile whenever samples are 748 // collected for non-danglie probes. This is for reporting all of the 749 // zero count probes of the frame later. 750 FrameSamples[Probe->getInlineTreeNode()].insert(&FunctionProfile); 751 FunctionProfile.addBodySamplesForProbe(Probe->getIndex(), Count); 752 FunctionProfile.addTotalSamples(Count); 753 if (Probe->isEntry()) { 754 FunctionProfile.addHeadSamples(Count); 755 // Look up for the caller's function profile 756 const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe); 757 if (InlinerDesc != nullptr) { 758 // Since the context id will be compressed, we have to use callee's 759 // context id to infer caller's context id to ensure they share the 760 // same context prefix. 761 SampleContextFrames CalleeContextId = 762 FunctionProfile.getContext().getContextFrames(); 763 SampleContextFrameVector CallerContextId; 764 SampleContextFrame &&CallerLeafFrameLoc = 765 getCallerContext(CalleeContextId, CallerContextId); 766 uint64_t CallerIndex = CallerLeafFrameLoc.Location.LineOffset; 767 assert(CallerIndex && 768 "Inferred caller's location index shouldn't be zero!"); 769 FunctionSamples &CallerProfile = 770 getFunctionProfileForContext(CallerContextId); 771 CallerProfile.setFunctionHash(InlinerDesc->FuncHash); 772 CallerProfile.addBodySamples(CallerIndex, 0, Count); 773 CallerProfile.addTotalSamples(Count); 774 CallerProfile.addCalledTargetSamples( 775 CallerIndex, 0, FunctionProfile.getContext().getName(), Count); 776 } 777 } 778 } 779 780 // Assign zero count for remaining probes without sample hits to 781 // differentiate from probes optimized away, of which the counts are unknown 782 // and will be inferred by the compiler. 783 for (auto &I : FrameSamples) { 784 for (auto *FunctionProfile : I.second) { 785 for (auto *Probe : I.first->getProbes()) { 786 FunctionProfile->addBodySamplesForProbe(Probe->getIndex(), 0); 787 } 788 } 789 } 790 } 791 792 void CSProfileGenerator::populateBoundarySamplesWithProbes( 793 const BranchSample &BranchCounter, SampleContextFrames ContextStack) { 794 for (auto BI : BranchCounter) { 795 uint64_t SourceOffset = BI.first.first; 796 uint64_t TargetOffset = BI.first.second; 797 uint64_t Count = BI.second; 798 uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset); 799 const MCDecodedPseudoProbe *CallProbe = 800 Binary->getCallProbeForAddr(SourceAddress); 801 if (CallProbe == nullptr) 802 continue; 803 FunctionSamples &FunctionProfile = 804 getFunctionProfileForLeafProbe(ContextStack, CallProbe); 805 FunctionProfile.addBodySamples(CallProbe->getIndex(), 0, Count); 806 FunctionProfile.addTotalSamples(Count); 807 StringRef CalleeName = FunctionSamples::getCanonicalFnName( 808 Binary->getFuncFromStartOffset(TargetOffset)); 809 if (CalleeName.size() == 0) 810 continue; 811 FunctionProfile.addCalledTargetSamples(CallProbe->getIndex(), 0, CalleeName, 812 Count); 813 } 814 } 815 816 FunctionSamples &CSProfileGenerator::getFunctionProfileForLeafProbe( 817 SampleContextFrames ContextStack, const MCDecodedPseudoProbe *LeafProbe) { 818 819 // Explicitly copy the context for appending the leaf context 820 SampleContextFrameVector NewContextStack(ContextStack.begin(), 821 ContextStack.end()); 822 Binary->getInlineContextForProbe(LeafProbe, NewContextStack, true); 823 // For leaf inlined context with the top frame, we should strip off the top 824 // frame's probe id, like: 825 // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar" 826 auto LeafFrame = NewContextStack.back(); 827 LeafFrame.Location = LineLocation(0, 0); 828 NewContextStack.pop_back(); 829 // Compress the context string except for the leaf frame 830 CSProfileGenerator::compressRecursionContext(NewContextStack); 831 CSProfileGenerator::trimContext(NewContextStack); 832 NewContextStack.push_back(LeafFrame); 833 834 const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->getGuid()); 835 bool WasLeafInlined = LeafProbe->getInlineTreeNode()->hasInlineSite(); 836 FunctionSamples &FunctionProile = 837 getFunctionProfileForContext(NewContextStack, WasLeafInlined); 838 FunctionProile.setFunctionHash(FuncDesc->FuncHash); 839 return FunctionProile; 840 } 841 842 } // end namespace sampleprof 843 } // end namespace llvm 844