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 
38 llvm::Expected<LinuxPerfZeroTscConversion &>
39 IntelPTCollector::FetchPerfTscConversionParameters() {
40   if (Expected<LinuxPerfZeroTscConversion> tsc_conversion =
41           LoadPerfTscConversionParameters())
42     return *tsc_conversion;
43   else
44     return createStringError(inconvertibleErrorCode(),
45                              "Unable to load TSC to wall time conversion: %s",
46                              toString(tsc_conversion.takeError()).c_str());
47 }
48 
49 Error IntelPTCollector::TraceStop(lldb::tid_t tid) {
50   if (m_process_trace_up && m_process_trace_up->TracesThread(tid))
51     return m_process_trace_up->TraceStop(tid);
52   return m_thread_traces.TraceStop(tid);
53 }
54 
55 Error IntelPTCollector::TraceStop(const TraceStopRequest &request) {
56   if (request.IsProcessTracing()) {
57     Clear();
58     return Error::success();
59   } else {
60     Error error = Error::success();
61     for (int64_t tid : *request.tids)
62       error = joinErrors(std::move(error),
63                          TraceStop(static_cast<lldb::tid_t>(tid)));
64     return error;
65   }
66 }
67 
68 Error IntelPTCollector::TraceStart(const TraceIntelPTStartRequest &request) {
69   if (request.IsProcessTracing()) {
70     if (m_process_trace_up) {
71       return createStringError(
72           inconvertibleErrorCode(),
73           "Process currently traced. Stop process tracing first");
74     }
75     if (request.IsPerCpuTracing()) {
76       if (m_thread_traces.GetTracedThreadsCount() > 0)
77         return createStringError(
78             inconvertibleErrorCode(),
79             "Threads currently traced. Stop tracing them first.");
80       // CPU tracing is useless if we can't convert tsc to nanos.
81       Expected<LinuxPerfZeroTscConversion &> tsc_conversion =
82           FetchPerfTscConversionParameters();
83       if (!tsc_conversion)
84         return tsc_conversion.takeError();
85 
86       // We force the enabledment of TSCs, which is needed for correlating the
87       // cpu traces.
88       TraceIntelPTStartRequest effective_request = request;
89       effective_request.enable_tsc = true;
90 
91       if (Expected<IntelPTProcessTraceUP> trace =
92               IntelPTMultiCoreTrace::StartOnAllCores(effective_request,
93                                                      m_process)) {
94         m_process_trace_up = std::move(*trace);
95         return Error::success();
96       } else {
97         return trace.takeError();
98       }
99     } else {
100       std::vector<lldb::tid_t> process_threads;
101       for (size_t i = 0; m_process.GetThreadAtIndex(i); i++)
102         process_threads.push_back(m_process.GetThreadAtIndex(i)->GetID());
103 
104       // per-thread process tracing
105       if (Expected<IntelPTProcessTraceUP> trace =
106               IntelPTPerThreadProcessTrace::Start(request, process_threads)) {
107         m_process_trace_up = std::move(trace.get());
108         return Error::success();
109       } else {
110         return trace.takeError();
111       }
112     }
113   } else {
114     // individual thread tracing
115     Error error = Error::success();
116     for (int64_t tid : *request.tids) {
117       if (m_process_trace_up && m_process_trace_up->TracesThread(tid))
118         error = joinErrors(
119             std::move(error),
120             createStringError(inconvertibleErrorCode(),
121                               formatv("Thread with tid {0} is currently "
122                                       "traced. Stop tracing it first.",
123                                       tid)
124                                   .str()
125                                   .c_str()));
126       else
127         error = joinErrors(std::move(error),
128                            m_thread_traces.TraceStart(tid, request));
129     }
130     return error;
131   }
132 }
133 
134 void IntelPTCollector::ProcessWillResume() {
135   if (m_process_trace_up)
136     m_process_trace_up->ProcessWillResume();
137 }
138 
139 void IntelPTCollector::ProcessDidStop() {
140   if (m_process_trace_up)
141     m_process_trace_up->ProcessDidStop();
142 }
143 
144 Error IntelPTCollector::OnThreadCreated(lldb::tid_t tid) {
145   if (m_process_trace_up)
146     return m_process_trace_up->TraceStart(tid);
147 
148   return Error::success();
149 }
150 
151 Error IntelPTCollector::OnThreadDestroyed(lldb::tid_t tid) {
152   if (m_process_trace_up && m_process_trace_up->TracesThread(tid))
153     return m_process_trace_up->TraceStop(tid);
154   else if (m_thread_traces.TracesThread(tid))
155     return m_thread_traces.TraceStop(tid);
156   return Error::success();
157 }
158 
159 Expected<json::Value> IntelPTCollector::GetState() {
160   Expected<ArrayRef<uint8_t>> cpu_info = GetProcfsCpuInfo();
161   if (!cpu_info)
162     return cpu_info.takeError();
163 
164   TraceIntelPTGetStateResponse state;
165   if (m_process_trace_up)
166     state = m_process_trace_up->GetState();
167 
168   state.process_binary_data.push_back(
169       {IntelPTDataKinds::kProcFsCpuInfo, cpu_info->size()});
170 
171   m_thread_traces.ForEachThread(
172       [&](lldb::tid_t tid, const IntelPTSingleBufferTrace &thread_trace) {
173         state.traced_threads.push_back(
174             {tid,
175              {{IntelPTDataKinds::kIptTrace, thread_trace.GetIptTraceSize()}}});
176       });
177 
178   if (Expected<LinuxPerfZeroTscConversion &> tsc_conversion =
179           FetchPerfTscConversionParameters())
180     state.tsc_perf_zero_conversion = *tsc_conversion;
181   else
182     state.AddWarning(toString(tsc_conversion.takeError()));
183   return toJSON(state);
184 }
185 
186 Expected<std::vector<uint8_t>>
187 IntelPTCollector::GetBinaryData(const TraceGetBinaryDataRequest &request) {
188   if (request.kind == IntelPTDataKinds::kProcFsCpuInfo)
189     return GetProcfsCpuInfo();
190 
191   if (m_process_trace_up) {
192     Expected<Optional<std::vector<uint8_t>>> data =
193         m_process_trace_up->TryGetBinaryData(request);
194     if (!data)
195       return data.takeError();
196     if (*data)
197       return **data;
198   }
199 
200   {
201     Expected<Optional<std::vector<uint8_t>>> data =
202         m_thread_traces.TryGetBinaryData(request);
203     if (!data)
204       return data.takeError();
205     if (*data)
206       return **data;
207   }
208 
209   return createStringError(
210       inconvertibleErrorCode(),
211       formatv("Can't fetch data kind {0} for cpu_id {1}, tid {2} and "
212               "\"process tracing\" mode {3}",
213               request.kind, request.cpu_id, request.tid,
214               m_process_trace_up ? "enabled" : "not enabled"));
215 }
216 
217 bool IntelPTCollector::IsSupported() {
218   if (Expected<uint32_t> intel_pt_type = GetIntelPTOSEventType()) {
219     return true;
220   } else {
221     llvm::consumeError(intel_pt_type.takeError());
222     return false;
223   }
224 }
225 
226 void IntelPTCollector::Clear() {
227   m_process_trace_up.reset();
228   m_thread_traces.Clear();
229 }
230