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