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