1//===-- PerfReader.cpp - perfscript reader ---------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8#include "PerfReader.h"
9#include "ErrorHandling.h"
10#include "Options.h"
11#include "ProfileGenerator.h"
12#include "llvm/ADT/SmallString.h"
13#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
14#include "llvm/ProfileData/ETMTraceDecoder.h"
15#include "llvm/Support/FileSystem.h"
16#include "llvm/Support/LineIterator.h"
17#include "llvm/Support/MemoryBuffer.h"
18#include "llvm/Support/Process.h"
19#include "llvm/Support/Timer.h"
20#include "llvm/Support/ToolOutputFile.h"
21#include "llvm/Support/raw_ostream.h"
22#include "llvm/TargetParser/Triple.h"
23
24#define DEBUG_TYPE "perf-reader"
25
26namespace llvm {
27
28cl::opt<bool> SkipSymbolization("skip-symbolization",
29 cl::desc("Dump the unsymbolized profile to the "
30 "output file. It will show unwinder "
31 "output for CS profile generation."),
32 cl::cat(ProfGenCategory));
33
34static cl::opt<bool> ShowMmapEvents("show-mmap-events",
35 cl::desc("Print binary load events."),
36 cl::cat(ProfGenCategory));
37
38static cl::opt<bool>
39 UseOffset("use-offset", cl::init(Val: true),
40 cl::desc("Work with `--skip-symbolization` or "
41 "`--unsymbolized-profile` to write/read the "
42 "offset instead of virtual address."),
43 cl::cat(ProfGenCategory));
44
45static cl::opt<bool> UseLoadableSegmentAsBase(
46 "use-first-loadable-segment-as-base",
47 cl::desc("Use first loadable segment address as base address "
48 "for offsets in unsymbolized profile. By default "
49 "first executable segment address is used"),
50 cl::cat(ProfGenCategory));
51
52static cl::opt<bool>
53 IgnoreStackSamples("ignore-stack-samples",
54 cl::desc("Ignore call stack samples for hybrid samples "
55 "and produce context-insensitive profile."),
56 cl::cat(ProfGenCategory));
57cl::opt<bool> ShowDetailedWarning("show-detailed-warning",
58 cl::desc("Show detailed warning message."),
59 cl::cat(ProfGenCategory));
60
61static cl::opt<int> CSProfMaxUnsymbolizedCtxDepth(
62 "csprof-max-unsymbolized-context-depth", cl::init(Val: -1),
63 cl::desc("Keep the last K contexts while merging unsymbolized profile. -1 "
64 "means no depth limit."),
65 cl::cat(ProfGenCategory));
66
67cl::opt<bool> TimeProfGen("time-profgen", cl::desc("Time llvm-profgen phases"),
68 cl::init(Val: false), cl::cat(ProfGenCategory));
69
70static const char *TimerGroupName = "profgen";
71static const char *TimerGroupDesc = "llvm-profgen";
72
73namespace sampleprof {
74
75void VirtualUnwinder::unwindCall(UnwindState &State) {
76 uint64_t Source = State.getCurrentLBRSource();
77 auto *ParentFrame = State.getParentFrame();
78 // The 2nd frame after leaf could be missing if stack sample is
79 // taken when IP is within prolog/epilog, as frame chain isn't
80 // setup yet. Fill in the missing frame in that case.
81 // TODO: Currently we just assume all the addr that can't match the
82 // 2nd frame is in prolog/epilog. In the future, we will switch to
83 // pro/epi tracker(Dwarf CFI) for the precise check.
84 if (ParentFrame == State.getDummyRootPtr() ||
85 ParentFrame->Address != Source) {
86 State.switchToFrame(Address: Source);
87 if (ParentFrame != State.getDummyRootPtr()) {
88 if (Source == ExternalAddr)
89 NumMismatchedExtCallBranch++;
90 else
91 NumMismatchedProEpiBranch++;
92 }
93 } else {
94 State.popFrame();
95 }
96 State.InstPtr.update(Addr: Source);
97}
98
99void VirtualUnwinder::unwindLinear(UnwindState &State, uint64_t Repeat) {
100 InstructionPointer &IP = State.InstPtr;
101 uint64_t Target = State.getCurrentLBRTarget();
102 uint64_t End = IP.Address;
103
104 if (End == ExternalAddr && Target == ExternalAddr) {
105 // Filter out the case when leaf external frame matches the external LBR
106 // target, this is a valid state, it happens that the code run into external
107 // address then return back. The call frame under the external frame
108 // remains valid and can be unwound later, just skip recording this range.
109 NumPairedExtAddr++;
110 return;
111 }
112
113 if (End == ExternalAddr || Target == ExternalAddr) {
114 // Range is invalid if only one point is external address. This means LBR
115 // traces contains a standalone external address failing to pair another
116 // one, likely due to interrupt jmp or broken perf script. Set the
117 // state to invalid.
118 NumUnpairedExtAddr++;
119 State.setInvalid();
120 return;
121 }
122
123 if (!isValidFallThroughRange(Start: Target, End, Binary)) {
124 // Skip unwinding the rest of LBR trace when a bogus range is seen.
125 State.setInvalid();
126 return;
127 }
128
129 if (Binary->usePseudoProbes()) {
130 // We don't need to top frame probe since it should be extracted
131 // from the range.
132 // The outcome of the virtual unwinding with pseudo probes is a
133 // map from a context key to the address range being unwound.
134 // This means basically linear unwinding is not needed for pseudo
135 // probes. The range will be simply recorded here and will be
136 // converted to a list of pseudo probes to report in ProfileGenerator.
137 State.getParentFrame()->recordRangeCount(Start: Target, End, Count: Repeat);
138 } else {
139 // Unwind linear execution part.
140 // Split and record the range by different inline context. For example:
141 // [0x01] ... main:1 # Target
142 // [0x02] ... main:2
143 // [0x03] ... main:3 @ foo:1
144 // [0x04] ... main:3 @ foo:2
145 // [0x05] ... main:3 @ foo:3
146 // [0x06] ... main:4
147 // [0x07] ... main:5 # End
148 // It will be recorded:
149 // [main:*] : [0x06, 0x07], [0x01, 0x02]
150 // [main:3 @ foo:*] : [0x03, 0x05]
151 while (IP.Address > Target) {
152 uint64_t PrevIP = IP.Address;
153 IP.backward();
154 // Break into segments for implicit call/return due to inlining
155 bool SameInlinee = Binary->inlineContextEqual(Add1: PrevIP, Add2: IP.Address);
156 if (!SameInlinee) {
157 State.switchToFrame(Address: PrevIP);
158 State.CurrentLeafFrame->recordRangeCount(Start: PrevIP, End, Count: Repeat);
159 End = IP.Address;
160 }
161 }
162 assert(IP.Address == Target && "The last one must be the target address.");
163 // Record the remaining range, [0x01, 0x02] in the example
164 State.switchToFrame(Address: IP.Address);
165 State.CurrentLeafFrame->recordRangeCount(Start: IP.Address, End, Count: Repeat);
166 }
167}
168
169void VirtualUnwinder::unwindReturn(UnwindState &State) {
170 // Add extra frame as we unwind through the return
171 const LBREntry &LBR = State.getCurrentLBR();
172 uint64_t CallAddr = Binary->getCallAddrFromFrameAddr(FrameAddr: LBR.Target);
173 State.switchToFrame(Address: CallAddr);
174 State.pushFrame(Address: LBR.Source);
175 State.InstPtr.update(Addr: LBR.Source);
176}
177
178void VirtualUnwinder::unwindBranch(UnwindState &State) {
179 // TODO: Tolerate tail call for now, as we may see tail call from libraries.
180 // This is only for intra function branches, excluding tail calls.
181 uint64_t Source = State.getCurrentLBRSource();
182 State.switchToFrame(Address: Source);
183 State.InstPtr.update(Addr: Source);
184}
185
186std::shared_ptr<StringBasedCtxKey> FrameStack::getContextKey() {
187 std::shared_ptr<StringBasedCtxKey> KeyStr =
188 std::make_shared<StringBasedCtxKey>();
189 KeyStr->Context = Binary->getExpandedContext(Stack, WasLeafInlined&: KeyStr->WasLeafInlined);
190 return KeyStr;
191}
192
193std::shared_ptr<AddrBasedCtxKey> AddressStack::getContextKey() {
194 std::shared_ptr<AddrBasedCtxKey> KeyStr = std::make_shared<AddrBasedCtxKey>();
195 KeyStr->Context = Stack;
196 CSProfileGenerator::compressRecursionContext<uint64_t>(Context&: KeyStr->Context);
197 // MaxContextDepth(--csprof-max-context-depth) is used to trim both symbolized
198 // and unsymbolized profile context. Sometimes we want to at least preserve
199 // the inlinings for the leaf frame(the profiled binary inlining),
200 // --csprof-max-context-depth may not be flexible enough, in this case,
201 // --csprof-max-unsymbolized-context-depth is used to limit the context for
202 // unsymbolized profile. If both are set, use the minimum of them.
203 int Depth = CSProfileGenerator::MaxContextDepth != -1
204 ? CSProfileGenerator::MaxContextDepth
205 : KeyStr->Context.size();
206 Depth = CSProfMaxUnsymbolizedCtxDepth != -1
207 ? std::min(a: static_cast<int>(CSProfMaxUnsymbolizedCtxDepth), b: Depth)
208 : Depth;
209 CSProfileGenerator::trimContext<uint64_t>(S&: KeyStr->Context, Depth);
210 return KeyStr;
211}
212
213template <typename T>
214void VirtualUnwinder::collectSamplesFromFrame(UnwindState::ProfiledFrame *Cur,
215 T &Stack) {
216 if (Cur->RangeSamples.empty() && Cur->BranchSamples.empty())
217 return;
218
219 std::shared_ptr<ContextKey> Key = Stack.getContextKey();
220 if (Key == nullptr)
221 return;
222 auto Ret = CtxCounterMap->try_emplace(Key: Hashable<ContextKey>(Key));
223 SampleCounter &SCounter = Ret.first->second;
224 for (auto &I : Cur->RangeSamples)
225 SCounter.recordRangeCount(Start: std::get<0>(t&: I), End: std::get<1>(t&: I), Repeat: std::get<2>(t&: I));
226
227 for (auto &I : Cur->BranchSamples)
228 SCounter.recordBranchCount(Source: std::get<0>(t&: I), Target: std::get<1>(t&: I), Repeat: std::get<2>(t&: I));
229}
230
231template <typename T>
232void VirtualUnwinder::collectSamplesFromFrameTrie(
233 UnwindState::ProfiledFrame *Cur, T &Stack) {
234 if (!Cur->isDummyRoot()) {
235 // Truncate the context for external frame since this isn't a real call
236 // context the compiler will see.
237 if (Cur->isExternalFrame() || !Stack.pushFrame(Cur)) {
238 // Process truncated context
239 // Start a new traversal ignoring its bottom context
240 T EmptyStack(Binary);
241 collectSamplesFromFrame(Cur, EmptyStack);
242 for (const auto &Item : Cur->Children) {
243 collectSamplesFromFrameTrie(Item.second.get(), EmptyStack);
244 }
245
246 // Keep note of untracked call site and deduplicate them
247 // for warning later.
248 if (!Cur->isLeafFrame())
249 UntrackedCallsites.insert(x: Cur->Address);
250
251 return;
252 }
253 }
254
255 collectSamplesFromFrame(Cur, Stack);
256 // Process children frame
257 for (const auto &Item : Cur->Children) {
258 collectSamplesFromFrameTrie(Item.second.get(), Stack);
259 }
260 // Recover the call stack
261 Stack.popFrame();
262}
263
264void VirtualUnwinder::collectSamplesFromFrameTrie(
265 UnwindState::ProfiledFrame *Cur) {
266 if (Binary->usePseudoProbes()) {
267 AddressStack Stack(Binary);
268 collectSamplesFromFrameTrie<AddressStack>(Cur, Stack);
269 } else {
270 FrameStack Stack(Binary);
271 collectSamplesFromFrameTrie<FrameStack>(Cur, Stack);
272 }
273}
274
275void VirtualUnwinder::recordBranchCount(const LBREntry &Branch,
276 UnwindState &State, uint64_t Repeat) {
277 if (Branch.Target == ExternalAddr)
278 return;
279
280 // Record external-to-internal pattern on the trie root, it later can be
281 // used for generating head samples.
282 if (Branch.Source == ExternalAddr) {
283 State.getDummyRootPtr()->recordBranchCount(Source: Branch.Source, Target: Branch.Target,
284 Count: Repeat);
285 return;
286 }
287
288 if (Binary->usePseudoProbes()) {
289 // Same as recordRangeCount, We don't need to top frame probe since we will
290 // extract it from branch's source address
291 State.getParentFrame()->recordBranchCount(Source: Branch.Source, Target: Branch.Target,
292 Count: Repeat);
293 } else {
294 State.CurrentLeafFrame->recordBranchCount(Source: Branch.Source, Target: Branch.Target,
295 Count: Repeat);
296 }
297}
298
299bool VirtualUnwinder::unwind(const PerfSample *Sample, uint64_t Repeat) {
300 // Capture initial state as starting point for unwinding.
301 UnwindState State(Sample, Binary);
302
303 // Sanity check - making sure leaf of LBR aligns with leaf of stack sample
304 // Stack sample sometimes can be unreliable, so filter out bogus ones.
305 if (!State.validateInitialState())
306 return false;
307
308 NumTotalBranches += State.LBRStack.size();
309 // Now process the LBR samples in parrallel with stack sample
310 // Note that we do not reverse the LBR entry order so we can
311 // unwind the sample stack as we walk through LBR entries.
312 while (State.hasNextLBR()) {
313 State.checkStateConsistency();
314
315 // Do not attempt linear unwind for the leaf range as it's incomplete.
316 if (!State.IsLastLBR()) {
317 // Unwind implicit calls/returns from inlining, along the linear path,
318 // break into smaller sub section each with its own calling context.
319 unwindLinear(State, Repeat);
320 }
321
322 // Save the LBR branch before it gets unwound.
323 const LBREntry &Branch = State.getCurrentLBR();
324 if (isCallState(State)) {
325 // Unwind calls - we know we encountered call if LBR overlaps with
326 // transition between leaf the 2nd frame. Note that for calls that
327 // were not in the original stack sample, we should have added the
328 // extra frame when processing the return paired with this call.
329 unwindCall(State);
330 } else if (isReturnState(State)) {
331 // Unwind returns - check whether the IP is indeed at a return
332 // instruction
333 unwindReturn(State);
334 } else if (isValidState(State)) {
335 // Unwind branches
336 unwindBranch(State);
337 } else {
338 // Skip unwinding the rest of LBR trace. Reset the stack and update the
339 // state so that the rest of the trace can still be processed as if they
340 // do not have stack samples.
341 State.clearCallStack();
342 State.InstPtr.update(Addr: State.getCurrentLBRSource());
343 State.pushFrame(Address: State.InstPtr.Address);
344 }
345
346 State.advanceLBR();
347 // Record `branch` with calling context after unwinding.
348 recordBranchCount(Branch, State, Repeat);
349 }
350 // As samples are aggregated on trie, record them into counter map
351 collectSamplesFromFrameTrie(Cur: State.getDummyRootPtr());
352
353 return true;
354}
355
356std::unique_ptr<PerfReaderBase>
357PerfReaderBase::create(ProfiledBinary *Binary, InputFile &Input,
358 std::optional<int32_t> PIDFilter) {
359 std::unique_ptr<PerfReaderBase> PerfReader;
360
361 if (Input.Format == InputFormat::UnsymbolizedProfile) {
362 PerfReader.reset(
363 p: new UnsymbolizedProfileReader(Binary, Input.InputFilePath));
364 return PerfReader;
365 }
366
367 // For perf data input, we need to convert them into perf script first.
368 // If this is a kernel perf file, there is no need for retrieving PIDs.
369 if (Input.Format == InputFormat::PerfData)
370 Input = PerfScriptReader::convertPerfDataToTrace(Binary, SkipPID: Binary->isKernel(),
371 File&: Input, PIDFilter);
372
373 assert((Input.Format == InputFormat::PerfScript) &&
374 "Should be a perfscript!");
375
376 Input.Content = PerfScriptReader::checkPerfScriptType(FileName: Input.InputFilePath);
377 if (Input.Content == PerfContent::LBRStack) {
378 PerfReader.reset(
379 p: new HybridPerfReader(Binary, Input.InputFilePath, PIDFilter));
380 } else if (Input.Content == PerfContent::LBR) {
381 PerfReader.reset(p: new LBRPerfReader(Binary, Input.InputFilePath, PIDFilter));
382 } else {
383 exitWithError(Message: "Unsupported perfscript!");
384 }
385
386 return PerfReader;
387}
388
389Error PerfReaderBase::parseDataAccessPerfTraces(
390 StringRef DataAccessPerfTraceFile, std::optional<int32_t> PIDFilter) {
391 // A perf_record_sample line is like
392 // . 1282514022939813 0x87b0 [0x60]: PERF_RECORD_SAMPLE(IP, 0x4002):
393 // 3446532/3446532: 0x2608a2 period: 233 addr: 0x3b3fb0
394 constexpr static StringRef DataAccessSamplePattern =
395 "PERF_RECORD_SAMPLE\\([A-Za-z]+, 0x[0-9a-fA-F]+\\): "
396 "([0-9]+)\\/[0-9]+: 0x([0-9a-fA-F]+) period: [0-9]+ addr: "
397 "0x([0-9a-fA-F]+)";
398
399 llvm::Regex LogRegex(DataAccessSamplePattern);
400
401 auto BufferOrErr = MemoryBuffer::getFile(Filename: DataAccessPerfTraceFile);
402 std::error_code EC = BufferOrErr.getError();
403 if (EC)
404 return make_error<StringError>(Args: "Failed to open perf trace file: " +
405 DataAccessPerfTraceFile,
406 Args: inconvertibleErrorCode());
407
408 assert(!SampleCounters.empty() && "Sample counters should not be empty!");
409 SampleCounter &Counter = SampleCounters.begin()->second;
410 line_iterator LineIt(*BufferOrErr.get(), true);
411
412 for (; !LineIt.is_at_eof(); ++LineIt) {
413 StringRef Line = *LineIt;
414
415 MMapEvent MMap;
416 if (Line.contains(Other: "PERF_RECORD_MMAP2")) {
417 if (PerfScriptReader::extractMMapEventForBinary(Binary, Line, MMap)) {
418 if (!MMap.MemProtectionFlag.contains(Other: "x")) {
419 if (Error E = Binary->addMMapNonTextEvent(Event: MMap)) {
420 return E;
421 }
422 }
423 }
424 continue;
425 }
426
427 SmallVector<StringRef> Fields;
428 if (LogRegex.match(String: Line, Matches: &Fields)) {
429 int32_t PID = 0;
430 if (Fields[1].getAsInteger(Radix: 10, Result&: PID))
431 return make_error<StringError>(
432 Args: "Failed to parse PID from perf trace line: " + Line,
433 Args: inconvertibleErrorCode());
434
435 if (PIDFilter.has_value() && *PIDFilter != PID) {
436 continue;
437 }
438
439 uint64_t DataAddress = 0;
440 if (Fields[3].getAsInteger(Radix: 16, Result&: DataAddress))
441 return make_error<StringError>(
442 Args: "Failed to parse data address from perf trace line: " + Line,
443 Args: inconvertibleErrorCode());
444 // Out of all the memory access events, the vtable accesses are used to
445 // construct type profiles. We assume that this is under the Itanium
446 // C++ ABI so we can use `_ZTV` prefix to identify vtable.
447 StringRef DataSymbol = Binary->symbolizeDataAddress(
448 Address: Binary->CanonicalizeNonTextAddress(Address: DataAddress));
449 if (DataSymbol.starts_with(Prefix: "_ZTV")) {
450 uint64_t IP = 0;
451 Fields[2].getAsInteger(Radix: 16, Result&: IP);
452 Counter.recordDataAccessCount(InstAddr: Binary->canonicalizeVirtualAddress(Address: IP),
453 DataSymbol, Repeat: 1);
454 }
455 }
456 }
457 return Error::success();
458}
459
460InputFile
461PerfScriptReader::convertPerfDataToTrace(ProfiledBinary *Binary, bool SkipPID,
462 InputFile &File,
463 std::optional<int32_t> PIDFilter) {
464 StringRef PerfData = File.InputFilePath;
465 // Run perf script to retrieve PIDs matching binary we're interested in.
466 auto PerfExecutable = sys::Process::FindInEnvPath(EnvName: "PATH", FileName: "perf");
467 if (!PerfExecutable) {
468 exitWithError(Message: "Perf not found.");
469 }
470 std::string PerfExecutablePath = *PerfExecutable;
471 SmallString<128> PerfTraceFile;
472 sys::fs::createUniquePath(Model: "perf-script-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%.tmp",
473 ResultPath&: PerfTraceFile, /*MakeAbsolute=*/true);
474 std::string ErrorFile = std::string(PerfTraceFile) + ".err";
475 std::optional<StringRef> Redirects[] = {std::nullopt, // Stdin
476 StringRef(PerfTraceFile), // Stdout
477 StringRef(ErrorFile)}; // Stderr
478 PerfScriptReader::TempFileCleanups.emplace_back(Args&: PerfTraceFile);
479 PerfScriptReader::TempFileCleanups.emplace_back(Args&: ErrorFile);
480
481 auto RunPerfScript = [&](ArrayRef<StringRef> Args) {
482 // ExecuteAndWait does not truncate redirected output files on Unix. Remove
483 // both files so a shorter invocation cannot retain output from the
484 // previous perf script invocation.
485 for (StringRef Path : {StringRef(PerfTraceFile), StringRef(ErrorFile)}) {
486 if (std::error_code EC = sys::fs::remove(path: Path))
487 exitWithError(EC, Whence: Path);
488 }
489
490 std::string ExecutionError;
491 bool ExecutionFailed = false;
492 int ExitCode =
493 sys::ExecuteAndWait(Program: PerfExecutablePath, Args, Env: std::nullopt, Redirects,
494 /*SecondsToWait=*/0, /*MemoryLimit=*/0,
495 ErrMsg: &ExecutionError, ExecutionFailed: &ExecutionFailed);
496 if (!ExecutionFailed && ExitCode == 0)
497 return;
498
499 std::string Message;
500 raw_string_ostream OS(Message);
501 if (ExecutionFailed || ExitCode == -1)
502 OS << "Failed to execute perf script";
503 else if (ExitCode == -2)
504 OS << "Perf script terminated abnormally";
505 else
506 OS << "Perf script failed with exit code " << ExitCode;
507 if (!ExecutionError.empty())
508 OS << ": " << ExecutionError;
509
510 if (auto ErrorBuffer = MemoryBuffer::getFile(Filename: ErrorFile)) {
511 StringRef Stderr = ErrorBuffer.get()->getBuffer().trim();
512 if (!Stderr.empty())
513 OS << "\n" << Stderr;
514 }
515 exitWithError(Message: OS.str());
516 };
517
518 std::string PIDs;
519 if (!SkipPID) {
520 StringRef ScriptMMapArgs[] = {PerfExecutablePath,
521 "script",
522 "--show-mmap-events",
523 "-F",
524 "comm,pid",
525 "-i",
526 PerfData};
527 RunPerfScript(ScriptMMapArgs);
528
529 // Collect the PIDs
530 TraceStream TraceIt(PerfTraceFile);
531 DenseSet<int32_t> PIDSet;
532 while (!TraceIt.isAtEoF()) {
533 MMapEvent MMap;
534 if (isMMapEvent(Line: TraceIt.getCurrentLine()) &&
535 extractMMapEventForBinary(Binary, Line: TraceIt.getCurrentLine(), MMap)) {
536 auto It = PIDSet.insert(V: MMap.PID);
537 if (It.second && (!PIDFilter || MMap.PID == *PIDFilter)) {
538 if (!PIDs.empty()) {
539 PIDs.append(s: ",");
540 }
541 PIDs.append(str: utostr(X: MMap.PID));
542 }
543 }
544 TraceIt.advance();
545 }
546
547 if (PIDs.empty()) {
548 exitWithError(Message: "No relevant mmap event is found in perf data.");
549 }
550 }
551
552 // Run perf script again to retrieve events for PIDs collected above
553 SmallVector<StringRef, 8> ScriptSampleArgs;
554 ScriptSampleArgs.push_back(Elt: PerfExecutablePath);
555 ScriptSampleArgs.push_back(Elt: "script");
556 ScriptSampleArgs.push_back(Elt: "--show-mmap-events");
557 ScriptSampleArgs.push_back(Elt: "-F");
558 ScriptSampleArgs.push_back(Elt: "ip,brstack");
559 ScriptSampleArgs.push_back(Elt: "-i");
560 ScriptSampleArgs.push_back(Elt: PerfData);
561 if (!PIDs.empty()) {
562 ScriptSampleArgs.push_back(Elt: "--pid");
563 ScriptSampleArgs.push_back(Elt: PIDs);
564 }
565 RunPerfScript(ScriptSampleArgs);
566
567 return {.InputFilePath: std::string(PerfTraceFile), .Format: InputFormat::PerfScript,
568 .Content: PerfContent::UnknownContent};
569}
570
571static StringRef filename(StringRef Path, bool UseBackSlash) {
572 llvm::sys::path::Style PathStyle =
573 UseBackSlash ? llvm::sys::path::Style::windows_backslash
574 : llvm::sys::path::Style::native;
575 StringRef FileName = llvm::sys::path::filename(path: Path, style: PathStyle);
576
577 // In case this file use \r\n as newline.
578 if (UseBackSlash && FileName.back() == '\r')
579 return FileName.drop_back();
580
581 return FileName;
582}
583
584void PerfScriptReader::updateBinaryAddress(const MMapEvent &Event) {
585 // Drop the event which doesn't belong to user-provided binary
586 StringRef BinaryName = filename(Path: Event.BinaryPath, UseBackSlash: Binary->isCOFF());
587 bool IsKernel = Binary->isKernel();
588 if (!IsKernel && Binary->getName() != BinaryName)
589 return;
590 if (IsKernel && !Binary->isKernelImageName(BinaryName))
591 return;
592
593 // Drop the event if process does not match pid filter
594 if (PIDFilter && Event.PID != *PIDFilter)
595 return;
596
597 // Drop the event if its image is loaded at the same address
598 if (Event.Address == Binary->getBaseAddress()) {
599 Binary->setIsLoadedByMMap(true);
600 return;
601 }
602
603 if (IsKernel || Event.Offset == Binary->getTextSegmentOffset()) {
604 // A binary image could be unloaded and then reloaded at different
605 // place, so update binary load address.
606 // Only update for the first executable segment and assume all other
607 // segments are loaded at consecutive memory addresses, which is the case on
608 // X64.
609 Binary->setBaseAddress(Event.Address);
610 Binary->setIsLoadedByMMap(true);
611 } else {
612 // Verify segments are loaded consecutively.
613 const auto &Offsets = Binary->getTextSegmentOffsets();
614 auto It = llvm::lower_bound(Range: Offsets, Value: Event.Offset);
615 if (It != Offsets.end() && *It == Event.Offset) {
616 // The event is for loading a separate executable segment.
617 auto I = std::distance(first: Offsets.begin(), last: It);
618 const auto &PreferredAddrs = Binary->getPreferredTextSegmentAddresses();
619 if (PreferredAddrs[I] - Binary->getPreferredBaseAddress() !=
620 Event.Address - Binary->getBaseAddress())
621 exitWithError(Message: "Executable segments not loaded consecutively");
622 } else {
623 if (It == Offsets.begin())
624 exitWithError(Message: "File offset not found");
625 else {
626 // Find the segment the event falls in. A large segment could be loaded
627 // via multiple mmap calls with consecutive memory addresses.
628 --It;
629 assert(*It < Event.Offset);
630 if (Event.Offset - *It != Event.Address - Binary->getBaseAddress())
631 exitWithError(Message: "Segment not loaded by consecutive mmaps");
632 }
633 }
634 }
635}
636
637static std::string getContextKeyStr(ContextKey *K,
638 const ProfiledBinary *Binary) {
639 if (const auto *CtxKey = dyn_cast<StringBasedCtxKey>(Val: K)) {
640 return SampleContext::getContextString(Context: CtxKey->Context);
641 } else if (const auto *CtxKey = dyn_cast<AddrBasedCtxKey>(Val: K)) {
642 std::ostringstream OContextStr;
643 for (uint32_t I = 0; I < CtxKey->Context.size(); I++) {
644 if (OContextStr.str().size())
645 OContextStr << " @ ";
646 uint64_t Address = CtxKey->Context[I];
647 if (UseOffset) {
648 if (UseLoadableSegmentAsBase)
649 Address -= Binary->getFirstLoadableAddress();
650 else
651 Address -= Binary->getPreferredBaseAddress();
652 }
653 OContextStr << "0x"
654 << utohexstr(X: Address,
655 /*LowerCase=*/true);
656 }
657 return OContextStr.str();
658 } else {
659 llvm_unreachable("unexpected key type");
660 }
661}
662
663void HybridPerfReader::unwindSamples() {
664 NamedRegionTimer T("unwind", "Unwind samples", TimerGroupName, TimerGroupDesc,
665 TimeProfGen);
666 VirtualUnwinder Unwinder(&SampleCounters, Binary);
667 for (const auto &Item : AggregatedSamples) {
668 const PerfSample *Sample = Item.first.getPtr();
669 Unwinder.unwind(Sample, Repeat: Item.second);
670 }
671
672 // Warn about untracked frames due to missing probes.
673 if (ShowDetailedWarning) {
674 for (auto Address : Unwinder.getUntrackedCallsites())
675 WithColor::warning() << "Profile context truncated due to missing probe "
676 << "for call instruction at "
677 << format(Fmt: "0x%" PRIx64, Vals: Address) << "\n";
678 }
679
680 emitWarningSummary(Num: Unwinder.getUntrackedCallsites().size(),
681 Total: SampleCounters.size(),
682 Msg: "of profiled contexts are truncated due to missing probe "
683 "for call instruction.");
684
685 emitWarningSummary(
686 Num: Unwinder.NumMismatchedExtCallBranch, Total: Unwinder.NumTotalBranches,
687 Msg: "of branches'source is a call instruction but doesn't match call frame "
688 "stack, likely due to unwinding error of external frame.");
689
690 emitWarningSummary(Num: Unwinder.NumPairedExtAddr * 2, Total: Unwinder.NumTotalBranches,
691 Msg: "of branches containing paired external address.");
692
693 emitWarningSummary(Num: Unwinder.NumUnpairedExtAddr, Total: Unwinder.NumTotalBranches,
694 Msg: "of branches containing external address but doesn't have "
695 "another external address to pair, likely due to "
696 "interrupt jmp or broken perf script.");
697
698 emitWarningSummary(
699 Num: Unwinder.NumMismatchedProEpiBranch, Total: Unwinder.NumTotalBranches,
700 Msg: "of branches'source is a call instruction but doesn't match call frame "
701 "stack, likely due to frame in prolog/epilog.");
702
703 emitWarningSummary(Num: Unwinder.NumMissingExternalFrame,
704 Total: Unwinder.NumExtCallBranch,
705 Msg: "of artificial call branches but doesn't have an external "
706 "frame to match.");
707}
708
709/// Parse a hex address from \p Str.
710static bool parseAddress(StringRef Str, uint64_t &Addr, bool HasPrefix) {
711 if (Str.consume_front(Prefix: "0x") != HasPrefix)
712 return true;
713 return Str.getAsInteger(Radix: 16, Result&: Addr);
714}
715
716bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
717 SmallVectorImpl<LBREntry> &LBRStack) {
718 // The raw format of LBR stack is like:
719 // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
720 // ... 0x4005c8/0x4005dc/P/-/-/0
721 // It's in FIFO order and separated by whitespace.
722 SmallVector<StringRef, 32> Records;
723 TraceIt.getCurrentLine().rtrim().split(A&: Records, Separator: " ", MaxSplit: -1, KeepEmpty: false);
724 auto WarnInvalidLBR = [](TraceStream &TraceIt) {
725 WithColor::warning() << "Invalid address in LBR record at line "
726 << TraceIt.getLineNumber() << ": "
727 << TraceIt.getCurrentLine() << "\n";
728 };
729
730 // Skip the leading instruction pointer.
731 size_t Index = 0;
732 uint64_t LeadingAddr;
733 if (!Records.empty() && !Records[0].contains(C: '/')) {
734 if (parseAddress(Str: Records[0], Addr&: LeadingAddr, HasPrefix: false)) {
735 WarnInvalidLBR(TraceIt);
736 TraceIt.advance();
737 return false;
738 }
739 Index = 1;
740 }
741
742 // Now extract LBR samples - note that we do not reverse the
743 // LBR entry order so we can unwind the sample stack as we walk
744 // through LBR entries.
745 while (Index < Records.size()) {
746 auto &Token = Records[Index++];
747 if (Token.size() == 0)
748 continue;
749
750 SmallVector<StringRef, 8> Addresses;
751 Token.split(A&: Addresses, Separator: "/");
752 uint64_t Src;
753 uint64_t Dst;
754
755 // Stop at broken LBR records.
756 if (Addresses.size() < 2 || parseAddress(Str: Addresses[0], Addr&: Src, HasPrefix: true) ||
757 parseAddress(Str: Addresses[1], Addr&: Dst, HasPrefix: true)) {
758 WarnInvalidLBR(TraceIt);
759 break;
760 }
761
762 // Canonicalize to use preferred load address as base address.
763 Src = Binary->canonicalizeVirtualAddress(Address: Src);
764 Dst = Binary->canonicalizeVirtualAddress(Address: Dst);
765 bool SrcIsInternal = Binary->addressIsCode(Address: Src);
766 bool DstIsInternal = Binary->addressIsCode(Address: Dst);
767 if (!SrcIsInternal)
768 Src = ExternalAddr;
769 if (!DstIsInternal)
770 Dst = ExternalAddr;
771 // Filter external-to-external case to reduce LBR trace size.
772 if (!SrcIsInternal && !DstIsInternal)
773 continue;
774
775 LBRStack.emplace_back(Args: LBREntry(Src, Dst));
776 }
777 TraceIt.advance();
778 return !LBRStack.empty();
779}
780
781bool PerfScriptReader::extractCallstack(TraceStream &TraceIt,
782 SmallVectorImpl<uint64_t> &CallStack) {
783 // The raw format of call stack is like:
784 // 4005dc # leaf frame
785 // 400634
786 // 400684 # root frame
787 // It's in bottom-up order with each frame in one line.
788
789 // Extract stack frames from sample
790 while (!TraceIt.isAtEoF() && !isLBRSample(Line: TraceIt.getCurrentLine(), CheckLineStart: true)) {
791 StringRef FrameStr = TraceIt.getCurrentLine().ltrim();
792 uint64_t FrameAddr = 0;
793 if (parseAddress(Str: FrameStr, Addr&: FrameAddr, HasPrefix: false)) {
794 // We might parse a non-perf sample line like empty line and comments,
795 // skip it
796 TraceIt.advance();
797 return false;
798 }
799 TraceIt.advance();
800
801 FrameAddr = Binary->canonicalizeVirtualAddress(Address: FrameAddr);
802 // Currently intermixed frame from different binaries is not supported.
803 if (!Binary->addressIsCode(Address: FrameAddr)) {
804 if (CallStack.empty())
805 NumLeafExternalFrame++;
806 // Push a special value(ExternalAddr) for the external frames so that
807 // unwinder can still work on this with artificial Call/Return branch.
808 // After unwinding, the context will be truncated for external frame.
809 // Also deduplicate the consecutive external addresses.
810 if (CallStack.empty() || CallStack.back() != ExternalAddr)
811 CallStack.emplace_back(Args: ExternalAddr);
812 continue;
813 }
814
815 // We need to translate return address to call address for non-leaf frames.
816 if (!CallStack.empty()) {
817 auto CallAddr = Binary->getCallAddrFromFrameAddr(FrameAddr);
818 if (!CallAddr) {
819 // Stop at an invalid return address caused by bad unwinding. This could
820 // happen to frame-pointer-based unwinding and the callee functions that
821 // do not have the frame pointer chain set up.
822 InvalidReturnAddresses.insert(x: FrameAddr);
823 break;
824 }
825 FrameAddr = CallAddr;
826 }
827
828 CallStack.emplace_back(Args&: FrameAddr);
829 }
830
831 // Strip out the bottom external addr.
832 if (CallStack.size() > 1 && CallStack.back() == ExternalAddr)
833 CallStack.pop_back();
834
835 // Skip other unrelated line, find the next valid LBR line
836 // Note that even for empty call stack, we should skip the address at the
837 // bottom, otherwise the following pass may generate a truncated callstack
838 while (!TraceIt.isAtEoF() && !isLBRSample(Line: TraceIt.getCurrentLine(), CheckLineStart: true)) {
839 TraceIt.advance();
840 }
841 // Filter out broken stack sample. We may not have complete frame info
842 // if sample end up in prolog/epilog, the result is dangling context not
843 // connected to entry point. This should be relatively rare thus not much
844 // impact on overall profile quality. However we do want to filter them
845 // out to reduce the number of different calling contexts. One instance
846 // of such case - when sample landed in prolog/epilog, somehow stack
847 // walking will be broken in an unexpected way that higher frames will be
848 // missing.
849 return !CallStack.empty() &&
850 !Binary->addressInPrologEpilog(Address: CallStack.front());
851}
852
853void PerfScriptReader::warnIfMissingMMap() {
854 if (!Binary->getMissingMMapWarned() && !Binary->getIsLoadedByMMap()) {
855 WithColor::warning() << "No relevant mmap event is matched for "
856 << Binary->getName()
857 << ", will use preferred address ("
858 << format(Fmt: "0x%" PRIx64,
859 Vals: Binary->getPreferredBaseAddress())
860 << ") as the base loading address!\n";
861 // Avoid redundant warning, only warn at the first unmatched sample.
862 Binary->setMissingMMapWarned(true);
863 }
864}
865
866void HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
867 // The raw hybird sample started with call stack in FILO order and followed
868 // intermediately by LBR sample
869 // e.g.
870 // 4005dc # call stack leaf
871 // 400634
872 // 400684 # call stack root
873 // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
874 // ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries
875 //
876 std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>();
877#ifndef NDEBUG
878 Sample->Linenum = TraceIt.getLineNumber();
879#endif
880 // Parsing call stack and populate into PerfSample.CallStack
881 if (!extractCallstack(TraceIt, CallStack&: Sample->CallStack)) {
882 // Skip the next LBR line matched current call stack
883 if (!TraceIt.isAtEoF() && isLBRSample(Line: TraceIt.getCurrentLine(), CheckLineStart: true))
884 TraceIt.advance();
885 return;
886 }
887
888 warnIfMissingMMap();
889
890 if (!TraceIt.isAtEoF() && isLBRSample(Line: TraceIt.getCurrentLine(), CheckLineStart: true)) {
891 // Parsing LBR stack and populate into PerfSample.LBRStack
892 if (extractLBRStack(TraceIt, LBRStack&: Sample->LBRStack)) {
893 if (IgnoreStackSamples) {
894 Sample->CallStack.clear();
895 } else {
896 // Canonicalize stack leaf to avoid 'random' IP from leaf frame skew LBR
897 // ranges
898 Sample->CallStack.front() = Sample->LBRStack[0].Target;
899 }
900 // Record samples by aggregation
901 AggregatedSamples[Hashable<PerfSample>(Sample)] += Count;
902 }
903 } else {
904 // LBR sample is encoded in single line after stack sample
905 exitWithError(Message: "'Hybrid perf sample is corrupted, No LBR sample line");
906 }
907}
908
909void PerfScriptReader::writeUnsymbolizedProfile(StringRef Filename) {
910 std::error_code EC;
911 raw_fd_ostream OS(Filename, EC, llvm::sys::fs::OF_TextWithCRLF);
912 if (EC)
913 exitWithError(EC, Whence: Filename);
914 writeUnsymbolizedProfile(OS);
915}
916
917// Use ordered map to make the output deterministic
918using OrderedCounterForPrint = std::map<std::string, SampleCounter *>;
919
920void PerfScriptReader::writeUnsymbolizedProfile(raw_fd_ostream &OS) {
921 OrderedCounterForPrint OrderedCounters;
922 for (auto &CI : SampleCounters) {
923 OrderedCounters[getContextKeyStr(K: CI.first.getPtr(), Binary)] = &CI.second;
924 }
925
926 auto SCounterPrinter = [&](RangeSample &Counter, StringRef Separator,
927 uint32_t Indent) {
928 OS.indent(NumSpaces: Indent);
929 OS << Counter.size() << "\n";
930 for (auto &I : Counter) {
931 uint64_t Start = I.first.first;
932 uint64_t End = I.first.second;
933
934 if (UseOffset) {
935 if (UseLoadableSegmentAsBase) {
936 Start -= Binary->getFirstLoadableAddress();
937 End -= Binary->getFirstLoadableAddress();
938 } else {
939 Start -= Binary->getPreferredBaseAddress();
940 End -= Binary->getPreferredBaseAddress();
941 }
942 }
943
944 OS.indent(NumSpaces: Indent);
945 OS << Twine::utohexstr(Val: Start) << Separator << Twine::utohexstr(Val: End) << ":"
946 << I.second << "\n";
947 }
948 };
949
950 for (auto &CI : OrderedCounters) {
951 uint32_t Indent = 0;
952 if (ProfileIsCS) {
953 // Context string key
954 OS << "[" << CI.first << "]\n";
955 Indent = 2;
956 }
957
958 SampleCounter &Counter = *CI.second;
959 SCounterPrinter(Counter.RangeCounter, "-", Indent);
960 SCounterPrinter(Counter.BranchCounter, "->", Indent);
961 }
962}
963
964// Format of input:
965// number of entries in RangeCounter
966// from_1-to_1:count_1
967// from_2-to_2:count_2
968// ......
969// from_n-to_n:count_n
970// number of entries in BranchCounter
971// src_1->dst_1:count_1
972// src_2->dst_2:count_2
973// ......
974// src_n->dst_n:count_n
975void UnsymbolizedProfileReader::readSampleCounters(TraceStream &TraceIt,
976 SampleCounter &SCounters) {
977 auto exitWithErrorForTraceLine = [](TraceStream &TraceIt) {
978 std::string Msg = TraceIt.isAtEoF()
979 ? "Invalid raw profile!"
980 : "Invalid raw profile at line " +
981 Twine(TraceIt.getLineNumber()).str() + ": " +
982 TraceIt.getCurrentLine().str();
983 exitWithError(Message: Msg);
984 };
985 auto ReadNumber = [&](uint64_t &Num) {
986 if (TraceIt.isAtEoF())
987 exitWithErrorForTraceLine(TraceIt);
988 if (TraceIt.getCurrentLine().ltrim().getAsInteger(Radix: 10, Result&: Num))
989 exitWithErrorForTraceLine(TraceIt);
990 TraceIt.advance();
991 };
992
993 auto ReadCounter = [&](RangeSample &Counter, StringRef Separator) {
994 uint64_t Num = 0;
995 ReadNumber(Num);
996 while (Num--) {
997 if (TraceIt.isAtEoF())
998 exitWithErrorForTraceLine(TraceIt);
999 StringRef Line = TraceIt.getCurrentLine().ltrim();
1000
1001 uint64_t Count = 0;
1002 auto LineSplit = Line.split(Separator: ":");
1003 if (LineSplit.second.empty() || LineSplit.second.getAsInteger(Radix: 10, Result&: Count))
1004 exitWithErrorForTraceLine(TraceIt);
1005
1006 uint64_t Source = 0;
1007 uint64_t Target = 0;
1008 auto Range = LineSplit.first.split(Separator);
1009 if (Range.second.empty() || Range.first.getAsInteger(Radix: 16, Result&: Source) ||
1010 Range.second.getAsInteger(Radix: 16, Result&: Target))
1011 exitWithErrorForTraceLine(TraceIt);
1012
1013 if (UseOffset) {
1014 if (UseLoadableSegmentAsBase) {
1015 Source += Binary->getFirstLoadableAddress();
1016 Target += Binary->getFirstLoadableAddress();
1017 } else {
1018 Source += Binary->getPreferredBaseAddress();
1019 Target += Binary->getPreferredBaseAddress();
1020 }
1021 }
1022
1023 Counter[{Source, Target}] += Count;
1024 TraceIt.advance();
1025 }
1026 };
1027
1028 ReadCounter(SCounters.RangeCounter, "-");
1029 ReadCounter(SCounters.BranchCounter, "->");
1030}
1031
1032void UnsymbolizedProfileReader::readUnsymbolizedProfile(StringRef FileName) {
1033 TraceStream TraceIt(FileName);
1034 while (!TraceIt.isAtEoF()) {
1035 std::shared_ptr<StringBasedCtxKey> Key =
1036 std::make_shared<StringBasedCtxKey>();
1037 StringRef Line = TraceIt.getCurrentLine();
1038 // Read context stack for CS profile.
1039 if (Line.starts_with(Prefix: "[")) {
1040 ProfileIsCS = true;
1041 auto I = ContextStrSet.insert(key: Line);
1042 SampleContext::createCtxVectorFromStr(ContextStr: I.first->getKey(), Context&: Key->Context);
1043 TraceIt.advance();
1044 }
1045 auto Ret = SampleCounters.try_emplace(Key: Hashable<ContextKey>(Key));
1046 readSampleCounters(TraceIt, SCounters&: Ret.first->second);
1047 }
1048}
1049
1050void UnsymbolizedProfileReader::parsePerfTraces() {
1051 readUnsymbolizedProfile(FileName: PerfTraceFile);
1052}
1053
1054void PerfScriptReader::computeCounterFromLBR(const PerfSample *Sample,
1055 uint64_t Repeat) {
1056 SampleCounter &Counter = SampleCounters.begin()->second;
1057 uint64_t EndAddress = 0;
1058 for (const LBREntry &LBR : Sample->LBRStack) {
1059 uint64_t SourceAddress = LBR.Source;
1060 uint64_t TargetAddress = LBR.Target;
1061
1062 // Record the branch if its SourceAddress is external. It can be the case an
1063 // external source call an internal function, later this branch will be used
1064 // to generate the function's head sample.
1065 if (Binary->addressIsCode(Address: TargetAddress)) {
1066 Counter.recordBranchCount(Source: SourceAddress, Target: TargetAddress, Repeat);
1067 }
1068
1069 // If this not the first LBR, update the range count between TO of current
1070 // LBR and FROM of next LBR.
1071 uint64_t StartAddress = TargetAddress;
1072 if (Binary->addressIsCode(Address: StartAddress) &&
1073 Binary->addressIsCode(Address: EndAddress) &&
1074 isValidFallThroughRange(Start: StartAddress, End: EndAddress, Binary))
1075 Counter.recordRangeCount(Start: StartAddress, End: EndAddress, Repeat);
1076 EndAddress = SourceAddress;
1077 }
1078}
1079
1080void LBRPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
1081 std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>();
1082 // Parsing LBR stack and populate into PerfSample.LBRStack
1083 if (extractLBRStack(TraceIt, LBRStack&: Sample->LBRStack)) {
1084 warnIfMissingMMap();
1085 // Record LBR only samples by aggregation
1086 AggregatedSamples[Hashable<PerfSample>(Sample)] += Count;
1087 }
1088}
1089
1090void PerfScriptReader::generateUnsymbolizedProfile() {
1091 // There is no context for LBR only sample, so initialize one entry with
1092 // fake "empty" context key.
1093 assert(SampleCounters.empty() &&
1094 "Sample counter map should be empty before raw profile generation");
1095 std::shared_ptr<StringBasedCtxKey> Key =
1096 std::make_shared<StringBasedCtxKey>();
1097 SampleCounters.try_emplace(Key: Hashable<ContextKey>(Key));
1098 for (const auto &Item : AggregatedSamples) {
1099 const PerfSample *Sample = Item.first.getPtr();
1100 computeCounterFromLBR(Sample, Repeat: Item.second);
1101 }
1102}
1103
1104uint64_t PerfScriptReader::parseAggregatedCount(TraceStream &TraceIt) {
1105 // The aggregated count is optional, so do not skip the line and return 1 if
1106 // it's unmatched
1107 uint64_t Count = 1;
1108 if (!TraceIt.getCurrentLine().getAsInteger(Radix: 10, Result&: Count))
1109 TraceIt.advance();
1110 return Count;
1111}
1112
1113void PerfScriptReader::parseSample(TraceStream &TraceIt) {
1114 NumTotalSample++;
1115 uint64_t Count = parseAggregatedCount(TraceIt);
1116 assert(Count >= 1 && "Aggregated count should be >= 1!");
1117 parseSample(TraceIt, Count);
1118}
1119
1120bool PerfScriptReader::extractMMapEventForBinary(ProfiledBinary *Binary,
1121 StringRef Line,
1122 MMapEvent &MMap) {
1123 if (!Binary->isKernel() && !Line.contains(Other: Binary->getName()) &&
1124 !ShowMmapEvents)
1125 return false;
1126 // Parse a MMap2 line like:
1127 // PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0
1128 // 08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so
1129 constexpr static const char *const MMap2Pattern =
1130 "PERF_RECORD_MMAP2 (-?[0-9]+)/[0-9]+: "
1131 "\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ "
1132 "(0x[a-f0-9]+|0) .*\\]: ([-a-z]+) (.*)";
1133 // Parse a MMap line like
1134 // PERF_RECORD_MMAP -1/0: [0xffffffff81e00000(0x3e8fa000) @ \
1135 // 0xffffffff81e00000]: x [kernel.kallsyms]_text
1136 constexpr static const char *const MMapPattern =
1137 "PERF_RECORD_MMAP (-?[0-9]+)/[0-9]+: "
1138 "\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ "
1139 "(0x[a-f0-9]+|0)\\]: ([-a-z]+) (.*)";
1140 // Field 0 - whole line
1141 // Field 1 - PID
1142 // Field 2 - base address
1143 // Field 3 - mmapped size
1144 // Field 4 - page offset
1145 // Field 5 - binary path
1146 enum EventIndex {
1147 WHOLE_LINE = 0,
1148 PID = 1,
1149 MMAPPED_ADDRESS = 2,
1150 MMAPPED_SIZE = 3,
1151 PAGE_OFFSET = 4,
1152 MEM_PROTECTION_FLAG = 5,
1153 BINARY_PATH = 6,
1154 };
1155
1156 bool R = false;
1157 SmallVector<StringRef, 7> Fields;
1158 if (Line.contains(Other: "PERF_RECORD_MMAP2 ")) {
1159 Regex RegMmap2(MMap2Pattern);
1160 R = RegMmap2.match(String: Line, Matches: &Fields);
1161 } else if (Line.contains(Other: "PERF_RECORD_MMAP ")) {
1162 Regex RegMmap(MMapPattern);
1163 R = RegMmap.match(String: Line, Matches: &Fields);
1164 } else
1165 llvm_unreachable("unexpected MMAP event entry");
1166
1167 if (!R) {
1168 std::string WarningMsg = "Cannot parse mmap event: " + Line.str() + " \n";
1169 WithColor::warning() << WarningMsg;
1170 return false;
1171 }
1172 long long MMapPID = 0;
1173 getAsSignedInteger(Str: Fields[PID], Radix: 10, Result&: MMapPID);
1174 MMap.PID = MMapPID;
1175 Fields[MMAPPED_ADDRESS].getAsInteger(Radix: 0, Result&: MMap.Address);
1176 Fields[MMAPPED_SIZE].getAsInteger(Radix: 0, Result&: MMap.Size);
1177 Fields[PAGE_OFFSET].getAsInteger(Radix: 0, Result&: MMap.Offset);
1178 MMap.MemProtectionFlag = Fields[MEM_PROTECTION_FLAG];
1179 MMap.BinaryPath = Fields[BINARY_PATH];
1180 if (ShowMmapEvents) {
1181 outs() << "Mmap: Binary " << MMap.BinaryPath << " loaded at "
1182 << format(Fmt: "0x%" PRIx64 ":", Vals: MMap.Address) << " \n";
1183 }
1184
1185 StringRef BinaryName = filename(Path: MMap.BinaryPath, UseBackSlash: Binary->isCOFF());
1186 if (Binary->isKernel()) {
1187 return Binary->isKernelImageName(BinaryName);
1188 }
1189 return Binary->getName() == BinaryName;
1190}
1191
1192void PerfScriptReader::parseMMapEvent(TraceStream &TraceIt) {
1193 MMapEvent MMap;
1194 if (extractMMapEventForBinary(Binary, Line: TraceIt.getCurrentLine(), MMap))
1195 updateBinaryAddress(Event: MMap);
1196 TraceIt.advance();
1197}
1198
1199void PerfScriptReader::parseEventOrSample(TraceStream &TraceIt) {
1200 if (isMMapEvent(Line: TraceIt.getCurrentLine()))
1201 parseMMapEvent(TraceIt);
1202 else
1203 parseSample(TraceIt);
1204}
1205
1206void PerfScriptReader::parseAndAggregateTrace() {
1207 NamedRegionTimer T("parseTrace", "Parse and aggregate trace", TimerGroupName,
1208 TimerGroupDesc, TimeProfGen);
1209 // Trace line iterator
1210 TraceStream TraceIt(PerfTraceFile);
1211 while (!TraceIt.isAtEoF())
1212 parseEventOrSample(TraceIt);
1213}
1214
1215// A LBR sample is like:
1216// 40062f 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ...
1217// A heuristic for fast detection by checking whether a
1218// leading " 0x" and the '/' exist.
1219bool PerfScriptReader::isLBRSample(StringRef Line, bool CheckLineStart) {
1220 // Skip the leading instruction pointer
1221 SmallVector<StringRef, 32> Records;
1222 if (!CheckLineStart)
1223 Line = Line.trim();
1224 // Line might start with IP or only contain brstack. Check first two records
1225 // and fail if no record exists.
1226 Line.split(A&: Records, Separator: " ", MaxSplit: 2, KeepEmpty: CheckLineStart);
1227 for (StringRef Record : Records)
1228 if (Record.starts_with(Prefix: "0x") && Record.contains(C: '/'))
1229 return true;
1230 return false;
1231}
1232
1233bool PerfScriptReader::isMMapEvent(StringRef Line) {
1234 // Short cut to avoid string find is possible.
1235 if (Line.empty() || Line.size() < 50)
1236 return false;
1237
1238 if (std::isdigit(Line[0]))
1239 return false;
1240
1241 // PERF_RECORD_MMAP2 or PERF_RECORD_MMAP does not appear at the beginning of
1242 // the line for ` perf script --show-mmap-events -i ...`
1243 return Line.contains(Other: "PERF_RECORD_MMAP");
1244}
1245
1246// The raw hybird sample is like
1247// e.g.
1248// 4005dc # call stack leaf
1249// 400634
1250// 400684 # call stack root
1251// 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
1252// ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries
1253// Determine the perfscript contains hybrid samples(call stack + LBRs) by
1254// checking whether there is a non-empty call stack immediately followed by
1255// a LBR sample
1256PerfContent PerfScriptReader::checkPerfScriptType(StringRef FileName) {
1257 TraceStream TraceIt(FileName);
1258 uint64_t FrameAddr = 0;
1259 while (!TraceIt.isAtEoF()) {
1260 // Skip the aggregated count
1261 if (!TraceIt.getCurrentLine().getAsInteger(Radix: 10, Result&: FrameAddr))
1262 TraceIt.advance();
1263
1264 // Detect sample with call stack
1265 int32_t Count = 0;
1266 while (!TraceIt.isAtEoF() &&
1267 !parseAddress(Str: TraceIt.getCurrentLine().ltrim(), Addr&: FrameAddr, HasPrefix: false)) {
1268 Count++;
1269 TraceIt.advance();
1270 }
1271 if (!TraceIt.isAtEoF()) {
1272 if (isLBRSample(Line: TraceIt.getCurrentLine(), CheckLineStart: false)) {
1273 if (Count > 0)
1274 return PerfContent::LBRStack;
1275 else
1276 return PerfContent::LBR;
1277 }
1278 TraceIt.advance();
1279 }
1280 }
1281
1282 exitWithError(Message: "Invalid perf script input!");
1283 return PerfContent::UnknownContent;
1284}
1285
1286void HybridPerfReader::generateUnsymbolizedProfile() {
1287 ProfileIsCS = !IgnoreStackSamples;
1288 if (ProfileIsCS)
1289 unwindSamples();
1290 else
1291 PerfScriptReader::generateUnsymbolizedProfile();
1292}
1293
1294void PerfScriptReader::warnTruncatedStack() {
1295 if (ShowDetailedWarning) {
1296 for (auto Address : InvalidReturnAddresses) {
1297 WithColor::warning()
1298 << "Truncated stack sample due to invalid return address at "
1299 << format(Fmt: "0x%" PRIx64, Vals: Address)
1300 << ", likely caused by frame pointer omission\n";
1301 }
1302 }
1303 emitWarningSummary(
1304 Num: InvalidReturnAddresses.size(), Total: AggregatedSamples.size(),
1305 Msg: "of truncated stack samples due to invalid return address, "
1306 "likely caused by frame pointer omission.");
1307}
1308
1309void PerfScriptReader::warnInvalidRange() {
1310 DenseMap<std::pair<uint64_t, uint64_t>, uint64_t> Ranges;
1311
1312 for (const auto &Item : AggregatedSamples) {
1313 const PerfSample *Sample = Item.first.getPtr();
1314 uint64_t Count = Item.second;
1315 uint64_t EndAddress = 0;
1316 for (const LBREntry &LBR : Sample->LBRStack) {
1317 uint64_t SourceAddress = LBR.Source;
1318 uint64_t StartAddress = LBR.Target;
1319 if (EndAddress != 0)
1320 Ranges[{StartAddress, EndAddress}] += Count;
1321 EndAddress = SourceAddress;
1322 }
1323 }
1324
1325 if (Ranges.empty()) {
1326 WithColor::warning() << "No samples in perf script!\n";
1327 return;
1328 }
1329
1330 auto WarnInvalidRange = [&](uint64_t StartAddress, uint64_t EndAddress,
1331 StringRef Msg) {
1332 if (!ShowDetailedWarning)
1333 return;
1334 WithColor::warning() << "[" << format(Fmt: "%8" PRIx64, Vals: StartAddress) << ","
1335 << format(Fmt: "%8" PRIx64, Vals: EndAddress) << "]: " << Msg
1336 << "\n";
1337 };
1338
1339 const char *EndNotBoundaryMsg = "Range is not on instruction boundary, "
1340 "likely due to profile and binary mismatch.";
1341 const char *DanglingRangeMsg = "Range does not belong to any functions, "
1342 "likely from PLT, .init or .fini section.";
1343 const char *RangeCrossFuncMsg =
1344 "Fall through range should not cross function boundaries, likely due to "
1345 "profile and binary mismatch.";
1346 const char *BogusRangeMsg = "Range start is after or too far from range end.";
1347
1348 uint64_t TotalRangeNum = 0;
1349 uint64_t InstNotBoundary = 0;
1350 uint64_t UnmatchedRange = 0;
1351 uint64_t RecoveredRange = 0;
1352 uint64_t RangeCrossFunc = 0;
1353 uint64_t BogusRange = 0;
1354
1355 for (auto &I : Ranges) {
1356 uint64_t StartAddress = I.first.first;
1357 uint64_t EndAddress = I.first.second;
1358 TotalRangeNum += I.second;
1359
1360 if (!Binary->addressIsCode(Address: StartAddress) &&
1361 !Binary->addressIsCode(Address: EndAddress))
1362 continue;
1363
1364 if (!Binary->addressIsCode(Address: StartAddress) ||
1365 !Binary->addressIsTransfer(Address: EndAddress)) {
1366 InstNotBoundary += I.second;
1367 WarnInvalidRange(StartAddress, EndAddress, EndNotBoundaryMsg);
1368 }
1369
1370 auto *FRange = Binary->findFuncRange(Address: StartAddress);
1371 if (!FRange) {
1372 UnmatchedRange += I.second;
1373 WarnInvalidRange(StartAddress, EndAddress, DanglingRangeMsg);
1374 continue;
1375 }
1376
1377 if (FRange->Func->NameStatus != DwarfNameStatus::Matched)
1378 RecoveredRange += I.second;
1379
1380 if (EndAddress >= FRange->EndAddress) {
1381 RangeCrossFunc += I.second;
1382 WarnInvalidRange(StartAddress, EndAddress, RangeCrossFuncMsg);
1383 }
1384
1385 if (Binary->addressIsCode(Address: StartAddress) &&
1386 Binary->addressIsCode(Address: EndAddress) &&
1387 !isValidFallThroughRange(Start: StartAddress, End: EndAddress, Binary)) {
1388 BogusRange += I.second;
1389 WarnInvalidRange(StartAddress, EndAddress, BogusRangeMsg);
1390 }
1391 }
1392
1393 emitWarningSummary(
1394 Num: InstNotBoundary, Total: TotalRangeNum,
1395 Msg: "of samples are from ranges that are not on instruction boundary.");
1396 emitWarningSummary(
1397 Num: UnmatchedRange, Total: TotalRangeNum,
1398 Msg: "of samples are from ranges that do not belong to any functions.");
1399 emitWarningSummary(Num: RecoveredRange, Total: TotalRangeNum,
1400 Msg: "of samples are from ranges that belong to functions "
1401 "recovered from symbol table.");
1402 emitWarningSummary(
1403 Num: RangeCrossFunc, Total: TotalRangeNum,
1404 Msg: "of samples are from ranges that do cross function boundaries.");
1405 emitWarningSummary(
1406 Num: BogusRange, Total: TotalRangeNum,
1407 Msg: "of samples are from ranges that have range start after or too far from "
1408 "range end acrossing the unconditinal jmp.");
1409}
1410
1411void PerfScriptReader::warnIfBranchTargetMismatch() {
1412 // Collect unique branch source and target addresses from LBR samples,
1413 // then check what percentage don't match known instructions in the binary.
1414
1415 uint64_t MismatchedBranches = 0;
1416 uint64_t MismatchedIndirectTargets = 0;
1417 uint64_t MismatchedTargets = 0;
1418 uint64_t TotalSamples = 0;
1419
1420 for (const auto &Item : AggregatedSamples) {
1421 const PerfSample *Sample = Item.first.getPtr();
1422 for (const LBREntry &LBR : Sample->LBRStack) {
1423 uint64_t Source = LBR.Source;
1424 uint64_t Target = LBR.Target;
1425 if (Source == ExternalAddr || Target == ExternalAddr)
1426 continue;
1427 TotalSamples++;
1428
1429 // Validate Branch sources are Call/Branch/Indirect Branch
1430 if (!Binary->addressIsTransfer(Address: Source))
1431 MismatchedBranches++;
1432
1433 // Validate Indirect Branch targets landed in code. This may over estimate
1434 // the vaid targets only because there's no good way to determine jump
1435 // table targets
1436 if (Binary->addressIsIndirectBranch(Address: Source)) {
1437 if (!Binary->addressIsCode(Address: Target))
1438 MismatchedIndirectTargets++;
1439 } else if (!Binary->addressIsBranchTarget(Address: Target) &&
1440 !Binary->findFuncRangeForStartAddr(Address: Target))
1441 MismatchedTargets++;
1442 }
1443 }
1444
1445 emitWarningSummary(Num: MismatchedBranches, Total: TotalSamples,
1446 Msg: "of branch samples do not match the binary.");
1447 emitWarningSummary(Num: MismatchedTargets, Total: TotalSamples,
1448 Msg: "of branch targets do not match the binary.");
1449 emitWarningSummary(Num: MismatchedIndirectTargets, Total: TotalSamples,
1450 Msg: "of indirect branch targets do not match the binary.");
1451}
1452
1453void PerfScriptReader::parsePerfTraces() {
1454 // Parse perf traces and do aggregation.
1455 parseAndAggregateTrace();
1456 if (Binary->isKernel() && !Binary->getIsLoadedByMMap()) {
1457 exitWithError(
1458 Message: "Kernel is requested, but no kernel is found in mmap events.");
1459 }
1460
1461 emitWarningSummary(Num: NumLeafExternalFrame, Total: NumTotalSample,
1462 Msg: "of samples have leaf external frame in call stack.");
1463 emitWarningSummary(Num: NumLeadingOutgoingLBR, Total: NumTotalSample,
1464 Msg: "of samples have leading external LBR.");
1465
1466 // Generate unsymbolized profile.
1467 warnTruncatedStack();
1468 warnInvalidRange();
1469 warnIfBranchTargetMismatch();
1470 generateUnsymbolizedProfile();
1471 AggregatedSamples.clear();
1472
1473 if (SkipSymbolization)
1474 writeUnsymbolizedProfile(Filename: OutputFilename);
1475}
1476
1477SmallVector<CleanupInstaller, 2> PerfScriptReader::TempFileCleanups;
1478
1479void ETMReader::recordProcessedRange(uint64_t Start, uint64_t End,
1480 uint64_t Count) {
1481 assert(!Counters.empty() && "Counters should not be empty!");
1482 auto &Counter = Counters.begin()->second;
1483 Counter.recordRangeCount(Start, End, Repeat: Count);
1484}
1485
1486class ETMCallback : public ETMDecoder::Callback {
1487 ETMReader *Reader;
1488
1489public:
1490 ETMCallback(ETMReader *R) : Reader(R) {}
1491 void processInstructionRange(uint64_t Start, uint64_t End) override {
1492 Reader->recordProcessedRange(Start, End, Count: 1);
1493 }
1494};
1495
1496void ETMReader::parseETMTraces() {
1497 auto BufferOrErr = MemoryBuffer::getFile(Filename: TraceFile);
1498 if (std::error_code EC = BufferOrErr.getError())
1499 exitWithError(Message: "Could not open ETM trace file: " + EC.message());
1500
1501 ArrayRef<uint8_t> Data(
1502 reinterpret_cast<const uint8_t *>((*BufferOrErr)->getBufferStart()),
1503 (*BufferOrErr)->getBufferSize());
1504
1505 // There is no context for ETM instruction traces.
1506 // Initialize the SampleCounters map with a single empty context key
1507 // to aggregate all instruction hits into a global bucket.
1508 auto Key = std::make_shared<StringBasedCtxKey>();
1509 Counters.try_emplace(Key: Hashable<ContextKey>(Key));
1510
1511 // The protocol utilizes a 0x80 byte as an initial synchronization header.
1512 // Perform a manual search for this sync point to discard any leading
1513 // padding or truncated packets before decoding begins.
1514 size_t StartIdx = 0;
1515 while (StartIdx < Data.size() && Data[StartIdx] != 0x80)
1516 StartIdx++;
1517 if (StartIdx >= Data.size())
1518 exitWithError(Message: "No synchronization header (0x80) found in the bitstream.");
1519 ArrayRef<uint8_t> TraceSlice = Data.slice(N: StartIdx);
1520
1521 auto DecoderOrErr = ETMDecoder::create(
1522 Binary: Binary->getBinary(), TargetTriple: Binary->getTriple(), TraceID: static_cast<uint8_t>(TraceID));
1523
1524 if (!DecoderOrErr)
1525 exitWithError(Message: toString(E: DecoderOrErr.takeError()));
1526 auto Decoder = std::move(*DecoderOrErr);
1527
1528 ETMCallback CB(this);
1529 if (Error E = Decoder->processTrace(TraceData: TraceSlice, TraceCallback&: CB))
1530 exitWithError(Message: toString(E: std::move(E)));
1531}
1532
1533} // end namespace sampleprof
1534} // end namespace llvm
1535