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   for (auto PI : ProbeCounter) {
514     const PseudoProbe *Probe = PI.first;
515     uint64_t Count = PI.second;
516     FunctionSamples &FunctionProfile =
517         getFunctionProfileForLeafProbe(ContextStrStack, Probe, Binary);
518 
519     // Use InvalidProbeCount(UINT64_MAX) to mark sample count for a dangling
520     // probe. Dangling probes are the probes associated to an empty block. With
521     // this place holder, sample count on dangling probe will not be trusted by
522     // the compiler and it will rely on the counts inference algorithm to get
523     // the probe a reasonable count.
524     if (Probe->isDangling()) {
525       FunctionProfile.addBodySamplesForProbe(
526           Probe->Index, FunctionSamples::InvalidProbeCount);
527       continue;
528     }
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 }
559 
560 void PseudoProbeCSProfileGenerator::populateBoundarySamplesWithProbes(
561     const BranchSample &BranchCounter,
562     SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) {
563   for (auto BI : BranchCounter) {
564     uint64_t SourceOffset = BI.first.first;
565     uint64_t TargetOffset = BI.first.second;
566     uint64_t Count = BI.second;
567     uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset);
568     const PseudoProbe *CallProbe = Binary->getCallProbeForAddr(SourceAddress);
569     if (CallProbe == nullptr)
570       continue;
571     FunctionSamples &FunctionProfile =
572         getFunctionProfileForLeafProbe(ContextStrStack, CallProbe, Binary);
573     FunctionProfile.addBodySamples(CallProbe->Index, 0, Count);
574     FunctionProfile.addTotalSamples(Count);
575     StringRef CalleeName = FunctionSamples::getCanonicalFnName(
576         Binary->getFuncFromStartOffset(TargetOffset));
577     if (CalleeName.size() == 0)
578       continue;
579     FunctionProfile.addCalledTargetSamples(CallProbe->Index, 0, CalleeName,
580                                            Count);
581   }
582 }
583 
584 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe(
585     SmallVectorImpl<std::string> &ContextStrStack,
586     const PseudoProbeFuncDesc *LeafFuncDesc, bool WasLeafInlined) {
587   assert(ContextStrStack.size() && "Profile context must have the leaf frame");
588   // Compress the context string except for the leaf frame
589   std::string LeafFrame = ContextStrStack.back();
590   ContextStrStack.pop_back();
591   CSProfileGenerator::compressRecursionContext(ContextStrStack);
592 
593   std::ostringstream OContextStr;
594   for (uint32_t I = 0; I < ContextStrStack.size(); I++) {
595     if (OContextStr.str().size())
596       OContextStr << " @ ";
597     OContextStr << ContextStrStack[I];
598   }
599   // For leaf inlined context with the top frame, we should strip off the top
600   // frame's probe id, like:
601   // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar"
602   if (OContextStr.str().size())
603     OContextStr << " @ ";
604   OContextStr << StringRef(LeafFrame).split(":").first.str();
605 
606   FunctionSamples &FunctionProile =
607       getFunctionProfileForContext(OContextStr.str(), WasLeafInlined);
608   FunctionProile.setFunctionHash(LeafFuncDesc->FuncHash);
609   return FunctionProile;
610 }
611 
612 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe(
613     SmallVectorImpl<std::string> &ContextStrStack, const PseudoProbe *LeafProbe,
614     ProfiledBinary *Binary) {
615   // Explicitly copy the context for appending the leaf context
616   SmallVector<std::string, 16> ContextStrStackCopy(ContextStrStack.begin(),
617                                                    ContextStrStack.end());
618   Binary->getInlineContextForProbe(LeafProbe, ContextStrStackCopy, true);
619   const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->GUID);
620   bool WasLeafInlined = LeafProbe->InlineTree->hasInlineSite();
621   return getFunctionProfileForLeafProbe(ContextStrStackCopy, FuncDesc,
622                                         WasLeafInlined);
623 }
624 
625 } // end namespace sampleprof
626 } // end namespace llvm
627