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