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