LLDB mainline
IntelPTMultiCoreTrace.cpp
Go to the documentation of this file.
1//===-- IntelPTMultiCoreTrace.cpp -----------------------------------------===//
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
11#include "Procfs.h"
12#include <optional>
13
14using namespace lldb;
15using namespace lldb_private;
16using namespace process_linux;
17using namespace llvm;
18
19static bool IsTotalBufferLimitReached(ArrayRef<cpu_id_t> cores,
20 const TraceIntelPTStartRequest &request) {
21 uint64_t required = cores.size() * request.ipt_trace_size;
22 uint64_t limit = request.process_buffer_size_limit.value_or(
23 std::numeric_limits<uint64_t>::max());
24 return required > limit;
25}
26
28 return createStringError(
29 inconvertibleErrorCode(),
30 "%s\nYou might need to rerun as sudo or to set "
31 "/proc/sys/kernel/perf_event_paranoid to a value of 0 or -1. You can use "
32 "`sudo sysctl -w kernel.perf_event_paranoid=-1` for that.",
33 toString(std::move(error)).c_str());
34}
35
36Expected<std::unique_ptr<IntelPTMultiCoreTrace>>
38 NativeProcessProtocol &process,
39 std::optional<int> cgroup_fd) {
40 Expected<ArrayRef<cpu_id_t>> cpu_ids = GetAvailableLogicalCoreIDs();
41 if (!cpu_ids)
42 return cpu_ids.takeError();
43
44 if (IsTotalBufferLimitReached(*cpu_ids, request))
45 return createStringError(
46 inconvertibleErrorCode(),
47 "The process can't be traced because the process trace size limit "
48 "has been reached. Consider retracing with a higher limit.");
49
50 DenseMap<cpu_id_t, std::pair<IntelPTSingleBufferTrace, ContextSwitchTrace>>
51 traces;
52
53 for (cpu_id_t cpu_id : *cpu_ids) {
54 Expected<IntelPTSingleBufferTrace> core_trace =
55 IntelPTSingleBufferTrace::Start(request, /*tid=*/std::nullopt, cpu_id,
56 /*disabled=*/true, cgroup_fd);
57 if (!core_trace)
58 return IncludePerfEventParanoidMessageInError(core_trace.takeError());
59
60 if (Expected<PerfEvent> context_switch_trace =
62 &core_trace->GetPerfEvent())) {
63 traces.try_emplace(cpu_id,
64 std::make_pair(std::move(*core_trace),
65 std::move(*context_switch_trace)));
66 } else {
67 return context_switch_trace.takeError();
68 }
69 }
70
71 return std::unique_ptr<IntelPTMultiCoreTrace>(
72 new IntelPTMultiCoreTrace(std::move(traces), process, (bool)cgroup_fd));
73}
74
76 std::function<void(cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace)>
77 callback) {
78 for (auto &it : m_traces_per_core)
79 callback(it.first, it.second.first);
80}
81
83 std::function<void(cpu_id_t cpu_id, IntelPTSingleBufferTrace &intelpt_trace,
84 ContextSwitchTrace &context_switch_trace)>
85 callback) {
86 for (auto &it : m_traces_per_core)
87 callback(it.first, it.second.first, it.second.second);
88}
89
91 ForEachCore([](cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace) {
92 if (Error err = core_trace.Pause()) {
93 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
94 "Unable to pause the core trace for core {0}", cpu_id);
95 }
96 });
97}
98
100 ForEachCore([](cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace) {
101 if (Error err = core_trace.Resume()) {
102 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
103 "Unable to resume the core trace for core {0}", cpu_id);
104 }
105 });
106}
107
111
112 for (NativeThreadProtocol &thread : m_process.Threads())
113 state.traced_threads.push_back(TraceThreadState{thread.GetID(), {}});
114
115 state.cpus.emplace();
116 ForEachCore([&](lldb::cpu_id_t cpu_id,
117 const IntelPTSingleBufferTrace &core_trace,
118 const ContextSwitchTrace &context_switch_trace) {
119 state.cpus->push_back(
120 {cpu_id,
123 context_switch_trace.GetEffectiveDataBufferSize()}}});
124 });
125
126 return state;
127}
128
130 // All the process' threads are being traced automatically.
131 return (bool)m_process.GetThreadByID(tid);
132}
133
135 // All the process' threads are being traced automatically.
136 if (!TracesThread(tid))
137 return createStringError(
138 inconvertibleErrorCode(),
139 "Thread %" PRIu64 " is not part of the target process", tid);
140 return Error::success();
141}
142
144 return createStringError(inconvertibleErrorCode(),
145 "Can't stop tracing an individual thread when "
146 "per-cpu process tracing is enabled.");
147}
148
149Expected<std::optional<std::vector<uint8_t>>>
151 const TraceGetBinaryDataRequest &request) {
152 if (!request.cpu_id)
153 return std::nullopt;
154 auto it = m_traces_per_core.find(*request.cpu_id);
155 if (it == m_traces_per_core.end())
156 return createStringError(
157 inconvertibleErrorCode(),
158 formatv("Core {0} is not being traced", *request.cpu_id));
159
160 if (request.kind == IntelPTDataKinds::kIptTrace)
161 return it->second.first.GetIptTrace();
163 return it->second.second.GetReadOnlyDataBuffer();
164 return std::nullopt;
165}
static llvm::raw_ostream & error(Stream &strm)
static bool IsTotalBufferLimitReached(ArrayRef< cpu_id_t > cores, const TraceIntelPTStartRequest &request)
static Error IncludePerfEventParanoidMessageInError(Error &&error)
llvm::Error Error
NativeThreadProtocol * GetThreadByID(lldb::tid_t tid)
TraceIntelPTGetStateResponse GetState() override
Construct a minimal jLLDBTraceGetState response for this process trace.
void ForEachCore(std::function< void(lldb::cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace)> callback)
Execute the provided callback on each core that is being traced.
static llvm::Expected< std::unique_ptr< IntelPTMultiCoreTrace > > StartOnAllCores(const TraceIntelPTStartRequest &request, NativeProcessProtocol &process, std::optional< int > cgroup_fd=std::nullopt)
Start tracing all CPU cores.
llvm::Error TraceStart(lldb::tid_t tid) override
Start tracing the thread given by its tid.
NativeProcessProtocol & m_process
The target process.
llvm::DenseMap< lldb::cpu_id_t, std::pair< IntelPTSingleBufferTrace, ContextSwitchTrace > > m_traces_per_core
llvm::Expected< std::optional< std::vector< uint8_t > > > TryGetBinaryData(const TraceGetBinaryDataRequest &request) override
llvm::Error TraceStop(lldb::tid_t tid) override
Stop tracing the thread given by its tid.
This class wraps a single perf event collecting intel pt data in a single buffer.
static llvm::Expected< IntelPTSingleBufferTrace > Start(const TraceIntelPTStartRequest &request, std::optional< lldb::tid_t > tid, std::optional< lldb::cpu_id_t > cpu_id=std::nullopt, bool disabled=false, std::optional< int > cgroup_fd=std::nullopt)
Start tracing using a single Intel PT trace buffer.
llvm::Error Resume()
Resume the collection of this trace.
llvm::Error Pause()
Pause the collection of this trace.
Thin wrapper of the perf_event_open API.
Definition: Perf.h:80
size_t GetEffectiveDataBufferSize() const
Definition: Perf.cpp:320
llvm::Expected< llvm::ArrayRef< lldb::cpu_id_t > > GetAvailableLogicalCoreIDs()
Definition: Procfs.cpp:56
llvm::Expected< PerfEvent > CreateContextSwitchTracePerfEvent(lldb::cpu_id_t cpu_id, const PerfEvent *parent_perf_event=nullptr)
Create a perf event that tracks context switches on a cpu.
Definition: Perf.cpp:333
A class that represents a running process on the host machine.
Definition: SBAttachInfo.h:14
const char * toString(AppleArm64ExceptionClass EC)
Definition: SBAddress.h:15
uint32_t cpu_id_t
Definition: lldb-types.h:89
uint64_t tid_t
Definition: lldb-types.h:82
Definition: Debugger.h:53
jLLDBTraceGetBinaryData gdb-remote packet
std::optional< lldb::cpu_id_t > cpu_id
Optional core id if the data is related to a cpu core.
std::string kind
Identifier for the data.
std::optional< std::vector< TraceCpuState > > cpus
std::vector< TraceThreadState > traced_threads
std::optional< uint64_t > process_buffer_size_limit
Required when doing "process tracing".
uint64_t ipt_trace_size
Size in bytes to use for each thread's trace buffer.