1 //===-- TraceIntelPT.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 "TraceIntelPT.h"
10
11 #include "../common/ThreadPostMortemTrace.h"
12 #include "CommandObjectTraceStartIntelPT.h"
13 #include "DecodedThread.h"
14 #include "TraceIntelPTConstants.h"
15 #include "TraceIntelPTSessionFileParser.h"
16 #include "lldb/Core/PluginManager.h"
17 #include "lldb/Target/Process.h"
18 #include "lldb/Target/Target.h"
19
20 using namespace lldb;
21 using namespace lldb_private;
22 using namespace lldb_private::trace_intel_pt;
23 using namespace llvm;
24
LLDB_PLUGIN_DEFINE(TraceIntelPT)25 LLDB_PLUGIN_DEFINE(TraceIntelPT)
26
27 lldb::CommandObjectSP
28 TraceIntelPT::GetProcessTraceStartCommand(CommandInterpreter &interpreter) {
29 return CommandObjectSP(
30 new CommandObjectProcessTraceStartIntelPT(*this, interpreter));
31 }
32
33 lldb::CommandObjectSP
GetThreadTraceStartCommand(CommandInterpreter & interpreter)34 TraceIntelPT::GetThreadTraceStartCommand(CommandInterpreter &interpreter) {
35 return CommandObjectSP(
36 new CommandObjectThreadTraceStartIntelPT(*this, interpreter));
37 }
38
Initialize()39 void TraceIntelPT::Initialize() {
40 PluginManager::RegisterPlugin(GetPluginNameStatic(), "Intel Processor Trace",
41 CreateInstanceForSessionFile,
42 CreateInstanceForLiveProcess,
43 TraceIntelPTSessionFileParser::GetSchema());
44 }
45
Terminate()46 void TraceIntelPT::Terminate() {
47 PluginManager::UnregisterPlugin(CreateInstanceForSessionFile);
48 }
49
GetPluginNameStatic()50 ConstString TraceIntelPT::GetPluginNameStatic() {
51 static ConstString g_name("intel-pt");
52 return g_name;
53 }
54
GetSchema()55 StringRef TraceIntelPT::GetSchema() {
56 return TraceIntelPTSessionFileParser::GetSchema();
57 }
58
59 //------------------------------------------------------------------
60 // PluginInterface protocol
61 //------------------------------------------------------------------
62
GetPluginName()63 ConstString TraceIntelPT::GetPluginName() { return GetPluginNameStatic(); }
64
GetPluginVersion()65 uint32_t TraceIntelPT::GetPluginVersion() { return 1; }
66
Dump(Stream * s) const67 void TraceIntelPT::Dump(Stream *s) const {}
68
CreateInstanceForSessionFile(const json::Value & trace_session_file,StringRef session_file_dir,Debugger & debugger)69 Expected<TraceSP> TraceIntelPT::CreateInstanceForSessionFile(
70 const json::Value &trace_session_file, StringRef session_file_dir,
71 Debugger &debugger) {
72 return TraceIntelPTSessionFileParser(debugger, trace_session_file,
73 session_file_dir)
74 .Parse();
75 }
76
CreateInstanceForLiveProcess(Process & process)77 Expected<TraceSP> TraceIntelPT::CreateInstanceForLiveProcess(Process &process) {
78 TraceSP instance(new TraceIntelPT(process));
79 process.GetTarget().SetTrace(instance);
80 return instance;
81 }
82
TraceIntelPT(const pt_cpu & cpu_info,const std::vector<ThreadPostMortemTraceSP> & traced_threads)83 TraceIntelPT::TraceIntelPT(
84 const pt_cpu &cpu_info,
85 const std::vector<ThreadPostMortemTraceSP> &traced_threads)
86 : m_cpu_info(cpu_info) {
87 for (const ThreadPostMortemTraceSP &thread : traced_threads)
88 m_thread_decoders.emplace(
89 thread.get(), std::make_unique<PostMortemThreadDecoder>(thread, *this));
90 }
91
Decode(Thread & thread)92 DecodedThreadSP TraceIntelPT::Decode(Thread &thread) {
93 RefreshLiveProcessState();
94 if (m_live_refresh_error.hasValue())
95 return std::make_shared<DecodedThread>(
96 thread.shared_from_this(),
97 createStringError(inconvertibleErrorCode(), *m_live_refresh_error));
98
99 auto it = m_thread_decoders.find(&thread);
100 if (it == m_thread_decoders.end())
101 return std::make_shared<DecodedThread>(
102 thread.shared_from_this(),
103 createStringError(inconvertibleErrorCode(), "thread not traced"));
104 return it->second->Decode();
105 }
106
GetCursor(Thread & thread)107 lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) {
108 return Decode(thread)->GetCursor();
109 }
110
DumpTraceInfo(Thread & thread,Stream & s,bool verbose)111 void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) {
112 Optional<size_t> raw_size = GetRawTraceSize(thread);
113 s.Printf("\nthread #%u: tid = %" PRIu64, thread.GetIndexID(), thread.GetID());
114 if (!raw_size) {
115 s.Printf(", not traced\n");
116 return;
117 }
118 s.Printf("\n Raw trace size: %zu bytes\n", *raw_size);
119 return;
120 }
121
GetRawTraceSize(Thread & thread)122 Optional<size_t> TraceIntelPT::GetRawTraceSize(Thread &thread) {
123 if (IsTraced(thread))
124 return Decode(thread)->GetRawTraceSize();
125 else
126 return None;
127 }
128
GetCPUInfoForLiveProcess()129 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
130 Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo");
131 if (!cpu_info)
132 return cpu_info.takeError();
133
134 int64_t cpu_family = -1;
135 int64_t model = -1;
136 int64_t stepping = -1;
137 std::string vendor_id;
138
139 StringRef rest(reinterpret_cast<const char *>(cpu_info->data()),
140 cpu_info->size());
141 while (!rest.empty()) {
142 StringRef line;
143 std::tie(line, rest) = rest.split('\n');
144
145 SmallVector<StringRef, 2> columns;
146 line.split(columns, StringRef(":"), -1, false);
147
148 if (columns.size() < 2)
149 continue; // continue searching
150
151 columns[1] = columns[1].trim(" ");
152 if (columns[0].contains("cpu family") &&
153 columns[1].getAsInteger(10, cpu_family))
154 continue;
155
156 else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
157 continue;
158
159 else if (columns[0].contains("stepping") &&
160 columns[1].getAsInteger(10, stepping))
161 continue;
162
163 else if (columns[0].contains("vendor_id")) {
164 vendor_id = columns[1].str();
165 if (!vendor_id.empty())
166 continue;
167 }
168
169 if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
170 (!vendor_id.empty())) {
171 return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown,
172 static_cast<uint16_t>(cpu_family),
173 static_cast<uint8_t>(model),
174 static_cast<uint8_t>(stepping)};
175 }
176 }
177 return createStringError(inconvertibleErrorCode(),
178 "Failed parsing the target's /proc/cpuinfo file");
179 }
180
GetCPUInfo()181 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
182 if (!m_cpu_info) {
183 if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess())
184 m_cpu_info = *cpu_info;
185 else
186 return cpu_info.takeError();
187 }
188 return *m_cpu_info;
189 }
190
DoRefreshLiveProcessState(Expected<TraceGetStateResponse> state)191 void TraceIntelPT::DoRefreshLiveProcessState(
192 Expected<TraceGetStateResponse> state) {
193 m_thread_decoders.clear();
194
195 if (!state) {
196 m_live_refresh_error = toString(state.takeError());
197 return;
198 }
199
200 for (const TraceThreadState &thread_state : state->tracedThreads) {
201 Thread &thread =
202 *m_live_process->GetThreadList().FindThreadByID(thread_state.tid);
203 m_thread_decoders.emplace(
204 &thread, std::make_unique<LiveThreadDecoder>(thread, *this));
205 }
206 }
207
IsTraced(const Thread & thread)208 bool TraceIntelPT::IsTraced(const Thread &thread) {
209 RefreshLiveProcessState();
210 return m_thread_decoders.count(&thread);
211 }
212
213 // The information here should match the description of the intel-pt section
214 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt
215 // documentation file. Similarly, it should match the CLI help messages of the
216 // TraceIntelPTOptions.td file.
GetStartConfigurationHelp()217 const char *TraceIntelPT::GetStartConfigurationHelp() {
218 return R"(Parameters:
219
220 Note: If a parameter is not specified, a default value will be used.
221
222 - int threadBufferSize (defaults to 4096 bytes):
223 [process and thread tracing]
224 Trace size in bytes per thread. It must be a power of 2 greater
225 than or equal to 4096 (2^12). The trace is circular keeping the
226 the most recent data.
227
228 - boolean enableTsc (default to false):
229 [process and thread tracing]
230 Whether to use enable TSC timestamps or not. This is supported on
231 all devices that support intel-pt.
232
233 - psbPeriod (defaults to null):
234 [process and thread tracing]
235 This value defines the period in which PSB packets will be generated.
236 A PSB packet is a synchronization packet that contains a TSC
237 timestamp and the current absolute instruction pointer.
238
239 This parameter can only be used if
240
241 /sys/bus/event_source/devices/intel_pt/caps/psb_cyc
242
243 is 1. Otherwise, the PSB period will be defined by the processor.
244
245 If supported, valid values for this period can be found in
246
247 /sys/bus/event_source/devices/intel_pt/caps/psb_periods
248
249 which contains a hexadecimal number, whose bits represent
250 valid values e.g. if bit 2 is set, then value 2 is valid.
251
252 The psb_period value is converted to the approximate number of
253 raw trace bytes between PSB packets as:
254
255 2 ^ (value + 11)
256
257 e.g. value 3 means 16KiB between PSB packets. Defaults to 0 if
258 supported.
259
260 - int processBufferSizeLimit (defaults to 500 MB):
261 [process tracing only]
262 Maximum total trace size per process in bytes. This limit applies
263 to the sum of the sizes of all thread traces of this process,
264 excluding the ones created explicitly with "thread tracing".
265 Whenever a thread is attempted to be traced due to this command
266 and the limit would be reached, the process is stopped with a
267 "processor trace" reason, so that the user can retrace the process
268 if needed.)";
269 }
270
Start(size_t thread_buffer_size,size_t total_buffer_size_limit,bool enable_tsc,Optional<size_t> psb_period)271 Error TraceIntelPT::Start(size_t thread_buffer_size,
272 size_t total_buffer_size_limit, bool enable_tsc,
273 Optional<size_t> psb_period) {
274 TraceIntelPTStartRequest request;
275 request.threadBufferSize = thread_buffer_size;
276 request.processBufferSizeLimit = total_buffer_size_limit;
277 request.enableTsc = enable_tsc;
278 request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; });
279 request.type = GetPluginName().AsCString();
280 return Trace::Start(toJSON(request));
281 }
282
Start(StructuredData::ObjectSP configuration)283 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) {
284 size_t thread_buffer_size = kDefaultThreadBufferSize;
285 size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
286 bool enable_tsc = kDefaultEnableTscValue;
287 Optional<size_t> psb_period = kDefaultPsbPeriod;
288
289 if (configuration) {
290 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
291 dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size);
292 dict->GetValueForKeyAsInteger("processBufferSizeLimit",
293 process_buffer_size_limit);
294 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
295 dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
296 } else {
297 return createStringError(inconvertibleErrorCode(),
298 "configuration object is not a dictionary");
299 }
300 }
301
302 return Start(thread_buffer_size, process_buffer_size_limit, enable_tsc,
303 psb_period);
304 }
305
Start(llvm::ArrayRef<lldb::tid_t> tids,size_t thread_buffer_size,bool enable_tsc,Optional<size_t> psb_period)306 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
307 size_t thread_buffer_size, bool enable_tsc,
308 Optional<size_t> psb_period) {
309 TraceIntelPTStartRequest request;
310 request.threadBufferSize = thread_buffer_size;
311 request.enableTsc = enable_tsc;
312 request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; });
313 request.type = GetPluginName().AsCString();
314 request.tids.emplace();
315 for (lldb::tid_t tid : tids)
316 request.tids->push_back(tid);
317 return Trace::Start(toJSON(request));
318 }
319
Start(llvm::ArrayRef<lldb::tid_t> tids,StructuredData::ObjectSP configuration)320 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
321 StructuredData::ObjectSP configuration) {
322 size_t thread_buffer_size = kDefaultThreadBufferSize;
323 bool enable_tsc = kDefaultEnableTscValue;
324 Optional<size_t> psb_period = kDefaultPsbPeriod;
325
326 if (configuration) {
327 if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
328 dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size);
329 dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
330 dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
331 } else {
332 return createStringError(inconvertibleErrorCode(),
333 "configuration object is not a dictionary");
334 }
335 }
336
337 return Start(tids, thread_buffer_size, enable_tsc, psb_period);
338 }
339
340 Expected<std::vector<uint8_t>>
GetLiveThreadBuffer(lldb::tid_t tid)341 TraceIntelPT::GetLiveThreadBuffer(lldb::tid_t tid) {
342 return Trace::GetLiveThreadBinaryData(tid, "threadTraceBuffer");
343 }
344