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 "llvm/ProfileData/ProfileCommon.h" 11 12 static cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), 13 cl::Required, 14 cl::desc("Output profile file")); 15 static cl::alias OutputA("o", cl::desc("Alias for --output"), 16 cl::aliasopt(OutputFilename)); 17 18 static cl::opt<SampleProfileFormat> OutputFormat( 19 "format", cl::desc("Format of output profile"), cl::init(SPF_Text), 20 cl::values( 21 clEnumValN(SPF_Binary, "binary", "Binary encoding (default)"), 22 clEnumValN(SPF_Compact_Binary, "compbinary", "Compact binary encoding"), 23 clEnumValN(SPF_Ext_Binary, "extbinary", "Extensible binary encoding"), 24 clEnumValN(SPF_Text, "text", "Text encoding"), 25 clEnumValN(SPF_GCC, "gcc", 26 "GCC encoding (only meaningful for -sample)"))); 27 28 static cl::opt<int32_t, true> RecursionCompression( 29 "compress-recursion", 30 cl::desc("Compressing recursion by deduplicating adjacent frame " 31 "sequences up to the specified size. -1 means no size limit."), 32 cl::Hidden, 33 cl::location(llvm::sampleprof::CSProfileGenerator::MaxCompressionSize)); 34 35 static cl::opt<uint64_t> CSProfColdThreshold( 36 "csprof-cold-thres", cl::init(100), cl::ZeroOrMore, 37 cl::desc("Specify the total samples threshold for a context profile to " 38 "be considered cold, any cold profiles will be merged into " 39 "context-less base profiles")); 40 41 static cl::opt<bool> CSProfMergeColdContext( 42 "csprof-merge-cold-context", cl::init(true), cl::ZeroOrMore, 43 cl::desc("This works together with --csprof-cold-thres. If the total count " 44 "of context profile is smaller than the threshold, it will be " 45 "merged into context-less base profile.")); 46 47 static cl::opt<bool> CSProfTrimColdContext( 48 "csprof-trim-cold-context", cl::init(true), cl::ZeroOrMore, 49 cl::desc("This works together with --csprof-cold-thres. If the total count " 50 "of the profile after all merge is done is still smaller than " 51 "threshold, it will be trimmed.")); 52 53 using namespace llvm; 54 using namespace sampleprof; 55 56 namespace llvm { 57 namespace sampleprof { 58 59 // Initialize the MaxCompressionSize to -1 which means no size limit 60 int32_t CSProfileGenerator::MaxCompressionSize = -1; 61 62 static bool 63 usePseudoProbes(const BinarySampleCounterMap &BinarySampleCounters) { 64 return BinarySampleCounters.size() && 65 BinarySampleCounters.begin()->first->usePseudoProbes(); 66 } 67 68 std::unique_ptr<ProfileGenerator> 69 ProfileGenerator::create(const BinarySampleCounterMap &BinarySampleCounters, 70 enum PerfScriptType SampleType) { 71 std::unique_ptr<ProfileGenerator> ProfileGenerator; 72 if (SampleType == PERF_LBR_STACK) { 73 if (usePseudoProbes(BinarySampleCounters)) { 74 ProfileGenerator.reset( 75 new PseudoProbeCSProfileGenerator(BinarySampleCounters)); 76 } else { 77 ProfileGenerator.reset(new CSProfileGenerator(BinarySampleCounters)); 78 } 79 } else { 80 // TODO: 81 llvm_unreachable("Unsupported perfscript!"); 82 } 83 84 return ProfileGenerator; 85 } 86 87 void ProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer, 88 StringMap<FunctionSamples> &ProfileMap) { 89 Writer->write(ProfileMap); 90 } 91 92 void ProfileGenerator::write() { 93 auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); 94 if (std::error_code EC = WriterOrErr.getError()) 95 exitWithError(EC, OutputFilename); 96 write(std::move(WriterOrErr.get()), ProfileMap); 97 } 98 99 void ProfileGenerator::findDisjointRanges(RangeSample &DisjointRanges, 100 const RangeSample &Ranges) { 101 102 /* 103 Regions may overlap with each other. Using the boundary info, find all 104 disjoint ranges and their sample count. BoundaryPoint contains the count 105 multiple samples begin/end at this points. 106 107 |<--100-->| Sample1 108 |<------200------>| Sample2 109 A B C 110 111 In the example above, 112 Sample1 begins at A, ends at B, its value is 100. 113 Sample2 beings at A, ends at C, its value is 200. 114 For A, BeginCount is the sum of sample begins at A, which is 300 and no 115 samples ends at A, so EndCount is 0. 116 Then boundary points A, B, and C with begin/end counts are: 117 A: (300, 0) 118 B: (0, 100) 119 C: (0, 200) 120 */ 121 struct BoundaryPoint { 122 // Sum of sample counts beginning at this point 123 uint64_t BeginCount; 124 // Sum of sample counts ending at this point 125 uint64_t EndCount; 126 127 BoundaryPoint() : BeginCount(0), EndCount(0){}; 128 129 void addBeginCount(uint64_t Count) { BeginCount += Count; } 130 131 void addEndCount(uint64_t Count) { EndCount += Count; } 132 }; 133 134 /* 135 For the above example. With boundary points, follwing logic finds two 136 disjoint region of 137 138 [A,B]: 300 139 [B+1,C]: 200 140 141 If there is a boundary point that both begin and end, the point itself 142 becomes a separate disjoint region. For example, if we have original 143 ranges of 144 145 |<--- 100 --->| 146 |<--- 200 --->| 147 A B C 148 149 there are three boundary points with their begin/end counts of 150 151 A: (100, 0) 152 B: (200, 100) 153 C: (0, 200) 154 155 the disjoint ranges would be 156 157 [A, B-1]: 100 158 [B, B]: 300 159 [B+1, C]: 200. 160 */ 161 std::map<uint64_t, BoundaryPoint> Boundaries; 162 163 for (auto Item : Ranges) { 164 uint64_t Begin = Item.first.first; 165 uint64_t End = Item.first.second; 166 uint64_t Count = Item.second; 167 if (Boundaries.find(Begin) == Boundaries.end()) 168 Boundaries[Begin] = BoundaryPoint(); 169 Boundaries[Begin].addBeginCount(Count); 170 171 if (Boundaries.find(End) == Boundaries.end()) 172 Boundaries[End] = BoundaryPoint(); 173 Boundaries[End].addEndCount(Count); 174 } 175 176 uint64_t BeginAddress = 0; 177 int Count = 0; 178 for (auto Item : Boundaries) { 179 uint64_t Address = Item.first; 180 BoundaryPoint &Point = Item.second; 181 if (Point.BeginCount) { 182 if (BeginAddress) 183 DisjointRanges[{BeginAddress, Address - 1}] = Count; 184 Count += Point.BeginCount; 185 BeginAddress = Address; 186 } 187 if (Point.EndCount) { 188 assert(BeginAddress && "First boundary point cannot be 'end' point"); 189 DisjointRanges[{BeginAddress, Address}] = Count; 190 Count -= Point.EndCount; 191 BeginAddress = Address + 1; 192 } 193 } 194 } 195 196 FunctionSamples & 197 CSProfileGenerator::getFunctionProfileForContext(StringRef ContextStr, 198 bool WasLeafInlined) { 199 auto Ret = ProfileMap.try_emplace(ContextStr, FunctionSamples()); 200 if (Ret.second) { 201 SampleContext FContext(Ret.first->first(), RawContext); 202 if (WasLeafInlined) 203 FContext.setAttribute(ContextWasInlined); 204 FunctionSamples &FProfile = Ret.first->second; 205 FProfile.setContext(FContext); 206 FProfile.setName(FContext.getNameWithoutContext()); 207 } 208 return Ret.first->second; 209 } 210 211 void CSProfileGenerator::generateProfile() { 212 FunctionSamples::ProfileIsCS = true; 213 for (const auto &BI : BinarySampleCounters) { 214 ProfiledBinary *Binary = BI.first; 215 for (const auto &CI : BI.second) { 216 const StringBasedCtxKey *CtxKey = 217 dyn_cast<StringBasedCtxKey>(CI.first.getPtr()); 218 StringRef ContextId(CtxKey->Context); 219 // Get or create function profile for the range 220 FunctionSamples &FunctionProfile = 221 getFunctionProfileForContext(ContextId, CtxKey->WasLeafInlined); 222 223 // Fill in function body samples 224 populateFunctionBodySamples(FunctionProfile, CI.second.RangeCounter, 225 Binary); 226 // Fill in boundary sample counts as well as call site samples for calls 227 populateFunctionBoundarySamples(ContextId, FunctionProfile, 228 CI.second.BranchCounter, Binary); 229 } 230 } 231 // Fill in call site value sample for inlined calls and also use context to 232 // infer missing samples. Since we don't have call count for inlined 233 // functions, we estimate it from inlinee's profile using the entry of the 234 // body sample. 235 populateInferredFunctionSamples(); 236 237 postProcessProfiles(); 238 } 239 240 void CSProfileGenerator::updateBodySamplesforFunctionProfile( 241 FunctionSamples &FunctionProfile, const FrameLocation &LeafLoc, 242 uint64_t Count) { 243 // Filter out invalid negative(int type) lineOffset 244 if (LeafLoc.second.LineOffset & 0x80000000) 245 return; 246 // Use the maximum count of samples with same line location 247 ErrorOr<uint64_t> R = FunctionProfile.findSamplesAt( 248 LeafLoc.second.LineOffset, LeafLoc.second.Discriminator); 249 uint64_t PreviousCount = R ? R.get() : 0; 250 if (PreviousCount < Count) { 251 FunctionProfile.addBodySamples(LeafLoc.second.LineOffset, 252 LeafLoc.second.Discriminator, 253 Count - PreviousCount); 254 } 255 } 256 257 void CSProfileGenerator::populateFunctionBodySamples( 258 FunctionSamples &FunctionProfile, const RangeSample &RangeCounter, 259 ProfiledBinary *Binary) { 260 // Compute disjoint ranges first, so we can use MAX 261 // for calculating count for each location. 262 RangeSample Ranges; 263 findDisjointRanges(Ranges, RangeCounter); 264 for (auto Range : Ranges) { 265 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 266 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 267 uint64_t Count = Range.second; 268 // Disjoint ranges have introduce zero-filled gap that 269 // doesn't belong to current context, filter them out. 270 if (Count == 0) 271 continue; 272 273 InstructionPointer IP(Binary, RangeBegin, true); 274 275 // Disjoint ranges may have range in the middle of two instr, 276 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 277 // can be Addr1+1 to Addr2-1. We should ignore such range. 278 if (IP.Address > RangeEnd) 279 continue; 280 281 while (IP.Address <= RangeEnd) { 282 uint64_t Offset = Binary->virtualAddrToOffset(IP.Address); 283 auto LeafLoc = Binary->getInlineLeafFrameLoc(Offset); 284 if (LeafLoc.hasValue()) { 285 // Recording body sample for this specific context 286 updateBodySamplesforFunctionProfile(FunctionProfile, *LeafLoc, Count); 287 } 288 // Accumulate total sample count even it's a line with invalid debug info 289 FunctionProfile.addTotalSamples(Count); 290 // Move to next IP within the range 291 IP.advance(); 292 } 293 } 294 } 295 296 void CSProfileGenerator::populateFunctionBoundarySamples( 297 StringRef ContextId, FunctionSamples &FunctionProfile, 298 const BranchSample &BranchCounters, ProfiledBinary *Binary) { 299 300 for (auto Entry : BranchCounters) { 301 uint64_t SourceOffset = Entry.first.first; 302 uint64_t TargetOffset = Entry.first.second; 303 uint64_t Count = Entry.second; 304 // Get the callee name by branch target if it's a call branch 305 StringRef CalleeName = FunctionSamples::getCanonicalFnName( 306 Binary->getFuncFromStartOffset(TargetOffset)); 307 if (CalleeName.size() == 0) 308 continue; 309 310 // Record called target sample and its count 311 auto LeafLoc = Binary->getInlineLeafFrameLoc(SourceOffset); 312 if (!LeafLoc.hasValue()) 313 continue; 314 FunctionProfile.addCalledTargetSamples(LeafLoc->second.LineOffset, 315 LeafLoc->second.Discriminator, 316 CalleeName, Count); 317 318 // Record head sample for called target(callee) 319 std::ostringstream OCalleeCtxStr; 320 if (ContextId.find(" @ ") != StringRef::npos) { 321 OCalleeCtxStr << ContextId.rsplit(" @ ").first.str(); 322 OCalleeCtxStr << " @ "; 323 } 324 OCalleeCtxStr << getCallSite(*LeafLoc) << " @ " << CalleeName.str(); 325 326 FunctionSamples &CalleeProfile = 327 getFunctionProfileForContext(OCalleeCtxStr.str()); 328 assert(Count != 0 && "Unexpected zero weight branch"); 329 CalleeProfile.addHeadSamples(Count); 330 } 331 } 332 333 static FrameLocation getCallerContext(StringRef CalleeContext, 334 StringRef &CallerNameWithContext) { 335 StringRef CallerContext = CalleeContext.rsplit(" @ ").first; 336 CallerNameWithContext = CallerContext.rsplit(':').first; 337 auto ContextSplit = CallerContext.rsplit(" @ "); 338 StringRef CallerFrameStr = ContextSplit.second.size() == 0 339 ? ContextSplit.first 340 : ContextSplit.second; 341 FrameLocation LeafFrameLoc = {"", {0, 0}}; 342 StringRef Funcname; 343 SampleContext::decodeContextString(CallerFrameStr, Funcname, 344 LeafFrameLoc.second); 345 LeafFrameLoc.first = Funcname.str(); 346 return LeafFrameLoc; 347 } 348 349 void CSProfileGenerator::populateInferredFunctionSamples() { 350 for (const auto &Item : ProfileMap) { 351 const StringRef CalleeContext = Item.first(); 352 const FunctionSamples &CalleeProfile = Item.second; 353 354 // If we already have head sample counts, we must have value profile 355 // for call sites added already. Skip to avoid double counting. 356 if (CalleeProfile.getHeadSamples()) 357 continue; 358 // If we don't have context, nothing to do for caller's call site. 359 // This could happen for entry point function. 360 if (CalleeContext.find(" @ ") == StringRef::npos) 361 continue; 362 363 // Infer Caller's frame loc and context ID through string splitting 364 StringRef CallerContextId; 365 FrameLocation &&CallerLeafFrameLoc = 366 getCallerContext(CalleeContext, CallerContextId); 367 368 // It's possible that we haven't seen any sample directly in the caller, 369 // in which case CallerProfile will not exist. But we can't modify 370 // ProfileMap while iterating it. 371 // TODO: created function profile for those callers too 372 if (ProfileMap.find(CallerContextId) == ProfileMap.end()) 373 continue; 374 FunctionSamples &CallerProfile = ProfileMap[CallerContextId]; 375 376 // Since we don't have call count for inlined functions, we 377 // estimate it from inlinee's profile using entry body sample. 378 uint64_t EstimatedCallCount = CalleeProfile.getEntrySamples(); 379 // If we don't have samples with location, use 1 to indicate live. 380 if (!EstimatedCallCount && !CalleeProfile.getBodySamples().size()) 381 EstimatedCallCount = 1; 382 CallerProfile.addCalledTargetSamples( 383 CallerLeafFrameLoc.second.LineOffset, 384 CallerLeafFrameLoc.second.Discriminator, 385 CalleeProfile.getContext().getNameWithoutContext(), EstimatedCallCount); 386 CallerProfile.addBodySamples(CallerLeafFrameLoc.second.LineOffset, 387 CallerLeafFrameLoc.second.Discriminator, 388 EstimatedCallCount); 389 CallerProfile.addTotalSamples(EstimatedCallCount); 390 } 391 } 392 393 void CSProfileGenerator::postProcessProfiles() { 394 // Compute hot/cold threshold based on profile. This will be used for cold 395 // context profile merging/trimming. 396 computeSummaryAndThreshold(); 397 398 // Run global pre-inliner to adjust/merge context profile based on estimated 399 // inline decisions. 400 CSPreInliner(ProfileMap, PSI->getHotCountThreshold(), 401 PSI->getColdCountThreshold()) 402 .run(); 403 404 mergeAndTrimColdProfile(ProfileMap); 405 } 406 407 void CSProfileGenerator::computeSummaryAndThreshold() { 408 SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); 409 auto Summary = Builder.computeSummaryForProfiles(ProfileMap); 410 PSI.reset(new ProfileSummaryInfo(std::move(Summary))); 411 } 412 413 void CSProfileGenerator::mergeAndTrimColdProfile( 414 StringMap<FunctionSamples> &ProfileMap) { 415 if (!CSProfMergeColdContext && !CSProfTrimColdContext) 416 return; 417 418 // Use threshold calculated from profile summary unless specified 419 uint64_t ColdThreshold = PSI->getColdCountThreshold(); 420 if (CSProfColdThreshold.getNumOccurrences()) { 421 ColdThreshold = CSProfColdThreshold; 422 } 423 424 // Nothing to merge if sample threshold is zero 425 if (ColdThreshold == 0) 426 return; 427 428 // Filter the cold profiles from ProfileMap and move them into a tmp 429 // container 430 std::vector<std::pair<StringRef, const FunctionSamples *>> ColdProfiles; 431 for (const auto &I : ProfileMap) { 432 const FunctionSamples &FunctionProfile = I.second; 433 if (FunctionProfile.getTotalSamples() >= ColdThreshold) 434 continue; 435 ColdProfiles.emplace_back(I.getKey(), &I.second); 436 } 437 438 // Remove the code profile from ProfileMap and merge them into BaseProileMap 439 StringMap<FunctionSamples> BaseProfileMap; 440 for (const auto &I : ColdProfiles) { 441 if (CSProfMergeColdContext) { 442 auto Ret = BaseProfileMap.try_emplace( 443 I.second->getContext().getNameWithoutContext(), FunctionSamples()); 444 FunctionSamples &BaseProfile = Ret.first->second; 445 BaseProfile.merge(*I.second); 446 } 447 ProfileMap.erase(I.first); 448 } 449 450 // Merge the base profiles into ProfileMap; 451 for (const auto &I : BaseProfileMap) { 452 // Filter the cold base profile 453 if (CSProfTrimColdContext && 454 I.second.getTotalSamples() < CSProfColdThreshold && 455 ProfileMap.find(I.getKey()) == ProfileMap.end()) 456 continue; 457 // Merge the profile if the original profile exists, otherwise just insert 458 // as a new profile 459 FunctionSamples &OrigProfile = getFunctionProfileForContext(I.getKey()); 460 OrigProfile.merge(I.second); 461 } 462 } 463 464 void CSProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer, 465 StringMap<FunctionSamples> &ProfileMap) { 466 // Add bracket for context key to support different profile binary format 467 StringMap<FunctionSamples> CxtWithBracketPMap; 468 for (const auto &Item : ProfileMap) { 469 // After CSPreInliner the key of ProfileMap is no longer accurate for 470 // context, use the context attached to function samples instead. 471 std::string ContextWithBracket = 472 "[" + Item.second.getNameWithContext().str() + "]"; 473 auto Ret = CxtWithBracketPMap.try_emplace(ContextWithBracket, Item.second); 474 assert(Ret.second && "Must be a unique context"); 475 SampleContext FContext(Ret.first->first(), RawContext); 476 FunctionSamples &FProfile = Ret.first->second; 477 FContext.setAllAttributes(FProfile.getContext().getAllAttributes()); 478 FProfile.setName(FContext.getNameWithoutContext()); 479 FProfile.setContext(FContext); 480 } 481 Writer->write(CxtWithBracketPMap); 482 } 483 484 // Helper function to extract context prefix string stack 485 // Extract context stack for reusing, leaf context stack will 486 // be added compressed while looking up function profile 487 static void 488 extractPrefixContextStack(SmallVectorImpl<std::string> &ContextStrStack, 489 const SmallVectorImpl<const PseudoProbe *> &Probes, 490 ProfiledBinary *Binary) { 491 for (const auto *P : Probes) { 492 Binary->getInlineContextForProbe(P, ContextStrStack, true); 493 } 494 } 495 496 void PseudoProbeCSProfileGenerator::generateProfile() { 497 // Enable pseudo probe functionalities in SampleProf 498 FunctionSamples::ProfileIsProbeBased = true; 499 FunctionSamples::ProfileIsCS = true; 500 for (const auto &BI : BinarySampleCounters) { 501 ProfiledBinary *Binary = BI.first; 502 for (const auto &CI : BI.second) { 503 const ProbeBasedCtxKey *CtxKey = 504 dyn_cast<ProbeBasedCtxKey>(CI.first.getPtr()); 505 SmallVector<std::string, 16> ContextStrStack; 506 extractPrefixContextStack(ContextStrStack, CtxKey->Probes, Binary); 507 // Fill in function body samples from probes, also infer caller's samples 508 // from callee's probe 509 populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStrStack, 510 Binary); 511 // Fill in boundary samples for a call probe 512 populateBoundarySamplesWithProbes(CI.second.BranchCounter, 513 ContextStrStack, Binary); 514 } 515 } 516 517 postProcessProfiles(); 518 } 519 520 void PseudoProbeCSProfileGenerator::extractProbesFromRange( 521 const RangeSample &RangeCounter, ProbeCounterMap &ProbeCounter, 522 ProfiledBinary *Binary) { 523 RangeSample Ranges; 524 findDisjointRanges(Ranges, RangeCounter); 525 for (const auto &Range : Ranges) { 526 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 527 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 528 uint64_t Count = Range.second; 529 // Disjoint ranges have introduce zero-filled gap that 530 // doesn't belong to current context, filter them out. 531 if (Count == 0) 532 continue; 533 534 InstructionPointer IP(Binary, RangeBegin, true); 535 536 // Disjoint ranges may have range in the middle of two instr, 537 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 538 // can be Addr1+1 to Addr2-1. We should ignore such range. 539 if (IP.Address > RangeEnd) 540 continue; 541 542 while (IP.Address <= RangeEnd) { 543 const AddressProbesMap &Address2ProbesMap = 544 Binary->getAddress2ProbesMap(); 545 auto It = Address2ProbesMap.find(IP.Address); 546 if (It != Address2ProbesMap.end()) { 547 for (const auto &Probe : It->second) { 548 if (!Probe.isBlock()) 549 continue; 550 ProbeCounter[&Probe] += Count; 551 } 552 } 553 554 IP.advance(); 555 } 556 } 557 } 558 559 void PseudoProbeCSProfileGenerator::populateBodySamplesWithProbes( 560 const RangeSample &RangeCounter, 561 SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) { 562 ProbeCounterMap ProbeCounter; 563 // Extract the top frame probes by looking up each address among the range in 564 // the Address2ProbeMap 565 extractProbesFromRange(RangeCounter, ProbeCounter, Binary); 566 for (auto PI : ProbeCounter) { 567 const PseudoProbe *Probe = PI.first; 568 uint64_t Count = PI.second; 569 FunctionSamples &FunctionProfile = 570 getFunctionProfileForLeafProbe(ContextStrStack, Probe, Binary); 571 572 // Use InvalidProbeCount(UINT64_MAX) to mark sample count for a dangling 573 // probe. Dangling probes are the probes associated to an empty block. With 574 // this place holder, sample count on dangling probe will not be trusted by 575 // the compiler and it will rely on the counts inference algorithm to get 576 // the probe a reasonable count. 577 if (Probe->isDangling()) { 578 FunctionProfile.addBodySamplesForProbe( 579 Probe->Index, FunctionSamples::InvalidProbeCount); 580 continue; 581 } 582 FunctionProfile.addBodySamplesForProbe(Probe->Index, Count); 583 FunctionProfile.addTotalSamples(Count); 584 if (Probe->isEntry()) { 585 FunctionProfile.addHeadSamples(Count); 586 // Look up for the caller's function profile 587 const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe); 588 if (InlinerDesc != nullptr) { 589 // Since the context id will be compressed, we have to use callee's 590 // context id to infer caller's context id to ensure they share the 591 // same context prefix. 592 StringRef CalleeContextId = 593 FunctionProfile.getContext().getNameWithContext(true); 594 StringRef CallerContextId; 595 FrameLocation &&CallerLeafFrameLoc = 596 getCallerContext(CalleeContextId, CallerContextId); 597 uint64_t CallerIndex = CallerLeafFrameLoc.second.LineOffset; 598 assert(CallerIndex && 599 "Inferred caller's location index shouldn't be zero!"); 600 FunctionSamples &CallerProfile = 601 getFunctionProfileForContext(CallerContextId); 602 CallerProfile.setFunctionHash(InlinerDesc->FuncHash); 603 CallerProfile.addBodySamples(CallerIndex, 0, Count); 604 CallerProfile.addTotalSamples(Count); 605 CallerProfile.addCalledTargetSamples( 606 CallerIndex, 0, 607 FunctionProfile.getContext().getNameWithoutContext(), Count); 608 } 609 } 610 } 611 } 612 613 void PseudoProbeCSProfileGenerator::populateBoundarySamplesWithProbes( 614 const BranchSample &BranchCounter, 615 SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) { 616 for (auto BI : BranchCounter) { 617 uint64_t SourceOffset = BI.first.first; 618 uint64_t TargetOffset = BI.first.second; 619 uint64_t Count = BI.second; 620 uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset); 621 const PseudoProbe *CallProbe = Binary->getCallProbeForAddr(SourceAddress); 622 if (CallProbe == nullptr) 623 continue; 624 FunctionSamples &FunctionProfile = 625 getFunctionProfileForLeafProbe(ContextStrStack, CallProbe, Binary); 626 FunctionProfile.addBodySamples(CallProbe->Index, 0, Count); 627 FunctionProfile.addTotalSamples(Count); 628 StringRef CalleeName = FunctionSamples::getCanonicalFnName( 629 Binary->getFuncFromStartOffset(TargetOffset)); 630 if (CalleeName.size() == 0) 631 continue; 632 FunctionProfile.addCalledTargetSamples(CallProbe->Index, 0, CalleeName, 633 Count); 634 } 635 } 636 637 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( 638 SmallVectorImpl<std::string> &ContextStrStack, 639 const PseudoProbeFuncDesc *LeafFuncDesc, bool WasLeafInlined) { 640 assert(ContextStrStack.size() && "Profile context must have the leaf frame"); 641 // Compress the context string except for the leaf frame 642 std::string LeafFrame = ContextStrStack.back(); 643 ContextStrStack.pop_back(); 644 CSProfileGenerator::compressRecursionContext(ContextStrStack); 645 646 std::ostringstream OContextStr; 647 for (uint32_t I = 0; I < ContextStrStack.size(); I++) { 648 if (OContextStr.str().size()) 649 OContextStr << " @ "; 650 OContextStr << ContextStrStack[I]; 651 } 652 // For leaf inlined context with the top frame, we should strip off the top 653 // frame's probe id, like: 654 // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar" 655 if (OContextStr.str().size()) 656 OContextStr << " @ "; 657 OContextStr << StringRef(LeafFrame).split(":").first.str(); 658 659 FunctionSamples &FunctionProile = 660 getFunctionProfileForContext(OContextStr.str(), WasLeafInlined); 661 FunctionProile.setFunctionHash(LeafFuncDesc->FuncHash); 662 return FunctionProile; 663 } 664 665 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( 666 SmallVectorImpl<std::string> &ContextStrStack, const PseudoProbe *LeafProbe, 667 ProfiledBinary *Binary) { 668 // Explicitly copy the context for appending the leaf context 669 SmallVector<std::string, 16> ContextStrStackCopy(ContextStrStack.begin(), 670 ContextStrStack.end()); 671 Binary->getInlineContextForProbe(LeafProbe, ContextStrStackCopy, true); 672 const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->GUID); 673 bool WasLeafInlined = LeafProbe->InlineTree->hasInlineSite(); 674 return getFunctionProfileForLeafProbe(ContextStrStackCopy, FuncDesc, 675 WasLeafInlined); 676 } 677 678 } // end namespace sampleprof 679 } // end namespace llvm 680