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