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