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