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 /// Similar to the methods above but it looks for an item in a map of maps.
67 template <typename K1, typename K2, typename V>
68 static Optional<V> Lookup(DenseMap<K1, DenseMap<K2, V>> &map, K1 k1, K2 k2) {
69   auto it = map.find(k1);
70   if (it == map.end())
71     return None;
72   return Lookup(it->second, k2);
73 }
74 
75 /// Similar to the methods above but it looks for an item in a map of maps.
76 template <typename K1, typename K2, typename V>
77 static V *LookupAsPtr(DenseMap<K1, DenseMap<K2, V>> &map, K1 k1, K2 k2) {
78   auto it = map.find(k1);
79   if (it == map.end())
80     return nullptr;
81   return LookupAsPtr(it->second, k2);
82 }
83 /// \}
84 
85 static Error createInvalidPlugInError(StringRef plugin_name) {
86   return createStringError(
87       std::errc::invalid_argument,
88       "no trace plug-in matches the specified type: \"%s\"",
89       plugin_name.data());
90 }
91 
92 Expected<lldb::TraceSP>
93 Trace::FindPluginForPostMortemProcess(Debugger &debugger,
94                                       const json::Value &trace_session_file,
95                                       StringRef session_file_dir) {
96   JSONSimpleTraceSession json_session;
97   json::Path::Root root("traceSession");
98   if (!json::fromJSON(trace_session_file, json_session, root))
99     return root.getError();
100 
101   if (auto create_callback =
102           PluginManager::GetTraceCreateCallback(json_session.type))
103     return create_callback(trace_session_file, session_file_dir, debugger);
104 
105   return createInvalidPlugInError(json_session.type);
106 }
107 
108 Expected<lldb::TraceSP> Trace::FindPluginForLiveProcess(llvm::StringRef name,
109                                                         Process &process) {
110   if (!process.IsLiveDebugSession())
111     return createStringError(inconvertibleErrorCode(),
112                              "Can't trace non-live processes");
113 
114   if (auto create_callback =
115           PluginManager::GetTraceCreateCallbackForLiveProcess(name))
116     return create_callback(process);
117 
118   return createInvalidPlugInError(name);
119 }
120 
121 Expected<StringRef> Trace::FindPluginSchema(StringRef name) {
122   StringRef schema = PluginManager::GetTraceSchema(name);
123   if (!schema.empty())
124     return schema;
125 
126   return createInvalidPlugInError(name);
127 }
128 
129 Error Trace::Start(const llvm::json::Value &request) {
130   if (!m_live_process)
131     return createStringError(
132         inconvertibleErrorCode(),
133         "Attempted to start tracing without a live process.");
134   return m_live_process->TraceStart(request);
135 }
136 
137 Error Trace::Stop() {
138   if (!m_live_process)
139     return createStringError(
140         inconvertibleErrorCode(),
141         "Attempted to stop tracing without a live process.");
142   return m_live_process->TraceStop(TraceStopRequest(GetPluginName()));
143 }
144 
145 Error Trace::Stop(llvm::ArrayRef<lldb::tid_t> tids) {
146   if (!m_live_process)
147     return createStringError(
148         inconvertibleErrorCode(),
149         "Attempted to stop tracing without a live process.");
150   return m_live_process->TraceStop(TraceStopRequest(GetPluginName(), tids));
151 }
152 
153 Expected<std::string> Trace::GetLiveProcessState() {
154   if (!m_live_process)
155     return createStringError(
156         inconvertibleErrorCode(),
157         "Attempted to fetch live trace information without a live process.");
158   return m_live_process->TraceGetState(GetPluginName());
159 }
160 
161 Optional<uint64_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid,
162                                                       llvm::StringRef kind) {
163   Storage &storage = GetUpdatedStorage();
164   return Lookup(storage.live_thread_data, tid, ConstString(kind));
165 }
166 
167 Optional<uint64_t> Trace::GetLiveCpuBinaryDataSize(lldb::cpu_id_t cpu_id,
168                                                    llvm::StringRef kind) {
169   Storage &storage = GetUpdatedStorage();
170   return Lookup(storage.live_cpu_data_sizes, cpu_id, ConstString(kind));
171 }
172 
173 Optional<uint64_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) {
174   Storage &storage = GetUpdatedStorage();
175   return Lookup(storage.live_process_data, ConstString(kind));
176 }
177 
178 Expected<std::vector<uint8_t>>
179 Trace::GetLiveTraceBinaryData(const TraceGetBinaryDataRequest &request,
180                               uint64_t expected_size) {
181   if (!m_live_process)
182     return createStringError(
183         inconvertibleErrorCode(),
184         formatv("Attempted to fetch live trace data without a live process. "
185                 "Data kind = {0}, tid = {1}, cpu id = {2}.",
186                 request.kind, request.tid, request.cpu_id));
187 
188   Expected<std::vector<uint8_t>> data =
189       m_live_process->TraceGetBinaryData(request);
190 
191   if (!data)
192     return data.takeError();
193 
194   if (data->size() != expected_size)
195     return createStringError(
196         inconvertibleErrorCode(),
197         formatv("Got incomplete live trace data. Data kind = {0}, expected "
198                 "size = {1}, actual size = {2}, tid = {3}, cpu id = {4}",
199                 request.kind, expected_size, data->size(), request.tid,
200                 request.cpu_id));
201 
202   return data;
203 }
204 
205 Expected<std::vector<uint8_t>>
206 Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) {
207   llvm::Optional<uint64_t> size = GetLiveThreadBinaryDataSize(tid, kind);
208   if (!size)
209     return createStringError(
210         inconvertibleErrorCode(),
211         "Tracing data \"%s\" is not available for thread %" PRIu64 ".",
212         kind.data(), tid);
213 
214   TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(), tid,
215                                     /*cpu_id=*/None};
216   return GetLiveTraceBinaryData(request, *size);
217 }
218 
219 Expected<std::vector<uint8_t>>
220 Trace::GetLiveCpuBinaryData(lldb::cpu_id_t cpu_id, llvm::StringRef kind) {
221   if (!m_live_process)
222     return createStringError(
223         inconvertibleErrorCode(),
224         "Attempted to fetch live cpu data without a live process.");
225   llvm::Optional<uint64_t> size = GetLiveCpuBinaryDataSize(cpu_id, kind);
226   if (!size)
227     return createStringError(
228         inconvertibleErrorCode(),
229         "Tracing data \"%s\" is not available for cpu_id %" PRIu64 ".",
230         kind.data(), cpu_id);
231 
232   TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(),
233                                     /*tid=*/None, cpu_id};
234   return m_live_process->TraceGetBinaryData(request);
235 }
236 
237 Expected<std::vector<uint8_t>>
238 Trace::GetLiveProcessBinaryData(llvm::StringRef kind) {
239   llvm::Optional<uint64_t> size = GetLiveProcessBinaryDataSize(kind);
240   if (!size)
241     return createStringError(
242         inconvertibleErrorCode(),
243         "Tracing data \"%s\" is not available for the process.", kind.data());
244 
245   TraceGetBinaryDataRequest request{GetPluginName().str(), kind.str(),
246                                     /*tid=*/None, /*cpu_id*/ None};
247   return GetLiveTraceBinaryData(request, *size);
248 }
249 
250 Trace::Storage &Trace::GetUpdatedStorage() {
251   RefreshLiveProcessState();
252   return m_storage;
253 }
254 
255 const char *Trace::RefreshLiveProcessState() {
256   if (!m_live_process)
257     return nullptr;
258 
259   uint32_t new_stop_id = m_live_process->GetStopID();
260   if (new_stop_id == m_stop_id)
261     return nullptr;
262 
263   Log *log = GetLog(LLDBLog::Target);
264   LLDB_LOG(log, "Trace::RefreshLiveProcessState invoked");
265 
266   m_stop_id = new_stop_id;
267   m_storage = Trace::Storage();
268 
269   auto do_refresh = [&]() -> Error {
270     Expected<std::string> json_string = GetLiveProcessState();
271     if (!json_string)
272       return json_string.takeError();
273 
274     Expected<TraceGetStateResponse> live_process_state =
275         json::parse<TraceGetStateResponse>(*json_string,
276                                            "TraceGetStateResponse");
277     if (!live_process_state)
278       return live_process_state.takeError();
279 
280     if (live_process_state->warnings) {
281       for (std::string &warning : *live_process_state->warnings)
282         LLDB_LOG(log, "== Warning when fetching the trace state: {0}", warning);
283     }
284 
285     for (const TraceThreadState &thread_state :
286          live_process_state->traced_threads) {
287       for (const TraceBinaryData &item : thread_state.binary_data)
288         m_storage.live_thread_data[thread_state.tid].insert(
289             {ConstString(item.kind), item.size});
290     }
291 
292     LLDB_LOG(log, "== Found {0} threads being traced",
293              live_process_state->traced_threads.size());
294 
295     if (live_process_state->cpus) {
296       m_storage.cpus.emplace();
297       for (const TraceCpuState &cpu_state : *live_process_state->cpus) {
298         m_storage.cpus->push_back(cpu_state.id);
299         for (const TraceBinaryData &item : cpu_state.binary_data)
300           m_storage.live_cpu_data_sizes[cpu_state.id].insert(
301               {ConstString(item.kind), item.size});
302       }
303       LLDB_LOG(log, "== Found {0} cpu cpus being traced",
304                live_process_state->cpus->size());
305     }
306 
307     for (const TraceBinaryData &item : live_process_state->process_binary_data)
308       m_storage.live_process_data.insert({ConstString(item.kind), item.size});
309 
310     return DoRefreshLiveProcessState(std::move(*live_process_state),
311                                      *json_string);
312   };
313 
314   if (Error err = do_refresh()) {
315     m_storage.live_refresh_error = toString(std::move(err));
316     return m_storage.live_refresh_error->c_str();
317   }
318 
319   return nullptr;
320 }
321 
322 Trace::Trace(ArrayRef<ProcessSP> postmortem_processes,
323              Optional<std::vector<lldb::cpu_id_t>> postmortem_cpus) {
324   for (ProcessSP process_sp : postmortem_processes)
325     m_storage.postmortem_processes.push_back(process_sp.get());
326   m_storage.cpus = postmortem_cpus;
327 }
328 
329 Process *Trace::GetLiveProcess() { return m_live_process; }
330 
331 ArrayRef<Process *> Trace::GetPostMortemProcesses() {
332   return m_storage.postmortem_processes;
333 }
334 
335 std::vector<Process *> Trace::GetAllProcesses() {
336   if (Process *proc = GetLiveProcess())
337     return {proc};
338   return GetPostMortemProcesses();
339 }
340 
341 uint32_t Trace::GetStopID() {
342   RefreshLiveProcessState();
343   return m_stop_id;
344 }
345 
346 llvm::Expected<FileSpec>
347 Trace::GetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind) {
348   Storage &storage = GetUpdatedStorage();
349   if (Optional<FileSpec> file =
350           Lookup(storage.postmortem_thread_data, tid, ConstString(kind)))
351     return *file;
352   else
353     return createStringError(
354         inconvertibleErrorCode(),
355         formatv("The thread with tid={0} doesn't have the tracing data {1}",
356                 tid, kind));
357 }
358 
359 llvm::Expected<FileSpec> Trace::GetPostMortemCpuDataFile(lldb::cpu_id_t cpu_id,
360                                                          llvm::StringRef kind) {
361   Storage &storage = GetUpdatedStorage();
362   if (Optional<FileSpec> file =
363           Lookup(storage.postmortem_cpu_data, cpu_id, ConstString(kind)))
364     return *file;
365   else
366     return createStringError(
367         inconvertibleErrorCode(),
368         formatv("The cpu with id={0} doesn't have the tracing data {1}", cpu_id,
369                 kind));
370 }
371 
372 void Trace::SetPostMortemThreadDataFile(lldb::tid_t tid, llvm::StringRef kind,
373                                         FileSpec file_spec) {
374   Storage &storage = GetUpdatedStorage();
375   storage.postmortem_thread_data[tid].insert({ConstString(kind), file_spec});
376 }
377 
378 void Trace::SetPostMortemCpuDataFile(lldb::cpu_id_t cpu_id,
379                                      llvm::StringRef kind, FileSpec file_spec) {
380   Storage &storage = GetUpdatedStorage();
381   storage.postmortem_cpu_data[cpu_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::OnLiveCpuBinaryDataRead(lldb::cpu_id_t cpu_id,
394                                            llvm::StringRef kind,
395                                            OnBinaryDataReadCallback callback) {
396   Storage &storage = GetUpdatedStorage();
397   if (std::vector<uint8_t> *cpu_data =
398           LookupAsPtr(storage.live_cpu_data, cpu_id, ConstString(kind)))
399     return callback(*cpu_data);
400 
401   Expected<std::vector<uint8_t>> data = GetLiveCpuBinaryData(cpu_id, kind);
402   if (!data)
403     return data.takeError();
404   auto it = storage.live_cpu_data[cpu_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::OnPostMortemCpuBinaryDataRead(lldb::cpu_id_t cpu_id,
436                                      llvm::StringRef kind,
437                                      OnBinaryDataReadCallback callback) {
438   if (Expected<FileSpec> file = GetPostMortemCpuDataFile(cpu_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::OnAllCpusBinaryDataRead(llvm::StringRef kind,
454                                OnCpusBinaryDataReadCallback callback) {
455   DenseMap<cpu_id_t, ArrayRef<uint8_t>> buffers;
456   Storage &storage = GetUpdatedStorage();
457   if (!storage.cpus)
458     return Error::success();
459 
460   std::function<Error(std::vector<cpu_id_t>::iterator)> process_cpu =
461       [&](std::vector<cpu_id_t>::iterator cpu_id) -> Error {
462     if (cpu_id == storage.cpus->end())
463       return callback(buffers);
464 
465     return OnCpuBinaryDataRead(*cpu_id, kind,
466                                [&](ArrayRef<uint8_t> data) -> Error {
467                                  buffers.try_emplace(*cpu_id, data);
468                                  auto next_id = cpu_id;
469                                  next_id++;
470                                  return process_cpu(next_id);
471                                });
472   };
473   return process_cpu(storage.cpus->begin());
474 }
475 
476 llvm::Error Trace::OnCpuBinaryDataRead(lldb::cpu_id_t cpu_id,
477                                        llvm::StringRef kind,
478                                        OnBinaryDataReadCallback callback) {
479   if (m_live_process)
480     return OnLiveCpuBinaryDataRead(cpu_id, kind, callback);
481   else
482     return OnPostMortemCpuBinaryDataRead(cpu_id, kind, callback);
483 }
484 
485 ArrayRef<lldb::cpu_id_t> Trace::GetTracedCpus() {
486   Storage &storage = GetUpdatedStorage();
487   if (storage.cpus)
488     return *storage.cpus;
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