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