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 IntelPTCollector::IntelPTCollector(NativeProcessProtocol &process)
36     : m_process(process) {
37   if (Expected<LinuxPerfZeroTscConversion> tsc_conversion =
38           LoadPerfTscConversionParameters())
39     m_tsc_conversion =
40         std::make_unique<LinuxPerfZeroTscConversion>(*tsc_conversion);
41   else
42     LLDB_LOG_ERROR(GetLog(POSIXLog::Trace), tsc_conversion.takeError(),
43                    "unable to load TSC to wall time conversion: {0}");
44 }
45 
46 Error IntelPTCollector::TraceStop(lldb::tid_t tid) {
47   if (m_per_thread_process_trace_up &&
48       m_per_thread_process_trace_up->TracesThread(tid))
49     return m_per_thread_process_trace_up->TraceStop(tid);
50   else if (m_per_core_process_trace_up)
51     return createStringError(inconvertibleErrorCode(),
52                              "Can't stop tracing an individual thread when "
53                              "per-core process tracing is enabled.");
54   return m_thread_traces.TraceStop(tid);
55 }
56 
57 Error IntelPTCollector::TraceStop(const TraceStopRequest &request) {
58   if (request.IsProcessTracing()) {
59     Clear();
60     return Error::success();
61   } else {
62     Error error = Error::success();
63     for (int64_t tid : *request.tids)
64       error = joinErrors(std::move(error),
65                          TraceStop(static_cast<lldb::tid_t>(tid)));
66     return error;
67   }
68 }
69 
70 Expected<IntelPTPerThreadProcessTraceUP>
71 IntelPTPerThreadProcessTrace::Start(const TraceIntelPTStartRequest &request,
72                                     ArrayRef<lldb::tid_t> current_tids) {
73   IntelPTPerThreadProcessTraceUP trace(
74       new IntelPTPerThreadProcessTrace(request));
75 
76   Error error = Error::success();
77   for (lldb::tid_t tid : current_tids)
78     error = joinErrors(std::move(error), trace->TraceStart(tid));
79   if (error)
80     return std::move(error);
81   return std::move(trace);
82 }
83 
84 Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) {
85   if (request.IsProcessTracing()) {
86     if (IsProcessTracingEnabled()) {
87       return createStringError(
88           inconvertibleErrorCode(),
89           "Process currently traced. Stop process tracing first");
90     }
91     if (request.IsPerCoreTracing()) {
92       if (m_thread_traces.GetTracedThreadsCount() > 0)
93         return createStringError(
94             inconvertibleErrorCode(),
95             "Threads currently traced. Stop tracing them first.");
96       if (Expected<IntelPTMultiCoreTraceUP> trace =
97               IntelPTMultiCoreTrace::StartOnAllCores(request)) {
98         m_per_core_process_trace_up = std::move(*trace);
99         return Error::success();
100       } else {
101         return trace.takeError();
102       }
103     } else {
104       std::vector<lldb::tid_t> process_threads;
105       for (size_t i = 0; m_process.GetThreadAtIndex(i); i++)
106         process_threads.push_back(m_process.GetThreadAtIndex(i)->GetID());
107 
108       // per-thread process tracing
109       if (Expected<IntelPTPerThreadProcessTraceUP> trace =
110               IntelPTPerThreadProcessTrace::Start(request, process_threads)) {
111         m_per_thread_process_trace_up = std::move(trace.get());
112         return Error::success();
113       } else {
114         return trace.takeError();
115       }
116     }
117   } else {
118     // individual thread tracing
119     if (m_per_core_process_trace_up)
120       return createStringError(inconvertibleErrorCode(),
121                                "Process currently traced with per-core "
122                                "tracing. Stop process tracing first");
123 
124     Error error = Error::success();
125     for (int64_t tid : *request.tids)
126       error = joinErrors(std::move(error),
127                          m_thread_traces.TraceStart(tid, request));
128     return error;
129   }
130 }
131 
132 void IntelPTCollector::OnProcessStateChanged(lldb::StateType state) {
133   if (m_per_core_process_trace_up)
134     m_per_core_process_trace_up->OnProcessStateChanged(state);
135 }
136 
137 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) {
138   if (m_per_thread_process_trace_up)
139     return m_per_thread_process_trace_up->TraceStart(tid);
140 
141   return Error::success();
142 }
143 
144 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) {
145   if (m_per_thread_process_trace_up &&
146       m_per_thread_process_trace_up->TracesThread(tid))
147     return m_per_thread_process_trace_up->TraceStop(tid);
148   else if (m_thread_traces.TracesThread(tid))
149     return m_thread_traces.TraceStop(tid);
150   return Error::success();
151 }
152 
153 Expected<json::Value> IntelPTCollector::GetState() {
154   Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo();
155   if (!cpu_info)
156     return cpu_info.takeError();
157 
158   TraceGetStateResponse state;
159   state.process_binary_data.push_back(
160       {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()});
161 
162   m_thread_traces.ForEachThread(
163       [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) {
164         state.traced_threads.push_back({tid,
165                                         {{IntelPTDataKinds::kTraceBuffer,
166                                           thread_trace.GetTraceBufferSize()}}});
167       });
168 
169   if (m_per_thread_process_trace_up) {
170     m_per_thread_process_trace_up->GetThreadTraces().ForEachThread(
171         [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) {
172           state.traced_threads.push_back(
173               {tid,
174                {{IntelPTDataKinds::kTraceBuffer,
175                  thread_trace.GetTraceBufferSize()}}});
176         });
177   }
178 
179   if (m_per_core_process_trace_up) {
180     for (size_t i = 0; m_process.GetThreadAtIndex(i); i++)
181       state.traced_threads.push_back(
182           TraceThreadState{m_process.GetThreadAtIndex(i)->GetID(), {}});
183 
184     state.cores.emplace();
185     m_per_core_process_trace_up->ForEachCore(
186         [&](lldb::core_id_t core_id,
187             const IntelPTSingleBufferTrace &core_trace) {
188           state.cores->push_back({core_id,
189                                   {{IntelPTDataKinds::kTraceBuffer,
190                                     core_trace.GetTraceBufferSize()}}});
191         });
192   }
193   return toJSON(state);
194 }
195 
196 Expected<IntelPTSingleBufferTrace &>
197 IntelPTCollector::GetTracedThread(lldb::tid_t tid) {
198   if (m_per_thread_process_trace_up &&
199       m_per_thread_process_trace_up->TracesThread(tid))
200     return m_per_thread_process_trace_up->GetThreadTraces().GetTracedThread(
201         tid);
202   return m_thread_traces.GetTracedThread(tid);
203 }
204 
205 Expected<std::vector<uint8_t>>
206 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) {
207   if (request.kind == IntelPTDataKinds::kTraceBuffer) {
208     if (Expected<IntelPTSingleBufferTrace &> trace =
209             GetTracedThread(*request.tid))
210       return trace->GetTraceBuffer(request.offset, request.size);
211     else
212       return trace.takeError();
213   } else if (request.kind == IntelPTDataKinds::kProcFsCpuInfo) {
214     return GetProcfsCpuInfo();
215   }
216   return createStringError(inconvertibleErrorCode(),
217                            "Unsuported trace binary data kind: %s",
218                            request.kind.c_str());
219 }
220 
221 void IntelPTCollector::ClearProcessTracing() {
222   m_per_thread_process_trace_up.reset();
223   m_per_core_process_trace_up.reset();
224 }
225 
226 bool IntelPTCollector::IsSupported() {
227   if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) {
228     return true;
229   } else {
230     llvm::consumeError(intel_pt_type.takeError());
231     return false;
232   }
233 }
234 
235 bool IntelPTCollector::IsProcessTracingEnabled() const {
236   return (bool)m_per_thread_process_trace_up ||
237          (bool)m_per_core_process_trace_up;
238 }
239 
240 void IntelPTCollector::Clear() {
241   ClearProcessTracing();
242   m_thread_traces.Clear();
243 }
244