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<size_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid, 118 llvm::StringRef kind) { 119 auto it = m_live_thread_data.find(tid); 120 if (it == m_live_thread_data.end()) 121 return None; 122 std::unordered_map<std::string, size_t> &single_thread_data = it->second; 123 auto single_thread_data_it = single_thread_data.find(kind.str()); 124 if (single_thread_data_it == single_thread_data.end()) 125 return None; 126 return single_thread_data_it->second; 127 } 128 129 Optional<size_t> Trace::GetLiveCoreBinaryDataSize(lldb::core_id_t core_id, 130 llvm::StringRef kind) { 131 auto it = m_live_core_data.find(core_id); 132 if (it == m_live_core_data.end()) 133 return None; 134 std::unordered_map<std::string, size_t> &single_core_data = it->second; 135 auto single_thread_data_it = single_core_data.find(kind.str()); 136 if (single_thread_data_it == single_core_data.end()) 137 return None; 138 return single_thread_data_it->second; 139 } 140 141 Optional<size_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) { 142 auto data_it = m_live_process_data.find(kind.str()); 143 if (data_it == m_live_process_data.end()) 144 return None; 145 return data_it->second; 146 } 147 148 Expected<std::vector<uint8_t>> 149 Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) { 150 if (!m_live_process) 151 return createStringError(inconvertibleErrorCode(), 152 "Tracing requires a live process."); 153 llvm::Optional<size_t> size = GetLiveThreadBinaryDataSize(tid, kind); 154 if (!size) 155 return createStringError( 156 inconvertibleErrorCode(), 157 "Tracing data \"%s\" is not available for thread %" PRIu64 ".", 158 kind.data(), tid); 159 160 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), tid, 161 /*core_id=*/None, /*offset=*/0, *size}; 162 return m_live_process->TraceGetBinaryData(request); 163 } 164 165 Expected<std::vector<uint8_t>> 166 Trace::GetLiveCoreBinaryData(lldb::core_id_t core_id, llvm::StringRef kind) { 167 if (!m_live_process) 168 return createStringError(inconvertibleErrorCode(), 169 "Tracing requires a live process."); 170 llvm::Optional<size_t> size = GetLiveCoreBinaryDataSize(core_id, kind); 171 if (!size) 172 return createStringError( 173 inconvertibleErrorCode(), 174 "Tracing data \"%s\" is not available for core_id %" PRIu64 ".", 175 kind.data(), core_id); 176 177 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), 178 /*tid=*/None, core_id, 179 /*offset=*/0, *size}; 180 return m_live_process->TraceGetBinaryData(request); 181 } 182 183 Expected<std::vector<uint8_t>> 184 Trace::GetLiveProcessBinaryData(llvm::StringRef kind) { 185 if (!m_live_process) 186 return createStringError(inconvertibleErrorCode(), 187 "Tracing requires a live process."); 188 llvm::Optional<size_t> size = GetLiveProcessBinaryDataSize(kind); 189 if (!size) 190 return createStringError( 191 inconvertibleErrorCode(), 192 "Tracing data \"%s\" is not available for the process.", kind.data()); 193 194 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), 195 /*tid=*/None, /*core_id*/ None, 196 /*offset=*/0, *size}; 197 return m_live_process->TraceGetBinaryData(request); 198 } 199 200 const char *Trace::RefreshLiveProcessState() { 201 if (!m_live_process) 202 return nullptr; 203 204 uint32_t new_stop_id = m_live_process->GetStopID(); 205 if (new_stop_id == m_stop_id) 206 return nullptr; 207 208 Log *log = GetLog(LLDBLog::Target); 209 LLDB_LOG(log, "Trace::RefreshLiveProcessState invoked"); 210 211 m_stop_id = new_stop_id; 212 m_live_thread_data.clear(); 213 m_live_refresh_error.reset(); 214 m_cores.reset(); 215 216 auto HandleError = [&](Error &&err) -> const char * { 217 m_live_refresh_error = toString(std::move(err)); 218 return m_live_refresh_error->c_str(); 219 }; 220 221 Expected<std::string> json_string = GetLiveProcessState(); 222 if (!json_string) 223 return HandleError(json_string.takeError()); 224 225 Expected<TraceGetStateResponse> live_process_state = 226 json::parse<TraceGetStateResponse>(*json_string, "TraceGetStateResponse"); 227 if (!live_process_state) 228 return HandleError(live_process_state.takeError()); 229 230 if (live_process_state->warnings) { 231 for (std::string &warning : *live_process_state->warnings) 232 LLDB_LOG(log, "== Warning when fetching the trace state: {0}", warning); 233 } 234 235 for (const TraceThreadState &thread_state : 236 live_process_state->traced_threads) { 237 for (const TraceBinaryData &item : thread_state.binary_data) 238 m_live_thread_data[thread_state.tid][item.kind] = item.size; 239 } 240 241 LLDB_LOG(log, "== Found {0} threads being traced", 242 live_process_state->traced_threads.size()); 243 244 if (live_process_state->cores) { 245 m_cores.emplace(); 246 for (const TraceCoreState &core_state : *live_process_state->cores) { 247 m_cores->push_back(core_state.core_id); 248 for (const TraceBinaryData &item : core_state.binary_data) 249 m_live_core_data[core_state.core_id][item.kind] = item.size; 250 } 251 LLDB_LOG(log, "== Found {0} cpu cores being traced", 252 live_process_state->cores->size()); 253 } 254 255 256 for (const TraceBinaryData &item : live_process_state->process_binary_data) 257 m_live_process_data[item.kind] = item.size; 258 259 if (Error err = DoRefreshLiveProcessState(std::move(*live_process_state), 260 *json_string)) 261 return HandleError(std::move(err)); 262 263 return nullptr; 264 } 265 266 Trace::Trace(ArrayRef<ProcessSP> postmortem_processes, 267 Optional<std::vector<lldb::core_id_t>> postmortem_cores) { 268 for (ProcessSP process_sp : postmortem_processes) 269 m_postmortem_processes.push_back(process_sp.get()); 270 m_cores = postmortem_cores; 271 } 272 273 Process *Trace::GetLiveProcess() { return m_live_process; } 274 275 ArrayRef<Process *> Trace::GetPostMortemProcesses() { 276 return m_postmortem_processes; 277 } 278 279 std::vector<Process *> Trace::GetAllProcesses() { 280 if (Process *proc = GetLiveProcess()) 281 return {proc}; 282 return GetPostMortemProcesses(); 283 } 284 285 uint32_t Trace::GetStopID() { 286 RefreshLiveProcessState(); 287 return m_stop_id; 288 } 289 290 llvm::Expected<FileSpec> 291 Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) { 292 auto NotFoundError = [&]() { 293 return createStringError( 294 inconvertibleErrorCode(), 295 formatv("The thread with tid={0} doesn't have the tracing data {1}", 296 tid, kind)); 297 }; 298 299 auto it = m_postmortem_thread_data.find(tid); 300 if (it == m_postmortem_thread_data.end()) 301 return NotFoundError(); 302 303 std::unordered_map<std::string, FileSpec> &data_kind_to_file_spec_map = 304 it->second; 305 auto it2 = data_kind_to_file_spec_map.find(kind.str()); 306 if (it2 == data_kind_to_file_spec_map.end()) 307 return NotFoundError(); 308 return it2->second; 309 } 310 311 llvm::Expected<FileSpec> 312 Trace::GetPostMortemCoreDataFile(lldb::core_id_t core_id, 313 llvm::StringRef kind) { 314 auto NotFoundError = [&]() { 315 return createStringError( 316 inconvertibleErrorCode(), 317 formatv("The core with id={0} doesn't have the tracing data {1}", 318 core_id, kind)); 319 }; 320 321 auto it = m_postmortem_core_data.find(core_id); 322 if (it == m_postmortem_core_data.end()) 323 return NotFoundError(); 324 325 std::unordered_map<std::string, FileSpec> &data_kind_to_file_spec_map = 326 it->second; 327 auto it2 = data_kind_to_file_spec_map.find(kind.str()); 328 if (it2 == data_kind_to_file_spec_map.end()) 329 return NotFoundError(); 330 return it2->second; 331 } 332 333 void Trace::SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind, 334 FileSpec file_spec) { 335 m_postmortem_thread_data[tid][kind.str()] = file_spec; 336 } 337 338 void Trace::SetPostMortemCoreDataFile(lldb::core_id_t core_id, 339 llvm::StringRef kind, 340 FileSpec file_spec) { 341 m_postmortem_core_data[core_id][kind.str()] = file_spec; 342 } 343 344 llvm::Error 345 Trace::OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 346 OnBinaryDataReadCallback callback) { 347 Expected<std::vector<uint8_t>> data = GetLiveThreadBinaryData(tid, kind); 348 if (!data) 349 return data.takeError(); 350 return callback(*data); 351 } 352 353 llvm::Error Trace::OnLiveCoreBinaryDataRead(lldb::core_id_t core_id, 354 llvm::StringRef kind, 355 OnBinaryDataReadCallback callback) { 356 Expected<std::vector<uint8_t>> data = GetLiveCoreBinaryData(core_id, kind); 357 if (!data) 358 return data.takeError(); 359 return callback(*data); 360 } 361 362 llvm::Error Trace::OnDataFileRead(FileSpec file, 363 OnBinaryDataReadCallback callback) { 364 ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error = 365 MemoryBuffer::getFile(file.GetPath()); 366 if (std::error_code err = trace_or_error.getError()) 367 return errorCodeToError(err); 368 369 MemoryBuffer &data = **trace_or_error; 370 ArrayRef<uint8_t> array_ref( 371 reinterpret_cast<const uint8_t *>(data.getBufferStart()), 372 data.getBufferSize()); 373 return callback(array_ref); 374 } 375 376 llvm::Error 377 Trace::OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 378 OnBinaryDataReadCallback callback) { 379 Expected<FileSpec> file = GetPostMortemThreadDataFile(tid, kind); 380 if (!file) 381 return file.takeError(); 382 return OnDataFileRead(*file, callback); 383 } 384 385 llvm::Error 386 Trace::OnPostMortemCoreBinaryDataRead(lldb::core_id_t core_id, 387 llvm::StringRef kind, 388 OnBinaryDataReadCallback callback) { 389 Expected<FileSpec> file = GetPostMortemCoreDataFile(core_id, kind); 390 if (!file) 391 return file.takeError(); 392 return OnDataFileRead(*file, callback); 393 } 394 395 llvm::Error Trace::OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 396 OnBinaryDataReadCallback callback) { 397 RefreshLiveProcessState(); 398 if (m_live_process) 399 return OnLiveThreadBinaryDataRead(tid, kind, callback); 400 else 401 return OnPostMortemThreadBinaryDataRead(tid, kind, callback); 402 } 403 404 llvm::Error Trace::OnCoreBinaryDataRead(lldb::core_id_t core_id, 405 llvm::StringRef kind, 406 OnBinaryDataReadCallback callback) { 407 RefreshLiveProcessState(); 408 if (m_live_process) 409 return OnLiveCoreBinaryDataRead(core_id, kind, callback); 410 else 411 return OnPostMortemCoreBinaryDataRead(core_id, kind, callback); 412 } 413 414 ArrayRef<lldb::core_id_t> Trace::GetTracedCores() { 415 RefreshLiveProcessState(); 416 if (m_cores) 417 return *m_cores; 418 return {}; 419 } 420