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