1 //===-- Trace.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 "lldb/Target/Trace.h" 10 11 #include "llvm/Support/Format.h" 12 13 #include "lldb/Core/Module.h" 14 #include "lldb/Core/PluginManager.h" 15 #include "lldb/Symbol/Function.h" 16 #include "lldb/Target/ExecutionContext.h" 17 #include "lldb/Target/Process.h" 18 #include "lldb/Target/SectionLoadList.h" 19 #include "lldb/Target/Thread.h" 20 #include "lldb/Utility/LLDBLog.h" 21 #include "lldb/Utility/Stream.h" 22 #include <optional> 23 24 using namespace lldb; 25 using namespace lldb_private; 26 using namespace llvm; 27 28 // Helper structs used to extract the type of a JSON trace bundle description 29 // object without having to parse the entire object. 30 31 struct JSONSimpleTraceBundleDescription { 32 std::string type; 33 }; 34 35 namespace llvm { 36 namespace json { 37 38 bool fromJSON(const Value &value, JSONSimpleTraceBundleDescription &bundle, 39 Path path) { 40 json::ObjectMapper o(value, path); 41 return o && o.map("type", bundle.type); 42 } 43 44 } // namespace json 45 } // namespace llvm 46 47 /// Helper functions for fetching data in maps and returning Optionals or 48 /// pointers instead of iterators for simplicity. It's worth mentioning that the 49 /// Optionals version can't return the inner data by reference because of 50 /// limitations in move constructors. 51 /// \{ 52 template <typename K, typename V> 53 static std::optional<V> Lookup(DenseMap<K, V> &map, K k) { 54 auto it = map.find(k); 55 if (it == map.end()) 56 return std::nullopt; 57 return it->second; 58 } 59 60 template <typename K, typename V> 61 static V *LookupAsPtr(DenseMap<K, V> &map, K k) { 62 auto it = map.find(k); 63 if (it == map.end()) 64 return nullptr; 65 return &it->second; 66 } 67 68 /// Similar to the methods above but it looks for an item in a map of maps. 69 template <typename K1, typename K2, typename V> 70 static std::optional<V> Lookup(DenseMap<K1, DenseMap<K2, V>> &map, K1 k1, 71 K2 k2) { 72 auto it = map.find(k1); 73 if (it == map.end()) 74 return std::nullopt; 75 return Lookup(it->second, k2); 76 } 77 78 /// Similar to the methods above but it looks for an item in a map of maps. 79 template <typename K1, typename K2, typename V> 80 static V *LookupAsPtr(DenseMap<K1, DenseMap<K2, V>> &map, K1 k1, K2 k2) { 81 auto it = map.find(k1); 82 if (it == map.end()) 83 return nullptr; 84 return LookupAsPtr(it->second, k2); 85 } 86 /// \} 87 88 static Error createInvalidPlugInError(StringRef plugin_name) { 89 return createStringError( 90 std::errc::invalid_argument, 91 "no trace plug-in matches the specified type: \"%s\"", 92 plugin_name.data()); 93 } 94 95 Expected<lldb::TraceSP> 96 Trace::LoadPostMortemTraceFromFile(Debugger &debugger, 97 const FileSpec &trace_description_file) { 98 99 auto buffer_or_error = 100 MemoryBuffer::getFile(trace_description_file.GetPath()); 101 if (!buffer_or_error) { 102 return createStringError(std::errc::invalid_argument, 103 "could not open input file: %s - %s.", 104 trace_description_file.GetPath().c_str(), 105 buffer_or_error.getError().message().c_str()); 106 } 107 108 Expected<json::Value> session_file = 109 json::parse(buffer_or_error.get()->getBuffer().str()); 110 if (!session_file) { 111 return session_file.takeError(); 112 } 113 114 return Trace::FindPluginForPostMortemProcess( 115 debugger, *session_file, 116 trace_description_file.GetDirectory().AsCString()); 117 } 118 119 Expected<lldb::TraceSP> Trace::FindPluginForPostMortemProcess( 120 Debugger &debugger, const json::Value &trace_bundle_description, 121 StringRef bundle_dir) { 122 JSONSimpleTraceBundleDescription json_bundle; 123 json::Path::Root root("traceBundle"); 124 if (!json::fromJSON(trace_bundle_description, json_bundle, root)) 125 return root.getError(); 126 127 if (auto create_callback = 128 PluginManager::GetTraceCreateCallback(json_bundle.type)) 129 return create_callback(trace_bundle_description, bundle_dir, debugger); 130 131 return createInvalidPlugInError(json_bundle.type); 132 } 133 134 Expected<lldb::TraceSP> Trace::FindPluginForLiveProcess(llvm::StringRef name, 135 Process &process) { 136 if (!process.IsLiveDebugSession()) 137 return createStringError(inconvertibleErrorCode(), 138 "Can't trace non-live processes"); 139 140 if (auto create_callback = 141 PluginManager::GetTraceCreateCallbackForLiveProcess(name)) 142 return create_callback(process); 143 144 return createInvalidPlugInError(name); 145 } 146 147 Expected<StringRef> Trace::FindPluginSchema(StringRef name) { 148 StringRef schema = PluginManager::GetTraceSchema(name); 149 if (!schema.empty()) 150 return schema; 151 152 return createInvalidPlugInError(name); 153 } 154 155 Error Trace::Start(const llvm::json::Value &request) { 156 if (!m_live_process) 157 return createStringError( 158 inconvertibleErrorCode(), 159 "Attempted to start tracing without a live process."); 160 return m_live_process->TraceStart(request); 161 } 162 163 Error Trace::Stop() { 164 if (!m_live_process) 165 return createStringError( 166 inconvertibleErrorCode(), 167 "Attempted to stop tracing without a live process."); 168 return m_live_process->TraceStop(TraceStopRequest(GetPluginName())); 169 } 170 171 Error Trace::Stop(llvm::ArrayRef<lldb::tid_t> tids) { 172 if (!m_live_process) 173 return createStringError( 174 inconvertibleErrorCode(), 175 "Attempted to stop tracing without a live process."); 176 return m_live_process->TraceStop(TraceStopRequest(GetPluginName(), tids)); 177 } 178 179 Expected<std::string> Trace::GetLiveProcessState() { 180 if (!m_live_process) 181 return createStringError( 182 inconvertibleErrorCode(), 183 "Attempted to fetch live trace information without a live process."); 184 return m_live_process->TraceGetState(GetPluginName()); 185 } 186 187 std::optional<uint64_t> 188 Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid, llvm::StringRef kind) { 189 Storage &storage = GetUpdatedStorage(); 190 return Lookup(storage.live_thread_data, tid, ConstString(kind)); 191 } 192 193 std::optional<uint64_t> Trace::GetLiveCpuBinaryDataSize(lldb::cpu_id_t cpu_id, 194 llvm::StringRef kind) { 195 Storage &storage = GetUpdatedStorage(); 196 return Lookup(storage.live_cpu_data_sizes, cpu_id, ConstString(kind)); 197 } 198 199 std::optional<uint64_t> 200 Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) { 201 Storage &storage = GetUpdatedStorage(); 202 return Lookup(storage.live_process_data, ConstString(kind)); 203 } 204 205 Expected<std::vector<uint8_t>> 206 Trace::GetLiveTraceBinaryData(const TraceGetBinaryDataRequest &request, 207 uint64_t expected_size) { 208 if (!m_live_process) 209 return createStringError( 210 inconvertibleErrorCode(), 211 formatv("Attempted to fetch live trace data without a live process. " 212 "Data kind = {0}, tid = {1}, cpu id = {2}.", 213 request.kind, request.tid, request.cpu_id)); 214 215 Expected<std::vector<uint8_t>> data = 216 m_live_process->TraceGetBinaryData(request); 217 218 if (!data) 219 return data.takeError(); 220 221 if (data->size() != expected_size) 222 return createStringError( 223 inconvertibleErrorCode(), 224 formatv("Got incomplete live trace data. Data kind = {0}, expected " 225 "size = {1}, actual size = {2}, tid = {3}, cpu id = {4}", 226 request.kind, expected_size, data->size(), request.tid, 227 request.cpu_id)); 228 229 return data; 230 } 231 232 Expected<std::vector<uint8_t>> 233 Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) { 234 std::optional<uint64_t> size = GetLiveThreadBinaryDataSize(tid, kind); 235 if (!size) 236 return createStringError( 237 inconvertibleErrorCode(), 238 "Tracing data \"%s\" is not available for thread %" PRIu64 ".", 239 kind.data(), tid); 240 241 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), tid, 242 /*cpu_id=*/std::nullopt}; 243 return GetLiveTraceBinaryData(request, *size); 244 } 245 246 Expected<std::vector<uint8_t>> 247 Trace::GetLiveCpuBinaryData(lldb::cpu_id_t cpu_id, llvm::StringRef kind) { 248 if (!m_live_process) 249 return createStringError( 250 inconvertibleErrorCode(), 251 "Attempted to fetch live cpu data without a live process."); 252 std::optional<uint64_t> size = GetLiveCpuBinaryDataSize(cpu_id, kind); 253 if (!size) 254 return createStringError( 255 inconvertibleErrorCode(), 256 "Tracing data \"%s\" is not available for cpu_id %" PRIu64 ".", 257 kind.data(), cpu_id); 258 259 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), 260 /*tid=*/std::nullopt, cpu_id}; 261 return m_live_process->TraceGetBinaryData(request); 262 } 263 264 Expected<std::vector<uint8_t>> 265 Trace::GetLiveProcessBinaryData(llvm::StringRef kind) { 266 std::optional<uint64_t> size = GetLiveProcessBinaryDataSize(kind); 267 if (!size) 268 return createStringError( 269 inconvertibleErrorCode(), 270 "Tracing data \"%s\" is not available for the process.", kind.data()); 271 272 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), 273 /*tid=*/std::nullopt, 274 /*cpu_id*/ std::nullopt}; 275 return GetLiveTraceBinaryData(request, *size); 276 } 277 278 Trace::Storage &Trace::GetUpdatedStorage() { 279 RefreshLiveProcessState(); 280 return m_storage; 281 } 282 283 const char *Trace::RefreshLiveProcessState() { 284 if (!m_live_process) 285 return nullptr; 286 287 uint32_t new_stop_id = m_live_process->GetStopID(); 288 if (new_stop_id == m_stop_id) 289 return nullptr; 290 291 Log *log = GetLog(LLDBLog::Target); 292 LLDB_LOG(log, "Trace::RefreshLiveProcessState invoked"); 293 294 m_stop_id = new_stop_id; 295 m_storage = Trace::Storage(); 296 297 auto do_refresh = [&]() -> Error { 298 Expected<std::string> json_string = GetLiveProcessState(); 299 if (!json_string) 300 return json_string.takeError(); 301 302 Expected<TraceGetStateResponse> live_process_state = 303 json::parse<TraceGetStateResponse>(*json_string, 304 "TraceGetStateResponse"); 305 if (!live_process_state) 306 return live_process_state.takeError(); 307 308 if (live_process_state->warnings) { 309 for (std::string &warning : *live_process_state->warnings) 310 LLDB_LOG(log, "== Warning when fetching the trace state: {0}", warning); 311 } 312 313 for (const TraceThreadState &thread_state : 314 live_process_state->traced_threads) { 315 for (const TraceBinaryData &item : thread_state.binary_data) 316 m_storage.live_thread_data[thread_state.tid].insert( 317 {ConstString(item.kind), item.size}); 318 } 319 320 LLDB_LOG(log, "== Found {0} threads being traced", 321 live_process_state->traced_threads.size()); 322 323 if (live_process_state->cpus) { 324 m_storage.cpus.emplace(); 325 for (const TraceCpuState &cpu_state : *live_process_state->cpus) { 326 m_storage.cpus->push_back(cpu_state.id); 327 for (const TraceBinaryData &item : cpu_state.binary_data) 328 m_storage.live_cpu_data_sizes[cpu_state.id].insert( 329 {ConstString(item.kind), item.size}); 330 } 331 LLDB_LOG(log, "== Found {0} cpu cpus being traced", 332 live_process_state->cpus->size()); 333 } 334 335 for (const TraceBinaryData &item : live_process_state->process_binary_data) 336 m_storage.live_process_data.insert({ConstString(item.kind), item.size}); 337 338 return DoRefreshLiveProcessState(std::move(*live_process_state), 339 *json_string); 340 }; 341 342 if (Error err = do_refresh()) { 343 m_storage.live_refresh_error = toString(std::move(err)); 344 return m_storage.live_refresh_error->c_str(); 345 } 346 347 return nullptr; 348 } 349 350 Trace::Trace(ArrayRef<ProcessSP> postmortem_processes, 351 std::optional<std::vector<lldb::cpu_id_t>> postmortem_cpus) { 352 for (ProcessSP process_sp : postmortem_processes) 353 m_storage.postmortem_processes.push_back(process_sp.get()); 354 m_storage.cpus = postmortem_cpus; 355 } 356 357 Process *Trace::GetLiveProcess() { return m_live_process; } 358 359 ArrayRef<Process *> Trace::GetPostMortemProcesses() { 360 return m_storage.postmortem_processes; 361 } 362 363 std::vector<Process *> Trace::GetAllProcesses() { 364 if (Process *proc = GetLiveProcess()) 365 return {proc}; 366 return GetPostMortemProcesses(); 367 } 368 369 uint32_t Trace::GetStopID() { 370 RefreshLiveProcessState(); 371 return m_stop_id; 372 } 373 374 llvm::Expected<FileSpec> 375 Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) { 376 Storage &storage = GetUpdatedStorage(); 377 if (std::optional<FileSpec> file = 378 Lookup(storage.postmortem_thread_data, tid, ConstString(kind))) 379 return *file; 380 else 381 return createStringError( 382 inconvertibleErrorCode(), 383 formatv("The thread with tid={0} doesn't have the tracing data {1}", 384 tid, kind)); 385 } 386 387 llvm::Expected<FileSpec> Trace::GetPostMortemCpuDataFile(lldb::cpu_id_t cpu_id, 388 llvm::StringRef kind) { 389 Storage &storage = GetUpdatedStorage(); 390 if (std::optional<FileSpec> file = 391 Lookup(storage.postmortem_cpu_data, cpu_id, ConstString(kind))) 392 return *file; 393 else 394 return createStringError( 395 inconvertibleErrorCode(), 396 formatv("The cpu with id={0} doesn't have the tracing data {1}", cpu_id, 397 kind)); 398 } 399 400 void Trace::SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind, 401 FileSpec file_spec) { 402 Storage &storage = GetUpdatedStorage(); 403 storage.postmortem_thread_data[tid].insert({ConstString(kind), file_spec}); 404 } 405 406 void Trace::SetPostMortemCpuDataFile(lldb::cpu_id_t cpu_id, 407 llvm::StringRef kind, FileSpec file_spec) { 408 Storage &storage = GetUpdatedStorage(); 409 storage.postmortem_cpu_data[cpu_id].insert({ConstString(kind), file_spec}); 410 } 411 412 llvm::Error 413 Trace::OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 414 OnBinaryDataReadCallback callback) { 415 Expected<std::vector<uint8_t>> data = GetLiveThreadBinaryData(tid, kind); 416 if (!data) 417 return data.takeError(); 418 return callback(*data); 419 } 420 421 llvm::Error Trace::OnLiveCpuBinaryDataRead(lldb::cpu_id_t cpu_id, 422 llvm::StringRef kind, 423 OnBinaryDataReadCallback callback) { 424 Storage &storage = GetUpdatedStorage(); 425 if (std::vector<uint8_t> *cpu_data = 426 LookupAsPtr(storage.live_cpu_data, cpu_id, ConstString(kind))) 427 return callback(*cpu_data); 428 429 Expected<std::vector<uint8_t>> data = GetLiveCpuBinaryData(cpu_id, kind); 430 if (!data) 431 return data.takeError(); 432 auto it = storage.live_cpu_data[cpu_id].insert( 433 {ConstString(kind), std::move(*data)}); 434 return callback(it.first->second); 435 } 436 437 llvm::Error Trace::OnDataFileRead(FileSpec file, 438 OnBinaryDataReadCallback callback) { 439 ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error = 440 MemoryBuffer::getFile(file.GetPath()); 441 if (std::error_code err = trace_or_error.getError()) 442 return createStringError( 443 inconvertibleErrorCode(), "Failed fetching trace-related file %s. %s", 444 file.GetPath().c_str(), toString(errorCodeToError(err)).c_str()); 445 446 MemoryBuffer &data = **trace_or_error; 447 ArrayRef<uint8_t> array_ref( 448 reinterpret_cast<const uint8_t *>(data.getBufferStart()), 449 data.getBufferSize()); 450 return callback(array_ref); 451 } 452 453 llvm::Error 454 Trace::OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 455 OnBinaryDataReadCallback callback) { 456 if (Expected<FileSpec> file = GetPostMortemThreadDataFile(tid, kind)) 457 return OnDataFileRead(*file, callback); 458 else 459 return file.takeError(); 460 } 461 462 llvm::Error 463 Trace::OnPostMortemCpuBinaryDataRead(lldb::cpu_id_t cpu_id, 464 llvm::StringRef kind, 465 OnBinaryDataReadCallback callback) { 466 if (Expected<FileSpec> file = GetPostMortemCpuDataFile(cpu_id, kind)) 467 return OnDataFileRead(*file, callback); 468 else 469 return file.takeError(); 470 } 471 472 llvm::Error Trace::OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 473 OnBinaryDataReadCallback callback) { 474 if (m_live_process) 475 return OnLiveThreadBinaryDataRead(tid, kind, callback); 476 else 477 return OnPostMortemThreadBinaryDataRead(tid, kind, callback); 478 } 479 480 llvm::Error 481 Trace::OnAllCpusBinaryDataRead(llvm::StringRef kind, 482 OnCpusBinaryDataReadCallback callback) { 483 DenseMap<cpu_id_t, ArrayRef<uint8_t>> buffers; 484 Storage &storage = GetUpdatedStorage(); 485 if (!storage.cpus) 486 return Error::success(); 487 488 std::function<Error(std::vector<cpu_id_t>::iterator)> process_cpu = 489 [&](std::vector<cpu_id_t>::iterator cpu_id) -> Error { 490 if (cpu_id == storage.cpus->end()) 491 return callback(buffers); 492 493 return OnCpuBinaryDataRead(*cpu_id, kind, 494 [&](ArrayRef<uint8_t> data) -> Error { 495 buffers.try_emplace(*cpu_id, data); 496 auto next_id = cpu_id; 497 next_id++; 498 return process_cpu(next_id); 499 }); 500 }; 501 return process_cpu(storage.cpus->begin()); 502 } 503 504 llvm::Error Trace::OnCpuBinaryDataRead(lldb::cpu_id_t cpu_id, 505 llvm::StringRef kind, 506 OnBinaryDataReadCallback callback) { 507 if (m_live_process) 508 return OnLiveCpuBinaryDataRead(cpu_id, kind, callback); 509 else 510 return OnPostMortemCpuBinaryDataRead(cpu_id, kind, callback); 511 } 512 513 ArrayRef<lldb::cpu_id_t> Trace::GetTracedCpus() { 514 Storage &storage = GetUpdatedStorage(); 515 if (storage.cpus) 516 return *storage.cpus; 517 return {}; 518 } 519 520 std::vector<Process *> Trace::GetTracedProcesses() { 521 std::vector<Process *> processes; 522 Storage &storage = GetUpdatedStorage(); 523 524 for (Process *proc : storage.postmortem_processes) 525 processes.push_back(proc); 526 527 if (m_live_process) 528 processes.push_back(m_live_process); 529 return processes; 530 } 531