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/Target/ThreadPostMortemTrace.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   ConstString plugin_name(json_session.trace.type);
72   if (auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name))
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>
79 Trace::FindPluginForLiveProcess(llvm::StringRef plugin_name, Process &process) {
80   if (!process.IsLiveDebugSession())
81     return createStringError(inconvertibleErrorCode(),
82                              "Can't trace non-live processes");
83 
84   ConstString name(plugin_name);
85   if (auto create_callback =
86           PluginManager::GetTraceCreateCallbackForLiveProcess(name))
87     return create_callback(process);
88 
89   return createInvalidPlugInError(plugin_name);
90 }
91 
92 Expected<StringRef> Trace::FindPluginSchema(StringRef name) {
93   ConstString plugin_name(name);
94   StringRef schema = PluginManager::GetTraceSchema(plugin_name);
95   if (!schema.empty())
96     return schema;
97 
98   return createInvalidPlugInError(name);
99 }
100 
101 Error Trace::Start(const llvm::json::Value &request) {
102   if (!m_live_process)
103     return createStringError(inconvertibleErrorCode(),
104                              "Tracing requires a live process.");
105   return m_live_process->TraceStart(request);
106 }
107 
108 Error Trace::Stop() {
109   if (!m_live_process)
110     return createStringError(inconvertibleErrorCode(),
111                              "Tracing requires a live process.");
112   return m_live_process->TraceStop(
113       TraceStopRequest(GetPluginName().AsCString()));
114 }
115 
116 Error Trace::Stop(llvm::ArrayRef<lldb::tid_t> tids) {
117   if (!m_live_process)
118     return createStringError(inconvertibleErrorCode(),
119                              "Tracing requires a live process.");
120   return m_live_process->TraceStop(
121       TraceStopRequest(GetPluginName().AsCString(), tids));
122 }
123 
124 Expected<std::string> Trace::GetLiveProcessState() {
125   if (!m_live_process)
126     return createStringError(inconvertibleErrorCode(),
127                              "Tracing requires a live process.");
128   return m_live_process->TraceGetState(GetPluginName().AsCString());
129 }
130 
131 Optional<size_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid,
132                                                     llvm::StringRef kind) {
133   auto it = m_live_thread_data.find(tid);
134   if (it == m_live_thread_data.end())
135     return None;
136   std::unordered_map<std::string, size_t> &single_thread_data = it->second;
137   auto single_thread_data_it = single_thread_data.find(kind.str());
138   if (single_thread_data_it == single_thread_data.end())
139     return None;
140   return single_thread_data_it->second;
141 }
142 
143 Optional<size_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) {
144   auto data_it = m_live_process_data.find(kind.str());
145   if (data_it == m_live_process_data.end())
146     return None;
147   return data_it->second;
148 }
149 
150 Expected<ArrayRef<uint8_t>>
151 Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) {
152   if (!m_live_process)
153     return createStringError(inconvertibleErrorCode(),
154                              "Tracing requires a live process.");
155   llvm::Optional<size_t> size = GetLiveThreadBinaryDataSize(tid, kind);
156   if (!size)
157     return createStringError(
158         inconvertibleErrorCode(),
159         "Tracing data \"%s\" is not available for thread %" PRIu64 ".",
160         kind.data(), tid);
161 
162   TraceGetBinaryDataRequest request{GetPluginName().AsCString(), kind.str(),
163                                     static_cast<int64_t>(tid), 0,
164                                     static_cast<int64_t>(*size)};
165   return m_live_process->TraceGetBinaryData(request);
166 }
167 
168 Expected<ArrayRef<uint8_t>>
169 Trace::GetLiveProcessBinaryData(llvm::StringRef kind) {
170   if (!m_live_process)
171     return createStringError(inconvertibleErrorCode(),
172                              "Tracing requires a live process.");
173   llvm::Optional<size_t> size = GetLiveProcessBinaryDataSize(kind);
174   if (!size)
175     return createStringError(
176         inconvertibleErrorCode(),
177         "Tracing data \"%s\" is not available for the process.", kind.data());
178 
179   TraceGetBinaryDataRequest request{GetPluginName().AsCString(), kind.str(),
180                                     None, 0, static_cast<int64_t>(*size)};
181   return m_live_process->TraceGetBinaryData(request);
182 }
183 
184 void Trace::RefreshLiveProcessState() {
185   if (!m_live_process)
186     return;
187 
188   uint32_t new_stop_id = m_live_process->GetStopID();
189   if (new_stop_id == m_stop_id)
190     return;
191 
192   m_stop_id = new_stop_id;
193   m_live_thread_data.clear();
194 
195   Expected<std::string> json_string = GetLiveProcessState();
196   if (!json_string) {
197     DoRefreshLiveProcessState(json_string.takeError());
198     return;
199   }
200   Expected<TraceGetStateResponse> live_process_state =
201       json::parse<TraceGetStateResponse>(*json_string, "TraceGetStateResponse");
202   if (!live_process_state) {
203     DoRefreshLiveProcessState(live_process_state.takeError());
204     return;
205   }
206 
207   for (const TraceThreadState &thread_state :
208        live_process_state->tracedThreads) {
209     for (const TraceBinaryData &item : thread_state.binaryData)
210       m_live_thread_data[thread_state.tid][item.kind] = item.size;
211   }
212 
213   for (const TraceBinaryData &item : live_process_state->processBinaryData)
214     m_live_process_data[item.kind] = item.size;
215 
216   DoRefreshLiveProcessState(std::move(live_process_state));
217 }
218 
219 uint32_t Trace::GetStopID() {
220   RefreshLiveProcessState();
221   return m_stop_id;
222 }
223