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