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 template <typename K1, typename K2, typename V> 67 static Optional<V> Lookup2(DenseMap<K1, DenseMap<K2, V>> &map, K1 k1, K2 k2) { 68 auto it = map.find(k1); 69 if (it == map.end()) 70 return None; 71 return Lookup(it->second, k2); 72 } 73 74 template <typename K1, typename K2, typename V> 75 static V *Lookup2AsPtr(DenseMap<K1, DenseMap<K2, V>> &map, K1 k1, K2 k2) { 76 auto it = map.find(k1); 77 if (it == map.end()) 78 return nullptr; 79 return LookupAsPtr(it->second, k2); 80 } 81 /// \} 82 83 static Error createInvalidPlugInError(StringRef plugin_name) { 84 return createStringError( 85 std::errc::invalid_argument, 86 "no trace plug-in matches the specified type: \"%s\"", 87 plugin_name.data()); 88 } 89 90 Expected<lldb::TraceSP> 91 Trace::FindPluginForPostMortemProcess(Debugger &debugger, 92 const json::Value &trace_session_file, 93 StringRef session_file_dir) { 94 JSONSimpleTraceSession json_session; 95 json::Path::Root root("traceSession"); 96 if (!json::fromJSON(trace_session_file, json_session, root)) 97 return root.getError(); 98 99 if (auto create_callback = 100 PluginManager::GetTraceCreateCallback(json_session.type)) 101 return create_callback(trace_session_file, session_file_dir, debugger); 102 103 return createInvalidPlugInError(json_session.type); 104 } 105 106 Expected<lldb::TraceSP> Trace::FindPluginForLiveProcess(llvm::StringRef name, 107 Process &process) { 108 if (!process.IsLiveDebugSession()) 109 return createStringError(inconvertibleErrorCode(), 110 "Can't trace non-live processes"); 111 112 if (auto create_callback = 113 PluginManager::GetTraceCreateCallbackForLiveProcess(name)) 114 return create_callback(process); 115 116 return createInvalidPlugInError(name); 117 } 118 119 Expected<StringRef> Trace::FindPluginSchema(StringRef name) { 120 StringRef schema = PluginManager::GetTraceSchema(name); 121 if (!schema.empty()) 122 return schema; 123 124 return createInvalidPlugInError(name); 125 } 126 127 Error Trace::Start(const llvm::json::Value &request) { 128 if (!m_live_process) 129 return createStringError( 130 inconvertibleErrorCode(), 131 "Attempted to start tracing without a live process."); 132 return m_live_process->TraceStart(request); 133 } 134 135 Error Trace::Stop() { 136 if (!m_live_process) 137 return createStringError( 138 inconvertibleErrorCode(), 139 "Attempted to stop tracing without a live process."); 140 return m_live_process->TraceStop(TraceStopRequest(GetPluginName())); 141 } 142 143 Error Trace::Stop(llvm::ArrayRef<lldb::tid_t> tids) { 144 if (!m_live_process) 145 return createStringError( 146 inconvertibleErrorCode(), 147 "Attempted to stop tracing without a live process."); 148 return m_live_process->TraceStop(TraceStopRequest(GetPluginName(), tids)); 149 } 150 151 Expected<std::string> Trace::GetLiveProcessState() { 152 if (!m_live_process) 153 return createStringError( 154 inconvertibleErrorCode(), 155 "Attempted to fetch live trace information without a live process."); 156 return m_live_process->TraceGetState(GetPluginName()); 157 } 158 159 Optional<uint64_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid, 160 llvm::StringRef kind) { 161 Storage &storage = GetUpdatedStorage(); 162 return Lookup2(storage.live_thread_data, tid, ConstString(kind)); 163 } 164 165 Optional<uint64_t> Trace::GetLiveCoreBinaryDataSize(lldb::core_id_t core_id, 166 llvm::StringRef kind) { 167 Storage &storage = GetUpdatedStorage(); 168 return Lookup2(storage.live_core_data_sizes, core_id, ConstString(kind)); 169 } 170 171 Optional<uint64_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) { 172 Storage &storage = GetUpdatedStorage(); 173 return Lookup(storage.live_process_data, ConstString(kind)); 174 } 175 176 Expected<std::vector<uint8_t>> 177 Trace::GetLiveTraceBinaryData(const TraceGetBinaryDataRequest &request, 178 uint64_t expected_size) { 179 if (!m_live_process) 180 return createStringError( 181 inconvertibleErrorCode(), 182 formatv("Attempted to fetch live trace data without a live process. " 183 "Data kind = {0}, tid = {1}, core id = {2}.", 184 request.kind, request.tid, request.core_id)); 185 186 Expected<std::vector<uint8_t>> data = 187 m_live_process->TraceGetBinaryData(request); 188 189 if (!data) 190 return data.takeError(); 191 192 if (data->size() != expected_size) 193 return createStringError( 194 inconvertibleErrorCode(), 195 formatv("Got incomplete live trace data. Data kind = {0}, expected " 196 "size = {1}, actual size = {2}, tid = {3}, core id = {4}", 197 request.kind, expected_size, data->size(), request.tid, 198 request.core_id)); 199 200 return data; 201 } 202 203 Expected<std::vector<uint8_t>> 204 Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) { 205 llvm::Optional<uint64_t> size = GetLiveThreadBinaryDataSize(tid, kind); 206 if (!size) 207 return createStringError( 208 inconvertibleErrorCode(), 209 "Tracing data \"%s\" is not available for thread %" PRIu64 ".", 210 kind.data(), tid); 211 212 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), tid, 213 /*core_id=*/None}; 214 return GetLiveTraceBinaryData(request, *size); 215 } 216 217 Expected<std::vector<uint8_t>> 218 Trace::GetLiveCoreBinaryData(lldb::core_id_t core_id, llvm::StringRef kind) { 219 if (!m_live_process) 220 return createStringError( 221 inconvertibleErrorCode(), 222 "Attempted to fetch live cpu data without a live process."); 223 llvm::Optional<uint64_t> size = GetLiveCoreBinaryDataSize(core_id, kind); 224 if (!size) 225 return createStringError( 226 inconvertibleErrorCode(), 227 "Tracing data \"%s\" is not available for core_id %" PRIu64 ".", 228 kind.data(), core_id); 229 230 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), 231 /*tid=*/None, core_id}; 232 return m_live_process->TraceGetBinaryData(request); 233 } 234 235 Expected<std::vector<uint8_t>> 236 Trace::GetLiveProcessBinaryData(llvm::StringRef kind) { 237 llvm::Optional<uint64_t> size = GetLiveProcessBinaryDataSize(kind); 238 if (!size) 239 return createStringError( 240 inconvertibleErrorCode(), 241 "Tracing data \"%s\" is not available for the process.", kind.data()); 242 243 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), 244 /*tid=*/None, /*core_id*/ None}; 245 return GetLiveTraceBinaryData(request, *size); 246 } 247 248 Trace::Storage &Trace::GetUpdatedStorage() { 249 RefreshLiveProcessState(); 250 return m_storage; 251 } 252 253 const char *Trace::RefreshLiveProcessState() { 254 if (!m_live_process) 255 return nullptr; 256 257 uint32_t new_stop_id = m_live_process->GetStopID(); 258 if (new_stop_id == m_stop_id) 259 return nullptr; 260 261 Log *log = GetLog(LLDBLog::Target); 262 LLDB_LOG(log, "Trace::RefreshLiveProcessState invoked"); 263 264 m_stop_id = new_stop_id; 265 m_storage = Trace::Storage(); 266 267 auto do_refresh = [&]() -> Error { 268 Expected<std::string> json_string = GetLiveProcessState(); 269 if (!json_string) 270 return json_string.takeError(); 271 272 Expected<TraceGetStateResponse> live_process_state = 273 json::parse<TraceGetStateResponse>(*json_string, 274 "TraceGetStateResponse"); 275 if (!live_process_state) 276 return live_process_state.takeError(); 277 278 if (live_process_state->warnings) { 279 for (std::string &warning : *live_process_state->warnings) 280 LLDB_LOG(log, "== Warning when fetching the trace state: {0}", warning); 281 } 282 283 for (const TraceThreadState &thread_state : 284 live_process_state->traced_threads) { 285 for (const TraceBinaryData &item : thread_state.binary_data) 286 m_storage.live_thread_data[thread_state.tid].insert( 287 {ConstString(item.kind), item.size}); 288 } 289 290 LLDB_LOG(log, "== Found {0} threads being traced", 291 live_process_state->traced_threads.size()); 292 293 if (live_process_state->cores) { 294 m_storage.cores.emplace(); 295 for (const TraceCoreState &core_state : *live_process_state->cores) { 296 m_storage.cores->push_back(core_state.core_id); 297 for (const TraceBinaryData &item : core_state.binary_data) 298 m_storage.live_core_data_sizes[core_state.core_id].insert( 299 {ConstString(item.kind), item.size}); 300 } 301 LLDB_LOG(log, "== Found {0} cpu cores being traced", 302 live_process_state->cores->size()); 303 } 304 305 for (const TraceBinaryData &item : live_process_state->process_binary_data) 306 m_storage.live_process_data.insert({ConstString(item.kind), item.size}); 307 308 return DoRefreshLiveProcessState(std::move(*live_process_state), 309 *json_string); 310 }; 311 312 if (Error err = do_refresh()) { 313 m_storage.live_refresh_error = toString(std::move(err)); 314 return m_storage.live_refresh_error->c_str(); 315 } 316 317 return nullptr; 318 } 319 320 Trace::Trace(ArrayRef<ProcessSP> postmortem_processes, 321 Optional<std::vector<lldb::core_id_t>> postmortem_cores) { 322 for (ProcessSP process_sp : postmortem_processes) 323 m_storage.postmortem_processes.push_back(process_sp.get()); 324 m_storage.cores = postmortem_cores; 325 } 326 327 Process *Trace::GetLiveProcess() { return m_live_process; } 328 329 ArrayRef<Process *> Trace::GetPostMortemProcesses() { 330 return m_storage.postmortem_processes; 331 } 332 333 std::vector<Process *> Trace::GetAllProcesses() { 334 if (Process *proc = GetLiveProcess()) 335 return {proc}; 336 return GetPostMortemProcesses(); 337 } 338 339 uint32_t Trace::GetStopID() { 340 RefreshLiveProcessState(); 341 return m_stop_id; 342 } 343 344 llvm::Expected<FileSpec> 345 Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) { 346 Storage &storage = GetUpdatedStorage(); 347 if (Optional<FileSpec> file = 348 Lookup2(storage.postmortem_thread_data, tid, ConstString(kind))) 349 return *file; 350 else 351 return createStringError( 352 inconvertibleErrorCode(), 353 formatv("The thread with tid={0} doesn't have the tracing data {1}", 354 tid, kind)); 355 } 356 357 llvm::Expected<FileSpec> 358 Trace::GetPostMortemCoreDataFile(lldb::core_id_t core_id, 359 llvm::StringRef kind) { 360 Storage &storage = GetUpdatedStorage(); 361 if (Optional<FileSpec> file = 362 Lookup2(storage.postmortem_core_data, core_id, ConstString(kind))) 363 return *file; 364 else 365 return createStringError( 366 inconvertibleErrorCode(), 367 formatv("The core with id={0} doesn't have the tracing data {1}", 368 core_id, kind)); 369 } 370 371 void Trace::SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind, 372 FileSpec file_spec) { 373 Storage &storage = GetUpdatedStorage(); 374 storage.postmortem_thread_data[tid].insert({ConstString(kind), file_spec}); 375 } 376 377 void Trace::SetPostMortemCoreDataFile(lldb::core_id_t core_id, 378 llvm::StringRef kind, 379 FileSpec file_spec) { 380 Storage &storage = GetUpdatedStorage(); 381 storage.postmortem_core_data[core_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::OnLiveCoreBinaryDataRead(lldb::core_id_t core_id, 394 llvm::StringRef kind, 395 OnBinaryDataReadCallback callback) { 396 Storage &storage = GetUpdatedStorage(); 397 if (std::vector<uint8_t> *core_data = 398 Lookup2AsPtr(storage.live_core_data, core_id, ConstString(kind))) 399 return callback(*core_data); 400 401 Expected<std::vector<uint8_t>> data = GetLiveCoreBinaryData(core_id, kind); 402 if (!data) 403 return data.takeError(); 404 auto it = storage.live_core_data[core_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::OnPostMortemCoreBinaryDataRead(lldb::core_id_t core_id, 436 llvm::StringRef kind, 437 OnBinaryDataReadCallback callback) { 438 if (Expected<FileSpec> file = GetPostMortemCoreDataFile(core_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::OnAllCoresBinaryDataRead(llvm::StringRef kind, 454 OnCoresBinaryDataReadCallback callback) { 455 DenseMap<core_id_t, ArrayRef<uint8_t>> buffers; 456 Storage &storage = GetUpdatedStorage(); 457 if (!storage.cores) 458 return Error::success(); 459 460 std::function<Error(std::vector<core_id_t>::iterator)> process_core = 461 [&](std::vector<core_id_t>::iterator core_id) -> Error { 462 if (core_id == storage.cores->end()) 463 return callback(buffers); 464 465 return OnCoreBinaryDataRead(*core_id, kind, 466 [&](ArrayRef<uint8_t> data) -> Error { 467 buffers.try_emplace(*core_id, data); 468 auto next_id = core_id; 469 next_id++; 470 return process_core(next_id); 471 }); 472 }; 473 return process_core(storage.cores->begin()); 474 } 475 476 llvm::Error Trace::OnCoreBinaryDataRead(lldb::core_id_t core_id, 477 llvm::StringRef kind, 478 OnBinaryDataReadCallback callback) { 479 if (m_live_process) 480 return OnLiveCoreBinaryDataRead(core_id, kind, callback); 481 else 482 return OnPostMortemCoreBinaryDataRead(core_id, kind, callback); 483 } 484 485 ArrayRef<lldb::core_id_t> Trace::GetTracedCores() { 486 Storage &storage = GetUpdatedStorage(); 487 if (storage.cores) 488 return *storage.cores; 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