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