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