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