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