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
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 u: std::numeric_limits<uint64_t>::max());
24 return required > limit;
25}
26
27static 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
36Expected<std::unique_ptr<IntelPTMultiCoreTrace>>
37IntelPTMultiCoreTrace::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
75void 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
82void 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
90void 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
99void 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
108TraceIntelPTGetStateResponse 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
129bool 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
134llvm::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
143Error 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
149Expected<std::optional<std::vector<uint8_t>>>
150IntelPTMultiCoreTrace::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

source code of lldb/source/Plugins/Process/Linux/IntelPTMultiCoreTrace.cpp