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 JSONSimplePluginSettings { 31 std::string type; 32 }; 33 34 struct JSONSimpleTraceSession { 35 JSONSimplePluginSettings trace; 36 }; 37 38 namespace llvm { 39 namespace json { 40 41 bool fromJSON(const Value &value, JSONSimplePluginSettings &plugin_settings, 42 Path path) { 43 json::ObjectMapper o(value, path); 44 return o && o.map("type", plugin_settings.type); 45 } 46 47 bool fromJSON(const Value &value, JSONSimpleTraceSession &session, Path path) { 48 json::ObjectMapper o(value, path); 49 return o && o.map("trace", session.trace); 50 } 51 52 } // namespace json 53 } // namespace llvm 54 55 static Error createInvalidPlugInError(StringRef plugin_name) { 56 return createStringError( 57 std::errc::invalid_argument, 58 "no trace plug-in matches the specified type: \"%s\"", 59 plugin_name.data()); 60 } 61 62 Expected<lldb::TraceSP> 63 Trace::FindPluginForPostMortemProcess(Debugger &debugger, 64 const json::Value &trace_session_file, 65 StringRef session_file_dir) { 66 JSONSimpleTraceSession json_session; 67 json::Path::Root root("traceSession"); 68 if (!json::fromJSON(trace_session_file, json_session, root)) 69 return root.getError(); 70 71 if (auto create_callback = 72 PluginManager::GetTraceCreateCallback(json_session.trace.type)) 73 return create_callback(trace_session_file, session_file_dir, debugger); 74 75 return createInvalidPlugInError(json_session.trace.type); 76 } 77 78 Expected<lldb::TraceSP> Trace::FindPluginForLiveProcess(llvm::StringRef name, 79 Process &process) { 80 if (!process.IsLiveDebugSession()) 81 return createStringError(inconvertibleErrorCode(), 82 "Can't trace non-live processes"); 83 84 if (auto create_callback = 85 PluginManager::GetTraceCreateCallbackForLiveProcess(name)) 86 return create_callback(process); 87 88 return createInvalidPlugInError(name); 89 } 90 91 Expected<StringRef> Trace::FindPluginSchema(StringRef name) { 92 StringRef schema = PluginManager::GetTraceSchema(name); 93 if (!schema.empty()) 94 return schema; 95 96 return createInvalidPlugInError(name); 97 } 98 99 Error Trace::Start(const llvm::json::Value &request) { 100 if (!m_live_process) 101 return createStringError(inconvertibleErrorCode(), 102 "Tracing requires a live process."); 103 return m_live_process->TraceStart(request); 104 } 105 106 Error Trace::Stop() { 107 if (!m_live_process) 108 return createStringError(inconvertibleErrorCode(), 109 "Tracing requires a live process."); 110 return m_live_process->TraceStop(TraceStopRequest(GetPluginName())); 111 } 112 113 Error Trace::Stop(llvm::ArrayRef<lldb::tid_t> tids) { 114 if (!m_live_process) 115 return createStringError(inconvertibleErrorCode(), 116 "Tracing requires a live process."); 117 return m_live_process->TraceStop(TraceStopRequest(GetPluginName(), tids)); 118 } 119 120 Expected<std::string> Trace::GetLiveProcessState() { 121 if (!m_live_process) 122 return createStringError(inconvertibleErrorCode(), 123 "Tracing requires a live process."); 124 return m_live_process->TraceGetState(GetPluginName()); 125 } 126 127 Optional<size_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid, 128 llvm::StringRef kind) { 129 auto it = m_live_thread_data.find(tid); 130 if (it == m_live_thread_data.end()) 131 return None; 132 std::unordered_map<std::string, size_t> &single_thread_data = it->second; 133 auto single_thread_data_it = single_thread_data.find(kind.str()); 134 if (single_thread_data_it == single_thread_data.end()) 135 return None; 136 return single_thread_data_it->second; 137 } 138 139 Optional<size_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) { 140 auto data_it = m_live_process_data.find(kind.str()); 141 if (data_it == m_live_process_data.end()) 142 return None; 143 return data_it->second; 144 } 145 146 Expected<std::vector<uint8_t>> 147 Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) { 148 if (!m_live_process) 149 return createStringError(inconvertibleErrorCode(), 150 "Tracing requires a live process."); 151 llvm::Optional<size_t> size = GetLiveThreadBinaryDataSize(tid, kind); 152 if (!size) 153 return createStringError( 154 inconvertibleErrorCode(), 155 "Tracing data \"%s\" is not available for thread %" PRIu64 ".", 156 kind.data(), tid); 157 158 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), tid, 0, 159 *size}; 160 return m_live_process->TraceGetBinaryData(request); 161 } 162 163 Expected<std::vector<uint8_t>> 164 Trace::GetLiveProcessBinaryData(llvm::StringRef kind) { 165 if (!m_live_process) 166 return createStringError(inconvertibleErrorCode(), 167 "Tracing requires a live process."); 168 llvm::Optional<size_t> size = GetLiveProcessBinaryDataSize(kind); 169 if (!size) 170 return createStringError( 171 inconvertibleErrorCode(), 172 "Tracing data \"%s\" is not available for the process.", kind.data()); 173 174 TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), None, 0, 175 *size}; 176 return m_live_process->TraceGetBinaryData(request); 177 } 178 179 const char *Trace::RefreshLiveProcessState() { 180 if (!m_live_process) 181 return nullptr; 182 183 uint32_t new_stop_id = m_live_process->GetStopID(); 184 if (new_stop_id == m_stop_id) 185 return nullptr; 186 187 Log *log = GetLog(LLDBLog::Target); 188 LLDB_LOG(log, "Trace::RefreshLiveProcessState invoked"); 189 190 m_stop_id = new_stop_id; 191 m_live_thread_data.clear(); 192 m_live_refresh_error.reset(); 193 194 auto HandleError = [&](Error &&err) -> const char * { 195 m_live_refresh_error = toString(std::move(err)); 196 return m_live_refresh_error->c_str(); 197 }; 198 199 Expected<std::string> json_string = GetLiveProcessState(); 200 if (!json_string) 201 return HandleError(json_string.takeError()); 202 203 Expected<TraceGetStateResponse> live_process_state = 204 json::parse<TraceGetStateResponse>(*json_string, "TraceGetStateResponse"); 205 if (!live_process_state) 206 return HandleError(live_process_state.takeError()); 207 208 for (std::string &warning : live_process_state->warnings) 209 LLDB_LOG(log, "Warning when fetching the trace state: {0}", warning); 210 211 for (const TraceThreadState &thread_state : 212 live_process_state->traced_threads) { 213 for (const TraceBinaryData &item : thread_state.binary_data) 214 m_live_thread_data[thread_state.tid][item.kind] = item.size; 215 } 216 217 for (const TraceBinaryData &item : live_process_state->process_binary_data) 218 m_live_process_data[item.kind] = item.size; 219 220 if (Error err = DoRefreshLiveProcessState(std::move(*live_process_state), 221 *json_string)) 222 return HandleError(std::move(err)); 223 224 return nullptr; 225 } 226 227 Process *Trace::GetLiveProcess() { return m_live_process; } 228 229 uint32_t Trace::GetStopID() { 230 RefreshLiveProcessState(); 231 return m_stop_id; 232 } 233 234 llvm::Expected<FileSpec> 235 Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) { 236 auto NotFoundError = [&]() { 237 return createStringError( 238 inconvertibleErrorCode(), 239 formatv("The thread with tid={0} doesn't have the tracing data {1}", 240 tid, kind)); 241 }; 242 243 auto it = m_postmortem_thread_data.find(tid); 244 if (it == m_postmortem_thread_data.end()) 245 return NotFoundError(); 246 247 std::unordered_map<std::string, FileSpec> &data_kind_to_file_spec_map = 248 it->second; 249 auto it2 = data_kind_to_file_spec_map.find(kind.str()); 250 if (it2 == data_kind_to_file_spec_map.end()) 251 return NotFoundError(); 252 return it2->second; 253 } 254 255 void Trace::SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind, 256 FileSpec file_spec) { 257 m_postmortem_thread_data[tid][kind.str()] = file_spec; 258 } 259 260 llvm::Error 261 Trace::OnLiveThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 262 OnBinaryDataReadCallback callback) { 263 Expected<std::vector<uint8_t>> data = GetLiveThreadBinaryData(tid, kind); 264 if (!data) 265 return data.takeError(); 266 return callback(*data); 267 } 268 269 llvm::Error 270 Trace::OnPostMortemThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 271 OnBinaryDataReadCallback callback) { 272 Expected<FileSpec> file = GetPostMortemThreadDataFile(tid, kind); 273 if (!file) 274 return file.takeError(); 275 ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error = 276 MemoryBuffer::getFile(file->GetPath()); 277 if (std::error_code err = trace_or_error.getError()) 278 return errorCodeToError(err); 279 280 MemoryBuffer &data = **trace_or_error; 281 ArrayRef<uint8_t> array_ref( 282 reinterpret_cast<const uint8_t *>(data.getBufferStart()), 283 data.getBufferSize()); 284 return callback(array_ref); 285 } 286 287 llvm::Error Trace::OnThreadBinaryDataRead(lldb::tid_t tid, llvm::StringRef kind, 288 OnBinaryDataReadCallback callback) { 289 if (m_live_process) 290 return OnLiveThreadBinaryDataRead(tid, kind, callback); 291 else 292 return OnPostMortemThreadBinaryDataRead(tid, kind, callback); 293 } 294