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   Writer->write(ProfileMap);
90 }
91 
92 void ProfileGenerator::write() {
93   auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat);
94   if (std::error_code EC = WriterOrErr.getError())
95     exitWithError(EC, OutputFilename);
96   write(std::move(WriterOrErr.get()), ProfileMap);
97 }
98 
99 void ProfileGenerator::findDisjointRanges(RangeSample &DisjointRanges,
100                                           const RangeSample &Ranges) {
101 
102   /*
103   Regions may overlap with each other. Using the boundary info, find all
104   disjoint ranges and their sample count. BoundaryPoint contains the count
105   multiple samples begin/end at this points.
106 
107   |<--100-->|           Sample1
108   |<------200------>|   Sample2
109   A         B       C
110 
111   In the example above,
112   Sample1 begins at A, ends at B, its value is 100.
113   Sample2 beings at A, ends at C, its value is 200.
114   For A, BeginCount is the sum of sample begins at A, which is 300 and no
115   samples ends at A, so EndCount is 0.
116   Then boundary points A, B, and C with begin/end counts are:
117   A: (300, 0)
118   B: (0, 100)
119   C: (0, 200)
120   */
121   struct BoundaryPoint {
122     // Sum of sample counts beginning at this point
123     uint64_t BeginCount;
124     // Sum of sample counts ending at this point
125     uint64_t EndCount;
126 
127     BoundaryPoint() : BeginCount(0), EndCount(0){};
128 
129     void addBeginCount(uint64_t Count) { BeginCount += Count; }
130 
131     void addEndCount(uint64_t Count) { EndCount += Count; }
132   };
133 
134   /*
135   For the above example. With boundary points, follwing logic finds two
136   disjoint region of
137 
138   [A,B]:   300
139   [B+1,C]: 200
140 
141   If there is a boundary point that both begin and end, the point itself
142   becomes a separate disjoint region. For example, if we have original
143   ranges of
144 
145   |<--- 100 --->|
146                 |<--- 200 --->|
147   A             B             C
148 
149   there are three boundary points with their begin/end counts of
150 
151   A: (100, 0)
152   B: (200, 100)
153   C: (0, 200)
154 
155   the disjoint ranges would be
156 
157   [A, B-1]: 100
158   [B, B]:   300
159   [B+1, C]: 200.
160   */
161   std::map<uint64_t, BoundaryPoint> Boundaries;
162 
163   for (auto Item : Ranges) {
164     uint64_t Begin = Item.first.first;
165     uint64_t End = Item.first.second;
166     uint64_t Count = Item.second;
167     if (Boundaries.find(Begin) == Boundaries.end())
168       Boundaries[Begin] = BoundaryPoint();
169     Boundaries[Begin].addBeginCount(Count);
170 
171     if (Boundaries.find(End) == Boundaries.end())
172       Boundaries[End] = BoundaryPoint();
173     Boundaries[End].addEndCount(Count);
174   }
175 
176   uint64_t BeginAddress = 0;
177   int Count = 0;
178   for (auto Item : Boundaries) {
179     uint64_t Address = Item.first;
180     BoundaryPoint &Point = Item.second;
181     if (Point.BeginCount) {
182       if (BeginAddress)
183         DisjointRanges[{BeginAddress, Address - 1}] = Count;
184       Count += Point.BeginCount;
185       BeginAddress = Address;
186     }
187     if (Point.EndCount) {
188       assert(BeginAddress && "First boundary point cannot be 'end' point");
189       DisjointRanges[{BeginAddress, Address}] = Count;
190       Count -= Point.EndCount;
191       BeginAddress = Address + 1;
192     }
193   }
194 }
195 
196 FunctionSamples &
197 CSProfileGenerator::getFunctionProfileForContext(StringRef ContextStr,
198                                                  bool WasLeafInlined) {
199   auto Ret = ProfileMap.try_emplace(ContextStr, FunctionSamples());
200   if (Ret.second) {
201     SampleContext FContext(Ret.first->first(), RawContext);
202     if (WasLeafInlined)
203       FContext.setAttribute(ContextWasInlined);
204     FunctionSamples &FProfile = Ret.first->second;
205     FProfile.setContext(FContext);
206     FProfile.setName(FContext.getNameWithoutContext());
207   }
208   return Ret.first->second;
209 }
210 
211 void CSProfileGenerator::generateProfile() {
212   FunctionSamples::ProfileIsCS = true;
213   for (const auto &BI : BinarySampleCounters) {
214     ProfiledBinary *Binary = BI.first;
215     for (const auto &CI : BI.second) {
216       const StringBasedCtxKey *CtxKey =
217           dyn_cast<StringBasedCtxKey>(CI.first.getPtr());
218       StringRef ContextId(CtxKey->Context);
219       // Get or create function profile for the range
220       FunctionSamples &FunctionProfile =
221           getFunctionProfileForContext(ContextId, CtxKey->WasLeafInlined);
222 
223       // Fill in function body samples
224       populateFunctionBodySamples(FunctionProfile, CI.second.RangeCounter,
225                                   Binary);
226       // Fill in boundary sample counts as well as call site samples for calls
227       populateFunctionBoundarySamples(ContextId, FunctionProfile,
228                                       CI.second.BranchCounter, Binary);
229     }
230   }
231   // Fill in call site value sample for inlined calls and also use context to
232   // infer missing samples. Since we don't have call count for inlined
233   // functions, we estimate it from inlinee's profile using the entry of the
234   // body sample.
235   populateInferredFunctionSamples();
236 
237   postProcessProfiles();
238 }
239 
240 void CSProfileGenerator::updateBodySamplesforFunctionProfile(
241     FunctionSamples &FunctionProfile, const FrameLocation &LeafLoc,
242     uint64_t Count) {
243   // Filter out invalid negative(int type) lineOffset
244   if (LeafLoc.second.LineOffset & 0x80000000)
245     return;
246   // Use the maximum count of samples with same line location
247   ErrorOr<uint64_t> R = FunctionProfile.findSamplesAt(
248       LeafLoc.second.LineOffset, LeafLoc.second.Discriminator);
249   uint64_t PreviousCount = R ? R.get() : 0;
250   if (PreviousCount < Count) {
251     FunctionProfile.addBodySamples(LeafLoc.second.LineOffset,
252                                    LeafLoc.second.Discriminator,
253                                    Count - PreviousCount);
254   }
255 }
256 
257 void CSProfileGenerator::populateFunctionBodySamples(
258     FunctionSamples &FunctionProfile, const RangeSample &RangeCounter,
259     ProfiledBinary *Binary) {
260   // Compute disjoint ranges first, so we can use MAX
261   // for calculating count for each location.
262   RangeSample Ranges;
263   findDisjointRanges(Ranges, RangeCounter);
264   for (auto Range : Ranges) {
265     uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
266     uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
267     uint64_t Count = Range.second;
268     // Disjoint ranges have introduce zero-filled gap that
269     // doesn't belong to current context, filter them out.
270     if (Count == 0)
271       continue;
272 
273     InstructionPointer IP(Binary, RangeBegin, true);
274 
275     // Disjoint ranges may have range in the middle of two instr,
276     // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range
277     // can be Addr1+1 to Addr2-1. We should ignore such range.
278     if (IP.Address > RangeEnd)
279       continue;
280 
281     while (IP.Address <= RangeEnd) {
282       uint64_t Offset = Binary->virtualAddrToOffset(IP.Address);
283       auto LeafLoc = Binary->getInlineLeafFrameLoc(Offset);
284       if (LeafLoc.hasValue()) {
285         // Recording body sample for this specific context
286         updateBodySamplesforFunctionProfile(FunctionProfile, *LeafLoc, Count);
287       }
288       // Accumulate total sample count even it's a line with invalid debug info
289       FunctionProfile.addTotalSamples(Count);
290       // Move to next IP within the range
291       IP.advance();
292     }
293   }
294 }
295 
296 void CSProfileGenerator::populateFunctionBoundarySamples(
297     StringRef ContextId, FunctionSamples &FunctionProfile,
298     const BranchSample &BranchCounters, ProfiledBinary *Binary) {
299 
300   for (auto Entry : BranchCounters) {
301     uint64_t SourceOffset = Entry.first.first;
302     uint64_t TargetOffset = Entry.first.second;
303     uint64_t Count = Entry.second;
304     // Get the callee name by branch target if it's a call branch
305     StringRef CalleeName = FunctionSamples::getCanonicalFnName(
306         Binary->getFuncFromStartOffset(TargetOffset));
307     if (CalleeName.size() == 0)
308       continue;
309 
310     // Record called target sample and its count
311     auto LeafLoc = Binary->getInlineLeafFrameLoc(SourceOffset);
312     if (!LeafLoc.hasValue())
313       continue;
314     FunctionProfile.addCalledTargetSamples(LeafLoc->second.LineOffset,
315                                            LeafLoc->second.Discriminator,
316                                            CalleeName, Count);
317 
318     // Record head sample for called target(callee)
319     std::ostringstream OCalleeCtxStr;
320     if (ContextId.find(" @ ") != StringRef::npos) {
321       OCalleeCtxStr << ContextId.rsplit(" @ ").first.str();
322       OCalleeCtxStr << " @ ";
323     }
324     OCalleeCtxStr << getCallSite(*LeafLoc) << " @ " << CalleeName.str();
325 
326     FunctionSamples &CalleeProfile =
327         getFunctionProfileForContext(OCalleeCtxStr.str());
328     assert(Count != 0 && "Unexpected zero weight branch");
329     CalleeProfile.addHeadSamples(Count);
330   }
331 }
332 
333 static FrameLocation getCallerContext(StringRef CalleeContext,
334                                       StringRef &CallerNameWithContext) {
335   StringRef CallerContext = CalleeContext.rsplit(" @ ").first;
336   CallerNameWithContext = CallerContext.rsplit(':').first;
337   auto ContextSplit = CallerContext.rsplit(" @ ");
338   StringRef CallerFrameStr = ContextSplit.second.size() == 0
339                                  ? ContextSplit.first
340                                  : ContextSplit.second;
341   FrameLocation LeafFrameLoc = {"", {0, 0}};
342   StringRef Funcname;
343   SampleContext::decodeContextString(CallerFrameStr, Funcname,
344                                      LeafFrameLoc.second);
345   LeafFrameLoc.first = Funcname.str();
346   return LeafFrameLoc;
347 }
348 
349 void CSProfileGenerator::populateInferredFunctionSamples() {
350   for (const auto &Item : ProfileMap) {
351     const StringRef CalleeContext = Item.first();
352     const FunctionSamples &CalleeProfile = Item.second;
353 
354     // If we already have head sample counts, we must have value profile
355     // for call sites added already. Skip to avoid double counting.
356     if (CalleeProfile.getHeadSamples())
357       continue;
358     // If we don't have context, nothing to do for caller's call site.
359     // This could happen for entry point function.
360     if (CalleeContext.find(" @ ") == StringRef::npos)
361       continue;
362 
363     // Infer Caller's frame loc and context ID through string splitting
364     StringRef CallerContextId;
365     FrameLocation &&CallerLeafFrameLoc =
366         getCallerContext(CalleeContext, CallerContextId);
367 
368     // It's possible that we haven't seen any sample directly in the caller,
369     // in which case CallerProfile will not exist. But we can't modify
370     // ProfileMap while iterating it.
371     // TODO: created function profile for those callers too
372     if (ProfileMap.find(CallerContextId) == ProfileMap.end())
373       continue;
374     FunctionSamples &CallerProfile = ProfileMap[CallerContextId];
375 
376     // Since we don't have call count for inlined functions, we
377     // estimate it from inlinee's profile using entry body sample.
378     uint64_t EstimatedCallCount = CalleeProfile.getEntrySamples();
379     // If we don't have samples with location, use 1 to indicate live.
380     if (!EstimatedCallCount && !CalleeProfile.getBodySamples().size())
381       EstimatedCallCount = 1;
382     CallerProfile.addCalledTargetSamples(
383         CallerLeafFrameLoc.second.LineOffset,
384         CallerLeafFrameLoc.second.Discriminator,
385         CalleeProfile.getContext().getNameWithoutContext(), EstimatedCallCount);
386     CallerProfile.addBodySamples(CallerLeafFrameLoc.second.LineOffset,
387                                  CallerLeafFrameLoc.second.Discriminator,
388                                  EstimatedCallCount);
389     CallerProfile.addTotalSamples(EstimatedCallCount);
390   }
391 }
392 
393 void CSProfileGenerator::postProcessProfiles() {
394   // Compute hot/cold threshold based on profile. This will be used for cold
395   // context profile merging/trimming.
396   computeSummaryAndThreshold();
397 
398   // Run global pre-inliner to adjust/merge context profile based on estimated
399   // inline decisions.
400   CSPreInliner(ProfileMap, PSI->getHotCountThreshold(),
401                PSI->getColdCountThreshold())
402       .run();
403 
404   mergeAndTrimColdProfile(ProfileMap);
405 }
406 
407 void CSProfileGenerator::computeSummaryAndThreshold() {
408   SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
409   auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
410   PSI.reset(new ProfileSummaryInfo(std::move(Summary)));
411 }
412 
413 void CSProfileGenerator::mergeAndTrimColdProfile(
414     StringMap<FunctionSamples> &ProfileMap) {
415   if (!CSProfMergeColdContext && !CSProfTrimColdContext)
416     return;
417 
418   // Use threshold calculated from profile summary unless specified
419   uint64_t ColdThreshold = PSI->getColdCountThreshold();
420   if (CSProfColdThreshold.getNumOccurrences()) {
421     ColdThreshold = CSProfColdThreshold;
422   }
423 
424   // Nothing to merge if sample threshold is zero
425   if (ColdThreshold == 0)
426     return;
427 
428   // Filter the cold profiles from ProfileMap and move them into a tmp
429   // container
430   std::vector<std::pair<StringRef, const FunctionSamples *>> ColdProfiles;
431   for (const auto &I : ProfileMap) {
432     const FunctionSamples &FunctionProfile = I.second;
433     if (FunctionProfile.getTotalSamples() >= ColdThreshold)
434       continue;
435     ColdProfiles.emplace_back(I.getKey(), &I.second);
436   }
437 
438   // Remove the code profile from ProfileMap and merge them into BaseProileMap
439   StringMap<FunctionSamples> BaseProfileMap;
440   for (const auto &I : ColdProfiles) {
441     if (CSProfMergeColdContext) {
442       auto Ret = BaseProfileMap.try_emplace(
443           I.second->getContext().getNameWithoutContext(), FunctionSamples());
444       FunctionSamples &BaseProfile = Ret.first->second;
445       BaseProfile.merge(*I.second);
446     }
447     ProfileMap.erase(I.first);
448   }
449 
450   // Merge the base profiles into ProfileMap;
451   for (const auto &I : BaseProfileMap) {
452     // Filter the cold base profile
453     if (CSProfTrimColdContext &&
454         I.second.getTotalSamples() < CSProfColdThreshold &&
455         ProfileMap.find(I.getKey()) == ProfileMap.end())
456       continue;
457     // Merge the profile if the original profile exists, otherwise just insert
458     // as a new profile
459     FunctionSamples &OrigProfile = getFunctionProfileForContext(I.getKey());
460     OrigProfile.merge(I.second);
461   }
462 }
463 
464 void CSProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer,
465                                StringMap<FunctionSamples> &ProfileMap) {
466   // Add bracket for context key to support different profile binary format
467   StringMap<FunctionSamples> CxtWithBracketPMap;
468   for (const auto &Item : ProfileMap) {
469     // After CSPreInliner the key of ProfileMap is no longer accurate for
470     // context, use the context attached to function samples instead.
471     std::string ContextWithBracket =
472         "[" + Item.second.getNameWithContext().str() + "]";
473     auto Ret = CxtWithBracketPMap.try_emplace(ContextWithBracket, Item.second);
474     assert(Ret.second && "Must be a unique context");
475     SampleContext FContext(Ret.first->first(), RawContext);
476     FunctionSamples &FProfile = Ret.first->second;
477     FContext.setAllAttributes(FProfile.getContext().getAllAttributes());
478     FProfile.setName(FContext.getNameWithoutContext());
479     FProfile.setContext(FContext);
480   }
481   Writer->write(CxtWithBracketPMap);
482 }
483 
484 // Helper function to extract context prefix string stack
485 // Extract context stack for reusing, leaf context stack will
486 // be added compressed while looking up function profile
487 static void
488 extractPrefixContextStack(SmallVectorImpl<std::string> &ContextStrStack,
489                           const SmallVectorImpl<const PseudoProbe *> &Probes,
490                           ProfiledBinary *Binary) {
491   for (const auto *P : Probes) {
492     Binary->getInlineContextForProbe(P, ContextStrStack, true);
493   }
494 }
495 
496 void PseudoProbeCSProfileGenerator::generateProfile() {
497   // Enable pseudo probe functionalities in SampleProf
498   FunctionSamples::ProfileIsProbeBased = true;
499   FunctionSamples::ProfileIsCS = true;
500   for (const auto &BI : BinarySampleCounters) {
501     ProfiledBinary *Binary = BI.first;
502     for (const auto &CI : BI.second) {
503       const ProbeBasedCtxKey *CtxKey =
504           dyn_cast<ProbeBasedCtxKey>(CI.first.getPtr());
505       SmallVector<std::string, 16> ContextStrStack;
506       extractPrefixContextStack(ContextStrStack, CtxKey->Probes, Binary);
507       // Fill in function body samples from probes, also infer caller's samples
508       // from callee's probe
509       populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStrStack,
510                                     Binary);
511       // Fill in boundary samples for a call probe
512       populateBoundarySamplesWithProbes(CI.second.BranchCounter,
513                                         ContextStrStack, Binary);
514     }
515   }
516 
517   postProcessProfiles();
518 }
519 
520 void PseudoProbeCSProfileGenerator::extractProbesFromRange(
521     const RangeSample &RangeCounter, ProbeCounterMap &ProbeCounter,
522     ProfiledBinary *Binary) {
523   RangeSample Ranges;
524   findDisjointRanges(Ranges, RangeCounter);
525   for (const auto &Range : Ranges) {
526     uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
527     uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
528     uint64_t Count = Range.second;
529     // Disjoint ranges have introduce zero-filled gap that
530     // doesn't belong to current context, filter them out.
531     if (Count == 0)
532       continue;
533 
534     InstructionPointer IP(Binary, RangeBegin, true);
535 
536     // Disjoint ranges may have range in the middle of two instr,
537     // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range
538     // can be Addr1+1 to Addr2-1. We should ignore such range.
539     if (IP.Address > RangeEnd)
540       continue;
541 
542     while (IP.Address <= RangeEnd) {
543       const AddressProbesMap &Address2ProbesMap =
544           Binary->getAddress2ProbesMap();
545       auto It = Address2ProbesMap.find(IP.Address);
546       if (It != Address2ProbesMap.end()) {
547         for (const auto &Probe : It->second) {
548           if (!Probe.isBlock())
549             continue;
550           ProbeCounter[&Probe] += Count;
551         }
552       }
553 
554       IP.advance();
555     }
556   }
557 }
558 
559 void PseudoProbeCSProfileGenerator::populateBodySamplesWithProbes(
560     const RangeSample &RangeCounter,
561     SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) {
562   ProbeCounterMap ProbeCounter;
563   // Extract the top frame probes by looking up each address among the range in
564   // the Address2ProbeMap
565   extractProbesFromRange(RangeCounter, ProbeCounter, Binary);
566   for (auto PI : ProbeCounter) {
567     const PseudoProbe *Probe = PI.first;
568     uint64_t Count = PI.second;
569     FunctionSamples &FunctionProfile =
570         getFunctionProfileForLeafProbe(ContextStrStack, Probe, Binary);
571 
572     // Use InvalidProbeCount(UINT64_MAX) to mark sample count for a dangling
573     // probe. Dangling probes are the probes associated to an empty block. With
574     // this place holder, sample count on dangling probe will not be trusted by
575     // the compiler and it will rely on the counts inference algorithm to get
576     // the probe a reasonable count.
577     if (Probe->isDangling()) {
578       FunctionProfile.addBodySamplesForProbe(
579           Probe->Index, FunctionSamples::InvalidProbeCount);
580       continue;
581     }
582     FunctionProfile.addBodySamplesForProbe(Probe->Index, Count);
583     FunctionProfile.addTotalSamples(Count);
584     if (Probe->isEntry()) {
585       FunctionProfile.addHeadSamples(Count);
586       // Look up for the caller's function profile
587       const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe);
588       if (InlinerDesc != nullptr) {
589         // Since the context id will be compressed, we have to use callee's
590         // context id to infer caller's context id to ensure they share the
591         // same context prefix.
592         StringRef CalleeContextId =
593             FunctionProfile.getContext().getNameWithContext(true);
594         StringRef CallerContextId;
595         FrameLocation &&CallerLeafFrameLoc =
596             getCallerContext(CalleeContextId, CallerContextId);
597         uint64_t CallerIndex = CallerLeafFrameLoc.second.LineOffset;
598         assert(CallerIndex &&
599                "Inferred caller's location index shouldn't be zero!");
600         FunctionSamples &CallerProfile =
601             getFunctionProfileForContext(CallerContextId);
602         CallerProfile.setFunctionHash(InlinerDesc->FuncHash);
603         CallerProfile.addBodySamples(CallerIndex, 0, Count);
604         CallerProfile.addTotalSamples(Count);
605         CallerProfile.addCalledTargetSamples(
606             CallerIndex, 0,
607             FunctionProfile.getContext().getNameWithoutContext(), Count);
608       }
609     }
610   }
611 }
612 
613 void PseudoProbeCSProfileGenerator::populateBoundarySamplesWithProbes(
614     const BranchSample &BranchCounter,
615     SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) {
616   for (auto BI : BranchCounter) {
617     uint64_t SourceOffset = BI.first.first;
618     uint64_t TargetOffset = BI.first.second;
619     uint64_t Count = BI.second;
620     uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset);
621     const PseudoProbe *CallProbe = Binary->getCallProbeForAddr(SourceAddress);
622     if (CallProbe == nullptr)
623       continue;
624     FunctionSamples &FunctionProfile =
625         getFunctionProfileForLeafProbe(ContextStrStack, CallProbe, Binary);
626     FunctionProfile.addBodySamples(CallProbe->Index, 0, Count);
627     FunctionProfile.addTotalSamples(Count);
628     StringRef CalleeName = FunctionSamples::getCanonicalFnName(
629         Binary->getFuncFromStartOffset(TargetOffset));
630     if (CalleeName.size() == 0)
631       continue;
632     FunctionProfile.addCalledTargetSamples(CallProbe->Index, 0, CalleeName,
633                                            Count);
634   }
635 }
636 
637 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe(
638     SmallVectorImpl<std::string> &ContextStrStack,
639     const PseudoProbeFuncDesc *LeafFuncDesc, bool WasLeafInlined) {
640   assert(ContextStrStack.size() && "Profile context must have the leaf frame");
641   // Compress the context string except for the leaf frame
642   std::string LeafFrame = ContextStrStack.back();
643   ContextStrStack.pop_back();
644   CSProfileGenerator::compressRecursionContext(ContextStrStack);
645 
646   std::ostringstream OContextStr;
647   for (uint32_t I = 0; I < ContextStrStack.size(); I++) {
648     if (OContextStr.str().size())
649       OContextStr << " @ ";
650     OContextStr << ContextStrStack[I];
651   }
652   // For leaf inlined context with the top frame, we should strip off the top
653   // frame's probe id, like:
654   // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar"
655   if (OContextStr.str().size())
656     OContextStr << " @ ";
657   OContextStr << StringRef(LeafFrame).split(":").first.str();
658 
659   FunctionSamples &FunctionProile =
660       getFunctionProfileForContext(OContextStr.str(), WasLeafInlined);
661   FunctionProile.setFunctionHash(LeafFuncDesc->FuncHash);
662   return FunctionProile;
663 }
664 
665 FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe(
666     SmallVectorImpl<std::string> &ContextStrStack, const PseudoProbe *LeafProbe,
667     ProfiledBinary *Binary) {
668   // Explicitly copy the context for appending the leaf context
669   SmallVector<std::string, 16> ContextStrStackCopy(ContextStrStack.begin(),
670                                                    ContextStrStack.end());
671   Binary->getInlineContextForProbe(LeafProbe, ContextStrStackCopy, true);
672   const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->GUID);
673   bool WasLeafInlined = LeafProbe->InlineTree->hasInlineSite();
674   return getFunctionProfileForLeafProbe(ContextStrStackCopy, FuncDesc,
675                                         WasLeafInlined);
676 }
677 
678 } // end namespace sampleprof
679 } // end namespace llvm
680