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 Binary->addMMapRange(Address: Event.Address, Size: Event.Size);
598
599 // Check if the FileOffset falls within the [Event.Offset, Event.Offset +
600 // Event.Size) range.
601 auto MMapContainsFileOffset = [&](uint64_t FileOffset) {
602 return Event.Offset <= FileOffset &&
603 (FileOffset - Event.Offset) < Event.Size;
604 };
605
606 if (IsKernel || MMapContainsFileOffset(Binary->getTextSegmentOffset())) {
607 // For ELF, subtract the file offset to get the runtime address
608 // corresponding to file offset zero. Kernel mmap events report the text
609 // address as Event.Offset, so use the text segment offset from the ELF
610 // instead.
611 const uint64_t RuntimeBaseAddress =
612 Binary->isCOFF()
613 ? Event.Address
614 : Event.Address -
615 (IsKernel ? Binary->getTextSegmentOffset() : Event.Offset);
616 // A binary image could be unloaded and then reloaded at different
617 // place, so update binary load address.
618 // Only update for the first executable segment and assume all other
619 // segments are loaded at consecutive memory addresses, which is the case on
620 // X64.
621 Binary->setBaseAddress(RuntimeBaseAddress);
622 Binary->setIsLoadedByMMap(true);
623 } else {
624 // Verify segments are loaded consecutively.
625 const auto &Offsets = Binary->getTextSegmentOffsets();
626 auto IsContiguousMMapForSegment = [&](auto SegmentIt, uint64_t FileOffset,
627 uint64_t RuntimeAddress) {
628 auto I = std::distance(Offsets.begin(), SegmentIt);
629 const auto &PreferredAddrs = Binary->getPreferredTextSegmentAddresses();
630 return PreferredAddrs[I] + (FileOffset - *SegmentIt) ==
631 Binary->canonicalizeVirtualAddress(Address: RuntimeAddress);
632 };
633
634 auto It = llvm::lower_bound(Range: Offsets, Value: Event.Offset);
635 if (It != Offsets.end() && MMapContainsFileOffset(*It)) {
636 // The event is for loading a separate executable segment.
637 uint64_t RuntimeSegmentAddress = Event.Address + (*It - Event.Offset);
638 if (!IsContiguousMMapForSegment(It, *It, RuntimeSegmentAddress))
639 exitWithError(Message: "Executable segments not loaded consecutively");
640 } else {
641 if (It == Offsets.begin())
642 exitWithError(Message: "File offset not found");
643 else {
644 // Find the segment the event falls in. A large segment could be loaded
645 // via multiple mmap calls with consecutive memory addresses.
646 --It;
647 assert(*It < Event.Offset);
648 if (!IsContiguousMMapForSegment(It, Event.Offset, Event.Address))
649 exitWithError(Message: "Segment not loaded by consecutive mmaps");
650 }
651 }
652 }
653}
654
655static std::string getContextKeyStr(ContextKey *K,
656 const ProfiledBinary *Binary) {
657 if (const auto *CtxKey = dyn_cast<StringBasedCtxKey>(Val: K)) {
658 return SampleContext::getContextString(Context: CtxKey->Context);
659 } else if (const auto *CtxKey = dyn_cast<AddrBasedCtxKey>(Val: K)) {
660 std::ostringstream OContextStr;
661 for (uint32_t I = 0; I < CtxKey->Context.size(); I++) {
662 if (OContextStr.str().size())
663 OContextStr << " @ ";
664 uint64_t Address = CtxKey->Context[I];
665 if (UseOffset) {
666 if (UseLoadableSegmentAsBase)
667 Address -= Binary->getFirstLoadableAddress();
668 else
669 Address -= Binary->getPreferredBaseAddress();
670 }
671 OContextStr << "0x"
672 << utohexstr(X: Address,
673 /*LowerCase=*/true);
674 }
675 return OContextStr.str();
676 } else {
677 llvm_unreachable("unexpected key type");
678 }
679}
680
681void HybridPerfReader::unwindSamples() {
682 NamedRegionTimer T("unwind", "Unwind samples", TimerGroupName, TimerGroupDesc,
683 TimeProfGen);
684 VirtualUnwinder Unwinder(&SampleCounters, Binary);
685 for (const auto &Item : AggregatedSamples) {
686 const PerfSample *Sample = Item.first.getPtr();
687 Unwinder.unwind(Sample, Repeat: Item.second);
688 }
689
690 // Warn about untracked frames due to missing probes.
691 if (ShowDetailedWarning) {
692 for (auto Address : Unwinder.getUntrackedCallsites())
693 WithColor::warning() << "Profile context truncated due to missing probe "
694 << "for call instruction at "
695 << format(Fmt: "0x%" PRIx64, Vals: Address) << "\n";
696 }
697
698 emitWarningSummary(Num: Unwinder.getUntrackedCallsites().size(),
699 Total: SampleCounters.size(),
700 Msg: "of profiled contexts are truncated due to missing probe "
701 "for call instruction.");
702
703 emitWarningSummary(
704 Num: Unwinder.NumMismatchedExtCallBranch, Total: Unwinder.NumTotalBranches,
705 Msg: "of branches'source is a call instruction but doesn't match call frame "
706 "stack, likely due to unwinding error of external frame.");
707
708 emitWarningSummary(Num: Unwinder.NumPairedExtAddr * 2, Total: Unwinder.NumTotalBranches,
709 Msg: "of branches containing paired external address.");
710
711 emitWarningSummary(Num: Unwinder.NumUnpairedExtAddr, Total: Unwinder.NumTotalBranches,
712 Msg: "of branches containing external address but doesn't have "
713 "another external address to pair, likely due to "
714 "interrupt jmp or broken perf script.");
715
716 emitWarningSummary(
717 Num: Unwinder.NumMismatchedProEpiBranch, Total: Unwinder.NumTotalBranches,
718 Msg: "of branches'source is a call instruction but doesn't match call frame "
719 "stack, likely due to frame in prolog/epilog.");
720
721 emitWarningSummary(Num: Unwinder.NumMissingExternalFrame,
722 Total: Unwinder.NumExtCallBranch,
723 Msg: "of artificial call branches but doesn't have an external "
724 "frame to match.");
725}
726
727/// Parse a hex address from \p Str.
728static bool parseAddress(StringRef Str, uint64_t &Addr, bool HasPrefix) {
729 if (Str.consume_front(Prefix: "0x") != HasPrefix)
730 return true;
731 return Str.getAsInteger(Radix: 16, Result&: Addr);
732}
733
734bool PerfScriptReader::extractLBRStack(TraceStream &TraceIt,
735 SmallVectorImpl<LBREntry> &LBRStack) {
736 // The raw format of LBR stack is like:
737 // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
738 // ... 0x4005c8/0x4005dc/P/-/-/0
739 // It's in FIFO order and separated by whitespace.
740 SmallVector<StringRef, 32> Records;
741 TraceIt.getCurrentLine().rtrim().split(A&: Records, Separator: " ", MaxSplit: -1, KeepEmpty: false);
742 auto WarnInvalidLBR = [](TraceStream &TraceIt) {
743 WithColor::warning() << "Invalid address in LBR record at line "
744 << TraceIt.getLineNumber() << ": "
745 << TraceIt.getCurrentLine() << "\n";
746 };
747
748 // Skip the leading instruction pointer.
749 size_t Index = 0;
750 uint64_t LeadingAddr;
751 if (!Records.empty() && !Records[0].contains(C: '/')) {
752 if (parseAddress(Str: Records[0], Addr&: LeadingAddr, HasPrefix: false)) {
753 WarnInvalidLBR(TraceIt);
754 TraceIt.advance();
755 return false;
756 }
757 Index = 1;
758 }
759
760 // Now extract LBR samples - note that we do not reverse the
761 // LBR entry order so we can unwind the sample stack as we walk
762 // through LBR entries.
763 while (Index < Records.size()) {
764 auto &Token = Records[Index++];
765 if (Token.size() == 0)
766 continue;
767
768 SmallVector<StringRef, 8> Addresses;
769 Token.split(A&: Addresses, Separator: "/");
770 uint64_t Src;
771 uint64_t Dst;
772
773 // Stop at broken LBR records.
774 if (Addresses.size() < 2 || parseAddress(Str: Addresses[0], Addr&: Src, HasPrefix: true) ||
775 parseAddress(Str: Addresses[1], Addr&: Dst, HasPrefix: true)) {
776 WarnInvalidLBR(TraceIt);
777 break;
778 }
779
780 // Canonicalize to use preferred load address as base address.
781 Src = Binary->canonicalizeVirtualAddress(Address: Src);
782 Dst = Binary->canonicalizeVirtualAddress(Address: Dst);
783 bool SrcIsInternal = Binary->addressIsCode(Address: Src);
784 bool DstIsInternal = Binary->addressIsCode(Address: Dst);
785 if (!SrcIsInternal)
786 Src = ExternalAddr;
787 if (!DstIsInternal)
788 Dst = ExternalAddr;
789 // Filter external-to-external case to reduce LBR trace size.
790 if (!SrcIsInternal && !DstIsInternal)
791 continue;
792
793 LBRStack.emplace_back(Args: LBREntry(Src, Dst));
794 }
795 TraceIt.advance();
796 return !LBRStack.empty();
797}
798
799bool PerfScriptReader::extractCallstack(TraceStream &TraceIt,
800 SmallVectorImpl<uint64_t> &CallStack) {
801 // The raw format of call stack is like:
802 // 4005dc # leaf frame
803 // 400634
804 // 400684 # root frame
805 // It's in bottom-up order with each frame in one line.
806
807 // Extract stack frames from sample
808 while (!TraceIt.isAtEoF() && !isLBRSample(Line: TraceIt.getCurrentLine(), CheckLineStart: true)) {
809 StringRef FrameStr = TraceIt.getCurrentLine().ltrim();
810 uint64_t FrameAddr = 0;
811 if (parseAddress(Str: FrameStr, Addr&: FrameAddr, HasPrefix: false)) {
812 // We might parse a non-perf sample line like empty line and comments,
813 // skip it
814 TraceIt.advance();
815 return false;
816 }
817 TraceIt.advance();
818
819 FrameAddr = Binary->canonicalizeVirtualAddress(Address: FrameAddr);
820 // Currently intermixed frame from different binaries is not supported.
821 if (!Binary->addressIsCode(Address: FrameAddr)) {
822 if (CallStack.empty())
823 NumLeafExternalFrame++;
824 // Push a special value(ExternalAddr) for the external frames so that
825 // unwinder can still work on this with artificial Call/Return branch.
826 // After unwinding, the context will be truncated for external frame.
827 // Also deduplicate the consecutive external addresses.
828 if (CallStack.empty() || CallStack.back() != ExternalAddr)
829 CallStack.emplace_back(Args: ExternalAddr);
830 continue;
831 }
832
833 // We need to translate return address to call address for non-leaf frames.
834 if (!CallStack.empty()) {
835 auto CallAddr = Binary->getCallAddrFromFrameAddr(FrameAddr);
836 if (!CallAddr) {
837 // Stop at an invalid return address caused by bad unwinding. This could
838 // happen to frame-pointer-based unwinding and the callee functions that
839 // do not have the frame pointer chain set up.
840 InvalidReturnAddresses.insert(x: FrameAddr);
841 break;
842 }
843 FrameAddr = CallAddr;
844 }
845
846 CallStack.emplace_back(Args&: FrameAddr);
847 }
848
849 // Strip out the bottom external addr.
850 if (CallStack.size() > 1 && CallStack.back() == ExternalAddr)
851 CallStack.pop_back();
852
853 // Skip other unrelated line, find the next valid LBR line
854 // Note that even for empty call stack, we should skip the address at the
855 // bottom, otherwise the following pass may generate a truncated callstack
856 while (!TraceIt.isAtEoF() && !isLBRSample(Line: TraceIt.getCurrentLine(), CheckLineStart: true)) {
857 TraceIt.advance();
858 }
859 // Filter out broken stack sample. We may not have complete frame info
860 // if sample end up in prolog/epilog, the result is dangling context not
861 // connected to entry point. This should be relatively rare thus not much
862 // impact on overall profile quality. However we do want to filter them
863 // out to reduce the number of different calling contexts. One instance
864 // of such case - when sample landed in prolog/epilog, somehow stack
865 // walking will be broken in an unexpected way that higher frames will be
866 // missing.
867 return !CallStack.empty() &&
868 !Binary->addressInPrologEpilog(Address: CallStack.front());
869}
870
871void PerfScriptReader::warnIfMissingMMap() {
872 if (!Binary->getMissingMMapWarned() && !Binary->getIsLoadedByMMap()) {
873 WithColor::warning() << "No relevant mmap event is matched for "
874 << Binary->getName()
875 << ", will use preferred address ("
876 << format(Fmt: "0x%" PRIx64,
877 Vals: Binary->getPreferredBaseAddress())
878 << ") as the base loading address!\n";
879 // Avoid redundant warning, only warn at the first unmatched sample.
880 Binary->setMissingMMapWarned(true);
881 }
882}
883
884void HybridPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
885 // The raw hybird sample started with call stack in FILO order and followed
886 // intermediately by LBR sample
887 // e.g.
888 // 4005dc # call stack leaf
889 // 400634
890 // 400684 # call stack root
891 // 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
892 // ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries
893 //
894 std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>();
895#ifndef NDEBUG
896 Sample->Linenum = TraceIt.getLineNumber();
897#endif
898 // Parsing call stack and populate into PerfSample.CallStack
899 if (!extractCallstack(TraceIt, CallStack&: Sample->CallStack)) {
900 // Skip the next LBR line matched current call stack
901 if (!TraceIt.isAtEoF() && isLBRSample(Line: TraceIt.getCurrentLine(), CheckLineStart: true))
902 TraceIt.advance();
903 return;
904 }
905
906 warnIfMissingMMap();
907
908 if (!TraceIt.isAtEoF() && isLBRSample(Line: TraceIt.getCurrentLine(), CheckLineStart: true)) {
909 // Parsing LBR stack and populate into PerfSample.LBRStack
910 if (extractLBRStack(TraceIt, LBRStack&: Sample->LBRStack)) {
911 if (IgnoreStackSamples) {
912 Sample->CallStack.clear();
913 } else {
914 // Canonicalize stack leaf to avoid 'random' IP from leaf frame skew LBR
915 // ranges
916 Sample->CallStack.front() = Sample->LBRStack[0].Target;
917 }
918 // Record samples by aggregation
919 AggregatedSamples[Hashable<PerfSample>(Sample)] += Count;
920 }
921 } else {
922 // LBR sample is encoded in single line after stack sample
923 exitWithError(Message: "'Hybrid perf sample is corrupted, No LBR sample line");
924 }
925}
926
927void PerfScriptReader::writeUnsymbolizedProfile(StringRef Filename) {
928 std::error_code EC;
929 raw_fd_ostream OS(Filename, EC, llvm::sys::fs::OF_TextWithCRLF);
930 if (EC)
931 exitWithError(EC, Whence: Filename);
932 writeUnsymbolizedProfile(OS);
933}
934
935// Use ordered map to make the output deterministic
936using OrderedCounterForPrint = std::map<std::string, SampleCounter *>;
937
938void PerfScriptReader::writeUnsymbolizedProfile(raw_fd_ostream &OS) {
939 OrderedCounterForPrint OrderedCounters;
940 for (auto &CI : SampleCounters) {
941 OrderedCounters[getContextKeyStr(K: CI.first.getPtr(), Binary)] = &CI.second;
942 }
943
944 auto SCounterPrinter = [&](RangeSample &Counter, StringRef Separator,
945 uint32_t Indent) {
946 OS.indent(NumSpaces: Indent);
947 OS << Counter.size() << "\n";
948 for (auto &I : Counter) {
949 uint64_t Start = I.first.first;
950 uint64_t End = I.first.second;
951
952 if (UseOffset) {
953 if (UseLoadableSegmentAsBase) {
954 Start -= Binary->getFirstLoadableAddress();
955 End -= Binary->getFirstLoadableAddress();
956 } else {
957 Start -= Binary->getPreferredBaseAddress();
958 End -= Binary->getPreferredBaseAddress();
959 }
960 }
961
962 OS.indent(NumSpaces: Indent);
963 OS << Twine::utohexstr(Val: Start) << Separator << Twine::utohexstr(Val: End) << ":"
964 << I.second << "\n";
965 }
966 };
967
968 for (auto &CI : OrderedCounters) {
969 uint32_t Indent = 0;
970 if (ProfileIsCS) {
971 // Context string key
972 OS << "[" << CI.first << "]\n";
973 Indent = 2;
974 }
975
976 SampleCounter &Counter = *CI.second;
977 SCounterPrinter(Counter.RangeCounter, "-", Indent);
978 SCounterPrinter(Counter.BranchCounter, "->", Indent);
979 }
980}
981
982// Format of input:
983// number of entries in RangeCounter
984// from_1-to_1:count_1
985// from_2-to_2:count_2
986// ......
987// from_n-to_n:count_n
988// number of entries in BranchCounter
989// src_1->dst_1:count_1
990// src_2->dst_2:count_2
991// ......
992// src_n->dst_n:count_n
993void UnsymbolizedProfileReader::readSampleCounters(TraceStream &TraceIt,
994 SampleCounter &SCounters) {
995 auto exitWithErrorForTraceLine = [](TraceStream &TraceIt) {
996 std::string Msg = TraceIt.isAtEoF()
997 ? "Invalid raw profile!"
998 : "Invalid raw profile at line " +
999 Twine(TraceIt.getLineNumber()).str() + ": " +
1000 TraceIt.getCurrentLine().str();
1001 exitWithError(Message: Msg);
1002 };
1003 auto ReadNumber = [&](uint64_t &Num) {
1004 if (TraceIt.isAtEoF())
1005 exitWithErrorForTraceLine(TraceIt);
1006 if (TraceIt.getCurrentLine().ltrim().getAsInteger(Radix: 10, Result&: Num))
1007 exitWithErrorForTraceLine(TraceIt);
1008 TraceIt.advance();
1009 };
1010
1011 auto ReadCounter = [&](RangeSample &Counter, StringRef Separator) {
1012 uint64_t Num = 0;
1013 ReadNumber(Num);
1014 while (Num--) {
1015 if (TraceIt.isAtEoF())
1016 exitWithErrorForTraceLine(TraceIt);
1017 StringRef Line = TraceIt.getCurrentLine().ltrim();
1018
1019 uint64_t Count = 0;
1020 auto LineSplit = Line.split(Separator: ":");
1021 if (LineSplit.second.empty() || LineSplit.second.getAsInteger(Radix: 10, Result&: Count))
1022 exitWithErrorForTraceLine(TraceIt);
1023
1024 uint64_t Source = 0;
1025 uint64_t Target = 0;
1026 auto Range = LineSplit.first.split(Separator);
1027 if (Range.second.empty() || Range.first.getAsInteger(Radix: 16, Result&: Source) ||
1028 Range.second.getAsInteger(Radix: 16, Result&: Target))
1029 exitWithErrorForTraceLine(TraceIt);
1030
1031 if (UseOffset) {
1032 if (UseLoadableSegmentAsBase) {
1033 Source += Binary->getFirstLoadableAddress();
1034 Target += Binary->getFirstLoadableAddress();
1035 } else {
1036 Source += Binary->getPreferredBaseAddress();
1037 Target += Binary->getPreferredBaseAddress();
1038 }
1039 }
1040
1041 Counter[{Source, Target}] += Count;
1042 TraceIt.advance();
1043 }
1044 };
1045
1046 ReadCounter(SCounters.RangeCounter, "-");
1047 ReadCounter(SCounters.BranchCounter, "->");
1048}
1049
1050void UnsymbolizedProfileReader::readUnsymbolizedProfile(StringRef FileName) {
1051 TraceStream TraceIt(FileName);
1052 while (!TraceIt.isAtEoF()) {
1053 std::shared_ptr<StringBasedCtxKey> Key =
1054 std::make_shared<StringBasedCtxKey>();
1055 StringRef Line = TraceIt.getCurrentLine();
1056 // Read context stack for CS profile.
1057 if (Line.starts_with(Prefix: "[")) {
1058 ProfileIsCS = true;
1059 auto I = ContextStrSet.insert(key: Line);
1060 SampleContext::createCtxVectorFromStr(ContextStr: I.first->getKey(), Context&: Key->Context);
1061 TraceIt.advance();
1062 }
1063 auto Ret = SampleCounters.try_emplace(Key: Hashable<ContextKey>(Key));
1064 readSampleCounters(TraceIt, SCounters&: Ret.first->second);
1065 }
1066}
1067
1068void UnsymbolizedProfileReader::parsePerfTraces() {
1069 readUnsymbolizedProfile(FileName: PerfTraceFile);
1070}
1071
1072void PerfScriptReader::computeCounterFromLBR(const PerfSample *Sample,
1073 uint64_t Repeat) {
1074 SampleCounter &Counter = SampleCounters.begin()->second;
1075 uint64_t EndAddress = 0;
1076 for (const LBREntry &LBR : Sample->LBRStack) {
1077 uint64_t SourceAddress = LBR.Source;
1078 uint64_t TargetAddress = LBR.Target;
1079
1080 // Record the branch if its SourceAddress is external. It can be the case an
1081 // external source call an internal function, later this branch will be used
1082 // to generate the function's head sample.
1083 if (Binary->addressIsCode(Address: TargetAddress)) {
1084 Counter.recordBranchCount(Source: SourceAddress, Target: TargetAddress, Repeat);
1085 }
1086
1087 // If this not the first LBR, update the range count between TO of current
1088 // LBR and FROM of next LBR.
1089 uint64_t StartAddress = TargetAddress;
1090 if (Binary->addressIsCode(Address: StartAddress) &&
1091 Binary->addressIsCode(Address: EndAddress) &&
1092 isValidFallThroughRange(Start: StartAddress, End: EndAddress, Binary))
1093 Counter.recordRangeCount(Start: StartAddress, End: EndAddress, Repeat);
1094 EndAddress = SourceAddress;
1095 }
1096}
1097
1098void LBRPerfReader::parseSample(TraceStream &TraceIt, uint64_t Count) {
1099 std::shared_ptr<PerfSample> Sample = std::make_shared<PerfSample>();
1100 // Parsing LBR stack and populate into PerfSample.LBRStack
1101 if (extractLBRStack(TraceIt, LBRStack&: Sample->LBRStack)) {
1102 warnIfMissingMMap();
1103 // Record LBR only samples by aggregation
1104 AggregatedSamples[Hashable<PerfSample>(Sample)] += Count;
1105 }
1106}
1107
1108void PerfScriptReader::generateUnsymbolizedProfile() {
1109 // There is no context for LBR only sample, so initialize one entry with
1110 // fake "empty" context key.
1111 assert(SampleCounters.empty() &&
1112 "Sample counter map should be empty before raw profile generation");
1113 std::shared_ptr<StringBasedCtxKey> Key =
1114 std::make_shared<StringBasedCtxKey>();
1115 SampleCounters.try_emplace(Key: Hashable<ContextKey>(Key));
1116 for (const auto &Item : AggregatedSamples) {
1117 const PerfSample *Sample = Item.first.getPtr();
1118 computeCounterFromLBR(Sample, Repeat: Item.second);
1119 }
1120}
1121
1122uint64_t PerfScriptReader::parseAggregatedCount(TraceStream &TraceIt) {
1123 // The aggregated count is optional, so do not skip the line and return 1 if
1124 // it's unmatched
1125 uint64_t Count = 1;
1126 if (!TraceIt.getCurrentLine().getAsInteger(Radix: 10, Result&: Count))
1127 TraceIt.advance();
1128 return Count;
1129}
1130
1131void PerfScriptReader::parseSample(TraceStream &TraceIt) {
1132 NumTotalSample++;
1133 uint64_t Count = parseAggregatedCount(TraceIt);
1134 assert(Count >= 1 && "Aggregated count should be >= 1!");
1135 parseSample(TraceIt, Count);
1136}
1137
1138bool PerfScriptReader::extractMMapEventForBinary(ProfiledBinary *Binary,
1139 StringRef Line,
1140 MMapEvent &MMap) {
1141 if (!Binary->isKernel() && !Line.contains(Other: Binary->getName()) &&
1142 !ShowMmapEvents)
1143 return false;
1144 // Parse a MMap2 line like:
1145 // PERF_RECORD_MMAP2 2113428/2113428: [0x7fd4efb57000(0x204000) @ 0
1146 // 08:04 19532229 3585508847]: r-xp /usr/lib64/libdl-2.17.so
1147 constexpr static const char *const MMap2Pattern =
1148 "PERF_RECORD_MMAP2 (-?[0-9]+)/[0-9]+: "
1149 "\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ "
1150 "(0x[a-f0-9]+|0) .*\\]: ([-a-z]+) (.*)";
1151 // Parse a MMap line like
1152 // PERF_RECORD_MMAP -1/0: [0xffffffff81e00000(0x3e8fa000) @ \
1153 // 0xffffffff81e00000]: x [kernel.kallsyms]_text
1154 constexpr static const char *const MMapPattern =
1155 "PERF_RECORD_MMAP (-?[0-9]+)/[0-9]+: "
1156 "\\[(0x[a-f0-9]+)\\((0x[a-f0-9]+)\\) @ "
1157 "(0x[a-f0-9]+|0)\\]: ([-a-z]+) (.*)";
1158 // Field 0 - whole line
1159 // Field 1 - PID
1160 // Field 2 - base address
1161 // Field 3 - mmapped size
1162 // Field 4 - page offset
1163 // Field 5 - binary path
1164 enum EventIndex {
1165 WHOLE_LINE = 0,
1166 PID = 1,
1167 MMAPPED_ADDRESS = 2,
1168 MMAPPED_SIZE = 3,
1169 PAGE_OFFSET = 4,
1170 MEM_PROTECTION_FLAG = 5,
1171 BINARY_PATH = 6,
1172 };
1173
1174 bool R = false;
1175 SmallVector<StringRef, 7> Fields;
1176 if (Line.contains(Other: "PERF_RECORD_MMAP2 ")) {
1177 Regex RegMmap2(MMap2Pattern);
1178 R = RegMmap2.match(String: Line, Matches: &Fields);
1179 } else if (Line.contains(Other: "PERF_RECORD_MMAP ")) {
1180 Regex RegMmap(MMapPattern);
1181 R = RegMmap.match(String: Line, Matches: &Fields);
1182 } else
1183 llvm_unreachable("unexpected MMAP event entry");
1184
1185 if (!R) {
1186 std::string WarningMsg = "Cannot parse mmap event: " + Line.str() + " \n";
1187 WithColor::warning() << WarningMsg;
1188 return false;
1189 }
1190 long long MMapPID = 0;
1191 getAsSignedInteger(Str: Fields[PID], Radix: 10, Result&: MMapPID);
1192 MMap.PID = MMapPID;
1193 Fields[MMAPPED_ADDRESS].getAsInteger(Radix: 0, Result&: MMap.Address);
1194 Fields[MMAPPED_SIZE].getAsInteger(Radix: 0, Result&: MMap.Size);
1195 Fields[PAGE_OFFSET].getAsInteger(Radix: 0, Result&: MMap.Offset);
1196 MMap.MemProtectionFlag = Fields[MEM_PROTECTION_FLAG];
1197 MMap.BinaryPath = Fields[BINARY_PATH];
1198 if (ShowMmapEvents) {
1199 outs() << "Mmap: Binary " << MMap.BinaryPath << " loaded at "
1200 << format(Fmt: "0x%" PRIx64 ":", Vals: MMap.Address) << " \n";
1201 }
1202
1203 StringRef BinaryName = filename(Path: MMap.BinaryPath, UseBackSlash: Binary->isCOFF());
1204 if (Binary->isKernel()) {
1205 return Binary->isKernelImageName(BinaryName);
1206 }
1207 return Binary->getName() == BinaryName;
1208}
1209
1210void PerfScriptReader::parseMMapEvent(TraceStream &TraceIt) {
1211 MMapEvent MMap;
1212 if (extractMMapEventForBinary(Binary, Line: TraceIt.getCurrentLine(), MMap))
1213 updateBinaryAddress(Event: MMap);
1214 TraceIt.advance();
1215}
1216
1217void PerfScriptReader::parseEventOrSample(TraceStream &TraceIt) {
1218 if (isMMapEvent(Line: TraceIt.getCurrentLine()))
1219 parseMMapEvent(TraceIt);
1220 else
1221 parseSample(TraceIt);
1222}
1223
1224void PerfScriptReader::parseAndAggregateTrace() {
1225 NamedRegionTimer T("parseTrace", "Parse and aggregate trace", TimerGroupName,
1226 TimerGroupDesc, TimeProfGen);
1227 // Trace line iterator
1228 TraceStream TraceIt(PerfTraceFile);
1229 while (!TraceIt.isAtEoF())
1230 parseEventOrSample(TraceIt);
1231}
1232
1233// A LBR sample is like:
1234// 40062f 0x5c6313f/0x5c63170/P/-/-/0 0x5c630e7/0x5c63130/P/-/-/0 ...
1235// A heuristic for fast detection by checking whether a
1236// leading " 0x" and the '/' exist.
1237bool PerfScriptReader::isLBRSample(StringRef Line, bool CheckLineStart) {
1238 // Skip the leading instruction pointer
1239 SmallVector<StringRef, 32> Records;
1240 if (!CheckLineStart)
1241 Line = Line.trim();
1242 // Line might start with IP or only contain brstack. Check first two records
1243 // and fail if no record exists.
1244 Line.split(A&: Records, Separator: " ", MaxSplit: 2, KeepEmpty: CheckLineStart);
1245 for (StringRef Record : Records)
1246 if (Record.starts_with(Prefix: "0x") && Record.contains(C: '/'))
1247 return true;
1248 return false;
1249}
1250
1251bool PerfScriptReader::isMMapEvent(StringRef Line) {
1252 // Short cut to avoid string find is possible.
1253 if (Line.empty() || Line.size() < 50)
1254 return false;
1255
1256 if (std::isdigit(Line[0]))
1257 return false;
1258
1259 // PERF_RECORD_MMAP2 or PERF_RECORD_MMAP does not appear at the beginning of
1260 // the line for ` perf script --show-mmap-events -i ...`
1261 return Line.contains(Other: "PERF_RECORD_MMAP");
1262}
1263
1264// The raw hybird sample is like
1265// e.g.
1266// 4005dc # call stack leaf
1267// 400634
1268// 400684 # call stack root
1269// 0x4005c8/0x4005dc/P/-/-/0 0x40062f/0x4005b0/P/-/-/0 ...
1270// ... 0x4005c8/0x4005dc/P/-/-/0 # LBR Entries
1271// Determine the perfscript contains hybrid samples(call stack + LBRs) by
1272// checking whether there is a non-empty call stack immediately followed by
1273// a LBR sample
1274PerfContent PerfScriptReader::checkPerfScriptType(StringRef FileName) {
1275 TraceStream TraceIt(FileName);
1276 uint64_t FrameAddr = 0;
1277 while (!TraceIt.isAtEoF()) {
1278 // Skip the aggregated count
1279 if (!TraceIt.getCurrentLine().getAsInteger(Radix: 10, Result&: FrameAddr))
1280 TraceIt.advance();
1281
1282 // Detect sample with call stack
1283 int32_t Count = 0;
1284 while (!TraceIt.isAtEoF() &&
1285 !parseAddress(Str: TraceIt.getCurrentLine().ltrim(), Addr&: FrameAddr, HasPrefix: false)) {
1286 Count++;
1287 TraceIt.advance();
1288 }
1289 if (!TraceIt.isAtEoF()) {
1290 if (isLBRSample(Line: TraceIt.getCurrentLine(), CheckLineStart: false)) {
1291 if (Count > 0)
1292 return PerfContent::LBRStack;
1293 else
1294 return PerfContent::LBR;
1295 }
1296 TraceIt.advance();
1297 }
1298 }
1299
1300 exitWithError(Message: "Invalid perf script input!");
1301 return PerfContent::UnknownContent;
1302}
1303
1304void HybridPerfReader::generateUnsymbolizedProfile() {
1305 ProfileIsCS = !IgnoreStackSamples;
1306 if (ProfileIsCS)
1307 unwindSamples();
1308 else
1309 PerfScriptReader::generateUnsymbolizedProfile();
1310}
1311
1312void PerfScriptReader::warnTruncatedStack() {
1313 if (ShowDetailedWarning) {
1314 for (auto Address : InvalidReturnAddresses) {
1315 WithColor::warning()
1316 << "Truncated stack sample due to invalid return address at "
1317 << format(Fmt: "0x%" PRIx64, Vals: Address)
1318 << ", likely caused by frame pointer omission\n";
1319 }
1320 }
1321 emitWarningSummary(
1322 Num: InvalidReturnAddresses.size(), Total: AggregatedSamples.size(),
1323 Msg: "of truncated stack samples due to invalid return address, "
1324 "likely caused by frame pointer omission.");
1325}
1326
1327void PerfScriptReader::warnInvalidRange() {
1328 DenseMap<std::pair<uint64_t, uint64_t>, uint64_t> Ranges;
1329
1330 for (const auto &Item : AggregatedSamples) {
1331 const PerfSample *Sample = Item.first.getPtr();
1332 uint64_t Count = Item.second;
1333 uint64_t EndAddress = 0;
1334 for (const LBREntry &LBR : Sample->LBRStack) {
1335 uint64_t SourceAddress = LBR.Source;
1336 uint64_t StartAddress = LBR.Target;
1337 if (EndAddress != 0)
1338 Ranges[{StartAddress, EndAddress}] += Count;
1339 EndAddress = SourceAddress;
1340 }
1341 }
1342
1343 if (Ranges.empty()) {
1344 WithColor::warning() << "No samples in perf script!\n";
1345 return;
1346 }
1347
1348 auto WarnInvalidRange = [&](uint64_t StartAddress, uint64_t EndAddress,
1349 StringRef Msg) {
1350 if (!ShowDetailedWarning)
1351 return;
1352 WithColor::warning() << "[" << format(Fmt: "%8" PRIx64, Vals: StartAddress) << ","
1353 << format(Fmt: "%8" PRIx64, Vals: EndAddress) << "]: " << Msg
1354 << "\n";
1355 };
1356
1357 const char *EndNotBoundaryMsg = "Range is not on instruction boundary, "
1358 "likely due to profile and binary mismatch.";
1359 const char *DanglingRangeMsg = "Range does not belong to any functions, "
1360 "likely from PLT, .init or .fini section.";
1361 const char *RangeCrossFuncMsg =
1362 "Fall through range should not cross function boundaries, likely due to "
1363 "profile and binary mismatch.";
1364 const char *BogusRangeMsg = "Range start is after or too far from range end.";
1365
1366 uint64_t TotalRangeNum = 0;
1367 uint64_t InstNotBoundary = 0;
1368 uint64_t UnmatchedRange = 0;
1369 uint64_t RecoveredRange = 0;
1370 uint64_t RangeCrossFunc = 0;
1371 uint64_t BogusRange = 0;
1372
1373 for (auto &I : Ranges) {
1374 uint64_t StartAddress = I.first.first;
1375 uint64_t EndAddress = I.first.second;
1376 TotalRangeNum += I.second;
1377
1378 if (!Binary->addressIsCode(Address: StartAddress) &&
1379 !Binary->addressIsCode(Address: EndAddress))
1380 continue;
1381
1382 if (!Binary->addressIsCode(Address: StartAddress) ||
1383 !Binary->addressIsTransfer(Address: EndAddress)) {
1384 InstNotBoundary += I.second;
1385 WarnInvalidRange(StartAddress, EndAddress, EndNotBoundaryMsg);
1386 }
1387
1388 auto *FRange = Binary->findFuncRange(Address: StartAddress);
1389 if (!FRange) {
1390 UnmatchedRange += I.second;
1391 WarnInvalidRange(StartAddress, EndAddress, DanglingRangeMsg);
1392 continue;
1393 }
1394
1395 if (FRange->Func->NameStatus != DwarfNameStatus::Matched)
1396 RecoveredRange += I.second;
1397
1398 if (EndAddress >= FRange->EndAddress) {
1399 RangeCrossFunc += I.second;
1400 WarnInvalidRange(StartAddress, EndAddress, RangeCrossFuncMsg);
1401 }
1402
1403 if (Binary->addressIsCode(Address: StartAddress) &&
1404 Binary->addressIsCode(Address: EndAddress) &&
1405 !isValidFallThroughRange(Start: StartAddress, End: EndAddress, Binary)) {
1406 BogusRange += I.second;
1407 WarnInvalidRange(StartAddress, EndAddress, BogusRangeMsg);
1408 }
1409 }
1410
1411 emitWarningSummary(
1412 Num: InstNotBoundary, Total: TotalRangeNum,
1413 Msg: "of samples are from ranges that are not on instruction boundary.");
1414 emitWarningSummary(
1415 Num: UnmatchedRange, Total: TotalRangeNum,
1416 Msg: "of samples are from ranges that do not belong to any functions.");
1417 emitWarningSummary(Num: RecoveredRange, Total: TotalRangeNum,
1418 Msg: "of samples are from ranges that belong to functions "
1419 "recovered from symbol table.");
1420 emitWarningSummary(
1421 Num: RangeCrossFunc, Total: TotalRangeNum,
1422 Msg: "of samples are from ranges that do cross function boundaries.");
1423 emitWarningSummary(
1424 Num: BogusRange, Total: TotalRangeNum,
1425 Msg: "of samples are from ranges that have range start after or too far from "
1426 "range end acrossing the unconditinal jmp.");
1427}
1428
1429void PerfScriptReader::warnIfBranchTargetMismatch() {
1430 // Collect unique branch source and target addresses from LBR samples,
1431 // then check what percentage don't match known instructions in the binary.
1432
1433 uint64_t MismatchedBranches = 0;
1434 uint64_t MismatchedIndirectTargets = 0;
1435 uint64_t MismatchedTargets = 0;
1436 uint64_t TotalSamples = 0;
1437
1438 for (const auto &Item : AggregatedSamples) {
1439 const PerfSample *Sample = Item.first.getPtr();
1440 for (const LBREntry &LBR : Sample->LBRStack) {
1441 uint64_t Source = LBR.Source;
1442 uint64_t Target = LBR.Target;
1443 if (Source == ExternalAddr || Target == ExternalAddr)
1444 continue;
1445 TotalSamples++;
1446
1447 // Validate Branch sources are Call/Branch/Indirect Branch
1448 if (!Binary->addressIsTransfer(Address: Source))
1449 MismatchedBranches++;
1450
1451 // Validate Indirect Branch targets landed in code. This may over estimate
1452 // the vaid targets only because there's no good way to determine jump
1453 // table targets
1454 if (Binary->addressIsIndirectBranch(Address: Source)) {
1455 if (!Binary->addressIsCode(Address: Target))
1456 MismatchedIndirectTargets++;
1457 } else if (!Binary->addressIsBranchTarget(Address: Target) &&
1458 !Binary->findFuncRangeForStartAddr(Address: Target))
1459 MismatchedTargets++;
1460 }
1461 }
1462
1463 emitWarningSummary(Num: MismatchedBranches, Total: TotalSamples,
1464 Msg: "of branch samples do not match the binary.");
1465 emitWarningSummary(Num: MismatchedTargets, Total: TotalSamples,
1466 Msg: "of branch targets do not match the binary.");
1467 emitWarningSummary(Num: MismatchedIndirectTargets, Total: TotalSamples,
1468 Msg: "of indirect branch targets do not match the binary.");
1469}
1470
1471void PerfScriptReader::parsePerfTraces() {
1472 // Parse perf traces and do aggregation.
1473 parseAndAggregateTrace();
1474 if (Binary->isKernel() && !Binary->getIsLoadedByMMap()) {
1475 exitWithError(
1476 Message: "Kernel is requested, but no kernel is found in mmap events.");
1477 }
1478
1479 emitWarningSummary(Num: NumLeafExternalFrame, Total: NumTotalSample,
1480 Msg: "of samples have leaf external frame in call stack.");
1481 emitWarningSummary(Num: NumLeadingOutgoingLBR, Total: NumTotalSample,
1482 Msg: "of samples have leading external LBR.");
1483
1484 // Generate unsymbolized profile.
1485 warnTruncatedStack();
1486 warnInvalidRange();
1487 warnIfBranchTargetMismatch();
1488 generateUnsymbolizedProfile();
1489 AggregatedSamples.clear();
1490
1491 if (SkipSymbolization)
1492 writeUnsymbolizedProfile(Filename: OutputFilename);
1493}
1494
1495SmallVector<CleanupInstaller, 2> PerfScriptReader::TempFileCleanups;
1496
1497void ETMReader::recordProcessedRange(uint64_t Start, uint64_t End,
1498 uint64_t Count) {
1499 assert(!Counters.empty() && "Counters should not be empty!");
1500 auto &Counter = Counters.begin()->second;
1501 Counter.recordRangeCount(Start, End, Repeat: Count);
1502}
1503
1504class ETMCallback : public ETMDecoder::Callback {
1505 ETMReader *Reader;
1506
1507public:
1508 ETMCallback(ETMReader *R) : Reader(R) {}
1509 void processInstructionRange(uint64_t Start, uint64_t End) override {
1510 Reader->recordProcessedRange(Start, End, Count: 1);
1511 }
1512};
1513
1514void ETMReader::parseETMTraces() {
1515 auto BufferOrErr = MemoryBuffer::getFile(Filename: TraceFile);
1516 if (std::error_code EC = BufferOrErr.getError())
1517 exitWithError(Message: "Could not open ETM trace file: " + EC.message());
1518
1519 ArrayRef<uint8_t> Data(
1520 reinterpret_cast<const uint8_t *>((*BufferOrErr)->getBufferStart()),
1521 (*BufferOrErr)->getBufferSize());
1522
1523 // There is no context for ETM instruction traces.
1524 // Initialize the SampleCounters map with a single empty context key
1525 // to aggregate all instruction hits into a global bucket.
1526 auto Key = std::make_shared<StringBasedCtxKey>();
1527 Counters.try_emplace(Key: Hashable<ContextKey>(Key));
1528
1529 // The protocol utilizes a 0x80 byte as an initial synchronization header.
1530 // Perform a manual search for this sync point to discard any leading
1531 // padding or truncated packets before decoding begins.
1532 size_t StartIdx = 0;
1533 while (StartIdx < Data.size() && Data[StartIdx] != 0x80)
1534 StartIdx++;
1535 if (StartIdx >= Data.size())
1536 exitWithError(Message: "No synchronization header (0x80) found in the bitstream.");
1537 ArrayRef<uint8_t> TraceSlice = Data.slice(N: StartIdx);
1538
1539 auto DecoderOrErr = ETMDecoder::create(
1540 Binary: Binary->getBinary(), TargetTriple: Binary->getTriple(), TraceID: static_cast<uint8_t>(TraceID));
1541
1542 if (!DecoderOrErr)
1543 exitWithError(Message: toString(E: DecoderOrErr.takeError()));
1544 auto Decoder = std::move(*DecoderOrErr);
1545
1546 ETMCallback CB(this);
1547 if (Error E = Decoder->processTrace(TraceData: TraceSlice, TraceCallback&: CB))
1548 exitWithError(Message: toString(E: std::move(E)));
1549}
1550
1551} // end namespace sampleprof
1552} // end namespace llvm
1553