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(
83         thread->GetID(),
84         std::make_unique<PostMortemThreadDecoder>(thread, *this));
85 }
86 
87 DecodedThreadSP TraceIntelPT::Decode(Thread &thread) {
88   RefreshLiveProcessState();
89   if (m_live_refresh_error.hasValue())
90     return std::make_shared<DecodedThread>(
91         thread.shared_from_this(),
92         createStringError(inconvertibleErrorCode(), *m_live_refresh_error));
93 
94   auto it = m_thread_decoders.find(thread.GetID());
95   if (it == m_thread_decoders.end())
96     return std::make_shared<DecodedThread>(
97         thread.shared_from_this(),
98         createStringError(inconvertibleErrorCode(), "thread not traced"));
99   return it->second->Decode();
100 }
101 
102 lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) {
103   return Decode(thread)->GetCursor();
104 }
105 
106 void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) {
107   Optional<size_t> raw_size = GetRawTraceSize(thread);
108   s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID());
109   if (!raw_size) {
110     s << ", not traced\n";
111     return;
112   }
113   s << "\n";
114   DecodedThreadSP decoded_trace_sp = Decode(thread);
115   size_t insn_len = decoded_trace_sp->GetInstructionsCount();
116   size_t mem_used = decoded_trace_sp->CalculateApproximateMemoryUsage();
117 
118   s.Format("  Raw trace size: {0} KiB\n", *raw_size / 1024);
119   s.Format("  Total number of instructions: {0}\n", insn_len);
120   s.Format("  Total approximate memory usage: {0:2} KiB\n",
121            (double)mem_used / 1024);
122   if (insn_len != 0)
123     s.Format("  Average memory usage per instruction: {0:2} bytes\n",
124              (double)mem_used / insn_len);
125 
126   const DecodedThread::LibiptErrors &tsc_errors =
127       decoded_trace_sp->GetTscErrors();
128   s.Format("\n  Number of TSC decoding errors: {0}\n", tsc_errors.total_count);
129   for (const auto &error_message_to_count : tsc_errors.libipt_errors) {
130     s.Format("    {0}: {1}\n", error_message_to_count.first,
131              error_message_to_count.second);
132   }
133 }
134 
135 Optional<size_t> TraceIntelPT::GetRawTraceSize(Thread &thread) {
136   if (IsTraced(thread.GetID()))
137     return Decode(thread)->GetRawTraceSize();
138   else
139     return None;
140 }
141 
142 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
143   Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo");
144   if (!cpu_info)
145     return cpu_info.takeError();
146 
147   int64_t cpu_family = -1;
148   int64_t model = -1;
149   int64_t stepping = -1;
150   std::string vendor_id;
151 
152   StringRef rest(reinterpret_cast<const char *>(cpu_info->data()),
153                  cpu_info->size());
154   while (!rest.empty()) {
155     StringRef line;
156     std::tie(line, rest) = rest.split('\n');
157 
158     SmallVector<StringRef, 2> columns;
159     line.split(columns, StringRef(":"), -1, false);
160 
161     if (columns.size() < 2)
162       continue; // continue searching
163 
164     columns[1] = columns[1].trim(" ");
165     if (columns[0].contains("cpu family") &&
166         columns[1].getAsInteger(10, cpu_family))
167       continue;
168 
169     else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
170       continue;
171 
172     else if (columns[0].contains("stepping") &&
173              columns[1].getAsInteger(10, stepping))
174       continue;
175 
176     else if (columns[0].contains("vendor_id")) {
177       vendor_id = columns[1].str();
178       if (!vendor_id.empty())
179         continue;
180     }
181 
182     if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
183         (!vendor_id.empty())) {
184       return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown,
185                     static_cast<uint16_t>(cpu_family),
186                     static_cast<uint8_t>(model),
187                     static_cast<uint8_t>(stepping)};
188     }
189   }
190   return createStringError(inconvertibleErrorCode(),
191                            "Failed parsing the target's /proc/cpuinfo file");
192 }
193 
194 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
195   if (!m_cpu_info) {
196     if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess())
197       m_cpu_info = *cpu_info;
198     else
199       return cpu_info.takeError();
200   }
201   return *m_cpu_info;
202 }
203 
204 Process *TraceIntelPT::GetLiveProcess() { return m_live_process; }
205 
206 void TraceIntelPT::DoRefreshLiveProcessState(
207     Expected<TraceGetStateResponse> state) {
208   m_thread_decoders.clear();
209 
210   if (!state) {
211     m_live_refresh_error = toString(state.takeError());
212     return;
213   }
214 
215   for (const TraceThreadState &thread_state : state->tracedThreads) {
216     Thread &thread =
217         *m_live_process->GetThreadList().FindThreadByID(thread_state.tid);
218     m_thread_decoders.emplace(
219         thread_state.tid, std::make_unique<LiveThreadDecoder>(thread, *this));
220   }
221 }
222 
223 bool TraceIntelPT::IsTraced(lldb::tid_t tid) {
224   RefreshLiveProcessState();
225   return m_thread_decoders.count(tid);
226 }
227 
228 // The information here should match the description of the intel-pt section
229 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt
230 // documentation file. Similarly, it should match the CLI help messages of the
231 // TraceIntelPTOptions.td file.
232 const char *TraceIntelPT::GetStartConfigurationHelp() {
233   return R"(Parameters:
234 
235   Note: If a parameter is not specified, a default value will be used.
236 
237   - int threadBufferSize (defaults to 4096 bytes):
238     [process and thread tracing]
239     Trace size in bytes per thread. It must be a power of 2 greater
240     than or equal to 4096 (2^12). The trace is circular keeping the
241     the most recent data.
242 
243   - boolean enableTsc (default to false):
244     [process and thread tracing]
245     Whether to use enable TSC timestamps or not. This is supported on
246     all devices that support intel-pt.
247 
248   - psbPeriod (defaults to null):
249     [process and thread tracing]
250     This value defines the period in which PSB packets will be generated.
251     A PSB packet is a synchronization packet that contains a TSC
252     timestamp and the current absolute instruction pointer.
253 
254     This parameter can only be used if
255 
256         /sys/bus/event_source/devices/intel_pt/caps/psb_cyc
257 
258     is 1. Otherwise, the PSB period will be defined by the processor.
259 
260     If supported, valid values for this period can be found in
261 
262         /sys/bus/event_source/devices/intel_pt/caps/psb_periods
263 
264     which contains a hexadecimal number, whose bits represent
265     valid values e.g. if bit 2 is set, then value 2 is valid.
266 
267     The psb_period value is converted to the approximate number of
268     raw trace bytes between PSB packets as:
269 
270         2 ^ (value + 11)
271 
272     e.g. value 3 means 16KiB between PSB packets. Defaults to 0 if
273     supported.
274 
275   - int processBufferSizeLimit (defaults to 500 MB):
276     [process tracing only]
277     Maximum total trace size per process in bytes. This limit applies
278     to the sum of the sizes of all thread traces of this process,
279     excluding the ones created explicitly with "thread tracing".
280     Whenever a thread is attempted to be traced due to this command
281     and the limit would be reached, the process is stopped with a
282     "processor trace" reason, so that the user can retrace the process
283     if needed.)";
284 }
285 
286 Error TraceIntelPT::Start(size_t thread_buffer_size,
287                           size_t total_buffer_size_limit, bool enable_tsc,
288                           Optional<size_t> psb_period) {
289   TraceIntelPTStartRequest request;
290   request.threadBufferSize = thread_buffer_size;
291   request.processBufferSizeLimit = total_buffer_size_limit;
292   request.enableTsc = enable_tsc;
293   request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; });
294   request.type = GetPluginName().str();
295   return Trace::Start(toJSON(request));
296 }
297 
298 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) {
299   size_t thread_buffer_size = kDefaultThreadBufferSize;
300   size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
301   bool enable_tsc = kDefaultEnableTscValue;
302   Optional<size_t> psb_period = kDefaultPsbPeriod;
303 
304   if (configuration) {
305     if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
306       dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size);
307       dict->GetValueForKeyAsInteger("processBufferSizeLimit",
308                                     process_buffer_size_limit);
309       dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
310       dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
311     } else {
312       return createStringError(inconvertibleErrorCode(),
313                                "configuration object is not a dictionary");
314     }
315   }
316 
317   return Start(thread_buffer_size, process_buffer_size_limit, enable_tsc,
318                psb_period);
319 }
320 
321 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
322                                 size_t thread_buffer_size, bool enable_tsc,
323                                 Optional<size_t> psb_period) {
324   TraceIntelPTStartRequest request;
325   request.threadBufferSize = thread_buffer_size;
326   request.enableTsc = enable_tsc;
327   request.psbPeriod = psb_period.map([](size_t val) { return (int64_t)val; });
328   request.type = GetPluginName().str();
329   request.tids.emplace();
330   for (lldb::tid_t tid : tids)
331     request.tids->push_back(tid);
332   return Trace::Start(toJSON(request));
333 }
334 
335 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
336                           StructuredData::ObjectSP configuration) {
337   size_t thread_buffer_size = kDefaultThreadBufferSize;
338   bool enable_tsc = kDefaultEnableTscValue;
339   Optional<size_t> psb_period = kDefaultPsbPeriod;
340 
341   if (configuration) {
342     if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
343       dict->GetValueForKeyAsInteger("threadBufferSize", thread_buffer_size);
344       dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
345       dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
346     } else {
347       return createStringError(inconvertibleErrorCode(),
348                                "configuration object is not a dictionary");
349     }
350   }
351 
352   return Start(tids, thread_buffer_size, enable_tsc, psb_period);
353 }
354 
355 Expected<std::vector<uint8_t>>
356 TraceIntelPT::GetLiveThreadBuffer(lldb::tid_t tid) {
357   return Trace::GetLiveThreadBinaryData(tid, "threadTraceBuffer");
358 }
359