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