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(), IntelPTDataKinds::kTraceBuffer,
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 =
183       GetLiveProcessBinaryData(IntelPTDataKinds::kProcFsCpuInfo);
184   if (!cpu_info)
185     return cpu_info.takeError();
186 
187   int64_t cpu_family = -1;
188   int64_t model = -1;
189   int64_t stepping = -1;
190   std::string vendor_id;
191 
192   StringRef rest(reinterpret_cast<const char *>(cpu_info->data()),
193                  cpu_info->size());
194   while (!rest.empty()) {
195     StringRef line;
196     std::tie(line, rest) = rest.split('\n');
197 
198     SmallVector<StringRef, 2> columns;
199     line.split(columns, StringRef(":"), -1, false);
200 
201     if (columns.size() < 2)
202       continue; // continue searching
203 
204     columns[1] = columns[1].trim(" ");
205     if (columns[0].contains("cpu family") &&
206         columns[1].getAsInteger(10, cpu_family))
207       continue;
208 
209     else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
210       continue;
211 
212     else if (columns[0].contains("stepping") &&
213              columns[1].getAsInteger(10, stepping))
214       continue;
215 
216     else if (columns[0].contains("vendor_id")) {
217       vendor_id = columns[1].str();
218       if (!vendor_id.empty())
219         continue;
220     }
221 
222     if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
223         (!vendor_id.empty())) {
224       return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown,
225                     static_cast<uint16_t>(cpu_family),
226                     static_cast<uint8_t>(model),
227                     static_cast<uint8_t>(stepping)};
228     }
229   }
230   return createStringError(inconvertibleErrorCode(),
231                            "Failed parsing the target's /proc/cpuinfo file");
232 }
233 
234 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
235   if (!m_cpu_info) {
236     if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess())
237       m_cpu_info = *cpu_info;
238     else
239       return cpu_info.takeError();
240   }
241   return *m_cpu_info;
242 }
243 
244 Process *TraceIntelPT::GetLiveProcess() { return m_live_process; }
245 
246 void TraceIntelPT::DoRefreshLiveProcessState(
247     Expected<TraceGetStateResponse> state) {
248   m_thread_decoders.clear();
249 
250   if (!state) {
251     m_live_refresh_error = toString(state.takeError());
252     return;
253   }
254 
255   for (const TraceThreadState &thread_state : state->tracedThreads) {
256     ThreadSP thread_sp =
257         m_live_process->GetThreadList().FindThreadByID(thread_state.tid);
258     m_thread_decoders.emplace(
259         thread_state.tid, std::make_unique<ThreadDecoder>(thread_sp, *this));
260   }
261 }
262 
263 bool TraceIntelPT::IsTraced(lldb::tid_t tid) {
264   RefreshLiveProcessState();
265   return m_thread_decoders.count(tid);
266 }
267 
268 // The information here should match the description of the intel-pt section
269 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt
270 // documentation file. Similarly, it should match the CLI help messages of the
271 // TraceIntelPTOptions.td file.
272 const char *TraceIntelPT::GetStartConfigurationHelp() {
273   static Optional<std::string> message;
274   if (!message) {
275     message.emplace(formatv(R"(Parameters:
276 
277   See the jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt for a
278   description of each parameter below.
279 
280   - int traceBufferSize (defaults to {0} bytes):
281     [process and thread tracing]
282 
283   - boolean enableTsc (default to {1}):
284     [process and thread tracing]
285 
286   - int psbPeriod (defaults to {2}):
287     [process and thread tracing]
288 
289   - boolean perCoreTracing (default to {3}):
290     [process tracing only]
291 
292   - int processBufferSizeLimit (defaults to {4} MiB):
293     [process tracing only])",
294                             kDefaultTraceBufferSize, kDefaultEnableTscValue,
295                             kDefaultPsbPeriod, kDefaultPerCoreTracing,
296                             kDefaultProcessBufferSizeLimit / 1024 / 1024));
297   }
298   return message->c_str();
299 }
300 
301 Error TraceIntelPT::Start(size_t trace_buffer_size,
302                           size_t total_buffer_size_limit, bool enable_tsc,
303                           Optional<size_t> psb_period, bool per_core_tracing) {
304   TraceIntelPTStartRequest request;
305   request.trace_buffer_size = trace_buffer_size;
306   request.process_buffer_size_limit = total_buffer_size_limit;
307   request.enable_tsc = enable_tsc;
308   request.psb_period = psb_period.map([](size_t val) { return (int64_t)val; });
309   request.type = GetPluginName().str();
310   request.per_core_tracing = per_core_tracing;
311   return Trace::Start(toJSON(request));
312 }
313 
314 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) {
315   size_t trace_buffer_size = kDefaultTraceBufferSize;
316   size_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
317   bool enable_tsc = kDefaultEnableTscValue;
318   Optional<size_t> psb_period = kDefaultPsbPeriod;
319   bool per_core_tracing = kDefaultPerCoreTracing;
320 
321   if (configuration) {
322     if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
323       dict->GetValueForKeyAsInteger("traceBufferSize", trace_buffer_size);
324       dict->GetValueForKeyAsInteger("processBufferSizeLimit",
325                                     process_buffer_size_limit);
326       dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
327       dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
328       dict->GetValueForKeyAsBoolean("perCoreTracing", per_core_tracing);
329     } else {
330       return createStringError(inconvertibleErrorCode(),
331                                "configuration object is not a dictionary");
332     }
333   }
334 
335   return Start(trace_buffer_size, process_buffer_size_limit, enable_tsc,
336                psb_period, per_core_tracing);
337 }
338 
339 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
340                                 size_t trace_buffer_size, bool enable_tsc,
341                                 Optional<size_t> psb_period) {
342   TraceIntelPTStartRequest request;
343   request.trace_buffer_size = trace_buffer_size;
344   request.enable_tsc = enable_tsc;
345   request.psb_period = psb_period.map([](size_t val) { return (int64_t)val; });
346   request.type = GetPluginName().str();
347   request.tids.emplace();
348   for (lldb::tid_t tid : tids)
349     request.tids->push_back(tid);
350   return Trace::Start(toJSON(request));
351 }
352 
353 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
354                           StructuredData::ObjectSP configuration) {
355   size_t trace_buffer_size = kDefaultTraceBufferSize;
356   bool enable_tsc = kDefaultEnableTscValue;
357   Optional<size_t> psb_period = kDefaultPsbPeriod;
358 
359   if (configuration) {
360     if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
361       dict->GetValueForKeyAsInteger("traceBufferSize", trace_buffer_size);
362       dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
363       dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
364     } else {
365       return createStringError(inconvertibleErrorCode(),
366                                "configuration object is not a dictionary");
367     }
368   }
369 
370   return Start(tids, trace_buffer_size, enable_tsc, psb_period);
371 }
372 
373 Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid,
374                                        OnBinaryDataReadCallback callback) {
375   return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kTraceBuffer, callback);
376 }
377 
378 TaskTimer &TraceIntelPT::GetTimer() { return m_task_timer; }
379