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