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 "ErrorHandling.h"
11 #include "ProfiledBinary.h"
12 #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
13 #include "llvm/ProfileData/ProfileCommon.h"
14 #include <float.h>
15 #include <unordered_set>
16 
17 cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
18                                     cl::Required,
19                                     cl::desc("Output profile file"));
20 static cl::alias OutputA("o", cl::desc("Alias for --output"),
21                          cl::aliasopt(OutputFilename));
22 
23 static cl::opt<SampleProfileFormat> OutputFormat(
24     "format", cl::desc("Format of output profile"), cl::init(SPF_Ext_Binary),
25     cl::values(
26         clEnumValN(SPF_Binary, "binary", "Binary encoding (default)"),
27         clEnumValN(SPF_Compact_Binary, "compbinary", "Compact binary encoding"),
28         clEnumValN(SPF_Ext_Binary, "extbinary", "Extensible binary encoding"),
29         clEnumValN(SPF_Text, "text", "Text encoding"),
30         clEnumValN(SPF_GCC, "gcc",
31                    "GCC encoding (only meaningful for -sample)")));
32 
33 cl::opt<bool> UseMD5(
34     "use-md5", cl::init(false), cl::Hidden,
35     cl::desc("Use md5 to represent function names in the output profile (only "
36              "meaningful for -extbinary)"));
37 
38 static cl::opt<bool> PopulateProfileSymbolList(
39     "populate-profile-symbol-list", cl::init(false), cl::Hidden,
40     cl::desc("Populate profile symbol list (only meaningful for -extbinary)"));
41 
42 static cl::opt<bool> FillZeroForAllFuncs(
43     "fill-zero-for-all-funcs", cl::init(false), cl::Hidden,
44     cl::desc("Attribute all functions' range with zero count "
45              "even it's not hit by any samples."));
46 
47 static cl::opt<int32_t, true> RecursionCompression(
48     "compress-recursion",
49     cl::desc("Compressing recursion by deduplicating adjacent frame "
50              "sequences up to the specified size. -1 means no size limit."),
51     cl::Hidden,
52     cl::location(llvm::sampleprof::CSProfileGenerator::MaxCompressionSize));
53 
54 static cl::opt<bool>
55     TrimColdProfile("trim-cold-profile", cl::init(false), cl::ZeroOrMore,
56                     cl::desc("If the total count of the profile is smaller "
57                              "than threshold, it will be trimmed."));
58 
59 static cl::opt<bool> CSProfMergeColdContext(
60     "csprof-merge-cold-context", cl::init(true), cl::ZeroOrMore,
61     cl::desc("If the total count of context profile is smaller than "
62              "the threshold, it will be merged into context-less base "
63              "profile."));
64 
65 static cl::opt<uint32_t> CSProfMaxColdContextDepth(
66     "csprof-max-cold-context-depth", cl::init(1), cl::ZeroOrMore,
67     cl::desc("Keep the last K contexts while merging cold profile. 1 means the "
68              "context-less base profile"));
69 
70 static cl::opt<int, true> CSProfMaxContextDepth(
71     "csprof-max-context-depth", cl::ZeroOrMore,
72     cl::desc("Keep the last K contexts while merging profile. -1 means no "
73              "depth limit."),
74     cl::location(llvm::sampleprof::CSProfileGenerator::MaxContextDepth));
75 
76 static cl::opt<double> HotFunctionDensityThreshold(
77     "hot-function-density-threshold", llvm::cl::init(1000),
78     llvm::cl::desc(
79         "specify density threshold for hot functions (default: 1000)"),
80     llvm::cl::Optional);
81 static cl::opt<bool> ShowDensity("show-density", llvm::cl::init(false),
82                                  llvm::cl::desc("show profile density details"),
83                                  llvm::cl::Optional);
84 
85 static cl::opt<bool> UpdateTotalSamples(
86     "update-total-samples", llvm::cl::init(false),
87     llvm::cl::desc(
88         "Update total samples by accumulating all its body samples."),
89     llvm::cl::Optional);
90 
91 extern cl::opt<int> ProfileSummaryCutoffHot;
92 
93 static cl::opt<bool> GenCSNestedProfile(
94     "gen-cs-nested-profile", cl::Hidden, cl::init(false),
95     cl::desc("Generate nested function profiles for CSSPGO"));
96 
97 using namespace llvm;
98 using namespace sampleprof;
99 
100 namespace llvm {
101 namespace sampleprof {
102 
103 // Initialize the MaxCompressionSize to -1 which means no size limit
104 int32_t CSProfileGenerator::MaxCompressionSize = -1;
105 
106 int CSProfileGenerator::MaxContextDepth = -1;
107 
108 bool ProfileGeneratorBase::UseFSDiscriminator = false;
109 
110 std::unique_ptr<ProfileGeneratorBase>
111 ProfileGeneratorBase::create(ProfiledBinary *Binary,
112                              const ContextSampleCounterMap &SampleCounters,
113                              bool ProfileIsCSFlat) {
114   std::unique_ptr<ProfileGeneratorBase> Generator;
115   if (ProfileIsCSFlat) {
116     if (Binary->useFSDiscriminator())
117       exitWithError("FS discriminator is not supported in CS profile.");
118     Generator.reset(new CSProfileGenerator(Binary, SampleCounters));
119   } else {
120     Generator.reset(new ProfileGenerator(Binary, SampleCounters));
121   }
122   ProfileGeneratorBase::UseFSDiscriminator = Binary->useFSDiscriminator();
123   FunctionSamples::ProfileIsFS = Binary->useFSDiscriminator();
124 
125   return Generator;
126 }
127 
128 void ProfileGeneratorBase::write(std::unique_ptr<SampleProfileWriter> Writer,
129                                  SampleProfileMap &ProfileMap) {
130   // Populate profile symbol list if extended binary format is used.
131   ProfileSymbolList SymbolList;
132 
133   if (PopulateProfileSymbolList && OutputFormat == SPF_Ext_Binary) {
134     Binary->populateSymbolListFromDWARF(SymbolList);
135     Writer->setProfileSymbolList(&SymbolList);
136   }
137 
138   if (std::error_code EC = Writer->write(ProfileMap))
139     exitWithError(std::move(EC));
140 }
141 
142 void ProfileGeneratorBase::write() {
143   auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat);
144   if (std::error_code EC = WriterOrErr.getError())
145     exitWithError(EC, OutputFilename);
146 
147   if (UseMD5) {
148     if (OutputFormat != SPF_Ext_Binary)
149       WithColor::warning() << "-use-md5 is ignored. Specify "
150                               "--format=extbinary to enable it\n";
151     else
152       WriterOrErr.get()->setUseMD5();
153   }
154 
155   write(std::move(WriterOrErr.get()), ProfileMap);
156 }
157 
158 void ProfileGeneratorBase::showDensitySuggestion(double Density) {
159   if (Density == 0.0)
160     WithColor::warning() << "The --profile-summary-cutoff-hot option may be "
161                             "set too low. Please check your command.\n";
162   else if (Density < HotFunctionDensityThreshold)
163     WithColor::warning()
164         << "AutoFDO is estimated to optimize better with "
165         << format("%.1f", HotFunctionDensityThreshold / Density)
166         << "x more samples. Please consider increasing sampling rate or "
167            "profiling for longer duration to get more samples.\n";
168 
169   if (ShowDensity)
170     outs() << "Minimum profile density for hot functions with top "
171            << format("%.2f",
172                      static_cast<double>(ProfileSummaryCutoffHot.getValue()) /
173                          10000)
174            << "% total samples: " << format("%.1f", Density) << "\n";
175 }
176 
177 double ProfileGeneratorBase::calculateDensity(const SampleProfileMap &Profiles,
178                                               uint64_t HotCntThreshold) {
179   double Density = DBL_MAX;
180   std::vector<const FunctionSamples *> HotFuncs;
181   for (auto &I : Profiles) {
182     auto &FuncSamples = I.second;
183     if (FuncSamples.getTotalSamples() < HotCntThreshold)
184       continue;
185     HotFuncs.emplace_back(&FuncSamples);
186   }
187 
188   for (auto *FuncSamples : HotFuncs) {
189     auto *Func = Binary->getBinaryFunction(FuncSamples->getName());
190     if (!Func)
191       continue;
192     uint64_t FuncSize = Func->getFuncSize();
193     if (FuncSize == 0)
194       continue;
195     Density =
196         std::min(Density, static_cast<double>(FuncSamples->getTotalSamples()) /
197                               FuncSize);
198   }
199 
200   return Density == DBL_MAX ? 0.0 : Density;
201 }
202 
203 void ProfileGeneratorBase::findDisjointRanges(RangeSample &DisjointRanges,
204                                               const RangeSample &Ranges) {
205 
206   /*
207   Regions may overlap with each other. Using the boundary info, find all
208   disjoint ranges and their sample count. BoundaryPoint contains the count
209   multiple samples begin/end at this points.
210 
211   |<--100-->|           Sample1
212   |<------200------>|   Sample2
213   A         B       C
214 
215   In the example above,
216   Sample1 begins at A, ends at B, its value is 100.
217   Sample2 beings at A, ends at C, its value is 200.
218   For A, BeginCount is the sum of sample begins at A, which is 300 and no
219   samples ends at A, so EndCount is 0.
220   Then boundary points A, B, and C with begin/end counts are:
221   A: (300, 0)
222   B: (0, 100)
223   C: (0, 200)
224   */
225   struct BoundaryPoint {
226     // Sum of sample counts beginning at this point
227     uint64_t BeginCount = UINT64_MAX;
228     // Sum of sample counts ending at this point
229     uint64_t EndCount = UINT64_MAX;
230     // Is the begin point of a zero range.
231     bool IsZeroRangeBegin = false;
232     // Is the end point of a zero range.
233     bool IsZeroRangeEnd = false;
234 
235     void addBeginCount(uint64_t Count) {
236       if (BeginCount == UINT64_MAX)
237         BeginCount = 0;
238       BeginCount += Count;
239     }
240 
241     void addEndCount(uint64_t Count) {
242       if (EndCount == UINT64_MAX)
243         EndCount = 0;
244       EndCount += Count;
245     }
246   };
247 
248   /*
249   For the above example. With boundary points, follwing logic finds two
250   disjoint region of
251 
252   [A,B]:   300
253   [B+1,C]: 200
254 
255   If there is a boundary point that both begin and end, the point itself
256   becomes a separate disjoint region. For example, if we have original
257   ranges of
258 
259   |<--- 100 --->|
260                 |<--- 200 --->|
261   A             B             C
262 
263   there are three boundary points with their begin/end counts of
264 
265   A: (100, 0)
266   B: (200, 100)
267   C: (0, 200)
268 
269   the disjoint ranges would be
270 
271   [A, B-1]: 100
272   [B, B]:   300
273   [B+1, C]: 200.
274 
275   Example for zero value range:
276 
277     |<--- 100 --->|
278                        |<--- 200 --->|
279   |<---------------  0 ----------------->|
280   A  B            C    D             E   F
281 
282   [A, B-1]  : 0
283   [B, C]    : 100
284   [C+1, D-1]: 0
285   [D, E]    : 200
286   [E+1, F]  : 0
287   */
288   std::map<uint64_t, BoundaryPoint> Boundaries;
289 
290   for (const auto &Item : Ranges) {
291     assert(Item.first.first <= Item.first.second &&
292            "Invalid instruction range");
293     auto &BeginPoint = Boundaries[Item.first.first];
294     auto &EndPoint = Boundaries[Item.first.second];
295     uint64_t Count = Item.second;
296 
297     BeginPoint.addBeginCount(Count);
298     EndPoint.addEndCount(Count);
299     if (Count == 0) {
300       BeginPoint.IsZeroRangeBegin = true;
301       EndPoint.IsZeroRangeEnd = true;
302     }
303   }
304 
305   // Use UINT64_MAX to indicate there is no existing range between BeginAddress
306   // and the next valid address
307   uint64_t BeginAddress = UINT64_MAX;
308   int ZeroRangeDepth = 0;
309   uint64_t Count = 0;
310   for (const auto &Item : Boundaries) {
311     uint64_t Address = Item.first;
312     const BoundaryPoint &Point = Item.second;
313     if (Point.BeginCount != UINT64_MAX) {
314       if (BeginAddress != UINT64_MAX)
315         DisjointRanges[{BeginAddress, Address - 1}] = Count;
316       Count += Point.BeginCount;
317       BeginAddress = Address;
318       ZeroRangeDepth += Point.IsZeroRangeBegin;
319     }
320     if (Point.EndCount != UINT64_MAX) {
321       assert((BeginAddress != UINT64_MAX) &&
322              "First boundary point cannot be 'end' point");
323       DisjointRanges[{BeginAddress, Address}] = Count;
324       assert(Count >= Point.EndCount && "Mismatched live ranges");
325       Count -= Point.EndCount;
326       BeginAddress = Address + 1;
327       ZeroRangeDepth -= Point.IsZeroRangeEnd;
328       // If the remaining count is zero and it's no longer in a zero range, this
329       // means we consume all the ranges before, thus mark BeginAddress as
330       // UINT64_MAX. e.g. supposing we have two non-overlapping ranges:
331       //  [<---- 10 ---->]
332       //                       [<---- 20 ---->]
333       //   A             B     C              D
334       // The BeginAddress(B+1) will reset to invalid(UINT64_MAX), so we won't
335       // have the [B+1, C-1] zero range.
336       if (Count == 0 && ZeroRangeDepth == 0)
337         BeginAddress = UINT64_MAX;
338     }
339   }
340 }
341 
342 void ProfileGeneratorBase::updateBodySamplesforFunctionProfile(
343     FunctionSamples &FunctionProfile, const SampleContextFrame &LeafLoc,
344     uint64_t Count) {
345   // Use the maximum count of samples with same line location
346   uint32_t Discriminator = getBaseDiscriminator(LeafLoc.Location.Discriminator);
347 
348   // Use duplication factor to compensated for loop unroll/vectorization.
349   // Note that this is only needed when we're taking MAX of the counts at
350   // the location instead of SUM.
351   Count *= getDuplicationFactor(LeafLoc.Location.Discriminator);
352 
353   ErrorOr<uint64_t> R =
354       FunctionProfile.findSamplesAt(LeafLoc.Location.LineOffset, Discriminator);
355 
356   uint64_t PreviousCount = R ? R.get() : 0;
357   if (PreviousCount <= Count) {
358     FunctionProfile.addBodySamples(LeafLoc.Location.LineOffset, Discriminator,
359                                    Count - PreviousCount);
360   }
361 }
362 
363 void ProfileGeneratorBase::updateTotalSamples() {
364   if (!UpdateTotalSamples)
365     return;
366 
367   for (auto &Item : ProfileMap) {
368     FunctionSamples &FunctionProfile = Item.second;
369     FunctionProfile.updateTotalSamples();
370   }
371 }
372 
373 FunctionSamples &
374 ProfileGenerator::getTopLevelFunctionProfile(StringRef FuncName) {
375   SampleContext Context(FuncName);
376   auto Ret = ProfileMap.emplace(Context, FunctionSamples());
377   if (Ret.second) {
378     FunctionSamples &FProfile = Ret.first->second;
379     FProfile.setContext(Context);
380   }
381   return Ret.first->second;
382 }
383 
384 void ProfileGenerator::generateProfile() {
385   if (Binary->usePseudoProbes()) {
386     // TODO: Support probe based profile generation
387     exitWithError("Probe based profile generation not supported for AutoFDO, "
388       "consider dropping `--ignore-stack-samples` or adding `--use-dwarf-correlation`.");
389   } else {
390     generateLineNumBasedProfile();
391   }
392   postProcessProfiles();
393 }
394 
395 void ProfileGenerator::postProcessProfiles() {
396   computeSummaryAndThreshold();
397   trimColdProfiles(ProfileMap, ColdCountThreshold);
398   calculateAndShowDensity(ProfileMap);
399 }
400 
401 void ProfileGenerator::trimColdProfiles(const SampleProfileMap &Profiles,
402                                         uint64_t ColdCntThreshold) {
403   if (!TrimColdProfile)
404     return;
405 
406   // Move cold profiles into a tmp container.
407   std::vector<SampleContext> ColdProfiles;
408   for (const auto &I : ProfileMap) {
409     if (I.second.getTotalSamples() < ColdCntThreshold)
410       ColdProfiles.emplace_back(I.first);
411   }
412 
413   // Remove the cold profile from ProfileMap.
414   for (const auto &I : ColdProfiles)
415     ProfileMap.erase(I);
416 }
417 
418 void ProfileGenerator::generateLineNumBasedProfile() {
419   assert(SampleCounters.size() == 1 &&
420          "Must have one entry for profile generation.");
421   const SampleCounter &SC = SampleCounters.begin()->second;
422   // Fill in function body samples
423   populateBodySamplesForAllFunctions(SC.RangeCounter);
424   // Fill in boundary sample counts as well as call site samples for calls
425   populateBoundarySamplesForAllFunctions(SC.BranchCounter);
426 
427   updateTotalSamples();
428 }
429 
430 FunctionSamples &ProfileGenerator::getLeafProfileAndAddTotalSamples(
431     const SampleContextFrameVector &FrameVec, uint64_t Count) {
432   // Get top level profile
433   FunctionSamples *FunctionProfile =
434       &getTopLevelFunctionProfile(FrameVec[0].FuncName);
435   FunctionProfile->addTotalSamples(Count);
436 
437   for (size_t I = 1; I < FrameVec.size(); I++) {
438     LineLocation Callsite(
439         FrameVec[I - 1].Location.LineOffset,
440         getBaseDiscriminator(FrameVec[I - 1].Location.Discriminator));
441     FunctionSamplesMap &SamplesMap =
442         FunctionProfile->functionSamplesAt(Callsite);
443     auto Ret =
444         SamplesMap.emplace(FrameVec[I].FuncName.str(), FunctionSamples());
445     if (Ret.second) {
446       SampleContext Context(FrameVec[I].FuncName);
447       Ret.first->second.setContext(Context);
448     }
449     FunctionProfile = &Ret.first->second;
450     FunctionProfile->addTotalSamples(Count);
451   }
452 
453   return *FunctionProfile;
454 }
455 
456 RangeSample
457 ProfileGenerator::preprocessRangeCounter(const RangeSample &RangeCounter) {
458   RangeSample Ranges(RangeCounter.begin(), RangeCounter.end());
459   if (FillZeroForAllFuncs) {
460     for (auto &FuncI : Binary->getAllBinaryFunctions()) {
461       for (auto &R : FuncI.second.Ranges) {
462         Ranges[{R.first, R.second - 1}] += 0;
463       }
464     }
465   } else {
466     // For each range, we search for all ranges of the function it belongs to
467     // and initialize it with zero count, so it remains zero if doesn't hit any
468     // samples. This is to be consistent with compiler that interpret zero count
469     // as unexecuted(cold).
470     for (const auto &I : RangeCounter) {
471       uint64_t StartOffset = I.first.first;
472       for (const auto &Range : Binary->getRangesForOffset(StartOffset))
473         Ranges[{Range.first, Range.second - 1}] += 0;
474     }
475   }
476   RangeSample DisjointRanges;
477   findDisjointRanges(DisjointRanges, Ranges);
478   return DisjointRanges;
479 }
480 
481 void ProfileGenerator::populateBodySamplesForAllFunctions(
482     const RangeSample &RangeCounter) {
483   for (const auto &Range : preprocessRangeCounter(RangeCounter)) {
484     uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
485     uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
486     uint64_t Count = Range.second;
487 
488     InstructionPointer IP(Binary, RangeBegin, true);
489     // Disjoint ranges may have range in the middle of two instr,
490     // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range
491     // can be Addr1+1 to Addr2-1. We should ignore such range.
492     if (IP.Address > RangeEnd)
493       continue;
494 
495     do {
496       uint64_t Offset = Binary->virtualAddrToOffset(IP.Address);
497       const SampleContextFrameVector &FrameVec =
498           Binary->getFrameLocationStack(Offset);
499       if (!FrameVec.empty()) {
500         // FIXME: As accumulating total count per instruction caused some
501         // regression, we changed to accumulate total count per byte as a
502         // workaround. Tuning hotness threshold on the compiler side might be
503         // necessary in the future.
504         FunctionSamples &FunctionProfile = getLeafProfileAndAddTotalSamples(
505             FrameVec, Count * Binary->getInstSize(Offset));
506         updateBodySamplesforFunctionProfile(FunctionProfile, FrameVec.back(),
507                                             Count);
508       }
509     } while (IP.advance() && IP.Address <= RangeEnd);
510   }
511 }
512 
513 StringRef ProfileGeneratorBase::getCalleeNameForOffset(uint64_t TargetOffset) {
514   // Get the function range by branch target if it's a call branch.
515   auto *FRange = Binary->findFuncRangeForStartOffset(TargetOffset);
516 
517   // We won't accumulate sample count for a range whose start is not the real
518   // function entry such as outlined function or inner labels.
519   if (!FRange || !FRange->IsFuncEntry)
520     return StringRef();
521 
522   return FunctionSamples::getCanonicalFnName(FRange->getFuncName());
523 }
524 
525 void ProfileGenerator::populateBoundarySamplesForAllFunctions(
526     const BranchSample &BranchCounters) {
527   for (const auto &Entry : BranchCounters) {
528     uint64_t SourceOffset = Entry.first.first;
529     uint64_t TargetOffset = Entry.first.second;
530     uint64_t Count = Entry.second;
531     assert(Count != 0 && "Unexpected zero weight branch");
532 
533     StringRef CalleeName = getCalleeNameForOffset(TargetOffset);
534     if (CalleeName.size() == 0)
535       continue;
536     // Record called target sample and its count.
537     const SampleContextFrameVector &FrameVec =
538         Binary->getFrameLocationStack(SourceOffset);
539     if (!FrameVec.empty()) {
540       FunctionSamples &FunctionProfile =
541           getLeafProfileAndAddTotalSamples(FrameVec, 0);
542       FunctionProfile.addCalledTargetSamples(
543           FrameVec.back().Location.LineOffset,
544           getBaseDiscriminator(FrameVec.back().Location.Discriminator),
545           CalleeName, Count);
546     }
547     // Add head samples for callee.
548     FunctionSamples &CalleeProfile = getTopLevelFunctionProfile(CalleeName);
549     CalleeProfile.addHeadSamples(Count);
550   }
551 }
552 
553 void ProfileGeneratorBase::calculateAndShowDensity(
554     const SampleProfileMap &Profiles) {
555   double Density = calculateDensity(Profiles, HotCountThreshold);
556   showDensitySuggestion(Density);
557 }
558 
559 FunctionSamples &CSProfileGenerator::getFunctionProfileForContext(
560     const SampleContextFrameVector &Context, bool WasLeafInlined) {
561   auto I = ProfileMap.find(SampleContext(Context));
562   if (I == ProfileMap.end()) {
563     // Save the new context for future references.
564     SampleContextFrames NewContext = *Contexts.insert(Context).first;
565     SampleContext FContext(NewContext, RawContext);
566     auto Ret = ProfileMap.emplace(FContext, FunctionSamples());
567     if (WasLeafInlined)
568       FContext.setAttribute(ContextWasInlined);
569     FunctionSamples &FProfile = Ret.first->second;
570     FProfile.setContext(FContext);
571     return Ret.first->second;
572   }
573   return I->second;
574 }
575 
576 void CSProfileGenerator::generateProfile() {
577   FunctionSamples::ProfileIsCSFlat = true;
578 
579   if (Binary->getTrackFuncContextSize())
580     computeSizeForProfiledFunctions();
581 
582   if (Binary->usePseudoProbes()) {
583     // Enable pseudo probe functionalities in SampleProf
584     FunctionSamples::ProfileIsProbeBased = true;
585     generateProbeBasedProfile();
586   } else {
587     generateLineNumBasedProfile();
588   }
589   postProcessProfiles();
590 }
591 
592 void CSProfileGenerator::computeSizeForProfiledFunctions() {
593   std::unordered_set<const BinaryFunction *> ProfiledFunctions;
594 
595   // Go through all the ranges in the CS counters, use the start of the range to
596   // look up the function it belongs and record the function.
597   for (const auto &CI : SampleCounters) {
598     for (const auto &Item : CI.second.RangeCounter) {
599       // FIXME: Filter the bogus crossing function range.
600       uint64_t StartOffset = Item.first.first;
601       if (FuncRange *FRange = Binary->findFuncRangeForOffset(StartOffset))
602         ProfiledFunctions.insert(FRange->Func);
603     }
604   }
605 
606   for (auto *Func : ProfiledFunctions)
607     Binary->computeInlinedContextSizeForFunc(Func);
608 
609   // Flush the symbolizer to save memory.
610   Binary->flushSymbolizer();
611 }
612 
613 void CSProfileGenerator::generateLineNumBasedProfile() {
614   for (const auto &CI : SampleCounters) {
615     const auto *CtxKey = cast<StringBasedCtxKey>(CI.first.getPtr());
616 
617     // Get or create function profile for the range
618     FunctionSamples &FunctionProfile =
619         getFunctionProfileForContext(CtxKey->Context, CtxKey->WasLeafInlined);
620 
621     // Fill in function body samples
622     populateBodySamplesForFunction(FunctionProfile, CI.second.RangeCounter);
623     // Fill in boundary sample counts as well as call site samples for calls
624     populateBoundarySamplesForFunction(CtxKey->Context, FunctionProfile,
625                                        CI.second.BranchCounter);
626   }
627   // Fill in call site value sample for inlined calls and also use context to
628   // infer missing samples. Since we don't have call count for inlined
629   // functions, we estimate it from inlinee's profile using the entry of the
630   // body sample.
631   populateInferredFunctionSamples();
632 
633   updateTotalSamples();
634 }
635 
636 void CSProfileGenerator::populateBodySamplesForFunction(
637     FunctionSamples &FunctionProfile, const RangeSample &RangeCounter) {
638   // Compute disjoint ranges first, so we can use MAX
639   // for calculating count for each location.
640   RangeSample Ranges;
641   findDisjointRanges(Ranges, RangeCounter);
642   for (const auto &Range : Ranges) {
643     uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
644     uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
645     uint64_t Count = Range.second;
646     // Disjoint ranges have introduce zero-filled gap that
647     // doesn't belong to current context, filter them out.
648     if (Count == 0)
649       continue;
650 
651     InstructionPointer IP(Binary, RangeBegin, true);
652     // Disjoint ranges may have range in the middle of two instr,
653     // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range
654     // can be Addr1+1 to Addr2-1. We should ignore such range.
655     if (IP.Address > RangeEnd)
656       continue;
657 
658     do {
659       uint64_t Offset = Binary->virtualAddrToOffset(IP.Address);
660       auto LeafLoc = Binary->getInlineLeafFrameLoc(Offset);
661       if (LeafLoc.hasValue()) {
662         // Recording body sample for this specific context
663         updateBodySamplesforFunctionProfile(FunctionProfile, *LeafLoc, Count);
664         FunctionProfile.addTotalSamples(Count);
665       }
666     } while (IP.advance() && IP.Address <= RangeEnd);
667   }
668 }
669 
670 void CSProfileGenerator::populateBoundarySamplesForFunction(
671     SampleContextFrames ContextId, FunctionSamples &FunctionProfile,
672     const BranchSample &BranchCounters) {
673 
674   for (const auto &Entry : BranchCounters) {
675     uint64_t SourceOffset = Entry.first.first;
676     uint64_t TargetOffset = Entry.first.second;
677     uint64_t Count = Entry.second;
678     assert(Count != 0 && "Unexpected zero weight branch");
679 
680     StringRef CalleeName = getCalleeNameForOffset(TargetOffset);
681     if (CalleeName.size() == 0)
682       continue;
683 
684     // Record called target sample and its count
685     auto LeafLoc = Binary->getInlineLeafFrameLoc(SourceOffset);
686     if (!LeafLoc.hasValue())
687       continue;
688     FunctionProfile.addCalledTargetSamples(
689         LeafLoc->Location.LineOffset,
690         getBaseDiscriminator(LeafLoc->Location.Discriminator), CalleeName,
691         Count);
692 
693     // Record head sample for called target(callee)
694     SampleContextFrameVector CalleeCtx(ContextId.begin(), ContextId.end());
695     assert(CalleeCtx.back().FuncName == LeafLoc->FuncName &&
696            "Leaf function name doesn't match");
697     CalleeCtx.back() = *LeafLoc;
698     CalleeCtx.emplace_back(CalleeName, LineLocation(0, 0));
699     FunctionSamples &CalleeProfile = getFunctionProfileForContext(CalleeCtx);
700     CalleeProfile.addHeadSamples(Count);
701   }
702 }
703 
704 static SampleContextFrame
705 getCallerContext(SampleContextFrames CalleeContext,
706                  SampleContextFrameVector &CallerContext) {
707   assert(CalleeContext.size() > 1 && "Unexpected empty context");
708   CalleeContext = CalleeContext.drop_back();
709   CallerContext.assign(CalleeContext.begin(), CalleeContext.end());
710   SampleContextFrame CallerFrame = CallerContext.back();
711   CallerContext.back().Location = LineLocation(0, 0);
712   return CallerFrame;
713 }
714 
715 void CSProfileGenerator::populateInferredFunctionSamples() {
716   for (const auto &Item : ProfileMap) {
717     const auto &CalleeContext = Item.first;
718     const FunctionSamples &CalleeProfile = Item.second;
719 
720     // If we already have head sample counts, we must have value profile
721     // for call sites added already. Skip to avoid double counting.
722     if (CalleeProfile.getHeadSamples())
723       continue;
724     // If we don't have context, nothing to do for caller's call site.
725     // This could happen for entry point function.
726     if (CalleeContext.isBaseContext())
727       continue;
728 
729     // Infer Caller's frame loc and context ID through string splitting
730     SampleContextFrameVector CallerContextId;
731     SampleContextFrame &&CallerLeafFrameLoc =
732         getCallerContext(CalleeContext.getContextFrames(), CallerContextId);
733     SampleContextFrames CallerContext(CallerContextId);
734 
735     // It's possible that we haven't seen any sample directly in the caller,
736     // in which case CallerProfile will not exist. But we can't modify
737     // ProfileMap while iterating it.
738     // TODO: created function profile for those callers too
739     if (ProfileMap.find(CallerContext) == ProfileMap.end())
740       continue;
741     FunctionSamples &CallerProfile = ProfileMap[CallerContext];
742 
743     // Since we don't have call count for inlined functions, we
744     // estimate it from inlinee's profile using entry body sample.
745     uint64_t EstimatedCallCount = CalleeProfile.getEntrySamples();
746     // If we don't have samples with location, use 1 to indicate live.
747     if (!EstimatedCallCount && !CalleeProfile.getBodySamples().size())
748       EstimatedCallCount = 1;
749     CallerProfile.addCalledTargetSamples(
750         CallerLeafFrameLoc.Location.LineOffset,
751         CallerLeafFrameLoc.Location.Discriminator,
752         CalleeProfile.getContext().getName(), EstimatedCallCount);
753     CallerProfile.addBodySamples(CallerLeafFrameLoc.Location.LineOffset,
754                                  CallerLeafFrameLoc.Location.Discriminator,
755                                  EstimatedCallCount);
756     CallerProfile.addTotalSamples(EstimatedCallCount);
757   }
758 }
759 
760 void CSProfileGenerator::postProcessProfiles() {
761   // Compute hot/cold threshold based on profile. This will be used for cold
762   // context profile merging/trimming.
763   computeSummaryAndThreshold();
764 
765   // Run global pre-inliner to adjust/merge context profile based on estimated
766   // inline decisions.
767   if (EnableCSPreInliner) {
768     CSPreInliner(ProfileMap, *Binary, HotCountThreshold, ColdCountThreshold)
769         .run();
770     // Turn off the profile merger by default unless it is explicitly enabled.
771     if (!CSProfMergeColdContext.getNumOccurrences())
772       CSProfMergeColdContext = false;
773   }
774 
775   // Trim and merge cold context profile using cold threshold above.
776   if (TrimColdProfile || CSProfMergeColdContext) {
777     SampleContextTrimmer(ProfileMap)
778         .trimAndMergeColdContextProfiles(
779             HotCountThreshold, TrimColdProfile, CSProfMergeColdContext,
780             CSProfMaxColdContextDepth, EnableCSPreInliner);
781   }
782 
783   // Merge function samples of CS profile to calculate profile density.
784   sampleprof::SampleProfileMap ContextLessProfiles;
785   for (const auto &I : ProfileMap) {
786     ContextLessProfiles[I.second.getName()].merge(I.second);
787   }
788 
789   calculateAndShowDensity(ContextLessProfiles);
790   if (GenCSNestedProfile) {
791     CSProfileConverter CSConverter(ProfileMap);
792     CSConverter.convertProfiles();
793     FunctionSamples::ProfileIsCSFlat = false;
794     FunctionSamples::ProfileIsCSNested = EnableCSPreInliner;
795   }
796 }
797 
798 void ProfileGeneratorBase::computeSummaryAndThreshold() {
799   SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
800   auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
801   HotCountThreshold = ProfileSummaryBuilder::getHotCountThreshold(
802       (Summary->getDetailedSummary()));
803   ColdCountThreshold = ProfileSummaryBuilder::getColdCountThreshold(
804       (Summary->getDetailedSummary()));
805 }
806 
807 // Helper function to extract context prefix string stack
808 // Extract context stack for reusing, leaf context stack will
809 // be added compressed while looking up function profile
810 static void extractPrefixContextStack(
811     SampleContextFrameVector &ContextStack,
812     const SmallVectorImpl<const MCDecodedPseudoProbe *> &Probes,
813     ProfiledBinary *Binary) {
814   for (const auto *P : Probes) {
815     Binary->getInlineContextForProbe(P, ContextStack, true);
816   }
817 }
818 
819 void CSProfileGenerator::generateProbeBasedProfile() {
820   for (const auto &CI : SampleCounters) {
821     const auto *CtxKey = cast<ProbeBasedCtxKey>(CI.first.getPtr());
822     SampleContextFrameVector ContextStack;
823     extractPrefixContextStack(ContextStack, CtxKey->Probes, Binary);
824     // Fill in function body samples from probes, also infer caller's samples
825     // from callee's probe
826     populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStack);
827     // Fill in boundary samples for a call probe
828     populateBoundarySamplesWithProbes(CI.second.BranchCounter, ContextStack);
829   }
830 }
831 
832 void CSProfileGenerator::extractProbesFromRange(const RangeSample &RangeCounter,
833                                                 ProbeCounterMap &ProbeCounter) {
834   RangeSample Ranges;
835   findDisjointRanges(Ranges, RangeCounter);
836   for (const auto &Range : Ranges) {
837     uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
838     uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
839     uint64_t Count = Range.second;
840     // Disjoint ranges have introduce zero-filled gap that
841     // doesn't belong to current context, filter them out.
842     if (Count == 0)
843       continue;
844 
845     InstructionPointer IP(Binary, RangeBegin, true);
846     // Disjoint ranges may have range in the middle of two instr,
847     // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range
848     // can be Addr1+1 to Addr2-1. We should ignore such range.
849     if (IP.Address > RangeEnd)
850       continue;
851 
852     do {
853       const AddressProbesMap &Address2ProbesMap =
854           Binary->getAddress2ProbesMap();
855       auto It = Address2ProbesMap.find(IP.Address);
856       if (It != Address2ProbesMap.end()) {
857         for (const auto &Probe : It->second) {
858           if (!Probe.isBlock())
859             continue;
860           ProbeCounter[&Probe] += Count;
861         }
862       }
863     } while (IP.advance() && IP.Address <= RangeEnd);
864   }
865 }
866 
867 void CSProfileGenerator::populateBodySamplesWithProbes(
868     const RangeSample &RangeCounter, SampleContextFrames ContextStack) {
869   ProbeCounterMap ProbeCounter;
870   // Extract the top frame probes by looking up each address among the range in
871   // the Address2ProbeMap
872   extractProbesFromRange(RangeCounter, ProbeCounter);
873   std::unordered_map<MCDecodedPseudoProbeInlineTree *,
874                      std::unordered_set<FunctionSamples *>>
875       FrameSamples;
876   for (const auto &PI : ProbeCounter) {
877     const MCDecodedPseudoProbe *Probe = PI.first;
878     uint64_t Count = PI.second;
879     FunctionSamples &FunctionProfile =
880         getFunctionProfileForLeafProbe(ContextStack, Probe);
881     // Record the current frame and FunctionProfile whenever samples are
882     // collected for non-danglie probes. This is for reporting all of the
883     // zero count probes of the frame later.
884     FrameSamples[Probe->getInlineTreeNode()].insert(&FunctionProfile);
885     FunctionProfile.addBodySamplesForProbe(Probe->getIndex(), Count);
886     FunctionProfile.addTotalSamples(Count);
887     if (Probe->isEntry()) {
888       FunctionProfile.addHeadSamples(Count);
889       // Look up for the caller's function profile
890       const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe);
891       SampleContextFrames CalleeContextId =
892           FunctionProfile.getContext().getContextFrames();
893       if (InlinerDesc != nullptr && CalleeContextId.size() > 1) {
894         // Since the context id will be compressed, we have to use callee's
895         // context id to infer caller's context id to ensure they share the
896         // same context prefix.
897         SampleContextFrameVector CallerContextId;
898         SampleContextFrame &&CallerLeafFrameLoc =
899             getCallerContext(CalleeContextId, CallerContextId);
900         uint64_t CallerIndex = CallerLeafFrameLoc.Location.LineOffset;
901         assert(CallerIndex &&
902                "Inferred caller's location index shouldn't be zero!");
903         FunctionSamples &CallerProfile =
904             getFunctionProfileForContext(CallerContextId);
905         CallerProfile.setFunctionHash(InlinerDesc->FuncHash);
906         CallerProfile.addBodySamples(CallerIndex, 0, Count);
907         CallerProfile.addTotalSamples(Count);
908         CallerProfile.addCalledTargetSamples(
909             CallerIndex, 0, FunctionProfile.getContext().getName(), Count);
910       }
911     }
912   }
913 
914   // Assign zero count for remaining probes without sample hits to
915   // differentiate from probes optimized away, of which the counts are unknown
916   // and will be inferred by the compiler.
917   for (auto &I : FrameSamples) {
918     for (auto *FunctionProfile : I.second) {
919       for (auto *Probe : I.first->getProbes()) {
920         FunctionProfile->addBodySamplesForProbe(Probe->getIndex(), 0);
921       }
922     }
923   }
924 }
925 
926 void CSProfileGenerator::populateBoundarySamplesWithProbes(
927     const BranchSample &BranchCounter, SampleContextFrames ContextStack) {
928   for (const auto &BI : BranchCounter) {
929     uint64_t SourceOffset = BI.first.first;
930     uint64_t TargetOffset = BI.first.second;
931     uint64_t Count = BI.second;
932     uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset);
933     const MCDecodedPseudoProbe *CallProbe =
934         Binary->getCallProbeForAddr(SourceAddress);
935     if (CallProbe == nullptr)
936       continue;
937     FunctionSamples &FunctionProfile =
938         getFunctionProfileForLeafProbe(ContextStack, CallProbe);
939     FunctionProfile.addBodySamples(CallProbe->getIndex(), 0, Count);
940     FunctionProfile.addTotalSamples(Count);
941     StringRef CalleeName = getCalleeNameForOffset(TargetOffset);
942     if (CalleeName.size() == 0)
943       continue;
944     FunctionProfile.addCalledTargetSamples(CallProbe->getIndex(), 0, CalleeName,
945                                            Count);
946   }
947 }
948 
949 FunctionSamples &CSProfileGenerator::getFunctionProfileForLeafProbe(
950     SampleContextFrames ContextStack, const MCDecodedPseudoProbe *LeafProbe) {
951 
952   // Explicitly copy the context for appending the leaf context
953   SampleContextFrameVector NewContextStack(ContextStack.begin(),
954                                            ContextStack.end());
955   Binary->getInlineContextForProbe(LeafProbe, NewContextStack, true);
956   // For leaf inlined context with the top frame, we should strip off the top
957   // frame's probe id, like:
958   // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar"
959   auto LeafFrame = NewContextStack.back();
960   LeafFrame.Location = LineLocation(0, 0);
961   NewContextStack.pop_back();
962   // Compress the context string except for the leaf frame
963   CSProfileGenerator::compressRecursionContext(NewContextStack);
964   CSProfileGenerator::trimContext(NewContextStack);
965   NewContextStack.push_back(LeafFrame);
966 
967   const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->getGuid());
968   bool WasLeafInlined = LeafProbe->getInlineTreeNode()->hasInlineSite();
969   FunctionSamples &FunctionProile =
970       getFunctionProfileForContext(NewContextStack, WasLeafInlined);
971   FunctionProile.setFunctionHash(FuncDesc->FuncHash);
972   return FunctionProile;
973 }
974 
975 } // end namespace sampleprof
976 } // end namespace llvm
977