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_Ext_Binary), 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<bool> CSProfMergeColdContext( 36 "csprof-merge-cold-context", cl::init(true), cl::ZeroOrMore, 37 cl::desc("If the total count of context profile is smaller than " 38 "the threshold, it will be merged into context-less base " 39 "profile.")); 40 41 static cl::opt<bool> CSProfTrimColdContext( 42 "csprof-trim-cold-context", cl::init(true), cl::ZeroOrMore, 43 cl::desc("If the total count of the profile after all merge is done " 44 "is still smaller than threshold, it will be trimmed.")); 45 46 static cl::opt<uint32_t> CSProfColdContextFrameDepth( 47 "csprof-frame-depth-for-cold-context", cl::init(1), cl::ZeroOrMore, 48 cl::desc("Keep the last K frames while merging cold profile. 1 means the " 49 "context-less base profile")); 50 51 extern cl::opt<int> ProfileSummaryCutoffCold; 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 if (std::error_code EC = Writer->write(ProfileMap)) 90 exitWithError(std::move(EC)); 91 } 92 93 void ProfileGenerator::write() { 94 auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat); 95 if (std::error_code EC = WriterOrErr.getError()) 96 exitWithError(EC, OutputFilename); 97 write(std::move(WriterOrErr.get()), ProfileMap); 98 } 99 100 void ProfileGenerator::findDisjointRanges(RangeSample &DisjointRanges, 101 const RangeSample &Ranges) { 102 103 /* 104 Regions may overlap with each other. Using the boundary info, find all 105 disjoint ranges and their sample count. BoundaryPoint contains the count 106 multiple samples begin/end at this points. 107 108 |<--100-->| Sample1 109 |<------200------>| Sample2 110 A B C 111 112 In the example above, 113 Sample1 begins at A, ends at B, its value is 100. 114 Sample2 beings at A, ends at C, its value is 200. 115 For A, BeginCount is the sum of sample begins at A, which is 300 and no 116 samples ends at A, so EndCount is 0. 117 Then boundary points A, B, and C with begin/end counts are: 118 A: (300, 0) 119 B: (0, 100) 120 C: (0, 200) 121 */ 122 struct BoundaryPoint { 123 // Sum of sample counts beginning at this point 124 uint64_t BeginCount; 125 // Sum of sample counts ending at this point 126 uint64_t EndCount; 127 128 BoundaryPoint() : BeginCount(0), EndCount(0){}; 129 130 void addBeginCount(uint64_t Count) { BeginCount += Count; } 131 132 void addEndCount(uint64_t Count) { EndCount += Count; } 133 }; 134 135 /* 136 For the above example. With boundary points, follwing logic finds two 137 disjoint region of 138 139 [A,B]: 300 140 [B+1,C]: 200 141 142 If there is a boundary point that both begin and end, the point itself 143 becomes a separate disjoint region. For example, if we have original 144 ranges of 145 146 |<--- 100 --->| 147 |<--- 200 --->| 148 A B C 149 150 there are three boundary points with their begin/end counts of 151 152 A: (100, 0) 153 B: (200, 100) 154 C: (0, 200) 155 156 the disjoint ranges would be 157 158 [A, B-1]: 100 159 [B, B]: 300 160 [B+1, C]: 200. 161 */ 162 std::map<uint64_t, BoundaryPoint> Boundaries; 163 164 for (auto Item : Ranges) { 165 uint64_t Begin = Item.first.first; 166 uint64_t End = Item.first.second; 167 uint64_t Count = Item.second; 168 if (Boundaries.find(Begin) == Boundaries.end()) 169 Boundaries[Begin] = BoundaryPoint(); 170 Boundaries[Begin].addBeginCount(Count); 171 172 if (Boundaries.find(End) == Boundaries.end()) 173 Boundaries[End] = BoundaryPoint(); 174 Boundaries[End].addEndCount(Count); 175 } 176 177 uint64_t BeginAddress = 0; 178 int Count = 0; 179 for (auto Item : Boundaries) { 180 uint64_t Address = Item.first; 181 BoundaryPoint &Point = Item.second; 182 if (Point.BeginCount) { 183 if (BeginAddress) 184 DisjointRanges[{BeginAddress, Address - 1}] = Count; 185 Count += Point.BeginCount; 186 BeginAddress = Address; 187 } 188 if (Point.EndCount) { 189 assert(BeginAddress && "First boundary point cannot be 'end' point"); 190 DisjointRanges[{BeginAddress, Address}] = Count; 191 Count -= Point.EndCount; 192 BeginAddress = Address + 1; 193 } 194 } 195 } 196 197 FunctionSamples & 198 CSProfileGenerator::getFunctionProfileForContext(StringRef ContextStr, 199 bool WasLeafInlined) { 200 auto Ret = ProfileMap.try_emplace(ContextStr, FunctionSamples()); 201 if (Ret.second) { 202 // Make a copy of the underlying context string in string table 203 // before StringRef wrapper is used for context. 204 auto It = ContextStrings.insert(ContextStr.str()); 205 SampleContext FContext(*It.first, RawContext); 206 if (WasLeafInlined) 207 FContext.setAttribute(ContextWasInlined); 208 FunctionSamples &FProfile = Ret.first->second; 209 FProfile.setContext(FContext); 210 FProfile.setName(FContext.getNameWithoutContext()); 211 } 212 return Ret.first->second; 213 } 214 215 void CSProfileGenerator::generateProfile() { 216 FunctionSamples::ProfileIsCS = true; 217 for (const auto &BI : BinarySampleCounters) { 218 ProfiledBinary *Binary = BI.first; 219 for (const auto &CI : BI.second) { 220 const StringBasedCtxKey *CtxKey = 221 dyn_cast<StringBasedCtxKey>(CI.first.getPtr()); 222 StringRef ContextId(CtxKey->Context); 223 // Get or create function profile for the range 224 FunctionSamples &FunctionProfile = 225 getFunctionProfileForContext(ContextId, CtxKey->WasLeafInlined); 226 227 // Fill in function body samples 228 populateFunctionBodySamples(FunctionProfile, CI.second.RangeCounter, 229 Binary); 230 // Fill in boundary sample counts as well as call site samples for calls 231 populateFunctionBoundarySamples(ContextId, FunctionProfile, 232 CI.second.BranchCounter, Binary); 233 } 234 } 235 // Fill in call site value sample for inlined calls and also use context to 236 // infer missing samples. Since we don't have call count for inlined 237 // functions, we estimate it from inlinee's profile using the entry of the 238 // body sample. 239 populateInferredFunctionSamples(); 240 241 postProcessProfiles(); 242 } 243 244 void CSProfileGenerator::updateBodySamplesforFunctionProfile( 245 FunctionSamples &FunctionProfile, const FrameLocation &LeafLoc, 246 uint64_t Count) { 247 // Filter out invalid negative(int type) lineOffset 248 if (LeafLoc.second.LineOffset & 0x80000000) 249 return; 250 // Use the maximum count of samples with same line location 251 ErrorOr<uint64_t> R = FunctionProfile.findSamplesAt( 252 LeafLoc.second.LineOffset, LeafLoc.second.Discriminator); 253 uint64_t PreviousCount = R ? R.get() : 0; 254 if (PreviousCount < Count) { 255 FunctionProfile.addBodySamples(LeafLoc.second.LineOffset, 256 LeafLoc.second.Discriminator, 257 Count - PreviousCount); 258 } 259 } 260 261 void CSProfileGenerator::populateFunctionBodySamples( 262 FunctionSamples &FunctionProfile, const RangeSample &RangeCounter, 263 ProfiledBinary *Binary) { 264 // Compute disjoint ranges first, so we can use MAX 265 // for calculating count for each location. 266 RangeSample Ranges; 267 findDisjointRanges(Ranges, RangeCounter); 268 for (auto Range : Ranges) { 269 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 270 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 271 uint64_t Count = Range.second; 272 // Disjoint ranges have introduce zero-filled gap that 273 // doesn't belong to current context, filter them out. 274 if (Count == 0) 275 continue; 276 277 InstructionPointer IP(Binary, RangeBegin, true); 278 279 // Disjoint ranges may have range in the middle of two instr, 280 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 281 // can be Addr1+1 to Addr2-1. We should ignore such range. 282 if (IP.Address > RangeEnd) 283 continue; 284 285 while (IP.Address <= RangeEnd) { 286 uint64_t Offset = Binary->virtualAddrToOffset(IP.Address); 287 auto LeafLoc = Binary->getInlineLeafFrameLoc(Offset); 288 if (LeafLoc.hasValue()) { 289 // Recording body sample for this specific context 290 updateBodySamplesforFunctionProfile(FunctionProfile, *LeafLoc, Count); 291 } 292 // Accumulate total sample count even it's a line with invalid debug info 293 FunctionProfile.addTotalSamples(Count); 294 // Move to next IP within the range 295 IP.advance(); 296 } 297 } 298 } 299 300 void CSProfileGenerator::populateFunctionBoundarySamples( 301 StringRef ContextId, FunctionSamples &FunctionProfile, 302 const BranchSample &BranchCounters, ProfiledBinary *Binary) { 303 304 for (auto Entry : BranchCounters) { 305 uint64_t SourceOffset = Entry.first.first; 306 uint64_t TargetOffset = Entry.first.second; 307 uint64_t Count = Entry.second; 308 // Get the callee name by branch target if it's a call branch 309 StringRef CalleeName = FunctionSamples::getCanonicalFnName( 310 Binary->getFuncFromStartOffset(TargetOffset)); 311 if (CalleeName.size() == 0) 312 continue; 313 314 // Record called target sample and its count 315 auto LeafLoc = Binary->getInlineLeafFrameLoc(SourceOffset); 316 if (!LeafLoc.hasValue()) 317 continue; 318 FunctionProfile.addCalledTargetSamples(LeafLoc->second.LineOffset, 319 LeafLoc->second.Discriminator, 320 CalleeName, Count); 321 322 // Record head sample for called target(callee) 323 std::ostringstream OCalleeCtxStr; 324 if (ContextId.find(" @ ") != StringRef::npos) { 325 OCalleeCtxStr << ContextId.rsplit(" @ ").first.str(); 326 OCalleeCtxStr << " @ "; 327 } 328 OCalleeCtxStr << getCallSite(*LeafLoc) << " @ " << CalleeName.str(); 329 330 FunctionSamples &CalleeProfile = 331 getFunctionProfileForContext(OCalleeCtxStr.str()); 332 assert(Count != 0 && "Unexpected zero weight branch"); 333 CalleeProfile.addHeadSamples(Count); 334 } 335 } 336 337 static FrameLocation getCallerContext(StringRef CalleeContext, 338 StringRef &CallerNameWithContext) { 339 StringRef CallerContext = CalleeContext.rsplit(" @ ").first; 340 CallerNameWithContext = CallerContext.rsplit(':').first; 341 auto ContextSplit = CallerContext.rsplit(" @ "); 342 StringRef CallerFrameStr = ContextSplit.second.size() == 0 343 ? ContextSplit.first 344 : ContextSplit.second; 345 FrameLocation LeafFrameLoc = {"", {0, 0}}; 346 StringRef Funcname; 347 SampleContext::decodeContextString(CallerFrameStr, Funcname, 348 LeafFrameLoc.second); 349 LeafFrameLoc.first = Funcname.str(); 350 return LeafFrameLoc; 351 } 352 353 void CSProfileGenerator::populateInferredFunctionSamples() { 354 for (const auto &Item : ProfileMap) { 355 const StringRef CalleeContext = Item.first(); 356 const FunctionSamples &CalleeProfile = Item.second; 357 358 // If we already have head sample counts, we must have value profile 359 // for call sites added already. Skip to avoid double counting. 360 if (CalleeProfile.getHeadSamples()) 361 continue; 362 // If we don't have context, nothing to do for caller's call site. 363 // This could happen for entry point function. 364 if (CalleeContext.find(" @ ") == StringRef::npos) 365 continue; 366 367 // Infer Caller's frame loc and context ID through string splitting 368 StringRef CallerContextId; 369 FrameLocation &&CallerLeafFrameLoc = 370 getCallerContext(CalleeContext, CallerContextId); 371 372 // It's possible that we haven't seen any sample directly in the caller, 373 // in which case CallerProfile will not exist. But we can't modify 374 // ProfileMap while iterating it. 375 // TODO: created function profile for those callers too 376 if (ProfileMap.find(CallerContextId) == ProfileMap.end()) 377 continue; 378 FunctionSamples &CallerProfile = ProfileMap[CallerContextId]; 379 380 // Since we don't have call count for inlined functions, we 381 // estimate it from inlinee's profile using entry body sample. 382 uint64_t EstimatedCallCount = CalleeProfile.getEntrySamples(); 383 // If we don't have samples with location, use 1 to indicate live. 384 if (!EstimatedCallCount && !CalleeProfile.getBodySamples().size()) 385 EstimatedCallCount = 1; 386 CallerProfile.addCalledTargetSamples( 387 CallerLeafFrameLoc.second.LineOffset, 388 CallerLeafFrameLoc.second.Discriminator, 389 CalleeProfile.getContext().getNameWithoutContext(), EstimatedCallCount); 390 CallerProfile.addBodySamples(CallerLeafFrameLoc.second.LineOffset, 391 CallerLeafFrameLoc.second.Discriminator, 392 EstimatedCallCount); 393 CallerProfile.addTotalSamples(EstimatedCallCount); 394 } 395 } 396 397 void CSProfileGenerator::postProcessProfiles() { 398 // Compute hot/cold threshold based on profile. This will be used for cold 399 // context profile merging/trimming. 400 computeSummaryAndThreshold(); 401 402 // Run global pre-inliner to adjust/merge context profile based on estimated 403 // inline decisions. 404 CSPreInliner(ProfileMap, HotCountThreshold, ColdCountThreshold).run(); 405 406 // Trim and merge cold context profile using cold threshold above; 407 SampleContextTrimmer(ProfileMap) 408 .trimAndMergeColdContextProfiles( 409 ColdCountThreshold, CSProfTrimColdContext, CSProfMergeColdContext, 410 CSProfColdContextFrameDepth); 411 } 412 413 void CSProfileGenerator::computeSummaryAndThreshold() { 414 // Update the default value of cold cutoff for llvm-profgen. 415 // Do it here because we don't want to change the global default, 416 // which would lead CS profile size too large. 417 if (!ProfileSummaryCutoffCold.getNumOccurrences()) 418 ProfileSummaryCutoffCold = 999000; 419 420 SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); 421 auto Summary = Builder.computeSummaryForProfiles(ProfileMap); 422 HotCountThreshold = ProfileSummaryBuilder::getHotCountThreshold( 423 (Summary->getDetailedSummary())); 424 ColdCountThreshold = ProfileSummaryBuilder::getColdCountThreshold( 425 (Summary->getDetailedSummary())); 426 } 427 428 void CSProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer, 429 StringMap<FunctionSamples> &ProfileMap) { 430 if (std::error_code EC = Writer->write(ProfileMap)) 431 exitWithError(std::move(EC)); 432 } 433 434 // Helper function to extract context prefix string stack 435 // Extract context stack for reusing, leaf context stack will 436 // be added compressed while looking up function profile 437 static void 438 extractPrefixContextStack(SmallVectorImpl<std::string> &ContextStrStack, 439 const SmallVectorImpl<const PseudoProbe *> &Probes, 440 ProfiledBinary *Binary) { 441 for (const auto *P : Probes) { 442 Binary->getInlineContextForProbe(P, ContextStrStack, true); 443 } 444 } 445 446 void PseudoProbeCSProfileGenerator::generateProfile() { 447 // Enable pseudo probe functionalities in SampleProf 448 FunctionSamples::ProfileIsProbeBased = true; 449 FunctionSamples::ProfileIsCS = true; 450 for (const auto &BI : BinarySampleCounters) { 451 ProfiledBinary *Binary = BI.first; 452 for (const auto &CI : BI.second) { 453 const ProbeBasedCtxKey *CtxKey = 454 dyn_cast<ProbeBasedCtxKey>(CI.first.getPtr()); 455 SmallVector<std::string, 16> ContextStrStack; 456 extractPrefixContextStack(ContextStrStack, CtxKey->Probes, Binary); 457 // Fill in function body samples from probes, also infer caller's samples 458 // from callee's probe 459 populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStrStack, 460 Binary); 461 // Fill in boundary samples for a call probe 462 populateBoundarySamplesWithProbes(CI.second.BranchCounter, 463 ContextStrStack, Binary); 464 } 465 } 466 467 postProcessProfiles(); 468 } 469 470 void PseudoProbeCSProfileGenerator::extractProbesFromRange( 471 const RangeSample &RangeCounter, ProbeCounterMap &ProbeCounter, 472 ProfiledBinary *Binary) { 473 RangeSample Ranges; 474 findDisjointRanges(Ranges, RangeCounter); 475 for (const auto &Range : Ranges) { 476 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 477 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 478 uint64_t Count = Range.second; 479 // Disjoint ranges have introduce zero-filled gap that 480 // doesn't belong to current context, filter them out. 481 if (Count == 0) 482 continue; 483 484 InstructionPointer IP(Binary, RangeBegin, true); 485 486 // Disjoint ranges may have range in the middle of two instr, 487 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 488 // can be Addr1+1 to Addr2-1. We should ignore such range. 489 if (IP.Address > RangeEnd) 490 continue; 491 492 while (IP.Address <= RangeEnd) { 493 const AddressProbesMap &Address2ProbesMap = 494 Binary->getAddress2ProbesMap(); 495 auto It = Address2ProbesMap.find(IP.Address); 496 if (It != Address2ProbesMap.end()) { 497 for (const auto &Probe : It->second) { 498 if (!Probe.isBlock()) 499 continue; 500 ProbeCounter[&Probe] += Count; 501 } 502 } 503 504 IP.advance(); 505 } 506 } 507 } 508 509 void PseudoProbeCSProfileGenerator::populateBodySamplesWithProbes( 510 const RangeSample &RangeCounter, 511 SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) { 512 ProbeCounterMap ProbeCounter; 513 // Extract the top frame probes by looking up each address among the range in 514 // the Address2ProbeMap 515 extractProbesFromRange(RangeCounter, ProbeCounter, Binary); 516 std::unordered_map<PseudoProbeInlineTree *, FunctionSamples *> FrameSamples; 517 for (auto PI : ProbeCounter) { 518 const PseudoProbe *Probe = PI.first; 519 uint64_t Count = PI.second; 520 // Ignore dangling probes since they will be reported later if needed. 521 if (Probe->isDangling()) 522 continue; 523 FunctionSamples &FunctionProfile = 524 getFunctionProfileForLeafProbe(ContextStrStack, Probe, Binary); 525 // Record the current frame and FunctionProfile whenever samples are 526 // collected for non-danglie probes. This is for reporting all of the 527 // dangling probes of the frame later. 528 FrameSamples[Probe->getInlineTreeNode()] = &FunctionProfile; 529 FunctionProfile.addBodySamplesForProbe(Probe->Index, Count); 530 FunctionProfile.addTotalSamples(Count); 531 if (Probe->isEntry()) { 532 FunctionProfile.addHeadSamples(Count); 533 // Look up for the caller's function profile 534 const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe); 535 if (InlinerDesc != nullptr) { 536 // Since the context id will be compressed, we have to use callee's 537 // context id to infer caller's context id to ensure they share the 538 // same context prefix. 539 StringRef CalleeContextId = 540 FunctionProfile.getContext().getNameWithContext(); 541 StringRef CallerContextId; 542 FrameLocation &&CallerLeafFrameLoc = 543 getCallerContext(CalleeContextId, CallerContextId); 544 uint64_t CallerIndex = CallerLeafFrameLoc.second.LineOffset; 545 assert(CallerIndex && 546 "Inferred caller's location index shouldn't be zero!"); 547 FunctionSamples &CallerProfile = 548 getFunctionProfileForContext(CallerContextId); 549 CallerProfile.setFunctionHash(InlinerDesc->FuncHash); 550 CallerProfile.addBodySamples(CallerIndex, 0, Count); 551 CallerProfile.addTotalSamples(Count); 552 CallerProfile.addCalledTargetSamples( 553 CallerIndex, 0, 554 FunctionProfile.getContext().getNameWithoutContext(), Count); 555 } 556 } 557 558 // Assign zero count for remaining probes without sample hits to 559 // differentiate from probes optimized away, of which the counts are unknown 560 // and will be inferred by the compiler. 561 for (auto &I : FrameSamples) { 562 auto *FunctionProfile = I.second; 563 for (auto *Probe : I.first->getProbes()) { 564 if (!Probe->isDangling()) 565 FunctionProfile->addBodySamplesForProbe(Probe->Index, 0); 566 } 567 } 568 } 569 } 570 571 void PseudoProbeCSProfileGenerator::populateBoundarySamplesWithProbes( 572 const BranchSample &BranchCounter, 573 SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) { 574 for (auto BI : BranchCounter) { 575 uint64_t SourceOffset = BI.first.first; 576 uint64_t TargetOffset = BI.first.second; 577 uint64_t Count = BI.second; 578 uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset); 579 const PseudoProbe *CallProbe = Binary->getCallProbeForAddr(SourceAddress); 580 if (CallProbe == nullptr) 581 continue; 582 FunctionSamples &FunctionProfile = 583 getFunctionProfileForLeafProbe(ContextStrStack, CallProbe, Binary); 584 FunctionProfile.addBodySamples(CallProbe->Index, 0, Count); 585 FunctionProfile.addTotalSamples(Count); 586 StringRef CalleeName = FunctionSamples::getCanonicalFnName( 587 Binary->getFuncFromStartOffset(TargetOffset)); 588 if (CalleeName.size() == 0) 589 continue; 590 FunctionProfile.addCalledTargetSamples(CallProbe->Index, 0, CalleeName, 591 Count); 592 } 593 } 594 595 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( 596 SmallVectorImpl<std::string> &ContextStrStack, 597 const PseudoProbeFuncDesc *LeafFuncDesc, bool WasLeafInlined) { 598 assert(ContextStrStack.size() && "Profile context must have the leaf frame"); 599 // Compress the context string except for the leaf frame 600 std::string LeafFrame = ContextStrStack.back(); 601 ContextStrStack.pop_back(); 602 CSProfileGenerator::compressRecursionContext(ContextStrStack); 603 604 std::ostringstream OContextStr; 605 for (uint32_t I = 0; I < ContextStrStack.size(); I++) { 606 if (OContextStr.str().size()) 607 OContextStr << " @ "; 608 OContextStr << ContextStrStack[I]; 609 } 610 // For leaf inlined context with the top frame, we should strip off the top 611 // frame's probe id, like: 612 // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar" 613 if (OContextStr.str().size()) 614 OContextStr << " @ "; 615 OContextStr << StringRef(LeafFrame).split(":").first.str(); 616 617 FunctionSamples &FunctionProile = 618 getFunctionProfileForContext(OContextStr.str(), WasLeafInlined); 619 FunctionProile.setFunctionHash(LeafFuncDesc->FuncHash); 620 return FunctionProile; 621 } 622 623 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( 624 SmallVectorImpl<std::string> &ContextStrStack, const PseudoProbe *LeafProbe, 625 ProfiledBinary *Binary) { 626 // Explicitly copy the context for appending the leaf context 627 SmallVector<std::string, 16> ContextStrStackCopy(ContextStrStack.begin(), 628 ContextStrStack.end()); 629 Binary->getInlineContextForProbe(LeafProbe, ContextStrStackCopy, true); 630 const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->GUID); 631 bool WasLeafInlined = LeafProbe->InlineTree->hasInlineSite(); 632 return getFunctionProfileForLeafProbe(ContextStrStackCopy, FuncDesc, 633 WasLeafInlined); 634 } 635 636 } // end namespace sampleprof 637 } // end namespace llvm 638