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
fromJSON(const Value & value,JSONSimpleTraceBundleDescription & bundle,Path path)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>
Lookup(DenseMap<K,V> & map,K k)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>
LookupAsPtr(DenseMap<K,V> & map,K k)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>
Lookup(DenseMap<K1,DenseMap<K2,V>> & map,K1 k1,K2 k2)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>
LookupAsPtr(DenseMap<K1,DenseMap<K2,V>> & map,K1 k1,K2 k2)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
createInvalidPlugInError(StringRef plugin_name)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>
LoadPostMortemTraceFromFile(Debugger & debugger,const FileSpec & trace_description_file)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
FindPluginForPostMortemProcess(Debugger & debugger,const json::Value & trace_bundle_description,StringRef bundle_dir)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
FindPluginForLiveProcess(llvm::StringRef name,Process & process)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
FindPluginSchema(StringRef name)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
Start(const llvm::json::Value & request)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
Stop()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
Stop(llvm::ArrayRef<lldb::tid_t> tids)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
GetLiveProcessState()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
GetLiveThreadBinaryDataSize(lldb::tid_t tid,llvm::StringRef kind)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
GetLiveCpuBinaryDataSize(lldb::cpu_id_t cpu_id,llvm::StringRef kind)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
GetLiveProcessBinaryDataSize(llvm::StringRef kind)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>>
GetLiveTraceBinaryData(const TraceGetBinaryDataRequest & request,uint64_t expected_size)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>>
GetLiveThreadBinaryData(lldb::tid_t tid,llvm::StringRef kind)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>>
GetLiveCpuBinaryData(lldb::cpu_id_t cpu_id,llvm::StringRef kind)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>>
GetLiveProcessBinaryData(llvm::StringRef kind)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
GetUpdatedStorage()274 Trace::Storage &Trace::GetUpdatedStorage() {
275 RefreshLiveProcessState();
276 return m_storage;
277 }
278
RefreshLiveProcessState()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
Trace(ArrayRef<ProcessSP> postmortem_processes,Optional<std::vector<lldb::cpu_id_t>> postmortem_cpus)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
GetLiveProcess()353 Process *Trace::GetLiveProcess() { return m_live_process; }
354
GetPostMortemProcesses()355 ArrayRef<Process *> Trace::GetPostMortemProcesses() {
356 return m_storage.postmortem_processes;
357 }
358
GetAllProcesses()359 std::vector<Process *> Trace::GetAllProcesses() {
360 if (Process *proc = GetLiveProcess())
361 return {proc};
362 return GetPostMortemProcesses();
363 }
364
GetStopID()365 uint32_t Trace::GetStopID() {
366 RefreshLiveProcessState();
367 return m_stop_id;
368 }
369
370 llvm::Expected<FileSpec>
GetPostMortemThreadDataFile(lldb::tid_t tid,llvm::StringRef kind)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
GetPostMortemCpuDataFile(lldb::cpu_id_t cpu_id,llvm::StringRef kind)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
SetPostMortemThreadDataFile(lldb::tid_t tid,llvm::StringRef kind,FileSpec file_spec)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
SetPostMortemCpuDataFile(lldb::cpu_id_t cpu_id,llvm::StringRef kind,FileSpec file_spec)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
OnLiveThreadBinaryDataRead(lldb::tid_t tid,llvm::StringRef kind,OnBinaryDataReadCallback callback)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
OnLiveCpuBinaryDataRead(lldb::cpu_id_t cpu_id,llvm::StringRef kind,OnBinaryDataReadCallback callback)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
OnDataFileRead(FileSpec file,OnBinaryDataReadCallback callback)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
OnPostMortemThreadBinaryDataRead(lldb::tid_t tid,llvm::StringRef kind,OnBinaryDataReadCallback callback)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
OnPostMortemCpuBinaryDataRead(lldb::cpu_id_t cpu_id,llvm::StringRef kind,OnBinaryDataReadCallback callback)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
OnThreadBinaryDataRead(lldb::tid_t tid,llvm::StringRef kind,OnBinaryDataReadCallback callback)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
OnAllCpusBinaryDataRead(llvm::StringRef kind,OnCpusBinaryDataReadCallback callback)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
OnCpuBinaryDataRead(lldb::cpu_id_t cpu_id,llvm::StringRef kind,OnBinaryDataReadCallback callback)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
GetTracedCpus()509 ArrayRef<lldb::cpu_id_t> Trace::GetTracedCpus() {
510 Storage &storage = GetUpdatedStorage();
511 if (storage.cpus)
512 return *storage.cpus;
513 return {};
514 }
515
GetTracedProcesses()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