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