aboutsummaryrefslogtreecommitdiff
blob: 9e1ba9bca1b8b6f8544fa3c54252f2fb324988d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
//===-- ProfileGenerator.cpp - Profile Generator  ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "ProfileGenerator.h"
#include "llvm/ProfileData/ProfileCommon.h"

static cl::opt<std::string> OutputFilename("output", cl::value_desc("output"),
                                           cl::Required,
                                           cl::desc("Output profile file"));
static cl::alias OutputA("o", cl::desc("Alias for --output"),
                         cl::aliasopt(OutputFilename));

static cl::opt<SampleProfileFormat> OutputFormat(
    "format", cl::desc("Format of output profile"), cl::init(SPF_Text),
    cl::values(
        clEnumValN(SPF_Binary, "binary", "Binary encoding (default)"),
        clEnumValN(SPF_Compact_Binary, "compbinary", "Compact binary encoding"),
        clEnumValN(SPF_Ext_Binary, "extbinary", "Extensible binary encoding"),
        clEnumValN(SPF_Text, "text", "Text encoding"),
        clEnumValN(SPF_GCC, "gcc",
                   "GCC encoding (only meaningful for -sample)")));

static cl::opt<int32_t, true> RecursionCompression(
    "compress-recursion",
    cl::desc("Compressing recursion by deduplicating adjacent frame "
             "sequences up to the specified size. -1 means no size limit."),
    cl::Hidden,
    cl::location(llvm::sampleprof::CSProfileGenerator::MaxCompressionSize));

static cl::opt<uint64_t> CSProfColdThreshold(
    "csprof-cold-thres", cl::init(100), cl::ZeroOrMore,
    cl::desc("Specify the total samples threshold for a context profile to "
             "be considered cold, any cold profiles will be merged into "
             "context-less base profiles"));

static cl::opt<bool> CSProfMergeColdContext(
    "csprof-merge-cold-context", cl::init(true), cl::ZeroOrMore,
    cl::desc("This works together with --csprof-cold-thres. If the total count "
             "of context profile is smaller than the threshold, it will be "
             "merged into context-less base profile."));

static cl::opt<bool> CSProfTrimColdContext(
    "csprof-trim-cold-context", cl::init(true), cl::ZeroOrMore,
    cl::desc("This works together with --csprof-cold-thres. If the total count "
             "of the profile after all merge is done is still smaller than "
             "threshold, it will be trimmed."));

using namespace llvm;
using namespace sampleprof;

namespace llvm {
namespace sampleprof {

// Initialize the MaxCompressionSize to -1 which means no size limit
int32_t CSProfileGenerator::MaxCompressionSize = -1;

static bool
usePseudoProbes(const BinarySampleCounterMap &BinarySampleCounters) {
  return BinarySampleCounters.size() &&
         BinarySampleCounters.begin()->first->usePseudoProbes();
}

std::unique_ptr<ProfileGenerator>
ProfileGenerator::create(const BinarySampleCounterMap &BinarySampleCounters,
                         enum PerfScriptType SampleType) {
  std::unique_ptr<ProfileGenerator> ProfileGenerator;
  if (SampleType == PERF_LBR_STACK) {
    if (usePseudoProbes(BinarySampleCounters)) {
      ProfileGenerator.reset(
          new PseudoProbeCSProfileGenerator(BinarySampleCounters));
    } else {
      ProfileGenerator.reset(new CSProfileGenerator(BinarySampleCounters));
    }
  } else {
    // TODO:
    llvm_unreachable("Unsupported perfscript!");
  }

