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