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::generateProfile() { 202 FunctionSamples::ProfileIsCS = true; 203 for (const auto &BI : BinarySampleCounters) { 204 ProfiledBinary *Binary = BI.first; 205 for (const auto &CI : BI.second) { 206 const StringBasedCtxKey *CtxKey = 207 dyn_cast<StringBasedCtxKey>(CI.first.getPtr()); 208 StringRef ContextId(CtxKey->Context); 209 // Get or create function profile for the range 210 FunctionSamples &FunctionProfile = 211 getFunctionProfileForContext(ContextId); 212 213 // Fill in function body samples 214 populateFunctionBodySamples(FunctionProfile, CI.second.RangeCounter, 215 Binary); 216 // Fill in boundary sample counts as well as call site samples for calls 217 populateFunctionBoundarySamples(ContextId, FunctionProfile, 218 CI.second.BranchCounter, Binary); 219 } 220 } 221 // Fill in call site value sample for inlined calls and also use context to 222 // infer missing samples. Since we don't have call count for inlined 223 // functions, we estimate it from inlinee's profile using the entry of the 224 // body sample. 225 populateInferredFunctionSamples(); 226 } 227 228 void CSProfileGenerator::updateBodySamplesforFunctionProfile( 229 FunctionSamples &FunctionProfile, const FrameLocation &LeafLoc, 230 uint64_t Count) { 231 // Filter out invalid negative(int type) lineOffset 232 if (LeafLoc.second.LineOffset & 0x80000000) 233 return; 234 // Use the maximum count of samples with same line location 235 ErrorOr<uint64_t> R = FunctionProfile.findSamplesAt( 236 LeafLoc.second.LineOffset, LeafLoc.second.Discriminator); 237 uint64_t PreviousCount = R ? R.get() : 0; 238 if (PreviousCount < Count) { 239 FunctionProfile.addBodySamples(LeafLoc.second.LineOffset, 240 LeafLoc.second.Discriminator, 241 Count - PreviousCount); 242 } 243 } 244 245 void CSProfileGenerator::populateFunctionBodySamples( 246 FunctionSamples &FunctionProfile, const RangeSample &RangeCounter, 247 ProfiledBinary *Binary) { 248 // Compute disjoint ranges first, so we can use MAX 249 // for calculating count for each location. 250 RangeSample Ranges; 251 findDisjointRanges(Ranges, RangeCounter); 252 for (auto Range : Ranges) { 253 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 254 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 255 uint64_t Count = Range.second; 256 // Disjoint ranges have introduce zero-filled gap that 257 // doesn't belong to current context, filter them out. 258 if (Count == 0) 259 continue; 260 261 InstructionPointer IP(Binary, RangeBegin, true); 262 263 // Disjoint ranges may have range in the middle of two instr, 264 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 265 // can be Addr1+1 to Addr2-1. We should ignore such range. 266 if (IP.Address > RangeEnd) 267 continue; 268 269 while (IP.Address <= RangeEnd) { 270 uint64_t Offset = Binary->virtualAddrToOffset(IP.Address); 271 auto LeafLoc = Binary->getInlineLeafFrameLoc(Offset); 272 if (LeafLoc.hasValue()) { 273 // Recording body sample for this specific context 274 updateBodySamplesforFunctionProfile(FunctionProfile, *LeafLoc, Count); 275 } 276 // Accumulate total sample count even it's a line with invalid debug info 277 FunctionProfile.addTotalSamples(Count); 278 // Move to next IP within the range 279 IP.advance(); 280 } 281 } 282 } 283 284 void CSProfileGenerator::populateFunctionBoundarySamples( 285 StringRef ContextId, FunctionSamples &FunctionProfile, 286 const BranchSample &BranchCounters, ProfiledBinary *Binary) { 287 288 for (auto Entry : BranchCounters) { 289 uint64_t SourceOffset = Entry.first.first; 290 uint64_t TargetOffset = Entry.first.second; 291 uint64_t Count = Entry.second; 292 // Get the callee name by branch target if it's a call branch 293 StringRef CalleeName = FunctionSamples::getCanonicalFnName( 294 Binary->getFuncFromStartOffset(TargetOffset)); 295 if (CalleeName.size() == 0) 296 continue; 297 298 // Record called target sample and its count 299 auto LeafLoc = Binary->getInlineLeafFrameLoc(SourceOffset); 300 if (!LeafLoc.hasValue()) 301 continue; 302 FunctionProfile.addCalledTargetSamples(LeafLoc->second.LineOffset, 303 LeafLoc->second.Discriminator, 304 CalleeName, Count); 305 306 // Record head sample for called target(callee) 307 std::ostringstream OCalleeCtxStr; 308 if (ContextId.find(" @ ") != StringRef::npos) { 309 OCalleeCtxStr << ContextId.rsplit(" @ ").first.str(); 310 OCalleeCtxStr << " @ "; 311 } 312 OCalleeCtxStr << getCallSite(*LeafLoc) << " @ " << CalleeName.str(); 313 314 FunctionSamples &CalleeProfile = 315 getFunctionProfileForContext(OCalleeCtxStr.str()); 316 assert(Count != 0 && "Unexpected zero weight branch"); 317 CalleeProfile.addHeadSamples(Count); 318 } 319 } 320 321 static FrameLocation getCallerContext(StringRef CalleeContext, 322 StringRef &CallerNameWithContext) { 323 StringRef CallerContext = CalleeContext.rsplit(" @ ").first; 324 CallerNameWithContext = CallerContext.rsplit(':').first; 325 auto ContextSplit = CallerContext.rsplit(" @ "); 326 StringRef CallerFrameStr = ContextSplit.second.size() == 0 327 ? ContextSplit.first 328 : ContextSplit.second; 329 FrameLocation LeafFrameLoc = {"", {0, 0}}; 330 StringRef Funcname; 331 SampleContext::decodeContextString(CallerFrameStr, Funcname, 332 LeafFrameLoc.second); 333 LeafFrameLoc.first = Funcname.str(); 334 return LeafFrameLoc; 335 } 336 337 void CSProfileGenerator::populateInferredFunctionSamples() { 338 for (const auto &Item : ProfileMap) { 339 const StringRef CalleeContext = Item.first(); 340 const FunctionSamples &CalleeProfile = Item.second; 341 342 // If we already have head sample counts, we must have value profile 343 // for call sites added already. Skip to avoid double counting. 344 if (CalleeProfile.getHeadSamples()) 345 continue; 346 // If we don't have context, nothing to do for caller's call site. 347 // This could happen for entry point function. 348 if (CalleeContext.find(" @ ") == StringRef::npos) 349 continue; 350 351 // Infer Caller's frame loc and context ID through string splitting 352 StringRef CallerContextId; 353 FrameLocation &&CallerLeafFrameLoc = 354 getCallerContext(CalleeContext, CallerContextId); 355 356 // It's possible that we haven't seen any sample directly in the caller, 357 // in which case CallerProfile will not exist. But we can't modify 358 // ProfileMap while iterating it. 359 // TODO: created function profile for those callers too 360 if (ProfileMap.find(CallerContextId) == ProfileMap.end()) 361 continue; 362 FunctionSamples &CallerProfile = ProfileMap[CallerContextId]; 363 364 // Since we don't have call count for inlined functions, we 365 // estimate it from inlinee's profile using entry body sample. 366 uint64_t EstimatedCallCount = CalleeProfile.getEntrySamples(); 367 // If we don't have samples with location, use 1 to indicate live. 368 if (!EstimatedCallCount && !CalleeProfile.getBodySamples().size()) 369 EstimatedCallCount = 1; 370 CallerProfile.addCalledTargetSamples( 371 CallerLeafFrameLoc.second.LineOffset, 372 CallerLeafFrameLoc.second.Discriminator, 373 CalleeProfile.getContext().getNameWithoutContext(), EstimatedCallCount); 374 CallerProfile.addBodySamples(CallerLeafFrameLoc.second.LineOffset, 375 CallerLeafFrameLoc.second.Discriminator, 376 EstimatedCallCount); 377 CallerProfile.addTotalSamples(EstimatedCallCount); 378 } 379 } 380 381 void CSProfileGenerator::mergeAndTrimColdProfile( 382 StringMap<FunctionSamples> &ProfileMap) { 383 // Nothing to merge if sample threshold is zero 384 if (!CSProfColdThres) 385 return; 386 387 // Filter the cold profiles from ProfileMap and move them into a tmp 388 // container 389 std::vector<std::pair<StringRef, const FunctionSamples *>> ToRemoveVec; 390 for (const auto &I : ProfileMap) { 391 const FunctionSamples &FunctionProfile = I.second; 392 if (FunctionProfile.getTotalSamples() >= CSProfColdThres) 393 continue; 394 ToRemoveVec.emplace_back(I.getKey(), &I.second); 395 } 396 397 // Remove the code profile from ProfileMap and merge them into BaseProileMap 398 StringMap<FunctionSamples> BaseProfileMap; 399 for (const auto &I : ToRemoveVec) { 400 auto Ret = BaseProfileMap.try_emplace( 401 I.second->getContext().getNameWithoutContext(), FunctionSamples()); 402 FunctionSamples &BaseProfile = Ret.first->second; 403 BaseProfile.merge(*I.second); 404 ProfileMap.erase(I.first); 405 } 406 407 // Merge the base profiles into ProfileMap; 408 for (const auto &I : BaseProfileMap) { 409 // Filter the cold base profile 410 if (!CSProfKeepCold && I.second.getTotalSamples() < CSProfColdThres && 411 ProfileMap.find(I.getKey()) == ProfileMap.end()) 412 continue; 413 // Merge the profile if the original profile exists, otherwise just insert 414 // as a new profile 415 FunctionSamples &OrigProfile = getFunctionProfileForContext(I.getKey()); 416 OrigProfile.merge(I.second); 417 } 418 } 419 420 void CSProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer, 421 StringMap<FunctionSamples> &ProfileMap) { 422 mergeAndTrimColdProfile(ProfileMap); 423 // Add bracket for context key to support different profile binary format 424 StringMap<FunctionSamples> CxtWithBracketPMap; 425 for (const auto &Item : ProfileMap) { 426 std::string ContextWithBracket = "[" + Item.first().str() + "]"; 427 auto Ret = CxtWithBracketPMap.try_emplace(ContextWithBracket, Item.second); 428 assert(Ret.second && "Must be a unique context"); 429 SampleContext FContext(Ret.first->first(), RawContext); 430 FunctionSamples &FProfile = Ret.first->second; 431 FProfile.setName(FContext.getNameWithContext(true)); 432 FProfile.setContext(FContext); 433 } 434 Writer->write(CxtWithBracketPMap); 435 } 436 437 // Helper function to extract context prefix string stack 438 // Extract context stack for reusing, leaf context stack will 439 // be added compressed while looking up function profile 440 static void 441 extractPrefixContextStack(SmallVectorImpl<std::string> &ContextStrStack, 442 const SmallVectorImpl<const PseudoProbe *> &Probes, 443 ProfiledBinary *Binary) { 444 for (const auto *P : Probes) { 445 Binary->getInlineContextForProbe(P, ContextStrStack, true); 446 } 447 } 448 449 void PseudoProbeCSProfileGenerator::generateProfile() { 450 // Enable pseudo probe functionalities in SampleProf 451 FunctionSamples::ProfileIsProbeBased = true; 452 FunctionSamples::ProfileIsCS = true; 453 for (const auto &BI : BinarySampleCounters) { 454 ProfiledBinary *Binary = BI.first; 455 for (const auto &CI : BI.second) { 456 const ProbeBasedCtxKey *CtxKey = 457 dyn_cast<ProbeBasedCtxKey>(CI.first.getPtr()); 458 SmallVector<std::string, 16> ContextStrStack; 459 extractPrefixContextStack(ContextStrStack, CtxKey->Probes, Binary); 460 // Fill in function body samples from probes, also infer caller's samples 461 // from callee's probe 462 populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStrStack, 463 Binary); 464 // Fill in boundary samples for a call probe 465 populateBoundarySamplesWithProbes(CI.second.BranchCounter, 466 ContextStrStack, Binary); 467 } 468 } 469 } 470 471 void PseudoProbeCSProfileGenerator::extractProbesFromRange( 472 const RangeSample &RangeCounter, ProbeCounterMap &ProbeCounter, 473 ProfiledBinary *Binary) { 474 RangeSample Ranges; 475 findDisjointRanges(Ranges, RangeCounter); 476 for (const auto &Range : Ranges) { 477 uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first); 478 uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second); 479 uint64_t Count = Range.second; 480 // Disjoint ranges have introduce zero-filled gap that 481 // doesn't belong to current context, filter them out. 482 if (Count == 0) 483 continue; 484 485 InstructionPointer IP(Binary, RangeBegin, true); 486 487 // Disjoint ranges may have range in the middle of two instr, 488 // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range 489 // can be Addr1+1 to Addr2-1. We should ignore such range. 490 if (IP.Address > RangeEnd) 491 continue; 492 493 while (IP.Address <= RangeEnd) { 494 const AddressProbesMap &Address2ProbesMap = 495 Binary->getAddress2ProbesMap(); 496 auto It = Address2ProbesMap.find(IP.Address); 497 if (It != Address2ProbesMap.end()) { 498 for (const auto &Probe : It->second) { 499 if (!Probe.isBlock()) 500 continue; 501 ProbeCounter[&Probe] += Count; 502 } 503 } 504 505 IP.advance(); 506 } 507 } 508 } 509 510 void PseudoProbeCSProfileGenerator::populateBodySamplesWithProbes( 511 const RangeSample &RangeCounter, 512 SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) { 513 ProbeCounterMap ProbeCounter; 514 // Extract the top frame probes by looking up each address among the range in 515 // the Address2ProbeMap 516 extractProbesFromRange(RangeCounter, ProbeCounter, Binary); 517 for (auto PI : ProbeCounter) { 518 const PseudoProbe *Probe = PI.first; 519 uint64_t Count = PI.second; 520 FunctionSamples &FunctionProfile = 521 getFunctionProfileForLeafProbe(ContextStrStack, Probe, Binary); 522 523 // Use InvalidProbeCount(UINT64_MAX) to mark sample count for a dangling 524 // probe. Dangling probes are the probes associated to an empty block. With 525 // this place holder, sample count on dangling probe will not be trusted by 526 // the compiler and it will rely on the counts inference algorithm to get 527 // the probe a reasonable count. 528 if (Probe->isDangling()) { 529 FunctionProfile.addBodySamplesForProbe( 530 Probe->Index, FunctionSamples::InvalidProbeCount); 531 continue; 532 } 533 FunctionProfile.addBodySamplesForProbe(Probe->Index, Count); 534 FunctionProfile.addTotalSamples(Count); 535 if (Probe->isEntry()) { 536 FunctionProfile.addHeadSamples(Count); 537 // Look up for the caller's function profile 538 const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe); 539 if (InlinerDesc != nullptr) { 540 // Since the context id will be compressed, we have to use callee's 541 // context id to infer caller's context id to ensure they share the 542 // same context prefix. 543 StringRef CalleeContextId = 544 FunctionProfile.getContext().getNameWithContext(true); 545 StringRef CallerContextId; 546 FrameLocation &&CallerLeafFrameLoc = 547 getCallerContext(CalleeContextId, CallerContextId); 548 uint64_t CallerIndex = CallerLeafFrameLoc.second.LineOffset; 549 assert(CallerIndex && 550 "Inferred caller's location index shouldn't be zero!"); 551 FunctionSamples &CallerProfile = 552 getFunctionProfileForContext(CallerContextId); 553 CallerProfile.setFunctionHash(InlinerDesc->FuncHash); 554 CallerProfile.addBodySamples(CallerIndex, 0, Count); 555 CallerProfile.addTotalSamples(Count); 556 CallerProfile.addCalledTargetSamples( 557 CallerIndex, 0, 558 FunctionProfile.getContext().getNameWithoutContext(), Count); 559 } 560 } 561 } 562 } 563 564 void PseudoProbeCSProfileGenerator::populateBoundarySamplesWithProbes( 565 const BranchSample &BranchCounter, 566 SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) { 567 for (auto BI : BranchCounter) { 568 uint64_t SourceOffset = BI.first.first; 569 uint64_t TargetOffset = BI.first.second; 570 uint64_t Count = BI.second; 571 uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset); 572 const PseudoProbe *CallProbe = Binary->getCallProbeForAddr(SourceAddress); 573 if (CallProbe == nullptr) 574 continue; 575 FunctionSamples &FunctionProfile = 576 getFunctionProfileForLeafProbe(ContextStrStack, CallProbe, Binary); 577 FunctionProfile.addBodySamples(CallProbe->Index, 0, Count); 578 FunctionProfile.addTotalSamples(Count); 579 StringRef CalleeName = FunctionSamples::getCanonicalFnName( 580 Binary->getFuncFromStartOffset(TargetOffset)); 581 if (CalleeName.size() == 0) 582 continue; 583 FunctionProfile.addCalledTargetSamples(CallProbe->Index, 0, CalleeName, 584 Count); 585 } 586 } 587 588 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( 589 SmallVectorImpl<std::string> &ContextStrStack, 590 const PseudoProbeFuncDesc *LeafFuncDesc) { 591 assert(ContextStrStack.size() && "Profile context must have the leaf frame"); 592 // Compress the context string except for the leaf frame 593 std::string LeafFrame = ContextStrStack.back(); 594 ContextStrStack.pop_back(); 595 CSProfileGenerator::compressRecursionContext(ContextStrStack); 596 597 std::ostringstream OContextStr; 598 for (uint32_t I = 0; I < ContextStrStack.size(); I++) { 599 if (OContextStr.str().size()) 600 OContextStr << " @ "; 601 OContextStr << ContextStrStack[I]; 602 } 603 // For leaf inlined context with the top frame, we should strip off the top 604 // frame's probe id, like: 605 // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar" 606 if (OContextStr.str().size()) 607 OContextStr << " @ "; 608 OContextStr << StringRef(LeafFrame).split(":").first.str(); 609 610 FunctionSamples &FunctionProile = 611 getFunctionProfileForContext(OContextStr.str()); 612 FunctionProile.setFunctionHash(LeafFuncDesc->FuncHash); 613 return FunctionProile; 614 } 615 616 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe( 617 SmallVectorImpl<std::string> &ContextStrStack, const PseudoProbe *LeafProbe, 618 ProfiledBinary *Binary) { 619 // Explicitly copy the context for appending the leaf context 620 SmallVector<std::string, 16> ContextStrStackCopy(ContextStrStack.begin(), 621 ContextStrStack.end()); 622 Binary->getInlineContextForProbe(LeafProbe, ContextStrStackCopy); 623 // Note that the context from probe doesn't include leaf frame, 624 // hence we need to retrieve and append the leaf frame. 625 const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->GUID); 626 ContextStrStackCopy.emplace_back(FuncDesc->FuncName + ":" + 627 Twine(LeafProbe->Index).str()); 628 return getFunctionProfileForLeafProbe(ContextStrStackCopy, FuncDesc); 629 } 630 631 } // end namespace sampleprof 632 } // end namespace llvm 633