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