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 static Error createInvalidPlugInError(StringRef plugin_name) { 46 return createStringError( 47 std::errc::invalid_argument, 48 "no trace plug-in matches the specified type: \"%s\"", 49 plugin_name.data()); 50 } 51 52 Expected<lldb::TraceSP> 53 Trace::FindPluginForPostMortemProcess(Debugger &debugger, 54 const json::Value &trace_session_file, 55 StringRef session_file_dir) { 56 JSONSimpleTraceSession json_session; 57 json::Path::Root root("traceSession"); 58 if (!json::fromJSON(trace_session_file, json_session, root)) 59 return root.getError(); 60 61 if (auto create_callback = 62 PluginManager::GetTraceCreateCallback(json_session.type)) 63 return create_callback(trace_session_file, session_file_dir, debugger); 64 65 return createInvalidPlugInError(json_session.type); 66 } 67 68 Expected<lldb::TraceSP> Trace::FindPluginForLiveProcess(llvm::StringRef name, 69 Process &process) { 70 if (!process.IsLiveDebugSession()) 71 return createStringError(inconvertibleErrorCode(), 72 "Can't trace non-live processes"); 73 74 if (auto create_callback = 75 PluginManager::GetTraceCreateCallbackForLiveProcess(name)) 76 return create_callback(process); 77 78 return createInvalidPlugInError(name); 79 } 80 81 Expected<StringRef> Trace::FindPluginSchema(StringRef name) { 82 StringRef schema = PluginManager::GetTraceSchema(name); 83 if (!schema.empty()) 84 return schema; 85 86 return createInvalidPlugInError(name); 87 } 88 89 Error Trace::Start(const llvm::json::Value &request) { 90 if (!m_live_process) 91 return createStringError(inconvertibleErrorCode(), 92 "Tracing requires a live process."); 93 return m_live_process->TraceStart(request); 94 } 95 96 Error Trace::Stop() { 97 if (!m_live_process) 98 return createStringError(inconvertibleErrorCode(), 99 "Tracing requires a live process."); 100 return m_live_process->TraceStop(TraceStopRequest(GetPluginName())); 101 } 102 103 Error Trace::Stop(llvm::ArrayRef<lldb::tid_t> tids) { 104 if (!m_live_process) 105 return createStringError(inconvertibleErrorCode(), 106 "Tracing requires a live process."); 107 return m_live_process->TraceStop(TraceStopRequest(GetPluginName(), tids)); 108 } 109 110 Expected<std::string> Trace::GetLiveProcessState() { 111 if (!m_live_process) 112 return createStringError(inconvertibleErrorCode(), 113 "Tracing requires a live process."); 114 return m_live_process->TraceGetState(GetPluginName()); 115 } 116 117 Optional<uint64_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid, 118 llvm::StringRef kind) { 119 Storage &storage = GetUpdatedStorage(); 120 auto it = storage.live_thread_data.find(tid); 121 if (it == storage.live_thread_data.end()) 122 return None; 123 std::unordered_map<std::string, uint64_t> &single_thread_data = it->second; 124 auto single_thread_data_it = single_thread_data.find(kind.str()); 125 if (single_thread_data_it == single_thread_data.end()) 126 return None; 127 return single_thread_data_it->second; 128 } 129 130 Optional<uint64_t> Trace::GetLiveCoreBinaryDataSize(lldb::core_id_t core_id, 131 llvm::StringRef kind) { 132 Storage &storage = GetUpdatedStorage(); 133 auto it = storage.live_core_data_sizes.find(core_id); 134 if (it == storage.live_core_data_sizes.end()) 135 return None; 136 std::unordered_map<std::string, uint64_t> &single_core_data = it->second; 137 auto single_thread_data_it = single_core_data.find(kind.str()); 138 if (single_thread_data_it == single_core_data.end()) 139 return None; 140 return single_thread_data_it->second; 141 } 142 143 Optional<uint64_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) { 144 Storage &storage = GetUpdatedStorage(); 145 auto data_it = storage.live_process_data.find(kind.str()); 146 if (data_it == storage.live_process_data.end()) 147 return None; 148 return data_it->second; 149 } 150 151 Expected<std::vector<uint8_t>> 152 Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) { 153 if (!m_live_process) 154 return createStringError(inconvertibleErrorCode(), 155 "Tracing requires a live process."); 156 llvm::Optional<uint64_t> size = GetLiveThreadBinaryDataSize(tid, kind); 157 if (!size) 158 return createStringError( 159 inconvertibleErrorCode(), 160 "Tracing data \"%s\" is not available for thread %" PRIu64 ".", 161 kind.data(), tid); 162 163 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), tid, 164 /*core_id=*/None, /*offset=*/0, *size}; 165 return m_live_process->TraceGetBinaryData(request); 166 } 167 168 Expected<std::vector<uint8_t>> 169 Trace::GetLiveCoreBinaryData(lldb::core_id_t core_id, llvm::StringRef kind) { 170 if (!m_live_process) 171 return createStringError(inconvertibleErrorCode(), 172 "Tracing requires a live process."); 173 llvm::Optional<uint64_t> size = GetLiveCoreBinaryDataSize(core_id, kind); 174 if (!size) 175 return createStringError( 176 inconvertibleErrorCode(), 177 "Tracing data \"%s\" is not available for core_id %" PRIu64 ".", 178 kind.data(), core_id); 179 180 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), 181 /*tid=*/None, core_id, 182 /*offset=*/0, *size}; 183 return m_live_process->TraceGetBinaryData(request); 184 } 185 186 Expected<std::vector<uint8_t>> 187 Trace::GetLiveProcessBinaryData(llvm::StringRef kind) { 188 if (!m_live_process) 189 return createStringError(inconvertibleErrorCode(), 190 "Tracing requires a live process."); 191 llvm::Optional<uint64_t> size = GetLiveProcessBinaryDataSize(kind); 192 if (!size) 193 return createStringError( 194 inconvertibleErrorCode(), 195 "Tracing data \"%s\" is not available for the process.", kind.data()); 196 197 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), 198 /*tid=*/None, /*core_id*/ None, 199 /*offset=*/0, *size}; 200 return m_live_process->TraceGetBinaryData(request); 201 } 202 203 Trace::Storage &Trace::GetUpdatedStorage() { 204 RefreshLiveProcessState(); 205 return m_storage; 206 } 207 208 const char *Trace::RefreshLiveProcessState() { 209 if (!m_live_process) 210 return nullptr; 211 212 uint32_t new_stop_id = m_live_process->GetStopID(); 213 if (new_stop_id == m_stop_id) 214 return nullptr; 215 216 Log *log = GetLog(LLDBLog::Target); 217 LLDB_LOG(log, "Trace::RefreshLiveProcessState invoked"); 218 219 m_stop_id = new_stop_id; 220 m_storage = Trace::Storage(); 221 222 auto HandleError = [&](Error &&err) -> const char * { 223 m_storage.live_refresh_error = toString(std::move(err)); 224 return m_storage.live_refresh_error->c_str(); 225 }; 226 227 Expected<std::string> json_string = GetLiveProcessState(); 228 if (!json_string) 229 return HandleError(json_string.takeError()); 230 231 Expected<TraceGetStateResponse> live_process_state = 232 json::parse<TraceGetStateResponse>(*json_string, "TraceGetStateResponse"); 233 if (!live_process_state) 234 return HandleError(live_process_state.takeError()); 235 236 if (live_process_state->warnings) { 237 for (std::string &warning : *live_process_state->warnings) 238 LLDB_LOG(log, "== Warning when fetching the trace state: {0}", warning); 239 } 240 241 for (const TraceThreadState &thread_state : 242 live_process_state->traced_threads) { 243 for (const TraceBinaryData &item : thread_state.binary_data) 244 m_storage.live_thread_data[thread_state.tid][item.kind] = item.size; 245 } 246 247 LLDB_LOG(log, "== Found {0} threads being traced", 248 live_process_state->traced_threads.size()); 249 250 if (live_process_state->cores) { 251 m_storage.cores.emplace(); 252 for (const TraceCoreState &core_state : *live_process_state->cores) { 253 m_storage.cores->push_back(core_state.core_id); 254 for (const TraceBinaryData &item : core_state.binary_data) 255 m_storage.live_core_data_sizes[core_state.core_id][item.kind] = 256 item.size; 257 } 258 LLDB_LOG(log, "== Found {0} cpu cores being traced", 259 live_process_state->cores->size()); 260 } 261 262 263 for (const TraceBinaryData &item : live_process_state->process_binary_data) 264 m_storage.live_process_data[item.kind] = item.size; 265 266 if (Error err = DoRefreshLiveProcessState(std::move(*live_process_state), 267 *json_string)) 268 return HandleError(std::move(err)); 269 270 return nullptr; 271 } 272 273 Trace::Trace(ArrayRef<ProcessSP> postmortem_processes, 274 Optional<std::vector<lldb::core_id_t>> postmortem_cores) { 275 for (ProcessSP process_sp : postmortem_processes) 276 m_storage.postmortem_processes.push_back(process_sp.get()); 277 m_storage.cores = postmortem_cores; 278 } 279 280 Process *Trace::GetLiveProcess() { return m_live_process; } 281 282 ArrayRef<Process *> Trace::GetPostMortemProcesses() { 283 return m_storage.postmortem_processes; 284 } 285 286 std::vector<Process *> Trace::GetAllProcesses() { 287 if (Process *proc = GetLiveProcess()) 288 return {proc}; 289 return GetPostMortemProcesses(); 290 } 291 292 uint32_t Trace::GetStopID() { 293 RefreshLiveProcessState(); 294 return m_stop_id; 295 } 296 297 llvm::Expected<FileSpec> 298 Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) { 299 auto NotFoundError = [&]() { 300 return createStringError( 301 inconvertibleErrorCode(), 302 formatv("The thread with tid={0} doesn't have the tracing data {1}", 303 tid, kind)); 304 }; 305 306 Storage &storage = GetUpdatedStorage(); 307 auto it = storage.postmortem_thread_data.find(tid); 308 if (it == storage.postmortem_thread_data.end()) 309 return NotFoundError(); 310 311 std::unordered_map<std::string, FileSpec> &data_kind_to_file_spec_map = 312 it->second; 313 auto it2 = data_kind_to_file_spec_map.find(kind.str()); 314 if (it2 == data_kind_to_file_spec_map.end()) 315 return NotFoundError(); 316 return it2->second; 317 } 318 319 llvm::Expected<FileSpec> 320 Trace::GetPostMortemCoreDataFile(lldb::core_id_t core_id, 321 llvm::StringRef kind) { 322 auto NotFoundError = [&]() { 323 return createStringError( 324 inconvertibleErrorCode(), 325 formatv("The core with id={0} doesn't have the tracing data {1}", 326 core_id, kind)); 327 }; 328 329 Storage &storage = GetUpdatedStorage(); 330 auto it = storage.postmortem_core_data.find(core_id); 331 if (it == storage.postmortem_core_data.end()) 332 return NotFoundError(); 333 334 std::unordered_map<std::string, FileSpec> &data_kind_to_file_spec_map = 335 it->second; 336 auto it2 = data_kind_to_file_spec_map.find(kind.str()); 337 if (it2 == data_kind_to_file_spec_map.end()) 338 return NotFoundError(); 339 return it2->second; 340 } 341 342 void Trace::SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind, 343 FileSpec file_spec) { 344 Storage &storage = GetUpdatedStorage(); 345 storage.postmortem_thread_data[tid][kind.str()] = file_spec; 346 } 347 348 void Trace::SetPostMortemCoreDataFile(lldb::core_id_t core_id, 349 llvm::StringRef kind, 350 FileSpec file_spec) { 351 Storage &storage = GetUpdatedStorage(); 352 storage.postmortem_core_data[core_id][kind.str()] = file_spec; 353 } 354 355 llvm::Error 356 Trace::OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 357 OnBinaryDataReadCallback callback) { 358 Expected<std::vector<uint8_t>> data = GetLiveThreadBinaryData(tid, kind); 359 if (!data) 360 return data.takeError(); 361 return callback(*data); 362 } 363 364 llvm::Error Trace::OnLiveCoreBinaryDataRead(lldb::core_id_t core_id, 365 llvm::StringRef kind, 366 OnBinaryDataReadCallback callback) { 367 Storage &storage = GetUpdatedStorage(); 368 auto core_data_entries = storage.live_core_data.find(core_id); 369 if (core_data_entries != storage.live_core_data.end()) { 370 auto core_data = core_data_entries->second.find(kind.str()); 371 if (core_data != core_data_entries->second.end()) 372 return callback(core_data->second); 373 } 374 375 Expected<std::vector<uint8_t>> data = GetLiveCoreBinaryData(core_id, kind); 376 if (!data) 377 return data.takeError(); 378 auto it = 379 storage.live_core_data[core_id].insert({kind.str(), std::move(*data)}); 380 return callback(it.first->second); 381 } 382 383 llvm::Error Trace::OnDataFileRead(FileSpec file, 384 OnBinaryDataReadCallback callback) { 385 ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error = 386 MemoryBuffer::getFile(file.GetPath()); 387 if (std::error_code err = trace_or_error.getError()) 388 return createStringError( 389 inconvertibleErrorCode(), "Failed fetching trace-related file %s. %s", 390 file.GetCString(), toString(errorCodeToError(err)).c_str()); 391 392 MemoryBuffer &data = **trace_or_error; 393 ArrayRef<uint8_t> array_ref( 394 reinterpret_cast<const uint8_t *>(data.getBufferStart()), 395 data.getBufferSize()); 396 return callback(array_ref); 397 } 398 399 llvm::Error 400 Trace::OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 401 OnBinaryDataReadCallback callback) { 402 Expected<FileSpec> file = GetPostMortemThreadDataFile(tid, kind); 403 if (!file) 404 return file.takeError(); 405 return OnDataFileRead(*file, callback); 406 } 407 408 llvm::Error 409 Trace::OnPostMortemCoreBinaryDataRead(lldb::core_id_t core_id, 410 llvm::StringRef kind, 411 OnBinaryDataReadCallback callback) { 412 Expected<FileSpec> file = GetPostMortemCoreDataFile(core_id, kind); 413 if (!file) 414 return file.takeError(); 415 return OnDataFileRead(*file, callback); 416 } 417 418 llvm::Error Trace::OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 419 OnBinaryDataReadCallback callback) { 420 if (m_live_process) 421 return OnLiveThreadBinaryDataRead(tid, kind, callback); 422 else 423 return OnPostMortemThreadBinaryDataRead(tid, kind, callback); 424 } 425 426 llvm::Error 427 Trace::OnAllCoresBinaryDataRead(llvm::StringRef kind, 428 OnCoresBinaryDataReadCallback callback) { 429 DenseMap<core_id_t, ArrayRef<uint8_t>> buffers; 430 Storage &storage = GetUpdatedStorage(); 431 if (!storage.cores) 432 return Error::success(); 433 434 std::function<Error(std::vector<core_id_t>::iterator)> process_core = 435 [&](std::vector<core_id_t>::iterator core_id) -> Error { 436 if (core_id == storage.cores->end()) 437 return callback(buffers); 438 439 return OnCoreBinaryDataRead(*core_id, kind, 440 [&](ArrayRef<uint8_t> data) -> Error { 441 buffers.try_emplace(*core_id, data); 442 auto next_id = core_id; 443 next_id++; 444 return process_core(next_id); 445 }); 446 }; 447 return process_core(storage.cores->begin()); 448 } 449 450 llvm::Error Trace::OnCoreBinaryDataRead(lldb::core_id_t core_id, 451 llvm::StringRef kind, 452 OnBinaryDataReadCallback callback) { 453 if (m_live_process) 454 return OnLiveCoreBinaryDataRead(core_id, kind, callback); 455 else 456 return OnPostMortemCoreBinaryDataRead(core_id, kind, callback); 457 } 458 459 ArrayRef<lldb::core_id_t> Trace::GetTracedCores() { 460 Storage &storage = GetUpdatedStorage(); 461 if (storage.cores) 462 return *storage.cores; 463 return {}; 464 } 465 466 std::vector<Process *> Trace::GetTracedProcesses() { 467 std::vector<Process *> processes; 468 Storage &storage = GetUpdatedStorage(); 469 470 for (Process *proc : storage.postmortem_processes) 471 processes.push_back(proc); 472 473 if (m_live_process) 474 processes.push_back(m_live_process); 475 return processes; 476 } 477