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   const DecodedThread::EventsStats &events_stats =
146       decoded_trace_sp->GetEventsStats();
147   s << "\n  Events:\n";
148   s.Format("    Number of instructions with events: {0}\n",
149            events_stats.total_instructions_with_events);
150   s.Format("    Number of individual events: {0}\n", events_stats.total_count);
151   for (const auto &event_to_count : events_stats.events_counts) {
152     s.Format("      {0}: {1}\n",
153              trace_event_utils::EventToDisplayString(event_to_count.first),
154              event_to_count.second);
155   }
156 
157   s << "\n  Errors:\n";
158   const DecodedThread::LibiptErrorsStats &tsc_errors_stats =
159       decoded_trace_sp->GetTscErrorsStats();
160   s.Format("    Number of TSC decoding errors: {0}\n",
161            tsc_errors_stats.total_count);
162   for (const auto &error_message_to_count :
163        tsc_errors_stats.libipt_errors_counts) {
164     s.Format("      {0}: {1}\n", error_message_to_count.first,
165              error_message_to_count.second);
166   }
167 }
168 
169 llvm::Expected<size_t> TraceIntelPT::GetRawTraceSize(Thread &thread) {
170   size_t size;
171   auto callback = [&](llvm::ArrayRef<uint8_t> data) {
172     size = data.size();
173     return Error::success();
174   };
175   if (Error err = OnThreadBufferRead(thread.GetID(), callback))
176     return std::move(err);
177 
178   return size;
179 }
180 
181 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
182   Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo");
183   if (!cpu_info)
184     return cpu_info.takeError();
185 
186   int64_t cpu_family = -1;
187   int64_t model = -1;
188   int64_t stepping = -1;
189   std::string vendor_id;
190 
191   StringRef rest(reinterpret_cast<const char *>(cpu_info->data()),
192                  cpu_info->size());
193   while (!rest.empty()) {
194     StringRef line;
195     std::tie(line, rest) = rest.split('\n');
196 
197     SmallVector<StringRef, 2> columns;
198     line.split(columns, StringRef(":"), -1, false);
199 
200     if (columns.size() < 2)
201       continue; // continue searching
202 
203     columns[1] = columns[1].trim(" ");
204     if (columns[0].contains("cpu family") &&
205         columns[1].getAsInteger(10, cpu_family))
206       continue;
207 
208     else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
209       continue;
210 
211     else if (columns[0].contains("stepping") &&
212              columns[1].getAsInteger(10, stepping))
213       continue;
214 
215     else if (columns[0].contains("vendor_id")) {
216       vendor_id = columns[1].str();
217       if (!vendor_id.empty())
218         continue;
219     }
220 
221     if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
222         (!vendor_id.empty())) {
223       return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown,
224                     static_cast<uint16_t>(cpu_family),
225                     static_cast<uint8_t>(model),
226                     static_cast<uint8_t>(stepping)};
227     }
228   }
229   return createStringError(inconvertibleErrorCode(),
230                            "Failed parsing the target's /proc/cpuinfo file");
231 }
232 
233 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
234   if (!m_cpu_info) {
235     if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess())
236       m_cpu_info = *cpu_info;
237     else
238       return cpu_info.takeError();
239   }
240   return *m_cpu_info;
241 }
242 
243 Process *TraceIntelPT::GetLiveProcess() { return m_live_process; }
244 
245 void TraceIntelPT::DoRefreshLiveProcessState(
246     Expected<TraceGetStateResponse> state) {
247   m_thread_decoders.clear();
248 
249   if (!state) {
250     m_live_refresh_error = toString(state.takeError());
251     return;
252   }
253 
254   for (const TraceThreadState &thread_state : state->tracedThreads) {
255     ThreadSP thread_sp =
256         m_live_process->GetThreadList().FindThreadByID(thread_state.tid);
257     m_thread_decoders.emplace(
258         thread_state.tid, std::make_unique<ThreadDecoder>(thread_sp, *this));
259   }
260 }
261 
262 bool TraceIntelPT::IsTraced(lldb::tid_t tid) {
263   RefreshLiveProcessState();
264   return m_thread_decoders.count(tid);
265 }
266 
267 // The information here should match the description of the intel-pt section
268 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt
269 // documentation file. Similarly, it should match the CLI help messages of the
270 // TraceIntelPTOptions.td file.
271 const char *TraceIntelPT::GetStartConfigurationHelp() {
272   return R"(Parameters:
273 
274   Note: If a parameter is not specified, a default value will be used.
275 
276   - int threadBufferSize (defaults to 4096 bytes):
277     [process and thread tracing]
278     Trace size in bytes per thread. It must be a power of 2 greater
279     than or equal to 4096 (2^12). The trace is circular keeping the
280     the most recent data.
281 
282   - boolean enableTsc (default to false):
283     [process and thread tracing]
284     Whether to use enable TSC timestamps or not. This is supported on
285     all devices that support intel-pt.
286 
287   - psbPeriod (defaults to null):
288     [process and thread tracing]
289     This value defines the period in which PSB packets will be generated.
290     A PSB packet is a synchronization packet that contains a TSC
291     timestamp and the current absolute instruction pointer.
292 
293     This parameter can only be used if
294 
295         /sys/bus/event_source/devices/intel_pt/caps/psb_cyc
296 
297     is 1. Otherwise, the PSB period will be defined by the processor.
298 
299     If supported, valid values for this period can be found in
300 
301         /sys/bus/event_source/devices/intel_pt/caps/psb_periods
302 
303     which contains a hexadecimal number, whose bits represent
304     valid values e.g. if bit 2 is set, then value 2 is valid.
305 
306     The psb_period value is converted to the approximate number of
307     raw trace bytes between PSB packets as:
308 
309         2 ^ (value + 11)
310 
311     e.g. value 3 means 16KiB between PSB packets. Defaults to 0 if
312     supported.
313 
314   - int processBufferSizeLimit (defaults to 500 MB):
315     [process tracing only]
316     Maximum total trace size per process in bytes. This limit applies
317     to the sum of the sizes of all thread traces of this process,
318     excluding the ones created explicitly with "thread tracing".
319     Whenever a thread is attempted to be traced due to this command
320     and the limit would be reached, the process is stopped with a
321     "processor trace" reason, so that the user can retrace the process
322     if needed.)";
323 }
324 
325 Error TraceIntelPT::Start(size_t thread_buffer_size,
326                           size_t total_buffer_size_limit, bool enable_tsc,
327                           Optional<size_t> psb_period) {
328   TraceIntelPTStartRequest request;
329   request.threadBufferSize = thread_buffer_size;
330   request.processBufferSizeLimit = total_buffer_size_limit;
331   request.enableTsc = enable_tsc;
332   request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; });
333   request.type = GetPluginName().str();
334   return Trace::Start(toJSON(request));
335 }
336 
337 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) {
338   size_t thread_buffer_size = kDefaultThreadBufferSize;
339   size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
340   bool enable_tsc = kDefaultEnableTscValue;
341   Optional<size_t> psb_period = kDefaultPsbPeriod;
342 
343   if (configuration) {
344     if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
345       dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size);
346       dict->GetValueForKeyAsInteger("processBufferSizeLimit",
347                                     process_buffer_size_limit);
348       dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
349       dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
350     } else {
351       return createStringError(inconvertibleErrorCode(),
352                                "configuration object is not a dictionary");
353     }
354   }
355 
356   return Start(thread_buffer_size, process_buffer_size_limit, enable_tsc,
357                psb_period);
358 }
359 
360 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
361                                 size_t thread_buffer_size, bool enable_tsc,
362                                 Optional<size_t> psb_period) {
363   TraceIntelPTStartRequest request;
364   request.threadBufferSize = thread_buffer_size;
365   request.enableTsc = enable_tsc;
366   request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; });
367   request.type = GetPluginName().str();
368   request.tids.emplace();
369   for (lldb::tid_t tid : tids)
370     request.tids->push_back(tid);
371   return Trace::Start(toJSON(request));
372 }
373 
374 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
375                           StructuredData::ObjectSP configuration) {
376   size_t thread_buffer_size = kDefaultThreadBufferSize;
377   bool enable_tsc = kDefaultEnableTscValue;
378   Optional<size_t> psb_period = kDefaultPsbPeriod;
379 
380   if (configuration) {
381     if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
382       dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size);
383       dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
384       dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
385     } else {
386       return createStringError(inconvertibleErrorCode(),
387                                "configuration object is not a dictionary");
388     }
389   }
390 
391   return Start(tids, thread_buffer_size, enable_tsc, psb_period);
392 }
393 
394 Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid,
395                                        OnBinaryDataReadCallback callback) {
396   return OnThreadBinaryDataRead(tid, "threadTraceBuffer", callback);
397 }
398 
399 TaskTimer &TraceIntelPT::GetTimer() { return m_task_timer; }
400