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 | |
9 | #include "IntelPTMultiCoreTrace.h" |
10 | #include "Plugins/Process/POSIX/ProcessPOSIXLog.h" |
11 | #include "Procfs.h" |
12 | #include <optional> |
13 | |
14 | using namespace lldb; |
15 | using namespace lldb_private; |
16 | using namespace process_linux; |
17 | using namespace llvm; |
18 | |
19 | static 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 | u: std::numeric_limits<uint64_t>::max()); |
24 | return required > limit; |
25 | } |
26 | |
27 | static Error IncludePerfEventParanoidMessageInError(Error &&error) { |
28 | return createStringError( |
29 | EC: inconvertibleErrorCode(), |
30 | Fmt: "%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 | Vals: toString(E: std::move(error)).c_str()); |
34 | } |
35 | |
36 | Expected<std::unique_ptr<IntelPTMultiCoreTrace>> |
37 | IntelPTMultiCoreTrace::StartOnAllCores(const TraceIntelPTStartRequest &request, |
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(cores: *cpu_ids, request)) |
45 | return createStringError( |
46 | EC: inconvertibleErrorCode(), |
47 | Msg: "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(error: core_trace.takeError()); |
59 | |
60 | if (Expected<PerfEvent> context_switch_trace = |
61 | CreateContextSwitchTracePerfEvent(cpu_id, |
62 | parent_perf_event: &core_trace->GetPerfEvent())) { |
63 | traces.try_emplace(Key: cpu_id, |
64 | Args: std::make_pair(x: std::move(*core_trace), |
65 | y: 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 | |
75 | void IntelPTMultiCoreTrace::ForEachCore( |
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 | |
82 | void IntelPTMultiCoreTrace::ForEachCore( |
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 | |
90 | void IntelPTMultiCoreTrace::ProcessDidStop() { |
91 | ForEachCore(callback: [](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 | |
99 | void IntelPTMultiCoreTrace::ProcessWillResume() { |
100 | ForEachCore(callback: [](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 | |
108 | TraceIntelPTGetStateResponse IntelPTMultiCoreTrace::GetState() { |
109 | TraceIntelPTGetStateResponse state; |
110 | state.using_cgroup_filtering = m_using_cgroup_filtering; |
111 | |
112 | for (NativeThreadProtocol &thread : m_process.Threads()) |
113 | state.traced_threads.push_back(x: TraceThreadState{.tid: thread.GetID(), .binary_data: {}}); |
114 | |
115 | state.cpus.emplace(); |
116 | ForEachCore(callback: [&](lldb::cpu_id_t cpu_id, |
117 | const IntelPTSingleBufferTrace &core_trace, |
118 | const ContextSwitchTrace &context_switch_trace) { |
119 | state.cpus->push_back( |
120 | x: {.id: cpu_id, |
121 | .binary_data: {{.kind: IntelPTDataKinds::kIptTrace, .size: core_trace.GetIptTraceSize()}, |
122 | {.kind: IntelPTDataKinds::kPerfContextSwitchTrace, |
123 | .size: context_switch_trace.GetEffectiveDataBufferSize()}}}); |
124 | }); |
125 | |
126 | return state; |
127 | } |
128 | |
129 | bool IntelPTMultiCoreTrace::TracesThread(lldb::tid_t tid) const { |
130 | // All the process' threads are being traced automatically. |
131 | return (bool)m_process.GetThreadByID(tid); |
132 | } |
133 | |
134 | llvm::Error IntelPTMultiCoreTrace::TraceStart(lldb::tid_t tid) { |
135 | // All the process' threads are being traced automatically. |
136 | if (!TracesThread(tid)) |
137 | return createStringError( |
138 | EC: inconvertibleErrorCode(), |
139 | Fmt: "Thread %" PRIu64 " is not part of the target process" , Vals: tid); |
140 | return Error::success(); |
141 | } |
142 | |
143 | Error IntelPTMultiCoreTrace::TraceStop(lldb::tid_t tid) { |
144 | return createStringError(EC: inconvertibleErrorCode(), |
145 | Msg: "Can't stop tracing an individual thread when " |
146 | "per-cpu process tracing is enabled." ); |
147 | } |
148 | |
149 | Expected<std::optional<std::vector<uint8_t>>> |
150 | IntelPTMultiCoreTrace::TryGetBinaryData( |
151 | const TraceGetBinaryDataRequest &request) { |
152 | if (!request.cpu_id) |
153 | return std::nullopt; |
154 | auto it = m_traces_per_core.find(Val: *request.cpu_id); |
155 | if (it == m_traces_per_core.end()) |
156 | return createStringError( |
157 | EC: inconvertibleErrorCode(), |
158 | S: formatv(Fmt: "Core {0} is not being traced" , Vals: *request.cpu_id)); |
159 | |
160 | if (request.kind == IntelPTDataKinds::kIptTrace) |
161 | return it->second.first.GetIptTrace(); |
162 | if (request.kind == IntelPTDataKinds::kPerfContextSwitchTrace) |
163 | return it->second.second.GetReadOnlyDataBuffer(); |
164 | return std::nullopt; |
165 | } |
166 | |