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 "TraceIntelPTBundleLoader.h"
16 #include "TraceIntelPTBundleSaver.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                                 CreateInstanceForTraceBundle,
44                                 CreateInstanceForLiveProcess,
45                                 TraceIntelPTBundleLoader::GetSchema());
46 }
47 
48 void TraceIntelPT::Terminate() {
49   PluginManager::UnregisterPlugin(CreateInstanceForTraceBundle);
50 }
51 
52 StringRef TraceIntelPT::GetSchema() {
53   return TraceIntelPTBundleLoader::GetSchema();
54 }
55 
56 void TraceIntelPT::Dump(Stream *s) const {}
57 
58 llvm::Error TraceIntelPT::SaveLiveTraceToDisk(FileSpec directory) {
59   RefreshLiveProcessState();
60   return TraceIntelPTBundleSaver().SaveToDisk(*this, directory);
61 }
62 
63 Expected<TraceSP> TraceIntelPT::CreateInstanceForTraceBundle(
64     const json::Value &bundle_description, StringRef bundle_dir,
65     Debugger &debugger) {
66   return TraceIntelPTBundleLoader(debugger, bundle_description,
67                                        bundle_dir)
68       .Load();
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 TraceIntelPTSP TraceIntelPT::GetSharedPtr() {
78   return std::static_pointer_cast<TraceIntelPT>(shared_from_this());
79 }
80 
81 TraceIntelPTSP TraceIntelPT::CreateInstanceForPostmortemTrace(
82     JSONTraceBundleDescription &bundle_description, ArrayRef<ProcessSP> traced_processes,
83     ArrayRef<ThreadPostMortemTraceSP> traced_threads) {
84   TraceIntelPTSP trace_sp(new TraceIntelPT(bundle_description, traced_processes));
85   trace_sp->m_storage.tsc_conversion = bundle_description.tsc_perf_zero_conversion;
86 
87   if (bundle_description.cpus) {
88     std::vector<cpu_id_t> cpus;
89 
90     for (const JSONCpu &cpu : *bundle_description.cpus) {
91       trace_sp->SetPostMortemCpuDataFile(cpu.id, IntelPTDataKinds::kIptTrace,
92                                          FileSpec(cpu.ipt_trace));
93 
94       trace_sp->SetPostMortemCpuDataFile(
95           cpu.id, IntelPTDataKinds::kPerfContextSwitchTrace,
96           FileSpec(cpu.context_switch_trace));
97       cpus.push_back(cpu.id);
98     }
99 
100     std::vector<tid_t> tids;
101     for (const JSONProcess &process : bundle_description.processes)
102       for (const JSONThread &thread : process.threads)
103         tids.push_back(thread.tid);
104 
105     trace_sp->m_storage.multicpu_decoder.emplace(trace_sp);
106   } else {
107     for (const ThreadPostMortemTraceSP &thread : traced_threads) {
108       trace_sp->m_storage.thread_decoders.try_emplace(
109           thread->GetID(), std::make_unique<ThreadDecoder>(thread, *trace_sp));
110       if (const Optional<FileSpec> &trace_file = thread->GetTraceFile()) {
111         trace_sp->SetPostMortemThreadDataFile(
112             thread->GetID(), IntelPTDataKinds::kIptTrace, *trace_file);
113       }
114     }
115   }
116 
117   for (const ProcessSP &process_sp : traced_processes)
118     process_sp->GetTarget().SetTrace(trace_sp);
119   return trace_sp;
120 }
121 
122 TraceIntelPT::TraceIntelPT(JSONTraceBundleDescription &bundle_description,
123                            ArrayRef<ProcessSP> traced_processes)
124     : Trace(traced_processes, bundle_description.GetCpuIds()),
125       m_cpu_info(bundle_description.cpu_info) {}
126 
127 Expected<DecodedThreadSP> TraceIntelPT::Decode(Thread &thread) {
128   if (const char *error = RefreshLiveProcessState())
129     return createStringError(inconvertibleErrorCode(), error);
130 
131   Storage &storage = GetUpdatedStorage();
132   if (storage.multicpu_decoder)
133     return storage.multicpu_decoder->Decode(thread);
134 
135   auto it = storage.thread_decoders.find(thread.GetID());
136   if (it == storage.thread_decoders.end())
137     return createStringError(inconvertibleErrorCode(), "thread not traced");
138   return it->second->Decode();
139 }
140 
141 llvm::Expected<lldb::TraceCursorUP>
142 TraceIntelPT::CreateNewCursor(Thread &thread) {
143   if (Expected<DecodedThreadSP> decoded_thread = Decode(thread))
144     return decoded_thread.get()->CreateNewCursor();
145   else
146     return decoded_thread.takeError();
147 }
148 
149 void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) {
150   Storage &storage = GetUpdatedStorage();
151 
152   lldb::tid_t tid = thread.GetID();
153   s.Format("\nthread #{0}: tid = {1}", thread.GetIndexID(), thread.GetID());
154   if (!IsTraced(tid)) {
155     s << ", not traced\n";
156     return;
157   }
158   s << "\n";
159 
160   Expected<DecodedThreadSP> decoded_thread_sp_or_err = Decode(thread);
161   if (!decoded_thread_sp_or_err) {
162     s << toString(decoded_thread_sp_or_err.takeError()) << "\n";
163     return;
164   }
165 
166   DecodedThreadSP &decoded_thread_sp = *decoded_thread_sp_or_err;
167 
168   Expected<Optional<uint64_t>> raw_size_or_error = GetRawTraceSize(thread);
169   if (!raw_size_or_error) {
170     s.Format("  {0}\n", toString(raw_size_or_error.takeError()));
171     return;
172   }
173   Optional<uint64_t> raw_size = *raw_size_or_error;
174 
175   /// Instruction stats
176   {
177     uint64_t items_count = decoded_thread_sp->GetItemsCount();
178     uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage();
179 
180     s.Format("  Total number of trace items: {0}\n", items_count);
181 
182     s << "\n  Memory usage:\n";
183     if (raw_size)
184       s.Format("    Raw trace size: {0} KiB\n", *raw_size / 1024);
185 
186     s.Format(
187         "    Total approximate memory usage (excluding raw trace): {0:2} KiB\n",
188         (double)mem_used / 1024);
189     if (items_count != 0)
190       s.Format("    Average memory usage per item (excluding raw trace): "
191                "{0:2} bytes\n",
192                (double)mem_used / items_count);
193   }
194 
195   // Timing
196   {
197     s << "\n  Timing for this thread:\n";
198     auto print_duration = [&](const std::string &name,
199                               std::chrono::milliseconds duration) {
200       s.Format("    {0}: {1:2}s\n", name, duration.count() / 1000.0);
201     };
202     GetTimer().ForThread(tid).ForEachTimedTask(print_duration);
203 
204     s << "\n  Timing for global tasks:\n";
205     GetTimer().ForGlobal().ForEachTimedTask(print_duration);
206   }
207 
208   // Instruction events stats
209   {
210     const DecodedThread::EventsStats &events_stats =
211         decoded_thread_sp->GetEventsStats();
212     s << "\n  Events:\n";
213     s.Format("    Number of individual events: {0}\n",
214              events_stats.total_count);
215     for (const auto &event_to_count : events_stats.events_counts) {
216       s.Format("      {0}: {1}\n",
217                TraceCursor::EventKindToString(event_to_count.first),
218                event_to_count.second);
219     }
220   }
221 
222   if (storage.multicpu_decoder) {
223     s << "\n  Multi-cpu decoding:\n";
224     s.Format("    Total number of continuous executions found: {0}\n",
225              storage.multicpu_decoder->GetTotalContinuousExecutionsCount());
226     s.Format(
227         "    Number of continuous executions for this thread: {0}\n",
228         storage.multicpu_decoder->GetNumContinuousExecutionsForThread(tid));
229   }
230 
231   // Errors
232   {
233     s << "\n  Errors:\n";
234     const DecodedThread::LibiptErrorsStats &tsc_errors_stats =
235         decoded_thread_sp->GetTscErrorsStats();
236     s.Format("    Number of TSC decoding errors: {0}\n",
237              tsc_errors_stats.total_count);
238     for (const auto &error_message_to_count :
239          tsc_errors_stats.libipt_errors_counts) {
240       s.Format("      {0}: {1}\n", error_message_to_count.first,
241                error_message_to_count.second);
242     }
243   }
244 }
245 
246 llvm::Expected<Optional<uint64_t>>
247 TraceIntelPT::GetRawTraceSize(Thread &thread) {
248   if (GetUpdatedStorage().multicpu_decoder)
249     return None; // TODO: calculate the amount of intel pt raw trace associated
250                  // with the given thread.
251   if (GetLiveProcess())
252     return GetLiveThreadBinaryDataSize(thread.GetID(),
253                                        IntelPTDataKinds::kIptTrace);
254   uint64_t size;
255   auto callback = [&](llvm::ArrayRef<uint8_t> data) {
256     size = data.size();
257     return Error::success();
258   };
259   if (Error err = OnThreadBufferRead(thread.GetID(), callback))
260     return std::move(err);
261 
262   return size;
263 }
264 
265 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
266   Expected<std::vector<uint8_t>> cpu_info =
267       GetLiveProcessBinaryData(IntelPTDataKinds::kProcFsCpuInfo);
268   if (!cpu_info)
269     return cpu_info.takeError();
270 
271   int64_t cpu_family = -1;
272   int64_t model = -1;
273   int64_t stepping = -1;
274   std::string vendor_id;
275 
276   StringRef rest(reinterpret_cast<const char *>(cpu_info->data()),
277                  cpu_info->size());
278   while (!rest.empty()) {
279     StringRef line;
280     std::tie(line, rest) = rest.split('\n');
281 
282     SmallVector<StringRef, 2> columns;
283     line.split(columns, StringRef(":"), -1, false);
284 
285     if (columns.size() < 2)
286       continue; // continue searching
287 
288     columns[1] = columns[1].trim(" ");
289     if (columns[0].contains("cpu family") &&
290         columns[1].getAsInteger(10, cpu_family))
291       continue;
292 
293     else if (columns[0].contains("model") && columns[1].getAsInteger(10, model))
294       continue;
295 
296     else if (columns[0].contains("stepping") &&
297              columns[1].getAsInteger(10, stepping))
298       continue;
299 
300     else if (columns[0].contains("vendor_id")) {
301       vendor_id = columns[1].str();
302       if (!vendor_id.empty())
303         continue;
304     }
305 
306     if ((cpu_family != -1) && (model != -1) && (stepping != -1) &&
307         (!vendor_id.empty())) {
308       return pt_cpu{vendor_id == "GenuineIntel" ? pcv_intel : pcv_unknown,
309                     static_cast<uint16_t>(cpu_family),
310                     static_cast<uint8_t>(model),
311                     static_cast<uint8_t>(stepping)};
312     }
313   }
314   return createStringError(inconvertibleErrorCode(),
315                            "Failed parsing the target's /proc/cpuinfo file");
316 }
317 
318 Expected<pt_cpu> TraceIntelPT::GetCPUInfo() {
319   if (!m_cpu_info) {
320     if (llvm::Expected<pt_cpu> cpu_info = GetCPUInfoForLiveProcess())
321       m_cpu_info = *cpu_info;
322     else
323       return cpu_info.takeError();
324   }
325   return *m_cpu_info;
326 }
327 
328 llvm::Optional<LinuxPerfZeroTscConversion>
329 TraceIntelPT::GetPerfZeroTscConversion() {
330   return GetUpdatedStorage().tsc_conversion;
331 }
332 
333 TraceIntelPT::Storage &TraceIntelPT::GetUpdatedStorage() {
334   RefreshLiveProcessState();
335   return m_storage;
336 }
337 
338 Error TraceIntelPT::DoRefreshLiveProcessState(TraceGetStateResponse state,
339                                               StringRef json_response) {
340   m_storage = Storage();
341 
342   Expected<TraceIntelPTGetStateResponse> intelpt_state =
343       json::parse<TraceIntelPTGetStateResponse>(json_response,
344                                                 "TraceIntelPTGetStateResponse");
345   if (!intelpt_state)
346     return intelpt_state.takeError();
347 
348   m_storage.tsc_conversion = intelpt_state->tsc_perf_zero_conversion;
349 
350   if (!intelpt_state->cpus) {
351     for (const TraceThreadState &thread_state : state.traced_threads) {
352       ThreadSP thread_sp =
353           GetLiveProcess()->GetThreadList().FindThreadByID(thread_state.tid);
354       m_storage.thread_decoders.try_emplace(
355           thread_state.tid, std::make_unique<ThreadDecoder>(thread_sp, *this));
356     }
357   } else {
358     std::vector<cpu_id_t> cpus;
359     for (const TraceCpuState &cpu : *intelpt_state->cpus)
360       cpus.push_back(cpu.id);
361 
362     std::vector<tid_t> tids;
363     for (const TraceThreadState &thread : intelpt_state->traced_threads)
364       tids.push_back(thread.tid);
365 
366     if (!intelpt_state->tsc_perf_zero_conversion)
367       return createStringError(inconvertibleErrorCode(),
368                                "Missing perf time_zero conversion values");
369     m_storage.multicpu_decoder.emplace(GetSharedPtr());
370   }
371 
372   if (m_storage.tsc_conversion) {
373     Log *log = GetLog(LLDBLog::Target);
374     LLDB_LOG(log, "TraceIntelPT found TSC conversion information");
375   }
376   return Error::success();
377 }
378 
379 bool TraceIntelPT::IsTraced(lldb::tid_t tid) {
380   Storage &storage = GetUpdatedStorage();
381   if (storage.multicpu_decoder)
382     return storage.multicpu_decoder->TracesThread(tid);
383   return storage.thread_decoders.count(tid);
384 }
385 
386 // The information here should match the description of the intel-pt section
387 // of the jLLDBTraceStart packet in the lldb/docs/lldb-gdb-remote.txt
388 // documentation file. Similarly, it should match the CLI help messages of the
389 // TraceIntelPTOptions.td file.
390 const char *TraceIntelPT::GetStartConfigurationHelp() {
391   static Optional<std::string> message;
392   if (!message) {
393     message.emplace(formatv(R"(Parameters:
394 
395   See the jLLDBTraceStart section in lldb/docs/lldb-gdb-remote.txt for a
396   description of each parameter below.
397 
398   - int iptTraceSize (defaults to {0} bytes):
399     [process and thread tracing]
400 
401   - boolean enableTsc (default to {1}):
402     [process and thread tracing]
403 
404   - int psbPeriod (defaults to {2}):
405     [process and thread tracing]
406 
407   - boolean perCpuTracing (default to {3}):
408     [process tracing only]
409 
410   - int processBufferSizeLimit (defaults to {4} MiB):
411     [process tracing only])",
412                             kDefaultIptTraceSize, kDefaultEnableTscValue,
413                             kDefaultPsbPeriod, kDefaultPerCpuTracing,
414                             kDefaultProcessBufferSizeLimit / 1024 / 1024));
415   }
416   return message->c_str();
417 }
418 
419 Error TraceIntelPT::Start(uint64_t ipt_trace_size,
420                           uint64_t total_buffer_size_limit, bool enable_tsc,
421                           Optional<uint64_t> psb_period, bool per_cpu_tracing) {
422   TraceIntelPTStartRequest request;
423   request.ipt_trace_size = ipt_trace_size;
424   request.process_buffer_size_limit = total_buffer_size_limit;
425   request.enable_tsc = enable_tsc;
426   request.psb_period = psb_period;
427   request.type = GetPluginName().str();
428   request.per_cpu_tracing = per_cpu_tracing;
429   return Trace::Start(toJSON(request));
430 }
431 
432 Error TraceIntelPT::Start(StructuredData::ObjectSP configuration) {
433   uint64_t ipt_trace_size = kDefaultIptTraceSize;
434   uint64_t process_buffer_size_limit = kDefaultProcessBufferSizeLimit;
435   bool enable_tsc = kDefaultEnableTscValue;
436   Optional<uint64_t> psb_period = kDefaultPsbPeriod;
437   bool per_cpu_tracing = kDefaultPerCpuTracing;
438 
439   if (configuration) {
440     if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
441       dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size);
442       dict->GetValueForKeyAsInteger("processBufferSizeLimit",
443                                     process_buffer_size_limit);
444       dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
445       dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
446       dict->GetValueForKeyAsBoolean("perCpuTracing", per_cpu_tracing);
447     } else {
448       return createStringError(inconvertibleErrorCode(),
449                                "configuration object is not a dictionary");
450     }
451   }
452 
453   return Start(ipt_trace_size, process_buffer_size_limit, enable_tsc,
454                psb_period, per_cpu_tracing);
455 }
456 
457 llvm::Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
458                                 uint64_t ipt_trace_size, bool enable_tsc,
459                                 Optional<uint64_t> psb_period) {
460   TraceIntelPTStartRequest request;
461   request.ipt_trace_size = ipt_trace_size;
462   request.enable_tsc = enable_tsc;
463   request.psb_period = psb_period;
464   request.type = GetPluginName().str();
465   request.tids.emplace();
466   for (lldb::tid_t tid : tids)
467     request.tids->push_back(tid);
468   return Trace::Start(toJSON(request));
469 }
470 
471 Error TraceIntelPT::Start(llvm::ArrayRef<lldb::tid_t> tids,
472                           StructuredData::ObjectSP configuration) {
473   uint64_t ipt_trace_size = kDefaultIptTraceSize;
474   bool enable_tsc = kDefaultEnableTscValue;
475   Optional<uint64_t> psb_period = kDefaultPsbPeriod;
476 
477   if (configuration) {
478     if (StructuredData::Dictionary *dict = configuration->GetAsDictionary()) {
479       dict->GetValueForKeyAsInteger("iptTraceSize", ipt_trace_size);
480       dict->GetValueForKeyAsBoolean("enableTsc", enable_tsc);
481       dict->GetValueForKeyAsInteger("psbPeriod", psb_period);
482     } else {
483       return createStringError(inconvertibleErrorCode(),
484                                "configuration object is not a dictionary");
485     }
486   }
487 
488   return Start(tids, ipt_trace_size, enable_tsc, psb_period);
489 }
490 
491 Error TraceIntelPT::OnThreadBufferRead(lldb::tid_t tid,
492                                        OnBinaryDataReadCallback callback) {
493   return OnThreadBinaryDataRead(tid, IntelPTDataKinds::kIptTrace, callback);
494 }
495 
496 TaskTimer &TraceIntelPT::GetTimer() { return GetUpdatedStorage().task_timer; }
497