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 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, PSI->getHotCountThreshold(), 405 PSI->getColdCountThreshold()) 406 .run(); 407 408 // Trim and merge cold context profile using cold threshold above; 409 SampleContextTrimmer(ProfileMap) 410 .trimAndMergeColdContextProfiles( 411 CSProfColdThreshold, CSProfTrimColdContext, CSProfMergeColdContext); 412 } 413 414 void CSProfileGenerator::computeSummaryAndThreshold() { 415 SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs); 416 auto Summary = Builder.computeSummaryForProfiles(ProfileMap); 417 PSI.reset(new ProfileSummaryInfo(std::move(Summary))); 418 419 // Use threshold calculated from profile summary unless specified. 420 if (!CSProfColdThreshold.getNumOccurrences()) { 421 CSProfColdThreshold = PSI->getColdCountThreshold(); 422 } 423 } 424 425 void CSProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer, 426 StringMap<FunctionSamples> &ProfileMap) { 427 if (std::error_code EC = Writer->write(ProfileMap)) 428 exitWithError(std::move(EC)); 429 } 430 431 // Helper function to extract context prefix string stack 432 // Extract context stack for reusing, leaf context stack will 433 // be added compressed while looking up function profile 434 static void 435 extractPrefixContextStack(SmallVectorImpl<std::string> &ContextStrStack, 436 const SmallVectorImpl<const PseudoProbe *> &Probes, 437 ProfiledBinary *Binary) { 438 for (const auto *P : Probes) { 439 Binary->getInlineContextForProbe(P, ContextStrStack, true); 440 } 441 } 442 443 void PseudoProbeCSProfileGenerator::generateProfile() { 444 // Enable pseudo probe functionalities in SampleProf 445 FunctionSamples::ProfileIsProbeBased = true; 446 FunctionSamples::ProfileIsCS = true; 447 for (const auto &BI : BinarySampleCounters) { 448 ProfiledBinary *Binary = BI.first; 449 for (const auto &CI : BI.second) { 450 const ProbeBasedCtxKey *CtxKey = 451 dyn_cast<ProbeBasedCtxKey>(CI.first.getPtr()); 452 SmallVector<std::string, 16> ContextStrStack; 453 extractPrefixContextStack(ContextStrStack, CtxKey->Probes, Binary); 454 // Fill in function body samples from probes, also infer caller's samples 455 // from callee's probe 456 populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStrStack, 457 Binary); 458 // Fill in boundary samples for a call probe 459 populateBoundarySamplesWithProbes(CI.second.BranchCounter, 460 ContextStrStack, Binary); 461 } 462 } 463 464 postProcessProfiles(); 465 } 466 467 void PseudoProbeCSProfileGenerator::extractProbesFromRange( 468 const RangeSample &RangeCounter, ProbeCounterMap &ProbeCounter, 469 ProfiledBinary *Binary) { 470 RangeSample Ranges; 471 findDisjointRanges(Ranges, RangeCounter); 472 for (const auto &Range : Ranges) { 473 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 474 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 475 uint64_t Count = Range.second; 476 // Disjoint ranges have introduce zero-filled gap that 477 // doesn't belong to current context, filter them out. 478 if (Count == 0) 479 continue; 480 481 InstructionPointer IP(Binary, RangeBegin, true); 482 483 // Disjoint ranges may have range in the middle of two instr, 484 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 485 // can be Addr1+1 to Addr2-1. We should ignore such range. 486 if (IP.Address > RangeEnd) 487 continue; 488 489 while (IP.Address <= RangeEnd) { 490 const AddressProbesMap &Address2ProbesMap = 491 Binary->getAddress2ProbesMap(); 492 auto It = Address2ProbesMap.find(IP.Address); 493 if (It != Address2ProbesMap.end()) { 494 for (const auto &Probe : It->second) { 495 if (!Probe.isBlock()) 496 continue; 497 ProbeCounter[&Probe] += Count; 498 } 499 } 500 501 IP.advance(); 502 } 503 } 504 } 505 506 void PseudoProbeCSProfileGenerator::populateBodySamplesWithProbes( 507 const RangeSample &RangeCounter, 508 SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) { 509 ProbeCounterMap ProbeCounter; 510 // Extract the top frame probes by looking up each address among the range in 511 // the Address2ProbeMap 512 extractProbesFromRange(RangeCounter, ProbeCounter, Binary); 513 std::unordered_map<PseudoProbeInlineTree *, FunctionSamples *> FrameSamples; 514 for (auto PI : ProbeCounter) { 515 const PseudoProbe *Probe = PI.first; 516 uint64_t Count = PI.second; 517 // Ignore dangling probes since they will be reported later if needed. 518 if (Probe->isDangling()) 519 continue; 520 FunctionSamples &FunctionProfile = 521 getFunctionProfileForLeafProbe(ContextStrStack, Probe, Binary); 522 // Record the current frame and FunctionProfile whenever samples are 523 // collected for non-danglie probes. This is for reporting all of the 524 // dangling probes of the frame later. 525 FrameSamples[Probe->getInlineTreeNode()] = &FunctionProfile; 526 FunctionProfile.addBodySamplesForProbe(Probe->Index, Count); 527 FunctionProfile.addTotalSamples(Count); 528 if (Probe->isEntry()) { 529 FunctionProfile.addHeadSamples(Count); 530 // Look up for the caller's function profile 531 const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe); 532 if (InlinerDesc != nullptr) { 533 // Since the context id will be compressed, we have to use callee's 534 // context id to infer caller's context id to ensure they share the 535 // same context prefix. 536 StringRef CalleeContextId = 537 FunctionProfile.getContext().getNameWithContext(); 538 StringRef CallerContextId; 539 FrameLocation &&CallerLeafFrameLoc = 540 getCallerContext(CalleeContextId, CallerContextId); 541 uint64_t CallerIndex = CallerLeafFrameLoc.second.LineOffset; 542 assert(CallerIndex && 543 "Inferred caller's location index shouldn't be zero!"); 544 FunctionSamples &CallerProfile = 545 getFunctionProfileForContext(CallerContextId); 546 CallerProfile.setFunctionHash(InlinerDesc->FuncHash); 547 CallerProfile.addBodySamples(CallerIndex, 0, Count); 548 CallerProfile.addTotalSamples(Count); 549 CallerProfile.addCalledTargetSamples( 550 CallerIndex, 0, 551 FunctionProfile.getContext().getNameWithoutContext(), Count); 552 } 553 } 554 555 // Report dangling probes for frames that have real samples collected. 556 // Dangling probes are the probes associated to an empty block. With this 557 // place holder, sample count on a dangling probe will not be trusted by the 558 // compiler and we will rely on the counts inference algorithm to get the 559 // probe a reasonable count. Use InvalidProbeCount to mark sample count for 560 // a dangling probe. 561 for (auto &I : FrameSamples) { 562 auto *FunctionProfile = I.second; 563 for (auto *Probe : I.first->getProbes()) { 564 if (Probe->isDangling()) { 565 FunctionProfile->addBodySamplesForProbe( 566 Probe->Index, FunctionSamples::InvalidProbeCount); 567 } 568 } 569 } 570 } 571 } 572 573 void PseudoProbeCSProfileGenerator::populateBoundarySamplesWithProbes( 574 const BranchSample &BranchCounter, 575 SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) { 576 for (auto BI : BranchCounter) { 577 uint64_t SourceOffset = BI.first.first; 578 uint64_t TargetOffset = BI.first.second; 579 uint64_t Count = BI.second; 580 uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset); 581 const PseudoProbe *CallProbe = Binary->getCallProbeForAddr(SourceAddress); 582 if (CallProbe == nullptr) 583 continue; 584 FunctionSamples &FunctionProfile = 585 getFunctionProfileForLeafProbe(ContextStrStack, CallProbe, Binary); 586 FunctionProfile.addBodySamples(CallProbe->Index, 0, Count); 587 FunctionProfile.addTotalSamples(Count); 588 StringRef CalleeName = FunctionSamples::getCanonicalFnName( 589 Binary->getFuncFromStartOffset(TargetOffset)); 590 if (CalleeName.size() == 0) 591 continue; 592 FunctionProfile.addCalledTargetSamples(CallProbe->Index, 0, CalleeName, 593 Count); 594 } 595 } 596 597 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( 598 SmallVectorImpl<std::string> &ContextStrStack, 599 const PseudoProbeFuncDesc *LeafFuncDesc, bool WasLeafInlined) { 600 assert(ContextStrStack.size() && "Profile context must have the leaf frame"); 601 // Compress the context string except for the leaf frame 602 std::string LeafFrame = ContextStrStack.back(); 603 ContextStrStack.pop_back(); 604 CSProfileGenerator::compressRecursionContext(ContextStrStack); 605 606 std::ostringstream OContextStr; 607 for (uint32_t I = 0; I < ContextStrStack.size(); I++) { 608 if (OContextStr.str().size()) 609 OContextStr << " @ "; 610 OContextStr << ContextStrStack[I]; 611 } 612 // For leaf inlined context with the top frame, we should strip off the top 613 // frame's probe id, like: 614 // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar" 615 if (OContextStr.str().size()) 616 OContextStr << " @ "; 617 OContextStr << StringRef(LeafFrame).split(":").first.str(); 618 619 FunctionSamples &FunctionProile = 620 getFunctionProfileForContext(OContextStr.str(), WasLeafInlined); 621 FunctionProile.setFunctionHash(LeafFuncDesc->FuncHash); 622 return FunctionProile; 623 } 624 625 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( 626 SmallVectorImpl<std::string> &ContextStrStack, const PseudoProbe *LeafProbe, 627 ProfiledBinary *Binary) { 628 // Explicitly copy the context for appending the leaf context 629 SmallVector<std::string, 16> ContextStrStackCopy(ContextStrStack.begin(), 630 ContextStrStack.end()); 631 Binary->getInlineContextForProbe(LeafProbe, ContextStrStackCopy, true); 632 const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->GUID); 633 bool WasLeafInlined = LeafProbe->InlineTree->hasInlineSite(); 634 return getFunctionProfileForLeafProbe(ContextStrStackCopy, FuncDesc, 635 WasLeafInlined); 636 } 637 638 } // end namespace sampleprof 639 } // end namespace llvm 640