  return ProfileGenerator;
}

void ProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer,
                             StringMap<FunctionSamples> &ProfileMap) {
  if (std::error_code EC = Writer->write(ProfileMap))
    exitWithError(std::move(EC));
}

void ProfileGenerator::write() {
  auto WriterOrErr = SampleProfileWriter::create(OutputFilename, OutputFormat);
  if (std::error_code EC = WriterOrErr.getError())
    exitWithError(EC, OutputFilename);
  write(std::move(WriterOrErr.get()), ProfileMap);
}

void ProfileGenerator::findDisjointRanges(RangeSample &DisjointRanges,
                                          const RangeSample &Ranges) {

  /*
  Regions may overlap with each other. Using the boundary info, find all
  disjoint ranges and their sample count. BoundaryPoint contains the count
  multiple samples begin/end at this points.

  |<--100-->|           Sample1
  |<------200------>|   Sample2
  A         B       C

  In the example above,
  Sample1 begins at A, ends at B, its value is 100.
  Sample2 beings at A, ends at C, its value is 200.
  For A, BeginCount is the sum of sample begins at A, which is 300 and no
  samples ends at A, so EndCount is 0.
  Then boundary points A, B, and C with begin/end counts are:
  A: (300, 0)
  B: (0, 100)
  C: (0, 200)
  */
  struct BoundaryPoint {
    // Sum of sample counts beginning at this point
    uint64_t BeginCount;
    // Sum of sample counts ending at this point
    uint64_t EndCount;

    BoundaryPoint() : BeginCount(0), EndCount(0){};

    void addBeginCount(uint64_t Count) { BeginCount += Count; }

    void addEndCount(uint64_t Count) { EndCount += Count; }
  };

  /*
  For the above example. With boundary points, follwing logic finds two
  disjoint region of

  [A,B]:   300
  [B+1,C]: 200

  If there is a boundary point that both begin and end, the point itself
  becomes a separate disjoint region. For example, if we have original
  ranges of

  |<--- 100 --->|
                |<--- 200 --->|
  A             B             C

  there are three boundary points with their begin/end counts of

  A: (100, 0)
  B: (200, 100)
  C: (0, 200)

  the disjoint ranges would be

  [A, B-1]: 100
  [B, B]:   300
  [B+1, C]: 200.
  */
  std::map<uint64_t, BoundaryPoint> Boundaries;

  for (auto Item : Ranges) {
    uint64_t Begin = Item.first.first;
    uint64_t End = Item.first.second;
    uint64_t Count = Item.second;
    if (Boundaries.find(Begin) == Boundaries.end())
      Boundaries[Begin] = BoundaryPoint();
    Boundaries[Begin].addBeginCount(Count);

    if (Boundaries.find(End) == Boundaries.end())
      Boundaries[End] = BoundaryPoint();
    Boundaries[End].addEndCount(Count);
  }

  uint64_t BeginAddress = 0;
  int Count = 0;
  for (auto Item : Boundaries) {
    uint64_t Address = Item.first;
    BoundaryPoint &Point = Item.second;
    if (Point.BeginCount) {
      if (BeginAddress)
        DisjointRanges[{BeginAddress, Address - 1}] = Count;
      Count += Point.BeginCount;
      BeginAddress = Address;
    }
    if (Point.EndCount) {
      assert(BeginAddress && "First boundary point cannot be 'end' point");
      DisjointRanges[{BeginAddress, Address}] = Count;
      Count -= Point.EndCount;
      BeginAddress = Address + 1;
    }
  }
}

FunctionSamples &
CSProfileGenerator::getFunctionProfileForContext(StringRef ContextStr,
                                                 bool WasLeafInlined) {
  auto Ret = ProfileMap.try_emplace(ContextStr, FunctionSamples());
  if (Ret.second) {
    // Make a copy of the underlying context string in string table
    // before StringRef wrapper is used for context.
    auto It = ContextStrings.insert(ContextStr.str());
    SampleContext FContext(*It.first, RawContext);
    if (WasLeafInlined)
      FContext.setAttribute(ContextWasInlined);
    FunctionSamples &FProfile = Ret.first->second;
    FProfile.setContext(FContext);
    FProfile.setName(FContext.getNameWithoutContext());
  }
  return Ret.first->second;
}

void CSProfileGenerator::generateProfile() {
  FunctionSamples::ProfileIsCS = true;
  for (const auto &BI : BinarySampleCounters) {
    ProfiledBinary *Binary = BI.first;
    for (const auto &CI : BI.second) {
      const StringBasedCtxKey *CtxKey =
          dyn_cast<StringBasedCtxKey>(CI.first.getPtr());
      StringRef ContextId(CtxKey->Context);
      // Get or create function profile for the range
      FunctionSamples &FunctionProfile =
          getFunctionProfileForContext(ContextId, CtxKey->WasLeafInlined);

      // Fill in function body samples
      populateFunctionBodySamples(FunctionProfile, CI.second.RangeCounter,
                                  Binary);
      // Fill in boundary sample counts as well as call site samples for calls
      populateFunctionBoundarySamples(ContextId, FunctionProfile,
                                      CI.second.BranchCounter, Binary);
    }
  }
  // Fill in call site value sample for inlined calls and also use context to
  // infer missing samples. Since we don't have call count for inlined
  // functions, we estimate it from inlinee's profile using the entry of the
  // body sample.
  populateInferredFunctionSamples();

  postProcessProfiles();
}

void CSProfileGenerator::updateBodySamplesforFunctionProfile(
    FunctionSamples &FunctionProfile, const FrameLocation &LeafLoc,
    uint64_t Count) {
  // Filter out invalid negative(int type) lineOffset
  if (LeafLoc.second.LineOffset & 0x80000000)
    return;
  // Use the maximum count of samples with same line location
  ErrorOr<uint64_t> R = FunctionProfile.findSamplesAt(
      LeafLoc.second.LineOffset, LeafLoc.second.Discriminator);
  uint64_t PreviousCount = R ? R.get() : 0;
  if (PreviousCount < Count) {
    FunctionProfile.addBodySamples(LeafLoc.second.LineOffset,
                                   LeafLoc.second.Discriminator,
                                   Count - PreviousCount);
  }
}

void CSProfileGenerator::populateFunctionBodySamples(
    FunctionSamples &FunctionProfile, const RangeSample &RangeCounter,
    ProfiledBinary *Binary) {
  // Compute disjoint ranges first, so we can use MAX
  // for calculating count for each location.
  RangeSample Ranges;
  findDisjointRanges(Ranges, RangeCounter);
  for (auto Range : Ranges) {
    uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
    uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
    uint64_t Count = Range.second;
    // Disjoint ranges have introduce zero-filled gap that
    // doesn't belong to current context, filter them out.
    if (Count == 0)
      continue;

    InstructionPointer IP(Binary, RangeBegin, true);

    // Disjoint ranges may have range in the middle of two instr,
    // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range
    // can be Addr1+1 to Addr2-1. We should ignore such range.
    if (IP.Address > RangeEnd)
      continue;

    while (IP.Address <= RangeEnd) {
      uint64_t Offset = Binary->virtualAddrToOffset(IP.Address);
      auto LeafLoc = Binary->getInlineLeafFrameLoc(Offset);
      if (LeafLoc.hasValue()) {
        // Recording body sample for this specific context
        updateBodySamplesforFunctionProfile(FunctionProfile, *LeafLoc, Count);
      }
      // Accumulate total sample count even it's a line with invalid debug info
      FunctionProfile.addTotalSamples(Count);
      // Move to next IP within the range
      IP.advance();
    }
  }
}

void CSProfileGenerator::populateFunctionBoundarySamples(
    StringRef ContextId, FunctionSamples &FunctionProfile,
    const BranchSample &BranchCounters, ProfiledBinary *Binary) {

  for (auto Entry : BranchCounters) {
    uint64_t SourceOffset = Entry.first.first;
    uint64_t TargetOffset = Entry.first.second;
    uint64_t Count = Entry.second;
    // Get the callee name by branch target if it's a call branch
    StringRef CalleeName = FunctionSamples::getCanonicalFnName(
        Binary->getFuncFromStartOffset(TargetOffset));
    if (CalleeName.size() == 0)
      continue;

    // Record called target sample and its count
    auto LeafLoc = Binary->getInlineLeafFrameLoc(SourceOffset);
    if (!LeafLoc.hasValue())
      continue;
    FunctionProfile.addCalledTargetSamples(LeafLoc->second.LineOffset,
                                           LeafLoc->second.Discriminator,
                                           CalleeName, Count);

    // Record head sample for called target(callee)
    std::ostringstream OCalleeCtxStr;
    if (ContextId.find(" @ ") != StringRef::npos) {
      OCalleeCtxStr << ContextId.rsplit(" @ ").first.str();
      OCalleeCtxStr << " @ ";
    }
    OCalleeCtxStr << getCallSite(*LeafLoc) << " @ " << CalleeName.str();

    FunctionSamples &CalleeProfile =
        getFunctionProfileForContext(OCalleeCtxStr.str());
    assert(Count != 0 && "Unexpected zero weight branch");
    CalleeProfile.addHeadSamples(Count);
  }
}

static FrameLocation getCallerContext(StringRef CalleeContext,
                                      StringRef &CallerNameWithContext) {
  StringRef CallerContext = CalleeContext.rsplit(" @ ").first;
  CallerNameWithContext = CallerContext.rsplit(':').first;
  auto ContextSplit = CallerContext.rsplit(" @ ");
  StringRef CallerFrameStr = ContextSplit.second.size() == 0
                                 ? ContextSplit.first
                                 : ContextSplit.second;
  FrameLocation LeafFrameLoc = {"", {0, 0}};
  StringRef Funcname;
  SampleContext::decodeContextString(CallerFrameStr, Funcname,
                                     LeafFrameLoc.second);
  LeafFrameLoc.first = Funcname.str();
  return LeafFrameLoc;
}

void CSProfileGenerator::populateInferredFunctionSamples() {
  for (const auto &Item : ProfileMap) {
    const StringRef CalleeContext = Item.first();
    const FunctionSamples &CalleeProfile = Item.second;

    // If we already have head sample counts, we must have value profile
    // for call sites added already. Skip to avoid double counting.
    if (CalleeProfile.getHeadSamples())
      continue;
    // If we don't have context, nothing to do for caller's call site.
    // This could happen for entry point function.
    if (CalleeContext.find(" @ ") == StringRef::npos)
      continue;

    // Infer Caller's frame loc and context ID through string splitting
    StringRef CallerContextId;
    FrameLocation &&CallerLeafFrameLoc =
        getCallerContext(CalleeContext, CallerContextId);

    // It's possible that we haven't seen any sample directly in the caller,
    // in which case CallerProfile will not exist. But we can't modify
    // ProfileMap while iterating it.
    // TODO: created function profile for those callers too
    if (ProfileMap.find(CallerContextId) == ProfileMap.end())
      continue;
    FunctionSamples &CallerProfile = ProfileMap[CallerContextId];

    // Since we don't have call count for inlined functions, we
    // estimate it from inlinee's profile using entry body sample.
    uint64_t EstimatedCallCount = CalleeProfile.getEntrySamples();
    // If we don't have samples with location, use 1 to indicate live.
    if (!EstimatedCallCount && !CalleeProfile.getBodySamples().size())
      EstimatedCallCount = 1;
    CallerProfile.addCalledTargetSamples(
        CallerLeafFrameLoc.second.LineOffset,
        CallerLeafFrameLoc.second.Discriminator,
        CalleeProfile.getContext().getNameWithoutContext(), EstimatedCallCount);
    CallerProfile.addBodySamples(CallerLeafFrameLoc.second.LineOffset,
                                 CallerLeafFrameLoc.second.Discriminator,
                                 EstimatedCallCount);
    CallerProfile.addTotalSamples(EstimatedCallCount);
  }
}

void CSProfileGenerator::postProcessProfiles() {
  // Compute hot/cold threshold based on profile. This will be used for cold
  // context profile merging/trimming.
  computeSummaryAndThreshold();

  // Run global pre-inliner to adjust/merge context profile based on estimated
  // inline decisions.
  CSPreInliner(ProfileMap, PSI->getHotCountThreshold(),
               PSI->getColdCountThreshold())
      .run();

  // Trim and merge cold context profile using cold threshold above;
  SampleContextTrimmer(ProfileMap)
      .trimAndMergeColdContextProfiles(
          CSProfColdThreshold, CSProfTrimColdContext, CSProfMergeColdContext);
}

void CSProfileGenerator::computeSummaryAndThreshold() {
  SampleProfileSummaryBuilder Builder(ProfileSummaryBuilder::DefaultCutoffs);
  auto Summary = Builder.computeSummaryForProfiles(ProfileMap);
  PSI.reset(new ProfileSummaryInfo(std::move(Summary)));

  // Use threshold calculated from profile summary unless specified.
  if (!CSProfColdThreshold.getNumOccurrences()) {
    CSProfColdThreshold = PSI->getColdCountThreshold();
  }
}

void CSProfileGenerator::write(std::unique_ptr<SampleProfileWriter> Writer,
                               StringMap<FunctionSamples> &ProfileMap) {
  if (std::error_code EC = Writer->write(ProfileMap))
    exitWithError(std::move(EC));
}

// Helper function to extract context prefix string stack
// Extract context stack for reusing, leaf context stack will
// be added compressed while looking up function profile
static void
extractPrefixContextStack(SmallVectorImpl<std::string> &ContextStrStack,
                          const SmallVectorImpl<const PseudoProbe *> &Probes,
                          ProfiledBinary *Binary) {
  for (const auto *P : Probes) {
    Binary->getInlineContextForProbe(P, ContextStrStack, true);
  }
}

void PseudoProbeCSProfileGenerator::generateProfile() {
  // Enable pseudo probe functionalities in SampleProf
  FunctionSamples::ProfileIsProbeBased = true;
  FunctionSamples::ProfileIsCS = true;
  for (const auto &BI : BinarySampleCounters) {
    ProfiledBinary *Binary = BI.first;
    for (const auto &CI : BI.second) {
      const ProbeBasedCtxKey *CtxKey =
          dyn_cast<ProbeBasedCtxKey>(CI.first.getPtr());
      SmallVector<std::string, 16> ContextStrStack;
      extractPrefixContextStack(ContextStrStack, CtxKey->Probes, Binary);
      // Fill in function body samples from probes, also infer caller's samples
      // from callee's probe
      populateBodySamplesWithProbes(CI.second.RangeCounter, ContextStrStack,
                                    Binary);
      // Fill in boundary samples for a call probe
      populateBoundarySamplesWithProbes(CI.second.BranchCounter,
                                        ContextStrStack, Binary);
    }
  }

  postProcessProfiles();
}

void PseudoProbeCSProfileGenerator::extractProbesFromRange(
    const RangeSample &RangeCounter, ProbeCounterMap &ProbeCounter,
    ProfiledBinary *Binary) {
  RangeSample Ranges;
  findDisjointRanges(Ranges, RangeCounter);
  for (const auto &Range : Ranges) {
    uint64_t RangeBegin = Binary->offsetToVirtualAddr(Range.first.first);
    uint64_t RangeEnd = Binary->offsetToVirtualAddr(Range.first.second);
    uint64_t Count = Range.second;
    // Disjoint ranges have introduce zero-filled gap that
    // doesn't belong to current context, filter them out.
    if (Count == 0)
      continue;

    InstructionPointer IP(Binary, RangeBegin, true);

    // Disjoint ranges may have range in the middle of two instr,
    // e.g. If Instr1 at Addr1, and Instr2 at Addr2, disjoint range
    // can be Addr1+1 to Addr2-1. We should ignore such range.
    if (IP.Address > RangeEnd)
      continue;

    while (IP.Address <= RangeEnd) {
      const AddressProbesMap &Address2ProbesMap =
          Binary->getAddress2ProbesMap();
      auto It = Address2ProbesMap.find(IP.Address);
      if (It != Address2ProbesMap.end()) {
        for (const auto &Probe : It->second) {
          if (!Probe.isBlock())
            continue;
          ProbeCounter[&Probe] += Count;
        }
      }

      IP.advance();
    }
  }
}

void PseudoProbeCSProfileGenerator::populateBodySamplesWithProbes(
    const RangeSample &RangeCounter,
    SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) {
  ProbeCounterMap ProbeCounter;
  // Extract the top frame probes by looking up each address among the range in
  // the Address2ProbeMap
  extractProbesFromRange(RangeCounter, ProbeCounter, Binary);
  for (auto PI : ProbeCounter) {
    const PseudoProbe *Probe = PI.first;
    uint64_t Count = PI.second;
    FunctionSamples &FunctionProfile =
        getFunctionProfileForLeafProbe(ContextStrStack, Probe, Binary);

    // Use InvalidProbeCount(UINT64_MAX) to mark sample count for a dangling
    // probe. Dangling probes are the probes associated to an empty block. With
    // this place holder, sample count on dangling probe will not be trusted by
    // the compiler and it will rely on the counts inference algorithm to get
    // the probe a reasonable count.
    if (Probe->isDangling()) {
      FunctionProfile.addBodySamplesForProbe(
          Probe->Index, FunctionSamples::InvalidProbeCount);
      continue;
    }
    FunctionProfile.addBodySamplesForProbe(Probe->Index, Count);
    FunctionProfile.addTotalSamples(Count);
    if (Probe->isEntry()) {
      FunctionProfile.addHeadSamples(Count);
      // Look up for the caller's function profile
      const auto *InlinerDesc = Binary->getInlinerDescForProbe(Probe);
      if (InlinerDesc != nullptr) {
        // Since the context id will be compressed, we have to use callee's
        // context id to infer caller's context id to ensure they share the
        // same context prefix.
        StringRef CalleeContextId =
            FunctionProfile.getContext().getNameWithContext();
        StringRef CallerContextId;
        FrameLocation &&CallerLeafFrameLoc =
            getCallerContext(CalleeContextId, CallerContextId);
        uint64_t CallerIndex = CallerLeafFrameLoc.second.LineOffset;
        assert(CallerIndex &&
               "Inferred caller's location index shouldn't be zero!");
        FunctionSamples &CallerProfile =
            getFunctionProfileForContext(CallerContextId);
        CallerProfile.setFunctionHash(InlinerDesc->FuncHash);
        CallerProfile.addBodySamples(CallerIndex, 0, Count);
        CallerProfile.addTotalSamples(Count);
        CallerProfile.addCalledTargetSamples(
            CallerIndex, 0,
            FunctionProfile.getContext().getNameWithoutContext(), Count);
      }
    }
  }
}

void PseudoProbeCSProfileGenerator::populateBoundarySamplesWithProbes(
    const BranchSample &BranchCounter,
    SmallVectorImpl<std::string> &ContextStrStack, ProfiledBinary *Binary) {
  for (auto BI : BranchCounter) {
    uint64_t SourceOffset = BI.first.first;
    uint64_t TargetOffset = BI.first.second;
    uint64_t Count = BI.second;
    uint64_t SourceAddress = Binary->offsetToVirtualAddr(SourceOffset);
    const PseudoProbe *CallProbe = Binary->getCallProbeForAddr(SourceAddress);
    if (CallProbe == nullptr)
      continue;
    FunctionSamples &FunctionProfile =
        getFunctionProfileForLeafProbe(ContextStrStack, CallProbe, Binary);
    FunctionProfile.addBodySamples(CallProbe->Index, 0, Count);
    FunctionProfile.addTotalSamples(Count);
    StringRef CalleeName = FunctionSamples::getCanonicalFnName(
        Binary->getFuncFromStartOffset(TargetOffset));
    if (CalleeName.size() == 0)
      continue;
    FunctionProfile.addCalledTargetSamples(CallProbe->Index, 0, CalleeName,
                                           Count);
  }
}

FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe(
    SmallVectorImpl<std::string> &ContextStrStack,
    const PseudoProbeFuncDesc *LeafFuncDesc, bool WasLeafInlined) {
  assert(ContextStrStack.size() && "Profile context must have the leaf frame");
  // Compress the context string except for the leaf frame
  std::string LeafFrame = ContextStrStack.back();
  ContextStrStack.pop_back();
  CSProfileGenerator::compressRecursionContext(ContextStrStack);

  std::ostringstream OContextStr;
  for (uint32_t I = 0; I < ContextStrStack.size(); I++) {
    if (OContextStr.str().size())
      OContextStr << " @ ";
    OContextStr << ContextStrStack[I];
  }
  // For leaf inlined context with the top frame, we should strip off the top
  // frame's probe id, like:
  // Inlined stack: [foo:1, bar:2], the ContextId will be "foo:1 @ bar"
  if (OContextStr.str().size())
    OContextStr << " @ ";
  OContextStr << StringRef(LeafFrame).split(":").first.str();

  FunctionSamples &FunctionProile =
      getFunctionProfileForContext(OContextStr.str(), WasLeafInlined);
  FunctionProile.setFunctionHash(LeafFuncDesc->FuncHash);
  return FunctionProile;
}

FunctionSamples &PseudoProbeCSProfileGenerator::getFunctionProfileForLeafProbe(
    SmallVectorImpl<std::string> &ContextStrStack, const PseudoProbe *LeafProbe,
    ProfiledBinary *Binary) {
  // Explicitly copy the context for appending the leaf context
  SmallVector<std::string, 16> ContextStrStackCopy(ContextStrStack.begin(),
                                                   ContextStrStack.end());
  Binary->getInlineContextForProbe(LeafProbe, ContextStrStackCopy, true);
  const auto *FuncDesc = Binary->getFuncDescForGUID(LeafProbe->GUID);
  bool WasLeafInlined = LeafProbe->InlineTree->hasInlineSite();
  return getFunctionProfileForLeafProbe(ContextStrStackCopy, FuncDesc,
                                        WasLeafInlined);
}

} // end namespace sampleprof
} // end namespace llvm