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