xref: /llvm-project/llvm/tools/llvm-profgen/PerfReader.cpp (revision 6c3c90b5a81a3c27dbc195d7e49dc4c89e0e604f)
1a94fa862Swlei //===-- PerfReader.cpp - perfscript reader  ---------------------*- C++ -*-===//
2a94fa862Swlei //
3a94fa862Swlei // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a94fa862Swlei // See https://llvm.org/LICENSE.txt for license information.
5a94fa862Swlei // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a94fa862Swlei //
7a94fa862Swlei //===----------------------------------------------------------------------===//
8a94fa862Swlei #include "PerfReader.h"
9ac14bb14Swlei #include "ProfileGenerator.h"
108466ab98SMatthias Braun #include "llvm/ADT/SmallString.h"
11db29f437Sserge-sans-paille #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
12964053d5Swlei #include "llvm/Support/FileSystem.h"
131f0bc617SWenlei He #include "llvm/Support/Process.h"
143f7f446dSHaohai Wen #include "llvm/Support/ToolOutputFile.h"
15a94fa862Swlei 
160057c718SHongtao Yu #define DEBUG_TYPE "perf-reader"
170057c718SHongtao Yu 
18d86a206fSFangrui Song cl::opt<bool> SkipSymbolization("skip-symbolization",
190057c718SHongtao Yu                                 cl::desc("Dump the unsymbolized profile to the "
20964053d5Swlei                                          "output file. It will show unwinder "
21964053d5Swlei                                          "output for CS profile generation."));
22a316343eSWenlei He 
23d86a206fSFangrui Song static cl::opt<bool> ShowMmapEvents("show-mmap-events",
24a316343eSWenlei He                                     cl::desc("Print binary load events."));
25a316343eSWenlei He 
26a316343eSWenlei He static cl::opt<bool>
27557efc9aSFangrui Song     UseOffset("use-offset", cl::init(true),
28a5f411b7Swlei               cl::desc("Work with `--skip-symbolization` or "
29a5f411b7Swlei                        "`--unsymbolized-profile` to write/read the "
301422fa5fSwlei                        "offset instead of virtual address."));
31f7976edcSWenlei He 
32f7976edcSWenlei He static cl::opt<bool> UseLoadableSegmentAsBase(
33557efc9aSFangrui Song     "use-first-loadable-segment-as-base",
34f7976edcSWenlei He     cl::desc("Use first loadable segment address as base address "
35f7976edcSWenlei He              "for offsets in unsymbolized profile. By default "
36f7976edcSWenlei He              "first executable segment address is used"));
37f7976edcSWenlei He 
38a316343eSWenlei He static cl::opt<bool>
39557efc9aSFangrui Song     IgnoreStackSamples("ignore-stack-samples",
40a03cf331Swlei                        cl::desc("Ignore call stack samples for hybrid samples "
41a03cf331Swlei                                 "and produce context-insensitive profile."));
42d86a206fSFangrui Song cl::opt<bool> ShowDetailedWarning("show-detailed-warning",
43138202a8Swlei                                   cl::desc("Show detailed warning message."));
4401d78364STim Creech 
45*6c3c90b5SLei Wang static cl::opt<int> CSProfMaxUnsymbolizedCtxDepth(
46*6c3c90b5SLei Wang     "csprof-max-unsymbolized-context-depth", cl::init(-1),
47*6c3c90b5SLei Wang     cl::desc("Keep the last K contexts while merging unsymbolized profile. -1 "
48*6c3c90b5SLei Wang              "means no depth limit."));
49*6c3c90b5SLei Wang 
501f0bc617SWenlei He extern cl::opt<std::string> PerfTraceFilename;
51426e326aSwlei extern cl::opt<bool> ShowDisassemblyOnly;
52426e326aSwlei extern cl::opt<bool> ShowSourceLocations;
53964053d5Swlei extern cl::opt<std::string> OutputFilename;
54426e326aSwlei 
55a94fa862Swlei namespace llvm {
56a94fa862Swlei namespace sampleprof {
57a94fa862Swlei 
581f05b1a9Swlei void VirtualUnwinder::unwindCall(UnwindState &State) {
590f53df86Swlei   uint64_t Source = State.getCurrentLBRSource();
600f53df86Swlei   auto *ParentFrame = State.getParentFrame();
611f05b1a9Swlei   // The 2nd frame after leaf could be missing if stack sample is
621f05b1a9Swlei   // taken when IP is within prolog/epilog, as frame chain isn't
631f05b1a9Swlei   // setup yet. Fill in the missing frame in that case.
641f05b1a9Swlei   // TODO: Currently we just assume all the addr that can't match the
651f05b1a9Swlei   // 2nd frame is in prolog/epilog. In the future, we will switch to
661f05b1a9Swlei   // pro/epi tracker(Dwarf CFI) for the precise check.
673869309aSwlei   if (ParentFrame == State.getDummyRootPtr() ||
683869309aSwlei       ParentFrame->Address != Source) {
693869309aSwlei     State.switchToFrame(Source);
700f53df86Swlei     if (ParentFrame != State.getDummyRootPtr()) {
71bfcb2c11Swlei       if (Source == ExternalAddr)
720f53df86Swlei         NumMismatchedExtCallBranch++;
730f53df86Swlei       else
740f53df86Swlei         NumMismatchedProEpiBranch++;
750f53df86Swlei     }
761f05b1a9Swlei   } else {
773869309aSwlei     State.popFrame();
781f05b1a9Swlei   }
791f05b1a9Swlei   State.InstPtr.update(Source);
801f05b1a9Swlei }
811f05b1a9Swlei 
821f05b1a9Swlei void VirtualUnwinder::unwindLinear(UnwindState &State, uint64_t Repeat) {
831f05b1a9Swlei   InstructionPointer &IP = State.InstPtr;
841f05b1a9Swlei   uint64_t Target = State.getCurrentLBRTarget();
851f05b1a9Swlei   uint64_t End = IP.Address;
86bfcb2c11Swlei 
87bfcb2c11Swlei   if (End == ExternalAddr && Target == ExternalAddr) {
88bfcb2c11Swlei     // Filter out the case when leaf external frame matches the external LBR
89bfcb2c11Swlei     // target, this is a valid state, it happens that the code run into external
90bfcb2c11Swlei     // address then return back.  The call frame under the external frame
91bfcb2c11Swlei     // remains valid and can be unwound later, just skip recording this range.
92bfcb2c11Swlei     NumPairedExtAddr++;
93bfcb2c11Swlei     return;
94bfcb2c11Swlei   }
95bfcb2c11Swlei 
96bfcb2c11Swlei   if (End == ExternalAddr || Target == ExternalAddr) {
97bfcb2c11Swlei     // Range is invalid if only one point is external address. This means LBR
98bfcb2c11Swlei     // traces contains a standalone external address failing to pair another
99bfcb2c11Swlei     // one, likely due to interrupt jmp or broken perf script. Set the
100bfcb2c11Swlei     // state to invalid.
101bfcb2c11Swlei     NumUnpairedExtAddr++;
102bfcb2c11Swlei     State.setInvalid();
103bfcb2c11Swlei     return;
104bfcb2c11Swlei   }
105bfcb2c11Swlei 
10646765248Swlei   if (!isValidFallThroughRange(Target, End, Binary)) {
1078a0406dcSHongtao Yu     // Skip unwinding the rest of LBR trace when a bogus range is seen.
1088a0406dcSHongtao Yu     State.setInvalid();
1098a0406dcSHongtao Yu     return;
1108a0406dcSHongtao Yu   }
111bfcb2c11Swlei 
1123869309aSwlei   if (Binary->usePseudoProbes()) {
1133869309aSwlei     // We don't need to top frame probe since it should be extracted
1143869309aSwlei     // from the range.
115c681400bSwlei     // The outcome of the virtual unwinding with pseudo probes is a
116c681400bSwlei     // map from a context key to the address range being unwound.
117c681400bSwlei     // This means basically linear unwinding is not needed for pseudo
118c681400bSwlei     // probes. The range will be simply recorded here and will be
119c681400bSwlei     // converted to a list of pseudo probes to report in ProfileGenerator.
1203869309aSwlei     State.getParentFrame()->recordRangeCount(Target, End, Repeat);
121c681400bSwlei   } else {
122686cc000Swlei     // Unwind linear execution part.
123686cc000Swlei     // Split and record the range by different inline context. For example:
124686cc000Swlei     // [0x01] ... main:1          # Target
125686cc000Swlei     // [0x02] ... main:2
126686cc000Swlei     // [0x03] ... main:3 @ foo:1
127686cc000Swlei     // [0x04] ... main:3 @ foo:2
128686cc000Swlei     // [0x05] ... main:3 @ foo:3
129686cc000Swlei     // [0x06] ... main:4
130686cc000Swlei     // [0x07] ... main:5          # End
131686cc000Swlei     // It will be recorded:
132686cc000Swlei     // [main:*]         : [0x06, 0x07], [0x01, 0x02]
133686cc000Swlei     // [main:3 @ foo:*] : [0x03, 0x05]
134686cc000Swlei     while (IP.Address > Target) {
1351f05b1a9Swlei       uint64_t PrevIP = IP.Address;
1361f05b1a9Swlei       IP.backward();
1371f05b1a9Swlei       // Break into segments for implicit call/return due to inlining
1383869309aSwlei       bool SameInlinee = Binary->inlineContextEqual(PrevIP, IP.Address);
139686cc000Swlei       if (!SameInlinee) {
140686cc000Swlei         State.switchToFrame(PrevIP);
1413869309aSwlei         State.CurrentLeafFrame->recordRangeCount(PrevIP, End, Repeat);
1421f05b1a9Swlei         End = IP.Address;
1431f05b1a9Swlei       }
1441f05b1a9Swlei     }
145686cc000Swlei     assert(IP.Address == Target && "The last one must be the target address.");
146686cc000Swlei     // Record the remaining range, [0x01, 0x02] in the example
147686cc000Swlei     State.switchToFrame(IP.Address);
148686cc000Swlei     State.CurrentLeafFrame->recordRangeCount(IP.Address, End, Repeat);
1491f05b1a9Swlei   }
150c681400bSwlei }
1511f05b1a9Swlei 
1521f05b1a9Swlei void VirtualUnwinder::unwindReturn(UnwindState &State) {
1531f05b1a9Swlei   // Add extra frame as we unwind through the return
1541f05b1a9Swlei   const LBREntry &LBR = State.getCurrentLBR();
1553869309aSwlei   uint64_t CallAddr = Binary->getCallAddrFromFrameAddr(LBR.Target);
1563869309aSwlei   State.switchToFrame(CallAddr);
1573869309aSwlei   State.pushFrame(LBR.Source);
1581f05b1a9Swlei   State.InstPtr.update(LBR.Source);
1591f05b1a9Swlei }
1601f05b1a9Swlei 
1613dcb60dbSwlei void VirtualUnwinder::unwindBranch(UnwindState &State) {
1621f05b1a9Swlei   // TODO: Tolerate tail call for now, as we may see tail call from libraries.
1631f05b1a9Swlei   // This is only for intra function branches, excluding tail calls.
1641f05b1a9Swlei   uint64_t Source = State.getCurrentLBRSource();
1653869309aSwlei   State.switchToFrame(Source);
1661f05b1a9Swlei   State.InstPtr.update(Source);
1671f05b1a9Swlei }
1681f05b1a9Swlei 
1693869309aSwlei std::shared_ptr<StringBasedCtxKey> FrameStack::getContextKey() {
170414930b9Swlei   std::shared_ptr<StringBasedCtxKey> KeyStr =
171414930b9Swlei       std::make_shared<StringBasedCtxKey>();
172b9db7036SHongtao Yu   KeyStr->Context = Binary->getExpandedContext(Stack, KeyStr->WasLeafInlined);
1733869309aSwlei   return KeyStr;
174414930b9Swlei }
175414930b9Swlei 
1763f970168SHongtao Yu std::shared_ptr<AddrBasedCtxKey> AddressStack::getContextKey() {
1773f970168SHongtao Yu   std::shared_ptr<AddrBasedCtxKey> KeyStr = std::make_shared<AddrBasedCtxKey>();
1783f970168SHongtao Yu   KeyStr->Context = Stack;
1793f970168SHongtao Yu   CSProfileGenerator::compressRecursionContext<uint64_t>(KeyStr->Context);
180*6c3c90b5SLei Wang   // MaxContextDepth(--csprof-max-context-depth) is used to trim both symbolized
181*6c3c90b5SLei Wang   // and unsymbolized profile context. Sometimes we want to at least preserve
182*6c3c90b5SLei Wang   // the inlinings for the leaf frame(the profiled binary inlining),
183*6c3c90b5SLei Wang   // --csprof-max-context-depth may not be flexible enough, in this case,
184*6c3c90b5SLei Wang   // --csprof-max-unsymbolized-context-depth is used to limit the context for
185*6c3c90b5SLei Wang   // unsymbolized profile. If both are set, use the minimum of them.
186*6c3c90b5SLei Wang   int Depth = CSProfileGenerator::MaxContextDepth != -1
187*6c3c90b5SLei Wang                   ? CSProfileGenerator::MaxContextDepth
188*6c3c90b5SLei Wang                   : KeyStr->Context.size();
189*6c3c90b5SLei Wang   Depth = CSProfMaxUnsymbolizedCtxDepth != -1
190*6c3c90b5SLei Wang               ? std::min(static_cast<int>(CSProfMaxUnsymbolizedCtxDepth), Depth)
191*6c3c90b5SLei Wang               : Depth;
192*6c3c90b5SLei Wang   CSProfileGenerator::trimContext<uint64_t>(KeyStr->Context, Depth);
1933f970168SHongtao Yu   return KeyStr;
194c681400bSwlei }
195c681400bSwlei 
1963869309aSwlei template <typename T>
1973869309aSwlei void VirtualUnwinder::collectSamplesFromFrame(UnwindState::ProfiledFrame *Cur,
1983869309aSwlei                                               T &Stack) {
1993869309aSwlei   if (Cur->RangeSamples.empty() && Cur->BranchSamples.empty())
2003869309aSwlei     return;
2013869309aSwlei 
2023869309aSwlei   std::shared_ptr<ContextKey> Key = Stack.getContextKey();
203afd8bd60Swlei   if (Key == nullptr)
204afd8bd60Swlei     return;
2053869309aSwlei   auto Ret = CtxCounterMap->emplace(Hashable<ContextKey>(Key), SampleCounter());
2063869309aSwlei   SampleCounter &SCounter = Ret.first->second;
20746765248Swlei   for (auto &I : Cur->RangeSamples)
20846765248Swlei     SCounter.recordRangeCount(std::get<0>(I), std::get<1>(I), std::get<2>(I));
2093869309aSwlei 
21046765248Swlei   for (auto &I : Cur->BranchSamples)
21146765248Swlei     SCounter.recordBranchCount(std::get<0>(I), std::get<1>(I), std::get<2>(I));
2123869309aSwlei }
2133869309aSwlei 
2143869309aSwlei template <typename T>
2153869309aSwlei void VirtualUnwinder::collectSamplesFromFrameTrie(
2163869309aSwlei     UnwindState::ProfiledFrame *Cur, T &Stack) {
2173869309aSwlei   if (!Cur->isDummyRoot()) {
2180f53df86Swlei     // Truncate the context for external frame since this isn't a real call
2190f53df86Swlei     // context the compiler will see.
2200f53df86Swlei     if (Cur->isExternalFrame() || !Stack.pushFrame(Cur)) {
2213869309aSwlei       // Process truncated context
2223869309aSwlei       // Start a new traversal ignoring its bottom context
2233b51b518SHongtao Yu       T EmptyStack(Binary);
2243b51b518SHongtao Yu       collectSamplesFromFrame(Cur, EmptyStack);
2253b51b518SHongtao Yu       for (const auto &Item : Cur->Children) {
2263b51b518SHongtao Yu         collectSamplesFromFrameTrie(Item.second.get(), EmptyStack);
2273869309aSwlei       }
2286eca242eSWenlei He 
2296eca242eSWenlei He       // Keep note of untracked call site and deduplicate them
2306eca242eSWenlei He       // for warning later.
2316eca242eSWenlei He       if (!Cur->isLeafFrame())
2326eca242eSWenlei He         UntrackedCallsites.insert(Cur->Address);
2336eca242eSWenlei He 
2343869309aSwlei       return;
2353869309aSwlei     }
2363869309aSwlei   }
2373869309aSwlei 
2383869309aSwlei   collectSamplesFromFrame(Cur, Stack);
2393869309aSwlei   // Process children frame
2403869309aSwlei   for (const auto &Item : Cur->Children) {
2413869309aSwlei     collectSamplesFromFrameTrie(Item.second.get(), Stack);
2423869309aSwlei   }
2433869309aSwlei   // Recover the call stack
2443869309aSwlei   Stack.popFrame();
2453869309aSwlei }
2463869309aSwlei 
2473869309aSwlei void VirtualUnwinder::collectSamplesFromFrameTrie(
2483869309aSwlei     UnwindState::ProfiledFrame *Cur) {
2493869309aSwlei   if (Binary->usePseudoProbes()) {
2503f970168SHongtao Yu     AddressStack Stack(Binary);
2513f970168SHongtao Yu     collectSamplesFromFrameTrie<AddressStack>(Cur, Stack);
2523869309aSwlei   } else {
2533869309aSwlei     FrameStack Stack(Binary);
2543869309aSwlei     collectSamplesFromFrameTrie<FrameStack>(Cur, Stack);
2553869309aSwlei   }
2561f05b1a9Swlei }
2571f05b1a9Swlei 
2581f05b1a9Swlei void VirtualUnwinder::recordBranchCount(const LBREntry &Branch,
2591f05b1a9Swlei                                         UnwindState &State, uint64_t Repeat) {
260bfcb2c11Swlei   if (Branch.Target == ExternalAddr)
2611f05b1a9Swlei     return;
2623869309aSwlei 
263bfcb2c11Swlei   // Record external-to-internal pattern on the trie root, it later can be
264bfcb2c11Swlei   // used for generating head samples.
265bfcb2c11Swlei   if (Branch.Source == ExternalAddr) {
266bfcb2c11Swlei     State.getDummyRootPtr()->recordBranchCount(Branch.Source, Branch.Target,
267bfcb2c11Swlei                                                Repeat);
268bfcb2c11Swlei     return;
269bfcb2c11Swlei   }
270bfcb2c11Swlei 
2713869309aSwlei   if (Binary->usePseudoProbes()) {
2723869309aSwlei     // Same as recordRangeCount, We don't need to top frame probe since we will
2733869309aSwlei     // extract it from branch's source address
2743869309aSwlei     State.getParentFrame()->recordBranchCount(Branch.Source, Branch.Target,
2753869309aSwlei                                               Repeat);
2763869309aSwlei   } else {
2773869309aSwlei     State.CurrentLeafFrame->recordBranchCount(Branch.Source, Branch.Target,
2783869309aSwlei                                               Repeat);
2793869309aSwlei   }
2801f05b1a9Swlei }
2811f05b1a9Swlei 
282964053d5Swlei bool VirtualUnwinder::unwind(const PerfSample *Sample, uint64_t Repeat) {
2831f05b1a9Swlei   // Capture initial state as starting point for unwinding.
284964053d5Swlei   UnwindState State(Sample, Binary);
2851f05b1a9Swlei 
2861f05b1a9Swlei   // Sanity check - making sure leaf of LBR aligns with leaf of stack sample
2871f05b1a9Swlei   // Stack sample sometimes can be unreliable, so filter out bogus ones.
2881f05b1a9Swlei   if (!State.validateInitialState())
2891f05b1a9Swlei     return false;
2901f05b1a9Swlei 
291bfcb2c11Swlei   NumTotalBranches += State.LBRStack.size();
2921f05b1a9Swlei   // Now process the LBR samples in parrallel with stack sample
2931f05b1a9Swlei   // Note that we do not reverse the LBR entry order so we can
2941f05b1a9Swlei   // unwind the sample stack as we walk through LBR entries.
2951f05b1a9Swlei   while (State.hasNextLBR()) {
2961f05b1a9Swlei     State.checkStateConsistency();
2971f05b1a9Swlei 
2983dcb60dbSwlei     // Do not attempt linear unwind for the leaf range as it's incomplete.
2993dcb60dbSwlei     if (!State.IsLastLBR()) {
3001f05b1a9Swlei       // Unwind implicit calls/returns from inlining, along the linear path,
3011f05b1a9Swlei       // break into smaller sub section each with its own calling context.
3021f05b1a9Swlei       unwindLinear(State, Repeat);
3031f05b1a9Swlei     }
3041f05b1a9Swlei 
3051f05b1a9Swlei     // Save the LBR branch before it gets unwound.
3061f05b1a9Swlei     const LBREntry &Branch = State.getCurrentLBR();
3071f05b1a9Swlei     if (isCallState(State)) {
3081f05b1a9Swlei       // Unwind calls - we know we encountered call if LBR overlaps with
3091f05b1a9Swlei       // transition between leaf the 2nd frame. Note that for calls that
3101f05b1a9Swlei       // were not in the original stack sample, we should have added the
3111f05b1a9Swlei       // extra frame when processing the return paired with this call.
3121f05b1a9Swlei       unwindCall(State);
3131f05b1a9Swlei     } else if (isReturnState(State)) {
3148a0406dcSHongtao Yu       // Unwind returns - check whether the IP is indeed at a return
3158a0406dcSHongtao Yu       // instruction
3161f05b1a9Swlei       unwindReturn(State);
3178a0406dcSHongtao Yu     } else if (isValidState(State)) {
3183dcb60dbSwlei       // Unwind branches
3193dcb60dbSwlei       unwindBranch(State);
3208a0406dcSHongtao Yu     } else {
3218a0406dcSHongtao Yu       // Skip unwinding the rest of LBR trace. Reset the stack and update the
3228a0406dcSHongtao Yu       // state so that the rest of the trace can still be processed as if they
3238a0406dcSHongtao Yu       // do not have stack samples.
3248a0406dcSHongtao Yu       State.clearCallStack();
3258a0406dcSHongtao Yu       State.InstPtr.update(State.getCurrentLBRSource());
3268a0406dcSHongtao Yu       State.pushFrame(State.InstPtr.Address);
3271f05b1a9Swlei     }
3288a0406dcSHongtao Yu 
3291f05b1a9Swlei     State.advanceLBR();
3301f05b1a9Swlei     // Record `branch` with calling context after unwinding.
3311f05b1a9Swlei     recordBranchCount(Branch, State, Repeat);
3321f05b1a9Swlei   }
3333869309aSwlei   // As samples are aggregated on trie, record them into counter map
3343869309aSwlei   collectSamplesFromFrameTrie(State.getDummyRootPtr());
3351f05b1a9Swlei 
3361f05b1a9Swlei   return true;
3371f05b1a9Swlei }
3381f05b1a9Swlei 
339a5f411b7Swlei std::unique_ptr<PerfReaderBase>
34017f6cba3SWenlei He PerfReaderBase::create(ProfiledBinary *Binary, PerfInputFile &PerfInput,
3412fa6eaf9Sxur-llvm                        std::optional<int32_t> PIDFilter) {
342a5f411b7Swlei   std::unique_ptr<PerfReaderBase> PerfReader;
343a5f411b7Swlei 
344a5f411b7Swlei   if (PerfInput.Format == PerfFormat::UnsymbolizedProfile) {
345a5f411b7Swlei     PerfReader.reset(
346a5f411b7Swlei         new UnsymbolizedProfileReader(Binary, PerfInput.InputFile));
347a5f411b7Swlei     return PerfReader;
3481f0bc617SWenlei He   }
3491f0bc617SWenlei He 
350a5f411b7Swlei   // For perf data input, we need to convert them into perf script first.
3512fa6eaf9Sxur-llvm   // If this is a kernel perf file, there is no need for retrieving PIDs.
352a5f411b7Swlei   if (PerfInput.Format == PerfFormat::PerfData)
3532fa6eaf9Sxur-llvm     PerfInput = PerfScriptReader::convertPerfDataToTrace(
3542fa6eaf9Sxur-llvm         Binary, Binary->isKernel(), PerfInput, PIDFilter);
355a5f411b7Swlei 
356a5f411b7Swlei   assert((PerfInput.Format == PerfFormat::PerfScript) &&
357a5f411b7Swlei          "Should be a perfscript!");
358a5f411b7Swlei 
359a5f411b7Swlei   PerfInput.Content =
360a5f411b7Swlei       PerfScriptReader::checkPerfScriptType(PerfInput.InputFile);
361a5f411b7Swlei   if (PerfInput.Content == PerfContent::LBRStack) {
36217f6cba3SWenlei He     PerfReader.reset(
36317f6cba3SWenlei He         new HybridPerfReader(Binary, PerfInput.InputFile, PIDFilter));
364a5f411b7Swlei   } else if (PerfInput.Content == PerfContent::LBR) {
36517f6cba3SWenlei He     PerfReader.reset(new LBRPerfReader(Binary, PerfInput.InputFile, PIDFilter));
3666da9241aSwlei   } else {
3676da9241aSwlei     exitWithError("Unsupported perfscript!");
3686da9241aSwlei   }
3696da9241aSwlei 
3706da9241aSwlei   return PerfReader;
3716da9241aSwlei }
3726da9241aSwlei 
373da2f5d0aSFangrui Song PerfInputFile
3742fa6eaf9Sxur-llvm PerfScriptReader::convertPerfDataToTrace(ProfiledBinary *Binary, bool SkipPID,
375da2f5d0aSFangrui Song                                          PerfInputFile &File,
3762fa6eaf9Sxur-llvm                                          std::optional<int32_t> PIDFilter) {
377a5f411b7Swlei   StringRef PerfData = File.InputFile;
3781f0bc617SWenlei He   // Run perf script to retrieve PIDs matching binary we're interested in.
3791f0bc617SWenlei He   auto PerfExecutable = sys::Process::FindInEnvPath("PATH", "perf");
3801f0bc617SWenlei He   if (!PerfExecutable) {
3811f0bc617SWenlei He     exitWithError("Perf not found.");
3821f0bc617SWenlei He   }
3831f0bc617SWenlei He   std::string PerfPath = *PerfExecutable;
3848466ab98SMatthias Braun   SmallString<128> PerfTraceFile;
3858466ab98SMatthias Braun   sys::fs::createUniquePath("perf-script-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%.tmp",
3868466ab98SMatthias Braun                             PerfTraceFile, /*MakeAbsolute=*/true);
3878466ab98SMatthias Braun   std::string ErrorFile = std::string(PerfTraceFile) + ".err";
388e748db0fSMatt Arsenault   std::optional<StringRef> Redirects[] = {std::nullopt,             // Stdin
3891f0bc617SWenlei He                                           StringRef(PerfTraceFile), // Stdout
3901b212d10Swlei                                           StringRef(ErrorFile)};    // Stderr
3913f7f446dSHaohai Wen   PerfScriptReader::TempFileCleanups.emplace_back(PerfTraceFile);
3923f7f446dSHaohai Wen   PerfScriptReader::TempFileCleanups.emplace_back(ErrorFile);
3933f7f446dSHaohai Wen 
3942fa6eaf9Sxur-llvm   std::string PIDs;
3952fa6eaf9Sxur-llvm   if (!SkipPID) {
3962fa6eaf9Sxur-llvm     StringRef ScriptMMapArgs[] = {PerfPath, "script",   "--show-mmap-events",
3972fa6eaf9Sxur-llvm                                   "-F",     "comm,pid", "-i",
3982fa6eaf9Sxur-llvm                                   PerfData};
3992fa6eaf9Sxur-llvm     sys::ExecuteAndWait(PerfPath, ScriptMMapArgs, std::nullopt, Redirects);
4002fa6eaf9Sxur-llvm 
4011f0bc617SWenlei He     // Collect the PIDs
4021f0bc617SWenlei He     TraceStream TraceIt(PerfTraceFile);
4032fa6eaf9Sxur-llvm     std::unordered_set<int32_t> PIDSet;
4041f0bc617SWenlei He     while (!TraceIt.isAtEoF()) {
4051f0bc617SWenlei He       MMapEvent MMap;
4062fa6eaf9Sxur-llvm       if (isMMapEvent(TraceIt.getCurrentLine()) &&
4072fa6eaf9Sxur-llvm           extractMMapEventForBinary(Binary, TraceIt.getCurrentLine(), MMap)) {
408da4e5fc8SWenlei He         auto It = PIDSet.emplace(MMap.PID);
40917f6cba3SWenlei He         if (It.second && (!PIDFilter || MMap.PID == *PIDFilter)) {
4101f0bc617SWenlei He           if (!PIDs.empty()) {
4111f0bc617SWenlei He             PIDs.append(",");
4121f0bc617SWenlei He           }
4131f0bc617SWenlei He           PIDs.append(utostr(MMap.PID));
4141f0bc617SWenlei He         }
415da4e5fc8SWenlei He       }
4161f0bc617SWenlei He       TraceIt.advance();
4171f0bc617SWenlei He     }
4181f0bc617SWenlei He 
419da4e5fc8SWenlei He     if (PIDs.empty()) {
420da4e5fc8SWenlei He       exitWithError("No relevant mmap event is found in perf data.");
421da4e5fc8SWenlei He     }
4222fa6eaf9Sxur-llvm   }
423da4e5fc8SWenlei He 
4241f0bc617SWenlei He   // Run perf script again to retrieve events for PIDs collected above
4252fa6eaf9Sxur-llvm   SmallVector<StringRef, 8> ScriptSampleArgs;
4262fa6eaf9Sxur-llvm   ScriptSampleArgs.push_back(PerfPath);
4272fa6eaf9Sxur-llvm   ScriptSampleArgs.push_back("script");
4282fa6eaf9Sxur-llvm   ScriptSampleArgs.push_back("--show-mmap-events");
4292fa6eaf9Sxur-llvm   ScriptSampleArgs.push_back("-F");
43023609a38STim Creech   ScriptSampleArgs.push_back("ip,brstack");
4312fa6eaf9Sxur-llvm   ScriptSampleArgs.push_back("-i");
4322fa6eaf9Sxur-llvm   ScriptSampleArgs.push_back(PerfData);
4332fa6eaf9Sxur-llvm   if (!PIDs.empty()) {
4342fa6eaf9Sxur-llvm     ScriptSampleArgs.push_back("--pid");
4352fa6eaf9Sxur-llvm     ScriptSampleArgs.push_back(PIDs);
4362fa6eaf9Sxur-llvm   }
437b4482f7cSKazu Hirata   sys::ExecuteAndWait(PerfPath, ScriptSampleArgs, std::nullopt, Redirects);
4381f0bc617SWenlei He 
4398466ab98SMatthias Braun   return {std::string(PerfTraceFile), PerfFormat::PerfScript,
4408466ab98SMatthias Braun           PerfContent::UnknownContent};
4411f0bc617SWenlei He }
4421f0bc617SWenlei He 
4438c03f400SHaohai Wen static StringRef filename(StringRef Path, bool UseBackSlash) {
4448c03f400SHaohai Wen   llvm::sys::path::Style PathStyle =
4458c03f400SHaohai Wen       UseBackSlash ? llvm::sys::path::Style::windows_backslash
4468c03f400SHaohai Wen                    : llvm::sys::path::Style::native;
4478c03f400SHaohai Wen   StringRef FileName = llvm::sys::path::filename(Path, PathStyle);
4488c03f400SHaohai Wen 
4498c03f400SHaohai Wen   // In case this file use \r\n as newline.
4508c03f400SHaohai Wen   if (UseBackSlash && FileName.back() == '\r')
4518c03f400SHaohai Wen     return FileName.drop_back();
4528c03f400SHaohai Wen 
4538c03f400SHaohai Wen   return FileName;
4548c03f400SHaohai Wen }
4558c03f400SHaohai Wen 
456a5f411b7Swlei void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) {
457f812c192Swlei   // Drop the event which doesn't belong to user-provided binary
4588c03f400SHaohai Wen   StringRef BinaryName = filename(Event.BinaryPath, Binary->isCOFF());
4592fa6eaf9Sxur-llvm   bool IsKernel = Binary->isKernel();
4602fa6eaf9Sxur-llvm   if (!IsKernel && Binary->getName() != BinaryName)
4612fa6eaf9Sxur-llvm     return;
4622fa6eaf9Sxur-llvm   if (IsKernel && !Binary->isKernelImageName(BinaryName))
463a94fa862Swlei     return;
464a94fa862Swlei 
46517f6cba3SWenlei He   // Drop the event if process does not match pid filter
46617f6cba3SWenlei He   if (PIDFilter && Event.PID != *PIDFilter)
46717f6cba3SWenlei He     return;
46817f6cba3SWenlei He 
469fe3ba908Swlei   // Drop the event if its image is loaded at the same address
470f812c192Swlei   if (Event.Address == Binary->getBaseAddress()) {
471f812c192Swlei     Binary->setIsLoadedByMMap(true);
472fe3ba908Swlei     return;
473fe3ba908Swlei   }
474a94fa862Swlei 
4752fa6eaf9Sxur-llvm   if (IsKernel || Event.Offset == Binary->getTextSegmentOffset()) {
47607120384SHongtao Yu     // A binary image could be unloaded and then reloaded at different
477f812c192Swlei     // place, so update binary load address.
47807120384SHongtao Yu     // Only update for the first executable segment and assume all other
47907120384SHongtao Yu     // segments are loaded at consecutive memory addresses, which is the case on
48007120384SHongtao Yu     // X64.
481f812c192Swlei     Binary->setBaseAddress(Event.Address);
482f812c192Swlei     Binary->setIsLoadedByMMap(true);
48307120384SHongtao Yu   } else {
48407120384SHongtao Yu     // Verify segments are loaded consecutively.
485f812c192Swlei     const auto &Offsets = Binary->getTextSegmentOffsets();
48689f14332SKazu Hirata     auto It = llvm::lower_bound(Offsets, Event.Offset);
48707120384SHongtao Yu     if (It != Offsets.end() && *It == Event.Offset) {
48807120384SHongtao Yu       // The event is for loading a separate executable segment.
48907120384SHongtao Yu       auto I = std::distance(Offsets.begin(), It);
490f812c192Swlei       const auto &PreferredAddrs = Binary->getPreferredTextSegmentAddresses();
491f812c192Swlei       if (PreferredAddrs[I] - Binary->getPreferredBaseAddress() !=
492f812c192Swlei           Event.Address - Binary->getBaseAddress())
49307120384SHongtao Yu         exitWithError("Executable segments not loaded consecutively");
49407120384SHongtao Yu     } else {
49507120384SHongtao Yu       if (It == Offsets.begin())
49607120384SHongtao Yu         exitWithError("File offset not found");
49707120384SHongtao Yu       else {
49807120384SHongtao Yu         // Find the segment the event falls in. A large segment could be loaded
49907120384SHongtao Yu         // via multiple mmap calls with consecutive memory addresses.
50007120384SHongtao Yu         --It;
50107120384SHongtao Yu         assert(*It < Event.Offset);
502f812c192Swlei         if (Event.Offset - *It != Event.Address - Binary->getBaseAddress())
50307120384SHongtao Yu           exitWithError("Segment not loaded by consecutive mmaps");
50407120384SHongtao Yu       }
50507120384SHongtao Yu     }
50607120384SHongtao Yu   }
507a94fa862Swlei }
508a94fa862Swlei 
509c681400bSwlei static std::string getContextKeyStr(ContextKey *K,
510c681400bSwlei                                     const ProfiledBinary *Binary) {
511c681400bSwlei   if (const auto *CtxKey = dyn_cast<StringBasedCtxKey>(K)) {
512b9db7036SHongtao Yu     return SampleContext::getContextString(CtxKey->Context);
5133f970168SHongtao Yu   } else if (const auto *CtxKey = dyn_cast<AddrBasedCtxKey>(K)) {
5143f970168SHongtao Yu     std::ostringstream OContextStr;
5153f970168SHongtao Yu     for (uint32_t I = 0; I < CtxKey->Context.size(); I++) {
5163f970168SHongtao Yu       if (OContextStr.str().size())
5173f970168SHongtao Yu         OContextStr << " @ ";
51846765248Swlei       uint64_t Address = CtxKey->Context[I];
51946765248Swlei       if (UseOffset) {
52046765248Swlei         if (UseLoadableSegmentAsBase)
52146765248Swlei           Address -= Binary->getFirstLoadableAddress();
52246765248Swlei         else
52346765248Swlei           Address -= Binary->getPreferredBaseAddress();
52446765248Swlei       }
5253f970168SHongtao Yu       OContextStr << "0x"
52646765248Swlei                   << utohexstr(Address,
527b62e3a73SCorentin Jabot                                /*LowerCase=*/true);
528c681400bSwlei     }
5293f970168SHongtao Yu     return OContextStr.str();
530b9db7036SHongtao Yu   } else {
531b9db7036SHongtao Yu     llvm_unreachable("unexpected key type");
532c681400bSwlei   }
533c681400bSwlei }
534c681400bSwlei 
5356da9241aSwlei void HybridPerfReader::unwindSamples() {
5363dcb60dbSwlei   VirtualUnwinder Unwinder(&SampleCounters, Binary);
5371f05b1a9Swlei   for (const auto &Item : AggregatedSamples) {
538964053d5Swlei     const PerfSample *Sample = Item.first.getPtr();
5391f05b1a9Swlei     Unwinder.unwind(Sample, Item.second);
5401f05b1a9Swlei   }
5411f05b1a9Swlei 
5426eca242eSWenlei He   // Warn about untracked frames due to missing probes.
543138202a8Swlei   if (ShowDetailedWarning) {
54430c3aba9Swlei     for (auto Address : Unwinder.getUntrackedCallsites())
5456eca242eSWenlei He       WithColor::warning() << "Profile context truncated due to missing probe "
5466eca242eSWenlei He                            << "for call instruction at "
547941191aaSWenlei He                            << format("0x%" PRIx64, Address) << "\n";
5481f05b1a9Swlei   }
5491f05b1a9Swlei 
55030c3aba9Swlei   emitWarningSummary(Unwinder.getUntrackedCallsites().size(),
55130c3aba9Swlei                      SampleCounters.size(),
552138202a8Swlei                      "of profiled contexts are truncated due to missing probe "
553138202a8Swlei                      "for call instruction.");
5540f53df86Swlei 
5550f53df86Swlei   emitWarningSummary(
5560f53df86Swlei       Unwinder.NumMismatchedExtCallBranch, Unwinder.NumTotalBranches,
5570f53df86Swlei       "of branches'source is a call instruction but doesn't match call frame "
5580f53df86Swlei       "stack, likely due to unwinding error of external frame.");
5590f53df86Swlei 
560bfcb2c11Swlei   emitWarningSummary(Unwinder.NumPairedExtAddr * 2, Unwinder.NumTotalBranches,
561bfcb2c11Swlei                      "of branches containing paired external address.");
562bfcb2c11Swlei 
563bfcb2c11Swlei   emitWarningSummary(Unwinder.NumUnpairedExtAddr, Unwinder.NumTotalBranches,
564bfcb2c11Swlei                      "of branches containing external address but doesn't have "
565bfcb2c11Swlei                      "another external address to pair, likely due to "
566bfcb2c11Swlei                      "interrupt jmp or broken perf script.");
567bfcb2c11Swlei 
5680f53df86Swlei   emitWarningSummary(
5690f53df86Swlei       Unwinder.NumMismatchedProEpiBranch, Unwinder.NumTotalBranches,
5700f53df86Swlei       "of branches'source is a call instruction but doesn't match call frame "
5710f53df86Swlei       "stack, likely due to frame in prolog/epilog.");
5720f53df86Swlei 
5730f53df86Swlei   emitWarningSummary(Unwinder.NumMissingExternalFrame,
5740f53df86Swlei                      Unwinder.NumExtCallBranch,
5750f53df86Swlei                      "of artificial call branches but doesn't have an external "
5760f53df86Swlei                      "frame to match.");
577138202a8Swlei }
578138202a8Swlei 
579a5f411b7Swlei bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
580f812c192Swlei                                        SmallVectorImpl<LBREntry> &LBRStack) {
5811f05b1a9Swlei   // The raw format of LBR stack is like:
5821f05b1a9Swlei   // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
5831f05b1a9Swlei   //                           ... 0x4005c8/0x4005dc/P/-/-/0
584d4a01549SJay Foad   // It's in FIFO order and separated by whitespace.
5851f05b1a9Swlei   SmallVector<StringRef, 32> Records;
5868f5a2325SHaohai Wen   TraceIt.getCurrentLine().rtrim().split(Records, " ", -1, false);
587a7cdcf25Swlei   auto WarnInvalidLBR = [](TraceStream &TraceIt) {
588a7cdcf25Swlei     WithColor::warning() << "Invalid address in LBR record at line "
589a7cdcf25Swlei                          << TraceIt.getLineNumber() << ": "
590a7cdcf25Swlei                          << TraceIt.getCurrentLine() << "\n";
591a7cdcf25Swlei   };
5921f05b1a9Swlei 
593964053d5Swlei   // Skip the leading instruction pointer.
5941f05b1a9Swlei   size_t Index = 0;
595a7cdcf25Swlei   uint64_t LeadingAddr;
59623609a38STim Creech   if (!Records.empty() && !Records[0].contains('/')) {
59723609a38STim Creech     if (Records[0].getAsInteger(16, LeadingAddr)) {
598a7cdcf25Swlei       WarnInvalidLBR(TraceIt);
599a7cdcf25Swlei       TraceIt.advance();
600a7cdcf25Swlei       return false;
601a7cdcf25Swlei     }
60223609a38STim Creech     Index = 1;
6031f05b1a9Swlei   }
604bfcb2c11Swlei 
6051f05b1a9Swlei   // Now extract LBR samples - note that we do not reverse the
6061f05b1a9Swlei   // LBR entry order so we can unwind the sample stack as we walk
6071f05b1a9Swlei   // through LBR entries.
6081f05b1a9Swlei   while (Index < Records.size()) {
6091f05b1a9Swlei     auto &Token = Records[Index++];
6101f05b1a9Swlei     if (Token.size() == 0)
6111f05b1a9Swlei       continue;
6121f05b1a9Swlei 
6131f05b1a9Swlei     SmallVector<StringRef, 8> Addresses;
6141f05b1a9Swlei     Token.split(Addresses, "/");
6151f05b1a9Swlei     uint64_t Src;
6161f05b1a9Swlei     uint64_t Dst;
6178cbbd7e0SHongtao Yu 
6188cbbd7e0SHongtao Yu     // Stop at broken LBR records.
619a7cdcf25Swlei     if (Addresses.size() < 2 || Addresses[0].substr(2).getAsInteger(16, Src) ||
6208cbbd7e0SHongtao Yu         Addresses[1].substr(2).getAsInteger(16, Dst)) {
621a7cdcf25Swlei       WarnInvalidLBR(TraceIt);
6228cbbd7e0SHongtao Yu       break;
6238cbbd7e0SHongtao Yu     }
6241f05b1a9Swlei 
62546765248Swlei     // Canonicalize to use preferred load address as base address.
62646765248Swlei     Src = Binary->canonicalizeVirtualAddress(Src);
62746765248Swlei     Dst = Binary->canonicalizeVirtualAddress(Dst);
6281f05b1a9Swlei     bool SrcIsInternal = Binary->addressIsCode(Src);
6291f05b1a9Swlei     bool DstIsInternal = Binary->addressIsCode(Dst);
630bfcb2c11Swlei     if (!SrcIsInternal)
631bfcb2c11Swlei       Src = ExternalAddr;
632bfcb2c11Swlei     if (!DstIsInternal)
633bfcb2c11Swlei       Dst = ExternalAddr;
634bfcb2c11Swlei     // Filter external-to-external case to reduce LBR trace size.
635bfcb2c11Swlei     if (!SrcIsInternal && !DstIsInternal)
6363dcb60dbSwlei       continue;
6378c2c9728SHongtao Yu 
638bfcb2c11Swlei     LBRStack.emplace_back(LBREntry(Src, Dst));
6391f05b1a9Swlei   }
6401f05b1a9Swlei   TraceIt.advance();
6411f05b1a9Swlei   return !LBRStack.empty();
6421f05b1a9Swlei }
6431f05b1a9Swlei 
644a5f411b7Swlei bool PerfScriptReader::extractCallstack(TraceStream &TraceIt,
6453869309aSwlei                                         SmallVectorImpl<uint64_t> &CallStack) {
6461f05b1a9Swlei   // The raw format of call stack is like:
6471f05b1a9Swlei   //            4005dc      # leaf frame
6481f05b1a9Swlei   //	          400634
6491f05b1a9Swlei   //	          400684      # root frame
6501f05b1a9Swlei   // It's in bottom-up order with each frame in one line.
6511f05b1a9Swlei 
6521f05b1a9Swlei   // Extract stack frames from sample
653586ecdf2SKazu Hirata   while (!TraceIt.isAtEoF() && !TraceIt.getCurrentLine().starts_with(" 0x")) {
6541f05b1a9Swlei     StringRef FrameStr = TraceIt.getCurrentLine().ltrim();
6551f05b1a9Swlei     uint64_t FrameAddr = 0;
6561f05b1a9Swlei     if (FrameStr.getAsInteger(16, FrameAddr)) {
657dd9e2190Swlei       // We might parse a non-perf sample line like empty line and comments,
658dd9e2190Swlei       // skip it
6591f05b1a9Swlei       TraceIt.advance();
660dd9e2190Swlei       return false;
6611f05b1a9Swlei     }
6621f05b1a9Swlei     TraceIt.advance();
66346765248Swlei 
66446765248Swlei     FrameAddr = Binary->canonicalizeVirtualAddress(FrameAddr);
6651f05b1a9Swlei     // Currently intermixed frame from different binaries is not supported.
6663dcb60dbSwlei     if (!Binary->addressIsCode(FrameAddr)) {
6673dcb60dbSwlei       if (CallStack.empty())
6683dcb60dbSwlei         NumLeafExternalFrame++;
6693dcb60dbSwlei       // Push a special value(ExternalAddr) for the external frames so that
6703dcb60dbSwlei       // unwinder can still work on this with artificial Call/Return branch.
6713dcb60dbSwlei       // After unwinding, the context will be truncated for external frame.
6723dcb60dbSwlei       // Also deduplicate the consecutive external addresses.
6733dcb60dbSwlei       if (CallStack.empty() || CallStack.back() != ExternalAddr)
6743dcb60dbSwlei         CallStack.emplace_back(ExternalAddr);
6753dcb60dbSwlei       continue;
6763dcb60dbSwlei     }
6771f05b1a9Swlei 
6780057c718SHongtao Yu     // We need to translate return address to call address for non-leaf frames.
6791f05b1a9Swlei     if (!CallStack.empty()) {
6800057c718SHongtao Yu       auto CallAddr = Binary->getCallAddrFromFrameAddr(FrameAddr);
6810057c718SHongtao Yu       if (!CallAddr) {
6820057c718SHongtao Yu         // Stop at an invalid return address caused by bad unwinding. This could
6830057c718SHongtao Yu         // happen to frame-pointer-based unwinding and the callee functions that
6840057c718SHongtao Yu         // do not have the frame pointer chain set up.
6850057c718SHongtao Yu         InvalidReturnAddresses.insert(FrameAddr);
6860057c718SHongtao Yu         break;
6870057c718SHongtao Yu       }
6880057c718SHongtao Yu       FrameAddr = CallAddr;
6891f05b1a9Swlei     }
6901f05b1a9Swlei 
6911f05b1a9Swlei     CallStack.emplace_back(FrameAddr);
6921f05b1a9Swlei   }
6931f05b1a9Swlei 
6943dcb60dbSwlei   // Strip out the bottom external addr.
6953dcb60dbSwlei   if (CallStack.size() > 1 && CallStack.back() == ExternalAddr)
6963dcb60dbSwlei     CallStack.pop_back();
6973dcb60dbSwlei 
6981f05b1a9Swlei   // Skip other unrelated line, find the next valid LBR line
699dd9e2190Swlei   // Note that even for empty call stack, we should skip the address at the
700dd9e2190Swlei   // bottom, otherwise the following pass may generate a truncated callstack
701586ecdf2SKazu Hirata   while (!TraceIt.isAtEoF() && !TraceIt.getCurrentLine().starts_with(" 0x")) {
7021f05b1a9Swlei     TraceIt.advance();
7031f05b1a9Swlei   }
7041f05b1a9Swlei   // Filter out broken stack sample. We may not have complete frame info
7051f05b1a9Swlei   // if sample end up in prolog/epilog, the result is dangling context not
7061f05b1a9Swlei   // connected to entry point. This should be relatively rare thus not much
7071f05b1a9Swlei   // impact on overall profile quality. However we do want to filter them
7081f05b1a9Swlei   // out to reduce the number of different calling contexts. One instance
7091f05b1a9Swlei   // of such case - when sample landed in prolog/epilog, somehow stack
7101f05b1a9Swlei   // walking will be broken in an unexpected way that higher frames will be
7111f05b1a9Swlei   // missing.
712dd9e2190Swlei   return !CallStack.empty() &&
713dd9e2190Swlei          !Binary->addressInPrologEpilog(CallStack.front());
7141f05b1a9Swlei }
7151f05b1a9Swlei 
716a5f411b7Swlei void PerfScriptReader::warnIfMissingMMap() {
717964053d5Swlei   if (!Binary->getMissingMMapWarned() && !Binary->getIsLoadedByMMap()) {
718941191aaSWenlei He     WithColor::warning() << "No relevant mmap event is matched for "
719941191aaSWenlei He                          << Binary->getName()
720941191aaSWenlei He                          << ", will use preferred address ("
721941191aaSWenlei He                          << format("0x%" PRIx64,
722941191aaSWenlei He                                    Binary->getPreferredBaseAddress())
723941191aaSWenlei He                          << ") as the base loading address!\n";
724964053d5Swlei     // Avoid redundant warning, only warn at the first unmatched sample.
725964053d5Swlei     Binary->setMissingMMapWarned(true);
726964053d5Swlei   }
727964053d5Swlei }
728964053d5Swlei 
729f1affe8dSwlei void HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
7301f05b1a9Swlei   // The raw hybird sample started with call stack in FILO order and followed
7311f05b1a9Swlei   // intermediately by LBR sample
7321f05b1a9Swlei   // e.g.
7331f05b1a9Swlei   // 	          4005dc    # call stack leaf
7341f05b1a9Swlei   //	          400634
7351f05b1a9Swlei   //	          400684    # call stack root
7361f05b1a9Swlei   // 0x4005c8/0x4005dc/P/-/-/0   0x40062f/0x4005b0/P/-/-/0 ...
7371f05b1a9Swlei   //          ... 0x4005c8/0x4005dc/P/-/-/0    # LBR Entries
7381f05b1a9Swlei   //
739964053d5Swlei   std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>();
7408a0406dcSHongtao Yu #ifndef NDEBUG
7418a0406dcSHongtao Yu   Sample->Linenum = TraceIt.getLineNumber();
7428a0406dcSHongtao Yu #endif
743964053d5Swlei   // Parsing call stack and populate into PerfSample.CallStack
744414930b9Swlei   if (!extractCallstack(TraceIt, Sample->CallStack)) {
7451f05b1a9Swlei     // Skip the next LBR line matched current call stack
746586ecdf2SKazu Hirata     if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().starts_with(" 0x"))
7471f05b1a9Swlei       TraceIt.advance();
7481f05b1a9Swlei     return;
7491f05b1a9Swlei   }
750f812c192Swlei 
751964053d5Swlei   warnIfMissingMMap();
7521f05b1a9Swlei 
753586ecdf2SKazu Hirata   if (!TraceIt.isAtEoF() && TraceIt.getCurrentLine().starts_with(" 0x")) {
754964053d5Swlei     // Parsing LBR stack and populate into PerfSample.LBRStack
755f812c192Swlei     if (extractLBRStack(TraceIt, Sample->LBRStack)) {
756ab5d65e6Swlei       if (IgnoreStackSamples) {
757ab5d65e6Swlei         Sample->CallStack.clear();
758ab5d65e6Swlei       } else {
7591f05b1a9Swlei         // Canonicalize stack leaf to avoid 'random' IP from leaf frame skew LBR
7601f05b1a9Swlei         // ranges
761414930b9Swlei         Sample->CallStack.front() = Sample->LBRStack[0].Target;
762ab5d65e6Swlei       }
7631f05b1a9Swlei       // Record samples by aggregation
764f1affe8dSwlei       AggregatedSamples[Hashable<PerfSample>(Sample)] += Count;
7651f05b1a9Swlei     }
7661f05b1a9Swlei   } else {
7671f05b1a9Swlei     // LBR sample is encoded in single line after stack sample
7681f05b1a9Swlei     exitWithError("'Hybrid perf sample is corrupted, No LBR sample line");
7691f05b1a9Swlei   }
7701f05b1a9Swlei }
7711f05b1a9Swlei 
772a5f411b7Swlei void PerfScriptReader::writeUnsymbolizedProfile(StringRef Filename) {
773964053d5Swlei   std::error_code EC;
774964053d5Swlei   raw_fd_ostream OS(Filename, EC, llvm::sys::fs::OF_TextWithCRLF);
775964053d5Swlei   if (EC)
776964053d5Swlei     exitWithError(EC, Filename);
777a5f411b7Swlei   writeUnsymbolizedProfile(OS);
778964053d5Swlei }
779964053d5Swlei 
7801422fa5fSwlei // Use ordered map to make the output deterministic
7811422fa5fSwlei using OrderedCounterForPrint = std::map<std::string, SampleCounter *>;
7821422fa5fSwlei 
783a5f411b7Swlei void PerfScriptReader::writeUnsymbolizedProfile(raw_fd_ostream &OS) {
7841422fa5fSwlei   OrderedCounterForPrint OrderedCounters;
7851422fa5fSwlei   for (auto &CI : SampleCounters) {
7861422fa5fSwlei     OrderedCounters[getContextKeyStr(CI.first.getPtr(), Binary)] = &CI.second;
787964053d5Swlei   }
788964053d5Swlei 
789a5f411b7Swlei   auto SCounterPrinter = [&](RangeSample &Counter, StringRef Separator,
7901422fa5fSwlei                              uint32_t Indent) {
7911422fa5fSwlei     OS.indent(Indent);
7921422fa5fSwlei     OS << Counter.size() << "\n";
793a5f411b7Swlei     for (auto &I : Counter) {
794f7976edcSWenlei He       uint64_t Start = I.first.first;
795f7976edcSWenlei He       uint64_t End = I.first.second;
796f7976edcSWenlei He 
79746765248Swlei       if (UseOffset) {
79846765248Swlei         if (UseLoadableSegmentAsBase) {
799f7976edcSWenlei He           Start -= Binary->getFirstLoadableAddress();
800f7976edcSWenlei He           End -= Binary->getFirstLoadableAddress();
80146765248Swlei         } else {
80246765248Swlei           Start -= Binary->getPreferredBaseAddress();
80346765248Swlei           End -= Binary->getPreferredBaseAddress();
80446765248Swlei         }
805f7976edcSWenlei He       }
806f7976edcSWenlei He 
8071422fa5fSwlei       OS.indent(Indent);
8081422fa5fSwlei       OS << Twine::utohexstr(Start) << Separator << Twine::utohexstr(End) << ":"
8091422fa5fSwlei          << I.second << "\n";
8101422fa5fSwlei     }
8111422fa5fSwlei   };
8121422fa5fSwlei 
8131422fa5fSwlei   for (auto &CI : OrderedCounters) {
8141422fa5fSwlei     uint32_t Indent = 0;
815e36786d1SHongtao Yu     if (ProfileIsCS) {
8161422fa5fSwlei       // Context string key
8171422fa5fSwlei       OS << "[" << CI.first << "]\n";
8181422fa5fSwlei       Indent = 2;
8191422fa5fSwlei     }
8201422fa5fSwlei 
8211422fa5fSwlei     SampleCounter &Counter = *CI.second;
8221422fa5fSwlei     SCounterPrinter(Counter.RangeCounter, "-", Indent);
8231422fa5fSwlei     SCounterPrinter(Counter.BranchCounter, "->", Indent);
824964053d5Swlei   }
825964053d5Swlei }
826964053d5Swlei 
827a5f411b7Swlei // Format of input:
828a5f411b7Swlei // number of entries in RangeCounter
829a5f411b7Swlei // from_1-to_1:count_1
830a5f411b7Swlei // from_2-to_2:count_2
831a5f411b7Swlei // ......
832a5f411b7Swlei // from_n-to_n:count_n
833a5f411b7Swlei // number of entries in BranchCounter
834a5f411b7Swlei // src_1->dst_1:count_1
835a5f411b7Swlei // src_2->dst_2:count_2
836a5f411b7Swlei // ......
837a5f411b7Swlei // src_n->dst_n:count_n
838a5f411b7Swlei void UnsymbolizedProfileReader::readSampleCounters(TraceStream &TraceIt,
839a5f411b7Swlei                                                    SampleCounter &SCounters) {
840a5f411b7Swlei   auto exitWithErrorForTraceLine = [](TraceStream &TraceIt) {
841a5f411b7Swlei     std::string Msg = TraceIt.isAtEoF()
842a5f411b7Swlei                           ? "Invalid raw profile!"
843a5f411b7Swlei                           : "Invalid raw profile at line " +
844a5f411b7Swlei                                 Twine(TraceIt.getLineNumber()).str() + ": " +
845a5f411b7Swlei                                 TraceIt.getCurrentLine().str();
846a5f411b7Swlei     exitWithError(Msg);
847a5f411b7Swlei   };
848a5f411b7Swlei   auto ReadNumber = [&](uint64_t &Num) {
849a5f411b7Swlei     if (TraceIt.isAtEoF())
850a5f411b7Swlei       exitWithErrorForTraceLine(TraceIt);
851a5f411b7Swlei     if (TraceIt.getCurrentLine().ltrim().getAsInteger(10, Num))
852a5f411b7Swlei       exitWithErrorForTraceLine(TraceIt);
853a5f411b7Swlei     TraceIt.advance();
854a5f411b7Swlei   };
855a5f411b7Swlei 
856a5f411b7Swlei   auto ReadCounter = [&](RangeSample &Counter, StringRef Separator) {
857a5f411b7Swlei     uint64_t Num = 0;
858a5f411b7Swlei     ReadNumber(Num);
859a5f411b7Swlei     while (Num--) {
860a5f411b7Swlei       if (TraceIt.isAtEoF())
861a5f411b7Swlei         exitWithErrorForTraceLine(TraceIt);
862a5f411b7Swlei       StringRef Line = TraceIt.getCurrentLine().ltrim();
863a5f411b7Swlei 
864a5f411b7Swlei       uint64_t Count = 0;
865a5f411b7Swlei       auto LineSplit = Line.split(":");
866a5f411b7Swlei       if (LineSplit.second.empty() || LineSplit.second.getAsInteger(10, Count))
867a5f411b7Swlei         exitWithErrorForTraceLine(TraceIt);
868a5f411b7Swlei 
869a5f411b7Swlei       uint64_t Source = 0;
870a5f411b7Swlei       uint64_t Target = 0;
871a5f411b7Swlei       auto Range = LineSplit.first.split(Separator);
872a5f411b7Swlei       if (Range.second.empty() || Range.first.getAsInteger(16, Source) ||
873a5f411b7Swlei           Range.second.getAsInteger(16, Target))
874a5f411b7Swlei         exitWithErrorForTraceLine(TraceIt);
875a5f411b7Swlei 
87646765248Swlei       if (UseOffset) {
87746765248Swlei         if (UseLoadableSegmentAsBase) {
87846765248Swlei           Source += Binary->getFirstLoadableAddress();
87946765248Swlei           Target += Binary->getFirstLoadableAddress();
88046765248Swlei         } else {
88146765248Swlei           Source += Binary->getPreferredBaseAddress();
88246765248Swlei           Target += Binary->getPreferredBaseAddress();
88346765248Swlei         }
884a5f411b7Swlei       }
885a5f411b7Swlei 
886a5f411b7Swlei       Counter[{Source, Target}] += Count;
887a5f411b7Swlei       TraceIt.advance();
888a5f411b7Swlei     }
889a5f411b7Swlei   };
890a5f411b7Swlei 
891a5f411b7Swlei   ReadCounter(SCounters.RangeCounter, "-");
892a5f411b7Swlei   ReadCounter(SCounters.BranchCounter, "->");
893a5f411b7Swlei }
894a5f411b7Swlei 
895a5f411b7Swlei void UnsymbolizedProfileReader::readUnsymbolizedProfile(StringRef FileName) {
896a5f411b7Swlei   TraceStream TraceIt(FileName);
897a5f411b7Swlei   while (!TraceIt.isAtEoF()) {
898a5f411b7Swlei     std::shared_ptr<StringBasedCtxKey> Key =
899a5f411b7Swlei         std::make_shared<StringBasedCtxKey>();
900a5f411b7Swlei     StringRef Line = TraceIt.getCurrentLine();
901a5f411b7Swlei     // Read context stack for CS profile.
902586ecdf2SKazu Hirata     if (Line.starts_with("[")) {
903e36786d1SHongtao Yu       ProfileIsCS = true;
904a5f411b7Swlei       auto I = ContextStrSet.insert(Line.str());
905a5f411b7Swlei       SampleContext::createCtxVectorFromStr(*I.first, Key->Context);
906a5f411b7Swlei       TraceIt.advance();
907a5f411b7Swlei     }
908a5f411b7Swlei     auto Ret =
909a5f411b7Swlei         SampleCounters.emplace(Hashable<ContextKey>(Key), SampleCounter());
910a5f411b7Swlei     readSampleCounters(TraceIt, Ret.first->second);
911a5f411b7Swlei   }
912a5f411b7Swlei }
913a5f411b7Swlei 
914a5f411b7Swlei void UnsymbolizedProfileReader::parsePerfTraces() {
915a5f411b7Swlei   readUnsymbolizedProfile(PerfTraceFile);
916a5f411b7Swlei }
917a5f411b7Swlei 
918a5f411b7Swlei void PerfScriptReader::computeCounterFromLBR(const PerfSample *Sample,
919964053d5Swlei                                              uint64_t Repeat) {
920964053d5Swlei   SampleCounter &Counter = SampleCounters.begin()->second;
92146765248Swlei   uint64_t EndAddress = 0;
922964053d5Swlei   for (const LBREntry &LBR : Sample->LBRStack) {
92346765248Swlei     uint64_t SourceAddress = LBR.Source;
92446765248Swlei     uint64_t TargetAddress = LBR.Target;
925964053d5Swlei 
92646765248Swlei     // Record the branch if its SourceAddress is external. It can be the case an
927bfcb2c11Swlei     // external source call an internal function, later this branch will be used
928bfcb2c11Swlei     // to generate the function's head sample.
92946765248Swlei     if (Binary->addressIsCode(TargetAddress)) {
93046765248Swlei       Counter.recordBranchCount(SourceAddress, TargetAddress, Repeat);
931964053d5Swlei     }
932964053d5Swlei 
933964053d5Swlei     // If this not the first LBR, update the range count between TO of current
934964053d5Swlei     // LBR and FROM of next LBR.
93546765248Swlei     uint64_t StartAddress = TargetAddress;
93646765248Swlei     if (Binary->addressIsCode(StartAddress) &&
93746765248Swlei         Binary->addressIsCode(EndAddress) &&
93846765248Swlei         isValidFallThroughRange(StartAddress, EndAddress, Binary))
93946765248Swlei       Counter.recordRangeCount(StartAddress, EndAddress, Repeat);
94046765248Swlei     EndAddress = SourceAddress;
941964053d5Swlei   }
942964053d5Swlei }
943964053d5Swlei 
944964053d5Swlei void LBRPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
945964053d5Swlei   std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>();
946964053d5Swlei   // Parsing LBR stack and populate into PerfSample.LBRStack
947964053d5Swlei   if (extractLBRStack(TraceIt, Sample->LBRStack)) {
948964053d5Swlei     warnIfMissingMMap();
949964053d5Swlei     // Record LBR only samples by aggregation
950964053d5Swlei     AggregatedSamples[Hashable<PerfSample>(Sample)] += Count;
951964053d5Swlei   }
952964053d5Swlei }
953964053d5Swlei 
954a5f411b7Swlei void PerfScriptReader::generateUnsymbolizedProfile() {
955a03cf331Swlei   // There is no context for LBR only sample, so initialize one entry with
956a03cf331Swlei   // fake "empty" context key.
957a03cf331Swlei   assert(SampleCounters.empty() &&
958a03cf331Swlei          "Sample counter map should be empty before raw profile generation");
959a03cf331Swlei   std::shared_ptr<StringBasedCtxKey> Key =
960a03cf331Swlei       std::make_shared<StringBasedCtxKey>();
961a03cf331Swlei   SampleCounters.emplace(Hashable<ContextKey>(Key), SampleCounter());
962964053d5Swlei   for (const auto &Item : AggregatedSamples) {
963964053d5Swlei     const PerfSample *Sample = Item.first.getPtr();
964964053d5Swlei     computeCounterFromLBR(Sample, Item.second);
965964053d5Swlei   }
966964053d5Swlei }
967964053d5Swlei 
968a5f411b7Swlei uint64_t PerfScriptReader::parseAggregatedCount(TraceStream &TraceIt) {
969f1affe8dSwlei   // The aggregated count is optional, so do not skip the line and return 1 if
970f1affe8dSwlei   // it's unmatched
971f1affe8dSwlei   uint64_t Count = 1;
972f1affe8dSwlei   if (!TraceIt.getCurrentLine().getAsInteger(10, Count))
973f1affe8dSwlei     TraceIt.advance();
974f1affe8dSwlei   return Count;
975f1affe8dSwlei }
976f1affe8dSwlei 
977a5f411b7Swlei void PerfScriptReader::parseSample(TraceStream &TraceIt) {
9783dcb60dbSwlei   NumTotalSample++;
979f1affe8dSwlei   uint64_t Count = parseAggregatedCount(TraceIt);
980f1affe8dSwlei   assert(Count >= 1 && "Aggregated count should be >= 1!");
981f1affe8dSwlei   parseSample(TraceIt, Count);
982f1affe8dSwlei }
983f1affe8dSwlei 
9842fa6eaf9Sxur-llvm bool PerfScriptReader::extractMMapEventForBinary(ProfiledBinary *Binary,
9851f0bc617SWenlei He                                                  StringRef Line,
9861f0bc617SWenlei He                                                  MMapEvent &MMap) {
9872fa6eaf9Sxur-llvm   // Parse a MMap2 line like:
988a94fa862Swlei   //  PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0
989a94fa862Swlei   //  08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so
9902fa6eaf9Sxur-llvm   constexpr static const char *const MMap2Pattern =
9912fa6eaf9Sxur-llvm       "PERF_RECORD_MMAP2 (-?[0-9]+)/[0-9]+: "
992a94fa862Swlei       "\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ "
993a94fa862Swlei       "(0x[a-f0-9]+|0) .*\\]: [-a-z]+ (.*)";
9942fa6eaf9Sxur-llvm   // Parse a MMap line like
9952fa6eaf9Sxur-llvm   // PERF_RECORD_MMAP -1/0: [0xffffffff81e00000(0x3e8fa000) @ \
9962fa6eaf9Sxur-llvm   //  0xffffffff81e00000]: x [kernel.kallsyms]_text
9972fa6eaf9Sxur-llvm   constexpr static const char *const MMapPattern =
9982fa6eaf9Sxur-llvm       "PERF_RECORD_MMAP (-?[0-9]+)/[0-9]+: "
9992fa6eaf9Sxur-llvm       "\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ "
10002fa6eaf9Sxur-llvm       "(0x[a-f0-9]+|0)\\]: [-a-z]+ (.*)";
1001a94fa862Swlei   // Field 0 - whole line
1002a94fa862Swlei   // Field 1 - PID
1003a94fa862Swlei   // Field 2 - base address
1004a94fa862Swlei   // Field 3 - mmapped size
1005a94fa862Swlei   // Field 4 - page offset
1006a94fa862Swlei   // Field 5 - binary path
1007a94fa862Swlei   enum EventIndex {
1008a94fa862Swlei     WHOLE_LINE = 0,
1009a94fa862Swlei     PID = 1,
1010cda2394dSHongtao Yu     MMAPPED_ADDRESS = 2,
1011a94fa862Swlei     MMAPPED_SIZE = 3,
1012a94fa862Swlei     PAGE_OFFSET = 4,
1013a94fa862Swlei     BINARY_PATH = 5
1014a94fa862Swlei   };
1015a94fa862Swlei 
10162fa6eaf9Sxur-llvm   bool R = false;
1017a94fa862Swlei   SmallVector<StringRef, 6> Fields;
10182fa6eaf9Sxur-llvm   if (Line.contains("PERF_RECORD_MMAP2 ")) {
10192fa6eaf9Sxur-llvm     Regex RegMmap2(MMap2Pattern);
10202fa6eaf9Sxur-llvm     R = RegMmap2.match(Line, &Fields);
10212fa6eaf9Sxur-llvm   } else if (Line.contains("PERF_RECORD_MMAP ")) {
10222fa6eaf9Sxur-llvm     Regex RegMmap(MMapPattern);
10232fa6eaf9Sxur-llvm     R = RegMmap.match(Line, &Fields);
10242fa6eaf9Sxur-llvm   } else
10252fa6eaf9Sxur-llvm     llvm_unreachable("unexpected MMAP event entry");
10262fa6eaf9Sxur-llvm 
1027a94fa862Swlei   if (!R) {
10281b212d10Swlei     std::string WarningMsg = "Cannot parse mmap event: " + Line.str() + " \n";
10291b212d10Swlei     WithColor::warning() << WarningMsg;
10302fa6eaf9Sxur-llvm     return false;
1031a94fa862Swlei   }
10322fa6eaf9Sxur-llvm   long long MMapPID = 0;
10332fa6eaf9Sxur-llvm   getAsSignedInteger(Fields[PID], 10, MMapPID);
10342fa6eaf9Sxur-llvm   MMap.PID = MMapPID;
10351f0bc617SWenlei He   Fields[MMAPPED_ADDRESS].getAsInteger(0, MMap.Address);
10361f0bc617SWenlei He   Fields[MMAPPED_SIZE].getAsInteger(0, MMap.Size);
10371f0bc617SWenlei He   Fields[PAGE_OFFSET].getAsInteger(0, MMap.Offset);
10381f0bc617SWenlei He   MMap.BinaryPath = Fields[BINARY_PATH];
1039a94fa862Swlei   if (ShowMmapEvents) {
10401f0bc617SWenlei He     outs() << "Mmap: Binary " << MMap.BinaryPath << " loaded at "
10411f0bc617SWenlei He            << format("0x%" PRIx64 ":", MMap.Address) << " \n";
1042a94fa862Swlei   }
10431f0bc617SWenlei He 
10448c03f400SHaohai Wen   StringRef BinaryName = filename(MMap.BinaryPath, Binary->isCOFF());
10452fa6eaf9Sxur-llvm   if (Binary->isKernel()) {
10462fa6eaf9Sxur-llvm     return Binary->isKernelImageName(BinaryName);
10472fa6eaf9Sxur-llvm   }
10481f0bc617SWenlei He   return Binary->getName() == BinaryName;
10491f0bc617SWenlei He }
10501f0bc617SWenlei He 
10512fa6eaf9Sxur-llvm void PerfScriptReader::parseMMapEvent(TraceStream &TraceIt) {
10521f0bc617SWenlei He   MMapEvent MMap;
10532fa6eaf9Sxur-llvm   if (extractMMapEventForBinary(Binary, TraceIt.getCurrentLine(), MMap))
10541f0bc617SWenlei He     updateBinaryAddress(MMap);
1055a94fa862Swlei   TraceIt.advance();
1056a94fa862Swlei }
1057a94fa862Swlei 
1058a5f411b7Swlei void PerfScriptReader::parseEventOrSample(TraceStream &TraceIt) {
10592fa6eaf9Sxur-llvm   if (isMMapEvent(TraceIt.getCurrentLine()))
10602fa6eaf9Sxur-llvm     parseMMapEvent(TraceIt);
10616da9241aSwlei   else
10626da9241aSwlei     parseSample(TraceIt);
10631f05b1a9Swlei }
10641f05b1a9Swlei 
1065a5f411b7Swlei void PerfScriptReader::parseAndAggregateTrace() {
1066a94fa862Swlei   // Trace line iterator
1067941191aaSWenlei He   TraceStream TraceIt(PerfTraceFile);
10681f05b1a9Swlei   while (!TraceIt.isAtEoF())
10691f05b1a9Swlei     parseEventOrSample(TraceIt);
10701f05b1a9Swlei }
10711f05b1a9Swlei 
1072941191aaSWenlei He // A LBR sample is like:
1073941191aaSWenlei He // 40062f 0x5c6313f/0x5c63170/P/-/-/0  0x5c630e7/0x5c63130/P/-/-/0 ...
1074941191aaSWenlei He // A heuristic for fast detection by checking whether a
1075941191aaSWenlei He // leading "  0x" and the '/' exist.
1076a5f411b7Swlei bool PerfScriptReader::isLBRSample(StringRef Line) {
1077941191aaSWenlei He   // Skip the leading instruction pointer
1078941191aaSWenlei He   SmallVector<StringRef, 32> Records;
1079941191aaSWenlei He   Line.trim().split(Records, " ", 2, false);
1080941191aaSWenlei He   if (Records.size() < 2)
1081941191aaSWenlei He     return false;
1082586ecdf2SKazu Hirata   if (Records[1].starts_with("0x") && Records[1].contains('/'))
1083941191aaSWenlei He     return true;
1084941191aaSWenlei He   return false;
10851f05b1a9Swlei }
1086941191aaSWenlei He 
10872fa6eaf9Sxur-llvm bool PerfScriptReader::isMMapEvent(StringRef Line) {
10881f0bc617SWenlei He   // Short cut to avoid string find is possible.
10891f0bc617SWenlei He   if (Line.empty() || Line.size() < 50)
10901f0bc617SWenlei He     return false;
10911f0bc617SWenlei He 
10921f0bc617SWenlei He   if (std::isdigit(Line[0]))
10931f0bc617SWenlei He     return false;
10941f0bc617SWenlei He 
10952fa6eaf9Sxur-llvm   // PERF_RECORD_MMAP2 or PERF_RECORD_MMAP does not appear at the beginning of
10962fa6eaf9Sxur-llvm   // the line for ` perf script  --show-mmap-events  -i ...`
10972fa6eaf9Sxur-llvm   return Line.contains("PERF_RECORD_MMAP");
10981f0bc617SWenlei He }
10991f0bc617SWenlei He 
1100941191aaSWenlei He // The raw hybird sample is like
1101941191aaSWenlei He // e.g.
1102941191aaSWenlei He // 	          4005dc    # call stack leaf
1103941191aaSWenlei He //	          400634
1104941191aaSWenlei He //	          400684    # call stack root
1105941191aaSWenlei He // 0x4005c8/0x4005dc/P/-/-/0   0x40062f/0x4005b0/P/-/-/0 ...
1106941191aaSWenlei He //          ... 0x4005c8/0x4005dc/P/-/-/0    # LBR Entries
1107941191aaSWenlei He // Determine the perfscript contains hybrid samples(call stack + LBRs) by
1108941191aaSWenlei He // checking whether there is a non-empty call stack immediately followed by
1109941191aaSWenlei He // a LBR sample
1110a5f411b7Swlei PerfContent PerfScriptReader::checkPerfScriptType(StringRef FileName) {
1111941191aaSWenlei He   TraceStream TraceIt(FileName);
1112941191aaSWenlei He   uint64_t FrameAddr = 0;
1113941191aaSWenlei He   while (!TraceIt.isAtEoF()) {
1114941191aaSWenlei He     // Skip the aggregated count
1115941191aaSWenlei He     if (!TraceIt.getCurrentLine().getAsInteger(10, FrameAddr))
1116941191aaSWenlei He       TraceIt.advance();
1117941191aaSWenlei He 
1118941191aaSWenlei He     // Detect sample with call stack
1119941191aaSWenlei He     int32_t Count = 0;
1120941191aaSWenlei He     while (!TraceIt.isAtEoF() &&
1121941191aaSWenlei He            !TraceIt.getCurrentLine().ltrim().getAsInteger(16, FrameAddr)) {
1122941191aaSWenlei He       Count++;
1123941191aaSWenlei He       TraceIt.advance();
1124941191aaSWenlei He     }
1125941191aaSWenlei He     if (!TraceIt.isAtEoF()) {
1126941191aaSWenlei He       if (isLBRSample(TraceIt.getCurrentLine())) {
1127941191aaSWenlei He         if (Count > 0)
1128a5f411b7Swlei           return PerfContent::LBRStack;
1129941191aaSWenlei He         else
1130a5f411b7Swlei           return PerfContent::LBR;
1131941191aaSWenlei He       }
1132941191aaSWenlei He       TraceIt.advance();
1133941191aaSWenlei He     }
1134941191aaSWenlei He   }
1135941191aaSWenlei He 
1136941191aaSWenlei He   exitWithError("Invalid perf script input!");
1137a5f411b7Swlei   return PerfContent::UnknownContent;
11381f05b1a9Swlei }
11391f05b1a9Swlei 
1140a5f411b7Swlei void HybridPerfReader::generateUnsymbolizedProfile() {
1141e36786d1SHongtao Yu   ProfileIsCS = !IgnoreStackSamples;
1142e36786d1SHongtao Yu   if (ProfileIsCS)
1143a03cf331Swlei     unwindSamples();
1144a03cf331Swlei   else
1145a5f411b7Swlei     PerfScriptReader::generateUnsymbolizedProfile();
1146a03cf331Swlei }
1147a94fa862Swlei 
1148a5f411b7Swlei void PerfScriptReader::warnTruncatedStack() {
1149138202a8Swlei   if (ShowDetailedWarning) {
11500057c718SHongtao Yu     for (auto Address : InvalidReturnAddresses) {
11510057c718SHongtao Yu       WithColor::warning()
11520057c718SHongtao Yu           << "Truncated stack sample due to invalid return address at "
11530057c718SHongtao Yu           << format("0x%" PRIx64, Address)
11540057c718SHongtao Yu           << ", likely caused by frame pointer omission\n";
11550057c718SHongtao Yu     }
11560057c718SHongtao Yu   }
1157138202a8Swlei   emitWarningSummary(
1158138202a8Swlei       InvalidReturnAddresses.size(), AggregatedSamples.size(),
1159138202a8Swlei       "of truncated stack samples due to invalid return address, "
1160138202a8Swlei       "likely caused by frame pointer omission.");
1161138202a8Swlei }
1162138202a8Swlei 
1163138202a8Swlei void PerfScriptReader::warnInvalidRange() {
1164138202a8Swlei   std::unordered_map<std::pair<uint64_t, uint64_t>, uint64_t,
1165138202a8Swlei                      pair_hash<uint64_t, uint64_t>>
1166138202a8Swlei       Ranges;
1167138202a8Swlei 
1168138202a8Swlei   for (const auto &Item : AggregatedSamples) {
1169138202a8Swlei     const PerfSample *Sample = Item.first.getPtr();
1170138202a8Swlei     uint64_t Count = Item.second;
117146765248Swlei     uint64_t EndAddress = 0;
1172138202a8Swlei     for (const LBREntry &LBR : Sample->LBRStack) {
117346765248Swlei       uint64_t SourceAddress = LBR.Source;
117446765248Swlei       uint64_t StartAddress = LBR.Target;
117546765248Swlei       if (EndAddress != 0)
117646765248Swlei         Ranges[{StartAddress, EndAddress}] += Count;
117746765248Swlei       EndAddress = SourceAddress;
1178138202a8Swlei     }
1179138202a8Swlei   }
1180138202a8Swlei 
1181138202a8Swlei   if (Ranges.empty()) {
1182138202a8Swlei     WithColor::warning() << "No samples in perf script!\n";
1183138202a8Swlei     return;
1184138202a8Swlei   }
1185138202a8Swlei 
118646765248Swlei   auto WarnInvalidRange = [&](uint64_t StartAddress, uint64_t EndAddress,
118746765248Swlei                               StringRef Msg) {
1188138202a8Swlei     if (!ShowDetailedWarning)
1189138202a8Swlei       return;
119046765248Swlei     WithColor::warning() << "[" << format("%8" PRIx64, StartAddress) << ","
119146765248Swlei                          << format("%8" PRIx64, EndAddress) << "]: " << Msg
119246765248Swlei                          << "\n";
1193138202a8Swlei   };
1194138202a8Swlei 
1195138202a8Swlei   const char *EndNotBoundaryMsg = "Range is not on instruction boundary, "
1196138202a8Swlei                                   "likely due to profile and binary mismatch.";
1197138202a8Swlei   const char *DanglingRangeMsg = "Range does not belong to any functions, "
1198138202a8Swlei                                  "likely from PLT, .init or .fini section.";
1199138202a8Swlei   const char *RangeCrossFuncMsg =
1200138202a8Swlei       "Fall through range should not cross function boundaries, likely due to "
1201138202a8Swlei       "profile and binary mismatch.";
12029f732af5SHongtao Yu   const char *BogusRangeMsg = "Range start is after or too far from range end.";
1203138202a8Swlei 
12048a0406dcSHongtao Yu   uint64_t TotalRangeNum = 0;
1205138202a8Swlei   uint64_t InstNotBoundary = 0;
1206138202a8Swlei   uint64_t UnmatchedRange = 0;
1207138202a8Swlei   uint64_t RangeCrossFunc = 0;
12088a0406dcSHongtao Yu   uint64_t BogusRange = 0;
1209138202a8Swlei 
1210138202a8Swlei   for (auto &I : Ranges) {
121146765248Swlei     uint64_t StartAddress = I.first.first;
121246765248Swlei     uint64_t EndAddress = I.first.second;
12138a0406dcSHongtao Yu     TotalRangeNum += I.second;
1214138202a8Swlei 
121546765248Swlei     if (!Binary->addressIsCode(StartAddress) &&
121646765248Swlei         !Binary->addressIsCode(EndAddress))
121746765248Swlei       continue;
121846765248Swlei 
121946765248Swlei     if (!Binary->addressIsCode(StartAddress) ||
122046765248Swlei         !Binary->addressIsTransfer(EndAddress)) {
12218a0406dcSHongtao Yu       InstNotBoundary += I.second;
122246765248Swlei       WarnInvalidRange(StartAddress, EndAddress, EndNotBoundaryMsg);
1223138202a8Swlei     }
1224138202a8Swlei 
122546765248Swlei     auto *FRange = Binary->findFuncRange(StartAddress);
1226138202a8Swlei     if (!FRange) {
12278a0406dcSHongtao Yu       UnmatchedRange += I.second;
122846765248Swlei       WarnInvalidRange(StartAddress, EndAddress, DanglingRangeMsg);
1229138202a8Swlei       continue;
1230138202a8Swlei     }
1231138202a8Swlei 
123246765248Swlei     if (EndAddress >= FRange->EndAddress) {
12338a0406dcSHongtao Yu       RangeCrossFunc += I.second;
123446765248Swlei       WarnInvalidRange(StartAddress, EndAddress, RangeCrossFuncMsg);
1235138202a8Swlei     }
12368a0406dcSHongtao Yu 
123746765248Swlei     if (Binary->addressIsCode(StartAddress) &&
123846765248Swlei         Binary->addressIsCode(EndAddress) &&
123946765248Swlei         !isValidFallThroughRange(StartAddress, EndAddress, Binary)) {
12408a0406dcSHongtao Yu       BogusRange += I.second;
124146765248Swlei       WarnInvalidRange(StartAddress, EndAddress, BogusRangeMsg);
12428a0406dcSHongtao Yu     }
1243138202a8Swlei   }
1244138202a8Swlei 
12458a0406dcSHongtao Yu   emitWarningSummary(
12468a0406dcSHongtao Yu       InstNotBoundary, TotalRangeNum,
12478a0406dcSHongtao Yu       "of samples are from ranges that are not on instruction boundary.");
12488a0406dcSHongtao Yu   emitWarningSummary(
12498a0406dcSHongtao Yu       UnmatchedRange, TotalRangeNum,
12508a0406dcSHongtao Yu       "of samples are from ranges that do not belong to any functions.");
12518a0406dcSHongtao Yu   emitWarningSummary(
12528a0406dcSHongtao Yu       RangeCrossFunc, TotalRangeNum,
12538a0406dcSHongtao Yu       "of samples are from ranges that do cross function boundaries.");
12548a0406dcSHongtao Yu   emitWarningSummary(
12558a0406dcSHongtao Yu       BogusRange, TotalRangeNum,
12569f732af5SHongtao Yu       "of samples are from ranges that have range start after or too far from "
12579f732af5SHongtao Yu       "range end acrossing the unconditinal jmp.");
1258138202a8Swlei }
12590057c718SHongtao Yu 
1260a5f411b7Swlei void PerfScriptReader::parsePerfTraces() {
12611f05b1a9Swlei   // Parse perf traces and do aggregation.
1262941191aaSWenlei He   parseAndAggregateTrace();
12632fa6eaf9Sxur-llvm   if (Binary->isKernel() && !Binary->getIsLoadedByMMap()) {
12642fa6eaf9Sxur-llvm     exitWithError(
12652fa6eaf9Sxur-llvm         "Kernel is requested, but no kernel is found in mmap events.");
12662fa6eaf9Sxur-llvm   }
12671f05b1a9Swlei 
12683dcb60dbSwlei   emitWarningSummary(NumLeafExternalFrame, NumTotalSample,
12693dcb60dbSwlei                      "of samples have leaf external frame in call stack.");
12703dcb60dbSwlei   emitWarningSummary(NumLeadingOutgoingLBR, NumTotalSample,
12713dcb60dbSwlei                      "of samples have leading external LBR.");
12723dcb60dbSwlei 
1273941191aaSWenlei He   // Generate unsymbolized profile.
12740057c718SHongtao Yu   warnTruncatedStack();
1275138202a8Swlei   warnInvalidRange();
1276a5f411b7Swlei   generateUnsymbolizedProfile();
127767db3111SHongtao Yu   AggregatedSamples.clear();
1278a03cf331Swlei 
1279a03cf331Swlei   if (SkipSymbolization)
1280a5f411b7Swlei     writeUnsymbolizedProfile(OutputFilename);
1281a94fa862Swlei }
1282a94fa862Swlei 
12833f7f446dSHaohai Wen SmallVector<CleanupInstaller, 2> PerfScriptReader::TempFileCleanups;
12843f7f446dSHaohai Wen 
128532221694Swlei } // end namespace sampleprof
128632221694Swlei } // end namespace llvm
1287