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