1 //===-- PerfReader.cpp - perfscript reader  ---------------------*- 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 #include "PerfReader.h"
9 
10 static cl::opt<bool> ShowMmapEvents("show-mmap-events", cl::ReallyHidden,
11                                     cl::init(false), cl::ZeroOrMore,
12                                     cl::desc("Print binary load events."));
13 
14 static cl::opt<bool> ShowUnwinderOutput("show-unwinder-output",
15                                         cl::ReallyHidden, cl::init(false),
16                                         cl::ZeroOrMore,
17                                         cl::desc("Print unwinder output"));
18 
19 namespace llvm {
20 namespace sampleprof {
21 
22 void VirtualUnwinder::unwindCall(UnwindState &State) {
23   // The 2nd frame after leaf could be missing if stack sample is
24   // taken when IP is within prolog/epilog, as frame chain isn't
25   // setup yet. Fill in the missing frame in that case.
26   // TODO: Currently we just assume all the addr that can't match the
27   // 2nd frame is in prolog/epilog. In the future, we will switch to
28   // pro/epi tracker(Dwarf CFI) for the precise check.
29   uint64_t Source = State.getCurrentLBRSource();
30   auto Iter = State.CallStack.begin();
31   if (State.CallStack.size() == 1 || *(++Iter) != Source) {
32     State.CallStack.front() = Source;
33   } else {
34     State.CallStack.pop_front();
35   }
36   State.InstPtr.update(Source);
37 }
38 
39 void VirtualUnwinder::unwindLinear(UnwindState &State, uint64_t Repeat) {
40   InstructionPointer &IP = State.InstPtr;
41   uint64_t Target = State.getCurrentLBRTarget();
42   uint64_t End = IP.Address;
43   if (State.getBinary()->usePseudoProbes()) {
44     // The outcome of the virtual unwinding with pseudo probes is a
45     // map from a context key to the address range being unwound.
46     // This means basically linear unwinding is not needed for pseudo
47     // probes. The range will be simply recorded here and will be
48     // converted to a list of pseudo probes to report in ProfileGenerator.
49     recordRangeCount(Target, End, State, Repeat);
50   } else {
51     // Unwind linear execution part
52     while (IP.Address >= Target) {
53       uint64_t PrevIP = IP.Address;
54       IP.backward();
55       // Break into segments for implicit call/return due to inlining
56       bool SameInlinee =
57           State.getBinary()->inlineContextEqual(PrevIP, IP.Address);
58       if (!SameInlinee || PrevIP == Target) {
59         recordRangeCount(PrevIP, End, State, Repeat);
60         End = IP.Address;
61       }
62       State.CallStack.front() = IP.Address;
63     }
64   }
65 }
66 
67 void VirtualUnwinder::unwindReturn(UnwindState &State) {
68   // Add extra frame as we unwind through the return
69   const LBREntry &LBR = State.getCurrentLBR();
70   uint64_t CallAddr = State.getBinary()->getCallAddrFromFrameAddr(LBR.Target);
71   State.CallStack.front() = CallAddr;
72   State.CallStack.push_front(LBR.Source);
73   State.InstPtr.update(LBR.Source);
74 }
75 
76 void VirtualUnwinder::unwindBranchWithinFrame(UnwindState &State) {
77   // TODO: Tolerate tail call for now, as we may see tail call from libraries.
78   // This is only for intra function branches, excluding tail calls.
79   uint64_t Source = State.getCurrentLBRSource();
80   State.CallStack.front() = Source;
81   State.InstPtr.update(Source);
82 }
83 
84 SampleCounter &
85 VirtualUnwinder::getOrCreateCounter(const ProfiledBinary *Binary,
86                                     std::list<uint64_t> &CallStack) {
87   if (Binary->usePseudoProbes()) {
88     return getOrCreateCounterForProbe(Binary, CallStack);
89   }
90   std::shared_ptr<StringBasedCtxKey> KeyStr =
91       std::make_shared<StringBasedCtxKey>();
92   KeyStr->Context = Binary->getExpandedContextStr(CallStack);
93   KeyStr->genHashCode();
94   auto Ret =
95       CtxCounterMap->emplace(Hashable<ContextKey>(KeyStr), SampleCounter());
96   return Ret.first->second;
97 }
98 
99 SampleCounter &
100 VirtualUnwinder::getOrCreateCounterForProbe(const ProfiledBinary *Binary,
101                                             std::list<uint64_t> &CallStack) {
102   std::shared_ptr<ProbeBasedCtxKey> ProbeBasedKey =
103       std::make_shared<ProbeBasedCtxKey>();
104   if (CallStack.size() > 1) {
105     // We don't need to top frame probe since it should be extracted
106     // from the range.
107     // The top of stack is an instruction from the function where
108     // the LBR address range physcially resides. Strip it since
109     // the function is not a part of the call context. We also
110     // don't need its inline context since the probes being unwound
111     // come with an inline context all the way back to the uninlined
112     // function in their prefix tree.
113     auto Iter = CallStack.rbegin();
114     auto EndT = std::prev(CallStack.rend());
115     for (; Iter != EndT; Iter++) {
116       uint64_t Address = *Iter;
117       const PseudoProbe *CallProbe = Binary->getCallProbeForAddr(Address);
118       // We may not find a probe for a merged or external callsite.
119       // Callsite merging may cause the loss of original probe IDs.
120       // Cutting off the context from here since the inline will
121       // not know how to consume a context with unknown callsites.
122       if (!CallProbe)
123         break;
124       ProbeBasedKey->Probes.emplace_back(CallProbe);
125     }
126   }
127   ProbeBasedKey->genHashCode();
128   Hashable<ContextKey> ContextId(ProbeBasedKey);
129   auto Ret = CtxCounterMap->emplace(ContextId, SampleCounter());
130   return Ret.first->second;
131 }
132 
133 void VirtualUnwinder::recordRangeCount(uint64_t Start, uint64_t End,
134                                        UnwindState &State, uint64_t Repeat) {
135   uint64_t StartOffset = State.getBinary()->virtualAddrToOffset(Start);
136   uint64_t EndOffset = State.getBinary()->virtualAddrToOffset(End);
137   SampleCounter &SCounter =
138       getOrCreateCounter(State.getBinary(), State.CallStack);
139   SCounter.recordRangeCount(StartOffset, EndOffset, Repeat);
140 }
141 
142 void VirtualUnwinder::recordBranchCount(const LBREntry &Branch,
143                                         UnwindState &State, uint64_t Repeat) {
144   if (Branch.IsArtificial)
145     return;
146   uint64_t SourceOffset = State.getBinary()->virtualAddrToOffset(Branch.Source);
147   uint64_t TargetOffset = State.getBinary()->virtualAddrToOffset(Branch.Target);
148   SampleCounter &SCounter =
149       getOrCreateCounter(State.getBinary(), State.CallStack);
150   SCounter.recordBranchCount(SourceOffset, TargetOffset, Repeat);
151 }
152 
153 bool VirtualUnwinder::unwind(const HybridSample *Sample, uint64_t Repeat) {
154   // Capture initial state as starting point for unwinding.
155   UnwindState State(Sample);
156 
157   // Sanity check - making sure leaf of LBR aligns with leaf of stack sample
158   // Stack sample sometimes can be unreliable, so filter out bogus ones.
159   if (!State.validateInitialState())
160     return false;
161 
162   // Also do not attempt linear unwind for the leaf range as it's incomplete.
163   bool IsLeaf = true;
164 
165   // Now process the LBR samples in parrallel with stack sample
166   // Note that we do not reverse the LBR entry order so we can
167   // unwind the sample stack as we walk through LBR entries.
168   while (State.hasNextLBR()) {
169     State.checkStateConsistency();
170 
171     // Unwind implicit calls/returns from inlining, along the linear path,
172     // break into smaller sub section each with its own calling context.
173     if (!IsLeaf) {
174       unwindLinear(State, Repeat);
175     }
176     IsLeaf = false;
177 
178     // Save the LBR branch before it gets unwound.
179     const LBREntry &Branch = State.getCurrentLBR();
180 
181     if (isCallState(State)) {
182       // Unwind calls - we know we encountered call if LBR overlaps with
183       // transition between leaf the 2nd frame. Note that for calls that
184       // were not in the original stack sample, we should have added the
185       // extra frame when processing the return paired with this call.
186       unwindCall(State);
187     } else if (isReturnState(State)) {
188       // Unwind returns - check whether the IP is indeed at a return instruction
189       unwindReturn(State);
190     } else {
191       // Unwind branches - for regular intra function branches, we only
192       // need to record branch with context.
193       unwindBranchWithinFrame(State);
194     }
195     State.advanceLBR();
196     // Record `branch` with calling context after unwinding.
197     recordBranchCount(Branch, State, Repeat);
198   }
199 
200   return true;
201 }
202 
203 PerfReader::PerfReader(cl::list<std::string> &BinaryFilenames) {
204   // Load the binaries.
205   for (auto Filename : BinaryFilenames)
206     loadBinary(Filename, /*AllowNameConflict*/ false);
207 }
208 
209 ProfiledBinary &PerfReader::loadBinary(const StringRef BinaryPath,
210                                        bool AllowNameConflict) {
211   // The binary table is currently indexed by the binary name not the full
212   // binary path. This is because the user-given path may not match the one
213   // that was actually executed.
214   StringRef BinaryName = llvm::sys::path::filename(BinaryPath);
215 
216   // Call to load the binary in the ctor of ProfiledBinary.
217   auto Ret = BinaryTable.insert({BinaryName, ProfiledBinary(BinaryPath)});
218 
219   if (!Ret.second && !AllowNameConflict) {
220     std::string ErrorMsg = "Binary name conflict: " + BinaryPath.str() +
221                            " and " + Ret.first->second.getPath().str() + " \n";
222     exitWithError(ErrorMsg);
223   }
224 
225   return Ret.first->second;
226 }
227 
228 void PerfReader::updateBinaryAddress(const MMapEvent &Event) {
229   // Load the binary.
230   StringRef BinaryPath = Event.BinaryPath;
231   StringRef BinaryName = llvm::sys::path::filename(BinaryPath);
232 
233   auto I = BinaryTable.find(BinaryName);
234   // Drop the event which doesn't belong to user-provided binaries
235   // or if its image is loaded at the same address
236   if (I == BinaryTable.end() || Event.BaseAddress == I->second.getBaseAddress())
237     return;
238 
239   ProfiledBinary &Binary = I->second;
240 
241   // A binary image could be uploaded and then reloaded at different
242   // place, so update the address map here
243   AddrToBinaryMap.erase(Binary.getBaseAddress());
244   AddrToBinaryMap[Event.BaseAddress] = &Binary;
245 
246   // Update binary load address.
247   Binary.setBaseAddress(Event.BaseAddress);
248 }
249 
250 ProfiledBinary *PerfReader::getBinary(uint64_t Address) {
251   auto Iter = AddrToBinaryMap.lower_bound(Address);
252   if (Iter == AddrToBinaryMap.end() || Iter->first != Address) {
253     if (Iter == AddrToBinaryMap.begin())
254       return nullptr;
255     Iter--;
256   }
257   return Iter->second;
258 }
259 
260 // Use ordered map to make the output deterministic
261 using OrderedCounterForPrint = std::map<std::string, RangeSample>;
262 
263 static void printSampleCounter(OrderedCounterForPrint &OrderedCounter) {
264   for (auto Range : OrderedCounter) {
265     outs() << Range.first << "\n";
266     for (auto I : Range.second) {
267       outs() << "  (" << format("%" PRIx64, I.first.first) << ", "
268              << format("%" PRIx64, I.first.second) << "): " << I.second << "\n";
269     }
270   }
271 }
272 
273 static std::string getContextKeyStr(ContextKey *K,
274                                     const ProfiledBinary *Binary) {
275   std::string ContextStr;
276   if (const auto *CtxKey = dyn_cast<StringBasedCtxKey>(K)) {
277     return CtxKey->Context;
278   } else if (const auto *CtxKey = dyn_cast<ProbeBasedCtxKey>(K)) {
279     SmallVector<std::string, 16> ContextStack;
280     for (const auto *Probe : CtxKey->Probes) {
281       Binary->getInlineContextForProbe(Probe, ContextStack, true);
282     }
283     for (const auto &Context : ContextStack) {
284       if (ContextStr.size())
285         ContextStr += " @ ";
286       ContextStr += Context;
287     }
288   }
289   return ContextStr;
290 }
291 
292 static void printRangeCounter(ContextSampleCounterMap &Counter,
293                               const ProfiledBinary *Binary) {
294   OrderedCounterForPrint OrderedCounter;
295   for (auto &CI : Counter) {
296     OrderedCounter[getContextKeyStr(CI.first.getPtr(), Binary)] =
297         CI.second.RangeCounter;
298   }
299   printSampleCounter(OrderedCounter);
300 }
301 
302 static void printBranchCounter(ContextSampleCounterMap &Counter,
303                                const ProfiledBinary *Binary) {
304   OrderedCounterForPrint OrderedCounter;
305   for (auto &CI : Counter) {
306     OrderedCounter[getContextKeyStr(CI.first.getPtr(), Binary)] =
307         CI.second.BranchCounter;
308   }
309   printSampleCounter(OrderedCounter);
310 }
311 
312 void PerfReader::printUnwinderOutput() {
313   for (auto I : BinarySampleCounters) {
314     const ProfiledBinary *Binary = I.first;
315     outs() << "Binary(" << Binary->getName().str() << ")'s Range Counter:\n";
316     printRangeCounter(I.second, Binary);
317     outs() << "\nBinary(" << Binary->getName().str() << ")'s Branch Counter:\n";
318     printBranchCounter(I.second, Binary);
319   }
320 }
321 
322 void PerfReader::unwindSamples() {
323   for (const auto &Item : AggregatedSamples) {
324     const HybridSample *Sample = dyn_cast<HybridSample>(Item.first.getPtr());
325     VirtualUnwinder Unwinder(&BinarySampleCounters[Sample->Binary]);
326     Unwinder.unwind(Sample, Item.second);
327   }
328 
329   if (ShowUnwinderOutput)
330     printUnwinderOutput();
331 }
332 
333 bool PerfReader::extractLBRStack(TraceStream &TraceIt,
334                                  SmallVector<LBREntry, 16> &LBRStack,
335                                  ProfiledBinary *Binary) {
336   // The raw format of LBR stack is like:
337   // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
338   //                           ... 0x4005c8/0x4005dc/P/-/-/0
339   // It's in FIFO order and seperated by whitespace.
340   SmallVector<StringRef, 32> Records;
341   TraceIt.getCurrentLine().split(Records, " ");
342 
343   // Extract leading instruction pointer if present, use single
344   // list to pass out as reference.
345   size_t Index = 0;
346   if (!Records.empty() && Records[0].find('/') == StringRef::npos) {
347     Index = 1;
348   }
349   // Now extract LBR samples - note that we do not reverse the
350   // LBR entry order so we can unwind the sample stack as we walk
351   // through LBR entries.
352   uint64_t PrevTrDst = 0;
353 
354   while (Index < Records.size()) {
355     auto &Token = Records[Index++];
356     if (Token.size() == 0)
357       continue;
358 
359     SmallVector<StringRef, 8> Addresses;
360     Token.split(Addresses, "/");
361     uint64_t Src;
362     uint64_t Dst;
363     Addresses[0].substr(2).getAsInteger(16, Src);
364     Addresses[1].substr(2).getAsInteger(16, Dst);
365 
366     bool SrcIsInternal = Binary->addressIsCode(Src);
367     bool DstIsInternal = Binary->addressIsCode(Dst);
368     bool IsArtificial = false;
369     // Ignore branches outside the current binary.
370     if (!SrcIsInternal && !DstIsInternal)
371       continue;
372     if (!SrcIsInternal && DstIsInternal) {
373       // For transition from external code (such as dynamic libraries) to
374       // the current binary, keep track of the branch target which will be
375       // grouped with the Source of the last transition from the current
376       // binary.
377       PrevTrDst = Dst;
378       continue;
379     }
380     if (SrcIsInternal && !DstIsInternal) {
381       // For transition to external code, group the Source with the next
382       // availabe transition target.
383       if (!PrevTrDst)
384         continue;
385       Dst = PrevTrDst;
386       PrevTrDst = 0;
387       IsArtificial = true;
388     }
389     // TODO: filter out buggy duplicate branches on Skylake
390 
391     LBRStack.emplace_back(LBREntry(Src, Dst, IsArtificial));
392   }
393   TraceIt.advance();
394   return !LBRStack.empty();
395 }
396 
397 bool PerfReader::extractCallstack(TraceStream &TraceIt,
398                                   std::list<uint64_t> &CallStack) {
399   // The raw format of call stack is like:
400   //            4005dc      # leaf frame
401   //	          400634
402   //	          400684      # root frame
403   // It's in bottom-up order with each frame in one line.
404 
405   // Extract stack frames from sample
406   ProfiledBinary *Binary = nullptr;
407   while (!TraceIt.isAtEoF() && !TraceIt.getCurrentLine().startswith(" 0x")) {
408     StringRef FrameStr = TraceIt.getCurrentLine().ltrim();
409     // We might get an empty line at the beginning or comments, skip it
410     uint64_t FrameAddr = 0;
411     if (FrameStr.getAsInteger(16, FrameAddr)) {
412       TraceIt.advance();
413       break;
414     }
415     TraceIt.advance();
416     if (!Binary) {
417       Binary = getBinary(FrameAddr);
418       // we might have addr not match the MMAP, skip it
419       if (!Binary) {
420         if (AddrToBinaryMap.size() == 0)
421           WithColor::warning() << "No MMAP event in the perfscript, create it "
422                                   "with '--show-mmap-events'\n";
423         break;
424       }
425     }
426     // Currently intermixed frame from different binaries is not supported.
427     // Ignore bottom frames not from binary of interest.
428     if (!Binary->addressIsCode(FrameAddr))
429       break;
430 
431     // We need to translate return address to call address
432     // for non-leaf frames
433     if (!CallStack.empty()) {
434       FrameAddr = Binary->getCallAddrFromFrameAddr(FrameAddr);
435     }
436 
437     CallStack.emplace_back(FrameAddr);
438   }
439 
440   if (CallStack.empty())
441     return false;
442   // Skip other unrelated line, find the next valid LBR line
443   while (!TraceIt.isAtEoF() && !TraceIt.getCurrentLine().startswith(" 0x")) {
444     TraceIt.advance();
445   }
446   // Filter out broken stack sample. We may not have complete frame info
447   // if sample end up in prolog/epilog, the result is dangling context not
448   // connected to entry point. This should be relatively rare thus not much
449   // impact on overall profile quality. However we do want to filter them
450   // out to reduce the number of different calling contexts. One instance
451   // of such case - when sample landed in prolog/epilog, somehow stack
452   // walking will be broken in an unexpected way that higher frames will be
453   // missing.
454   return !Binary->addressInPrologEpilog(CallStack.front());
455 }
456 
457 void PerfReader::parseHybridSample(TraceStream &TraceIt) {
458   // The raw hybird sample started with call stack in FILO order and followed
459   // intermediately by LBR sample
460   // e.g.
461   // 	          4005dc    # call stack leaf
462   //	          400634
463   //	          400684    # call stack root
464   // 0x4005c8/0x4005dc/P/-/-/0   0x40062f/0x4005b0/P/-/-/0 ...
465   //          ... 0x4005c8/0x4005dc/P/-/-/0    # LBR Entries
466   //
467   std::shared_ptr<HybridSample> Sample = std::make_shared<HybridSample>();
468 
469   // Parsing call stack and populate into HybridSample.CallStack
470   if (!extractCallstack(TraceIt, Sample->CallStack)) {
471     // Skip the next LBR line matched current call stack
472     if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().startswith(" 0x"))
473       TraceIt.advance();
474     return;
475   }
476   // Set the binary current sample belongs to
477   Sample->Binary = getBinary(Sample->CallStack.front());
478 
479   if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().startswith(" 0x")) {
480     // Parsing LBR stack and populate into HybridSample.LBRStack
481     if (extractLBRStack(TraceIt, Sample->LBRStack, Sample->Binary)) {
482       // Canonicalize stack leaf to avoid 'random' IP from leaf frame skew LBR
483       // ranges
484       Sample->CallStack.front() = Sample->LBRStack[0].Target;
485       // Record samples by aggregation
486       Sample->genHashCode();
487       AggregatedSamples[Hashable<PerfSample>(Sample)]++;
488     }
489   } else {
490     // LBR sample is encoded in single line after stack sample
491     exitWithError("'Hybrid perf sample is corrupted, No LBR sample line");
492   }
493 }
494 
495 void PerfReader::parseMMap2Event(TraceStream &TraceIt) {
496   // Parse a line like:
497   //  PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0
498   //  08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so
499   constexpr static const char *const Pattern =
500       "PERF_RECORD_MMAP2 ([0-9]+)/[0-9]+: "
501       "\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ "
502       "(0x[a-f0-9]+|0) .*\\]: [-a-z]+ (.*)";
503   // Field 0 - whole line
504   // Field 1 - PID
505   // Field 2 - base address
506   // Field 3 - mmapped size
507   // Field 4 - page offset
508   // Field 5 - binary path
509   enum EventIndex {
510     WHOLE_LINE = 0,
511     PID = 1,
512     BASE_ADDRESS = 2,
513     MMAPPED_SIZE = 3,
514     PAGE_OFFSET = 4,
515     BINARY_PATH = 5
516   };
517 
518   Regex RegMmap2(Pattern);
519   SmallVector<StringRef, 6> Fields;
520   bool R = RegMmap2.match(TraceIt.getCurrentLine(), &Fields);
521   if (!R) {
522     std::string ErrorMsg = "Cannot parse mmap event: Line" +
523                            Twine(TraceIt.getLineNumber()).str() + ": " +
524                            TraceIt.getCurrentLine().str() + " \n";
525     exitWithError(ErrorMsg);
526   }
527   MMapEvent Event;
528   Fields[PID].getAsInteger(10, Event.PID);
529   Fields[BASE_ADDRESS].getAsInteger(0, Event.BaseAddress);
530   Fields[MMAPPED_SIZE].getAsInteger(0, Event.Size);
531   Fields[PAGE_OFFSET].getAsInteger(0, Event.Offset);
532   Event.BinaryPath = Fields[BINARY_PATH];
533   updateBinaryAddress(Event);
534   if (ShowMmapEvents) {
535     outs() << "Mmap: Binary " << Event.BinaryPath << " loaded at "
536            << format("0x%" PRIx64 ":", Event.BaseAddress) << " \n";
537   }
538   TraceIt.advance();
539 }
540 
541 void PerfReader::parseEventOrSample(TraceStream &TraceIt) {
542   if (TraceIt.getCurrentLine().startswith("PERF_RECORD_MMAP2"))
543     parseMMap2Event(TraceIt);
544   else if (getPerfScriptType() == PERF_LBR_STACK)
545     parseHybridSample(TraceIt);
546   else {
547     // TODO: parse other type sample
548     TraceIt.advance();
549   }
550 }
551 
552 void PerfReader::parseAndAggregateTrace(StringRef Filename) {
553   // Trace line iterator
554   TraceStream TraceIt(Filename);
555   while (!TraceIt.isAtEoF())
556     parseEventOrSample(TraceIt);
557 }
558 
559 void PerfReader::checkAndSetPerfType(
560     cl::list<std::string> &PerfTraceFilenames) {
561   bool HasHybridPerf = true;
562   for (auto FileName : PerfTraceFilenames) {
563     if (!isHybridPerfScript(FileName)) {
564       HasHybridPerf = false;
565       break;
566     }
567   }
568 
569   if (HasHybridPerf) {
570     // Set up ProfileIsCS to enable context-sensitive functionalities
571     // in SampleProf
572     FunctionSamples::ProfileIsCS = true;
573     PerfType = PERF_LBR_STACK;
574 
575   } else {
576     // TODO: Support other type of perf script
577     PerfType = PERF_INVILID;
578   }
579 
580   if (BinaryTable.size() > 1) {
581     // TODO: remove this if everything is ready to support multiple binaries.
582     exitWithError("Currently only support one input binary, multiple binaries' "
583                   "profile will be merged in one profile and make profile "
584                   "summary info inaccurate. Please use `perfdata` to merge "
585                   "profiles from multiple binaries.");
586   }
587 }
588 
589 void PerfReader::generateRawProfile() {
590   if (getPerfScriptType() == PERF_LBR_STACK) {
591     // Unwind samples if it's hybird sample
592     unwindSamples();
593   } else if (getPerfScriptType() == PERF_LBR) {
594     // TODO: range overlap computation for regular AutoFDO
595   }
596 }
597 
598 void PerfReader::parsePerfTraces(cl::list<std::string> &PerfTraceFilenames) {
599   // Check and set current perfscript type
600   checkAndSetPerfType(PerfTraceFilenames);
601   // Parse perf traces and do aggregation.
602   for (auto Filename : PerfTraceFilenames)
603     parseAndAggregateTrace(Filename);
604 
605   generateRawProfile();
606 }
607 
608 } // end namespace sampleprof
609 } // end namespace llvm
610