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
10
13
14#include <optional>
15
16using namespace lldb;
17using namespace lldb_private;
18using namespace process_linux;
19using namespace llvm;
20
21static bool IsTotalBufferLimitReached(ArrayRef<cpu_id_t> cores,
22 const TraceIntelPTStartRequest &request) {
23 uint64_t required = cores.size() * request.ipt_trace_size;
24 uint64_t limit = request.process_buffer_size_limit.value_or(
25 std::numeric_limits<uint64_t>::max());
26 return required > limit;
27}
28
30 return createStringError(
31 inconvertibleErrorCode(),
32 "%s\nYou might need to rerun as sudo or to set "
33 "/proc/sys/kernel/perf_event_paranoid to a value of 0 or -1. You can use "
34 "`sudo sysctl -w kernel.perf_event_paranoid=-1` for that.",
35 toString(std::move(error)).c_str());
36}
37
38Expected<std::unique_ptr<IntelPTMultiCoreTrace>>
40 NativeProcessProtocol &process,
41 std::optional<int> cgroup_fd) {
42 Expected<ArrayRef<cpu_id_t>> cpu_ids = GetAvailableLogicalCoreIDs();
43 if (!cpu_ids)
44 return cpu_ids.takeError();
45
46 if (IsTotalBufferLimitReached(*cpu_ids, request))
47 return createStringError(
48 inconvertibleErrorCode(),
49 "The process can't be traced because the process trace size limit "
50 "has been reached. Consider retracing with a higher limit.");
51
52 DenseMap<cpu_id_t, std::pair<IntelPTSingleBufferTrace, ContextSwitchTrace>>
53 traces;
54
55 for (cpu_id_t cpu_id : *cpu_ids) {
56 Expected<IntelPTSingleBufferTrace> core_trace =
57 IntelPTSingleBufferTrace::Start(request, /*tid=*/std::nullopt, cpu_id,
58 /*disabled=*/true, cgroup_fd);
59 if (!core_trace)
60 return IncludePerfEventParanoidMessageInError(core_trace.takeError());
61
62 if (Expected<PerfEvent> context_switch_trace =
64 &core_trace->GetPerfEvent())) {
65 traces.try_emplace(cpu_id,
66 std::make_pair(std::move(*core_trace),
67 std::move(*context_switch_trace)));
68 } else {
69 return context_switch_trace.takeError();
70 }
71 }
72
73 return std::unique_ptr<IntelPTMultiCoreTrace>(
74 new IntelPTMultiCoreTrace(std::move(traces), process, (bool)cgroup_fd));
75}
76
78 std::function<void(cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace)>
79 callback) {
80 for (auto &it : m_traces_per_core)
81 callback(it.first, it.second.first);
82}
83
85 std::function<void(cpu_id_t cpu_id, IntelPTSingleBufferTrace &intelpt_trace,
86 ContextSwitchTrace &context_switch_trace)>
87 callback) {
88 for (auto &it : m_traces_per_core)
89 callback(it.first, it.second.first, it.second.second);
90}
91
93 ForEachCore([](cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace) {
94 if (Error err = core_trace.Pause()) {
95 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
96 "Unable to pause the core trace for core {1}: {0}",
97 cpu_id);
98 }
99 });
100}
101
103 ForEachCore([](cpu_id_t cpu_id, IntelPTSingleBufferTrace &core_trace) {
104 if (Error err = core_trace.Resume()) {
105 LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), std::move(err),
106 "Unable to resume the core trace for core {1}: {0}",
107 cpu_id);
108 }
109 });
110}
111
115
116 for (NativeThreadProtocol &thread : m_process.Threads())
117 state.traced_threads.push_back(TraceThreadState{thread.GetID(), {}});
118
119 state.cpus.emplace();
120 ForEachCore([&](lldb::cpu_id_t cpu_id,
121 const IntelPTSingleBufferTrace &core_trace,
122 const ContextSwitchTrace &context_switch_trace) {
123 state.cpus->push_back(
124 {cpu_id,
127 context_switch_trace.GetEffectiveDataBufferSize()}}});
128 });
129
130 return state;
131}
132
134 // All the process' threads are being traced automatically.
135 return (bool)m_process.GetThreadByID(tid);
136}
137
139 // All the process' threads are being traced automatically.
140 if (!TracesThread(tid))
141 return createStringError(
142 inconvertibleErrorCode(),
143 "Thread %" PRIu64 " is not part of the target process", tid);
144 return Error::success();
145}
146
148 return createStringError(inconvertibleErrorCode(),
149 "Can't stop tracing an individual thread when "
150 "per-cpu process tracing is enabled.");
151}
152
153Expected<std::optional<std::vector<uint8_t>>>
155 const TraceGetBinaryDataRequest &request) {
156 if (!request.cpu_id)
157 return std::nullopt;
158 auto it = m_traces_per_core.find(*request.cpu_id);
159 if (it == m_traces_per_core.end())
160 return createStringError(
161 inconvertibleErrorCode(),
162 formatv("Core {0} is not being traced", *request.cpu_id));
163
164 if (request.kind == IntelPTDataKinds::kIptTrace)
165 return it->second.first.GetIptTrace();
167 return it->second.second.GetReadOnlyDataBuffer();
168 return std::nullopt;
169}
static llvm::raw_ostream & error(Stream &strm)
static bool IsTotalBufferLimitReached(ArrayRef< cpu_id_t > cores, const TraceIntelPTStartRequest &request)
static Error IncludePerfEventParanoidMessageInError(Error &&error)
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:405
llvm::Error Error
IntelPTMultiCoreTrace(llvm::DenseMap< lldb::cpu_id_t, std::pair< IntelPTSingleBufferTrace, ContextSwitchTrace > > &&traces_per_core, NativeProcessProtocol &process, bool using_cgroup_filtering)
This assumes that all underlying perf_events for each core are part of the same perf event group.
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.
size_t GetEffectiveDataBufferSize() const
Definition Perf.cpp:319
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:332
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:338
std::string toString(FormatterBytecode::OpCodes op)
uint32_t cpu_id_t
Definition lldb-types.h:93
uint64_t tid_t
Definition lldb-types.h:85
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.