1 //===-- IntelPTCollector.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 "IntelPTCollector.h"
10 
11 #include "Perf.h"
12 #include "Procfs.h"
13 
14 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
15 #include "lldb/Host/linux/Support.h"
16 #include "lldb/Utility/StreamString.h"
17 
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/Error.h"
20 #include "llvm/Support/MathExtras.h"
21 
22 #include <algorithm>
23 #include <cstddef>
24 #include <fstream>
25 #include <linux/perf_event.h>
26 #include <sstream>
27 #include <sys/ioctl.h>
28 #include <sys/syscall.h>
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 using namespace process_linux;
33 using namespace llvm;
34 
35 /// IntelPTThreadTraceCollection
36 
37 bool IntelPTThreadTraceCollection::TracesThread(lldb::tid_t tid) const {
38   return m_thread_traces.count(tid);
39 }
40 
41 Error IntelPTThreadTraceCollection::TraceStop(lldb::tid_t tid) {
42   auto it = m_thread_traces.find(tid);
43   if (it == m_thread_traces.end())
44     return createStringError(inconvertibleErrorCode(),
45                              "Thread %" PRIu64 " not currently traced", tid);
46   m_total_buffer_size -= it->second->GetTraceBufferSize();
47   m_thread_traces.erase(tid);
48   return Error::success();
49 }
50 
51 Error IntelPTThreadTraceCollection::TraceStart(
52     lldb::tid_t tid, const TraceIntelPTStartRequest &request) {
53   if (TracesThread(tid))
54     return createStringError(inconvertibleErrorCode(),
55                              "Thread %" PRIu64 " already traced", tid);
56 
57   Expected<IntelPTSingleBufferTraceUP> trace_up =
58       IntelPTSingleBufferTrace::Start(request, tid, /*core_id=*/None,
59                                       TraceCollectionState::Running);
60   if (!trace_up)
61     return trace_up.takeError();
62 
63   m_total_buffer_size += (*trace_up)->GetTraceBufferSize();
64   m_thread_traces.try_emplace(tid, std::move(*trace_up));
65   return Error::success();
66 }
67 
68 size_t IntelPTThreadTraceCollection::GetTotalBufferSize() const {
69   return m_total_buffer_size;
70 }
71 
72 std::vector<TraceThreadState>
73 IntelPTThreadTraceCollection::GetThreadStates() const {
74   std::vector<TraceThreadState> states;
75   for (const auto &it : m_thread_traces)
76     states.push_back({it.first,
77                       {TraceBinaryData{IntelPTDataKinds::kTraceBuffer,
78                                        it.second->GetTraceBufferSize()}}});
79   return states;
80 }
81 
82 Expected<IntelPTSingleBufferTrace &>
83 IntelPTThreadTraceCollection::GetTracedThread(lldb::tid_t tid) {
84   auto it = m_thread_traces.find(tid);
85   if (it == m_thread_traces.end())
86     return createStringError(inconvertibleErrorCode(),
87                              "Thread %" PRIu64 " not currently traced", tid);
88   return *it->second.get();
89 }
90 
91 void IntelPTThreadTraceCollection::Clear() {
92   m_thread_traces.clear();
93   m_total_buffer_size = 0;
94 }
95 
96 size_t IntelPTThreadTraceCollection::GetTracedThreadsCount() const {
97   return m_thread_traces.size();
98 }
99 
100 /// IntelPTPerThreadProcessTrace
101 
102 bool IntelPTPerThreadProcessTrace::TracesThread(lldb::tid_t tid) const {
103   return m_thread_traces.TracesThread(tid);
104 }
105 
106 Error IntelPTPerThreadProcessTrace::TraceStop(lldb::tid_t tid) {
107   return m_thread_traces.TraceStop(tid);
108 }
109 
110 Error IntelPTPerThreadProcessTrace::TraceStart(lldb::tid_t tid) {
111   if (m_thread_traces.GetTotalBufferSize() +
112           m_tracing_params.trace_buffer_size >
113       static_cast<size_t>(*m_tracing_params.process_buffer_size_limit))
114     return createStringError(
115         inconvertibleErrorCode(),
116         "Thread %" PRIu64 " can't be traced as the process trace size limit "
117         "has been reached. Consider retracing with a higher "
118         "limit.",
119         tid);
120 
121   return m_thread_traces.TraceStart(tid, m_tracing_params);
122 }
123 
124 IntelPTThreadTraceCollection &IntelPTPerThreadProcessTrace::GetThreadTraces() {
125   return m_thread_traces;
126 }
127 
128 /// IntelPTCollector
129 
130 IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process)
131     : m_process(process) {
132   if (Expected<LinuxPerfZeroTscConversion> tsc_conversion =
133           LoadPerfTscConversionParameters())
134     m_tsc_conversion =
135         std::make_unique<LinuxPerfZeroTscConversion>(*tsc_conversion);
136   else
137     LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), tsc_conversion.takeError(),
138                    "unable to load TSC to wall time conversion: {0}");
139 }
140 
141 Error IntelPTCollector::TraceStop(lldb::tid_t tid) {
142   if (m_per_thread_process_trace_up &&
143       m_per_thread_process_trace_up->TracesThread(tid))
144     return m_per_thread_process_trace_up->TraceStop(tid);
145   else if (m_per_core_process_trace_up)
146     return createStringError(inconvertibleErrorCode(),
147                              "Can't stop tracing an individual thread when "
148                              "per-core process tracing is enabled.");
149   return m_thread_traces.TraceStop(tid);
150 }
151 
152 Error IntelPTCollector::TraceStop(const TraceStopRequest &request) {
153   if (request.IsProcessTracing()) {
154     Clear();
155     return Error::success();
156   } else {
157     Error error = Error::success();
158     for (int64_t tid : *request.tids)
159       error = joinErrors(std::move(error),
160                          TraceStop(static_cast<lldb::tid_t>(tid)));
161     return error;
162   }
163 }
164 
165 Expected<IntelPTPerThreadProcessTraceUP>
166 IntelPTPerThreadProcessTrace::Start(const TraceIntelPTStartRequest &request,
167                                     ArrayRef<lldb::tid_t> current_tids) {
168   IntelPTPerThreadProcessTraceUP trace(
169       new IntelPTPerThreadProcessTrace(request));
170 
171   Error error = Error::success();
172   for (lldb::tid_t tid : current_tids)
173     error = joinErrors(std::move(error), trace->TraceStart(tid));
174   if (error)
175     return std::move(error);
176   return trace;
177 }
178 
179 Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) {
180   if (request.IsProcessTracing()) {
181     if (IsProcessTracingEnabled()) {
182       return createStringError(
183           inconvertibleErrorCode(),
184           "Process currently traced. Stop process tracing first");
185     }
186     if (request.IsPerCoreTracing()) {
187       if (m_thread_traces.GetTracedThreadsCount() > 0)
188         return createStringError(
189             inconvertibleErrorCode(),
190             "Threads currently traced. Stop tracing them first.");
191       if (Expected<IntelPTMultiCoreTraceUP> trace =
192               IntelPTMultiCoreTrace::StartOnAllCores(request)) {
193         m_per_core_process_trace_up = std::move(*trace);
194         return Error::success();
195       } else {
196         return trace.takeError();
197       }
198     } else {
199       std::vector<lldb::tid_t> process_threads;
200       for (size_t i = 0; m_process.GetThreadAtIndex(i); i++)
201         process_threads.push_back(m_process.GetThreadAtIndex(i)->GetID());
202 
203       // per-thread process tracing
204       if (Expected<IntelPTPerThreadProcessTraceUP> trace =
205               IntelPTPerThreadProcessTrace::Start(request, process_threads)) {
206         m_per_thread_process_trace_up = std::move(trace.get());
207         return Error::success();
208       } else {
209         return trace.takeError();
210       }
211     }
212   } else {
213     // individual thread tracing
214     if (m_per_core_process_trace_up)
215       return createStringError(inconvertibleErrorCode(),
216                                "Process currently traced with per-core "
217                                "tracing. Stop process tracing first");
218 
219     Error error = Error::success();
220     for (int64_t tid : *request.tids)
221       error = joinErrors(std::move(error),
222                          m_thread_traces.TraceStart(tid, request));
223     return error;
224   }
225 }
226 
227 void IntelPTCollector::OnProcessStateChanged(lldb::StateType state) {
228   if (m_per_core_process_trace_up)
229     m_per_core_process_trace_up->OnProcessStateChanged(state);
230 }
231 
232 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) {
233   if (m_per_thread_process_trace_up)
234     return m_per_thread_process_trace_up->TraceStart(tid);
235 
236   return Error::success();
237 }
238 
239 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) {
240   if (m_per_thread_process_trace_up &&
241       m_per_thread_process_trace_up->TracesThread(tid))
242     return m_per_thread_process_trace_up->TraceStop(tid);
243   else if (m_thread_traces.TracesThread(tid))
244     return m_thread_traces.TraceStop(tid);
245   return Error::success();
246 }
247 
248 Expected<json::Value> IntelPTCollector::GetState() const {
249   Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo();
250   if (!cpu_info)
251     return cpu_info.takeError();
252 
253   TraceGetStateResponse state;
254   state.process_binary_data.push_back(
255       {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()});
256 
257   std::vector<TraceThreadState> thread_states =
258       m_thread_traces.GetThreadStates();
259   state.traced_threads.insert(state.traced_threads.end(), thread_states.begin(),
260                               thread_states.end());
261 
262   if (m_per_thread_process_trace_up) {
263     thread_states =
264         m_per_thread_process_trace_up->GetThreadTraces().GetThreadStates();
265     state.traced_threads.insert(state.traced_threads.end(),
266                                 thread_states.begin(), thread_states.end());
267   } else if (m_per_core_process_trace_up) {
268     for (size_t i = 0; m_process.GetThreadAtIndex(i); i++)
269       state.traced_threads.push_back(
270           TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}});
271 
272     state.cores.emplace();
273     m_per_core_process_trace_up->ForEachCore(
274         [&](lldb::core_id_t core_id,
275             const IntelPTSingleBufferTrace &core_trace) {
276           state.cores->push_back({core_id,
277                                   {{IntelPTDataKinds::kTraceBuffer,
278                                     core_trace.GetTraceBufferSize()}}});
279         });
280   }
281   return toJSON(state);
282 }
283 
284 Expected<IntelPTSingleBufferTrace &>
285 IntelPTCollector::GetTracedThread(lldb::tid_t tid) {
286   if (m_per_thread_process_trace_up &&
287       m_per_thread_process_trace_up->TracesThread(tid))
288     return m_per_thread_process_trace_up->GetThreadTraces().GetTracedThread(
289         tid);
290   return m_thread_traces.GetTracedThread(tid);
291 }
292 
293 Expected<std::vector<uint8_t>>
294 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) {
295   if (request.kind == IntelPTDataKinds::kTraceBuffer) {
296     if (Expected<IntelPTSingleBufferTrace &> trace =
297             GetTracedThread(*request.tid))
298       return trace->GetTraceBuffer(request.offset, request.size);
299     else
300       return trace.takeError();
301   } else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) {
302     return GetProcfsCpuInfo();
303   }
304   return createStringError(inconvertibleErrorCode(),
305                            "Unsuported trace binary data kind: %s",
306                            request.kind.c_str());
307 }
308 
309 void IntelPTCollector::ClearProcessTracing() {
310   m_per_thread_process_trace_up.reset();
311   m_per_core_process_trace_up.reset();
312 }
313 
314 bool IntelPTCollector::IsSupported() {
315   if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) {
316     return true;
317   } else {
318     llvm::consumeError(intel_pt_type.takeError());
319     return false;
320   }
321 }
322 
323 bool IntelPTCollector::IsProcessTracingEnabled() const {
324   return (bool)m_per_thread_process_trace_up ||
325          (bool)m_per_core_process_trace_up;
326 }
327 
328 void IntelPTCollector::Clear() {
329   ClearProcessTracing();
330   m_thread_traces.Clear();
331 }
332