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/Stream.h"
21
22 using namespace lldb;
23 using namespace lldb_private;
24 using namespace llvm;
25
26 // Helper structs used to extract the type of a trace session json without
27 // having to parse the entire object.
28
29 struct JSONSimplePluginSettings {
30 std::string type;
31 };
32
33 struct JSONSimpleTraceSession {
34 JSONSimplePluginSettings trace;
35 };
36
37 namespace llvm {
38 namespace json {
39
fromJSON(const Value & value,JSONSimplePluginSettings & plugin_settings,Path path)40 bool fromJSON(const Value &value, JSONSimplePluginSettings &plugin_settings,
41 Path path) {
42 json::ObjectMapper o(value, path);
43 return o && o.map("type", plugin_settings.type);
44 }
45
fromJSON(const Value & value,JSONSimpleTraceSession & session,Path path)46 bool fromJSON(const Value &value, JSONSimpleTraceSession &session, Path path) {
47 json::ObjectMapper o(value, path);
48 return o && o.map("trace", session.trace);
49 }
50
51 } // namespace json
52 } // namespace llvm
53
createInvalidPlugInError(StringRef plugin_name)54 static Error createInvalidPlugInError(StringRef plugin_name) {
55 return createStringError(
56 std::errc::invalid_argument,
57 "no trace plug-in matches the specified type: \"%s\"",
58 plugin_name.data());
59 }
60
61 Expected<lldb::TraceSP>
FindPluginForPostMortemProcess(Debugger & debugger,const json::Value & trace_session_file,StringRef session_file_dir)62 Trace::FindPluginForPostMortemProcess(Debugger &debugger,
63 const json::Value &trace_session_file,
64 StringRef session_file_dir) {
65 JSONSimpleTraceSession json_session;
66 json::Path::Root root("traceSession");
67 if (!json::fromJSON(trace_session_file, json_session, root))
68 return root.getError();
69
70 ConstString plugin_name(json_session.trace.type);
71 if (auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name))
72 return create_callback(trace_session_file, session_file_dir, debugger);
73
74 return createInvalidPlugInError(json_session.trace.type);
75 }
76
77 Expected<lldb::TraceSP>
FindPluginForLiveProcess(llvm::StringRef plugin_name,Process & process)78 Trace::FindPluginForLiveProcess(llvm::StringRef plugin_name, Process &process) {
79 if (!process.IsLiveDebugSession())
80 return createStringError(inconvertibleErrorCode(),
81 "Can't trace non-live processes");
82
83 ConstString name(plugin_name);
84 if (auto create_callback =
85 PluginManager::GetTraceCreateCallbackForLiveProcess(name))
86 return create_callback(process);
87
88 return createInvalidPlugInError(plugin_name);
89 }
90
FindPluginSchema(StringRef name)91 Expected<StringRef> Trace::FindPluginSchema(StringRef name) {
92 ConstString plugin_name(name);
93 StringRef schema = PluginManager::GetTraceSchema(plugin_name);
94 if (!schema.empty())
95 return schema;
96
97 return createInvalidPlugInError(name);
98 }
99
Start(const llvm::json::Value & request)100 Error Trace::Start(const llvm::json::Value &request) {
101 if (!m_live_process)
102 return createStringError(inconvertibleErrorCode(),
103 "Tracing requires a live process.");
104 return m_live_process->TraceStart(request);
105 }
106
Stop()107 Error Trace::Stop() {
108 if (!m_live_process)
109 return createStringError(inconvertibleErrorCode(),
110 "Tracing requires a live process.");
111 return m_live_process->TraceStop(
112 TraceStopRequest(GetPluginName().AsCString()));
113 }
114
Stop(llvm::ArrayRef<lldb::tid_t> tids)115 Error Trace::Stop(llvm::ArrayRef<lldb::tid_t> tids) {
116 if (!m_live_process)
117 return createStringError(inconvertibleErrorCode(),
118 "Tracing requires a live process.");
119 return m_live_process->TraceStop(
120 TraceStopRequest(GetPluginName().AsCString(), tids));
121 }
122
GetLiveProcessState()123 Expected<std::string> Trace::GetLiveProcessState() {
124 if (!m_live_process)
125 return createStringError(inconvertibleErrorCode(),
126 "Tracing requires a live process.");
127 return m_live_process->TraceGetState(GetPluginName().AsCString());
128 }
129
GetLiveThreadBinaryDataSize(lldb::tid_t tid,llvm::StringRef kind)130 Optional<size_t> Trace::GetLiveThreadBinaryDataSize(lldb::tid_t tid,
131 llvm::StringRef kind) {
132 auto it = m_live_thread_data.find(tid);
133 if (it == m_live_thread_data.end())
134 return None;
135 std::unordered_map<std::string, size_t> &single_thread_data = it->second;
136 auto single_thread_data_it = single_thread_data.find(kind.str());
137 if (single_thread_data_it == single_thread_data.end())
138 return None;
139 return single_thread_data_it->second;
140 }
141
GetLiveProcessBinaryDataSize(llvm::StringRef kind)142 Optional<size_t> Trace::GetLiveProcessBinaryDataSize(llvm::StringRef kind) {
143 auto data_it = m_live_process_data.find(kind.str());
144 if (data_it == m_live_process_data.end())
145 return None;
146 return data_it->second;
147 }
148
149 Expected<ArrayRef<uint8_t>>
GetLiveThreadBinaryData(lldb::tid_t tid,llvm::StringRef kind)150 Trace::GetLiveThreadBinaryData(lldb::tid_t tid, llvm::StringRef kind) {
151 if (!m_live_process)
152 return createStringError(inconvertibleErrorCode(),
153 "Tracing requires a live process.");
154 llvm::Optional<size_t> size = GetLiveThreadBinaryDataSize(tid, kind);
155 if (!size)
156 return createStringError(
157 inconvertibleErrorCode(),
158 "Tracing data \"%s\" is not available for thread %" PRIu64 ".",
159 kind.data(), tid);
160
161 TraceGetBinaryDataRequest request{GetPluginName().AsCString(), kind.str(),
162 static_cast<int64_t>(tid), 0,
163 static_cast<int64_t>(*size)};
164 return m_live_process->TraceGetBinaryData(request);
165 }
166
167 Expected<ArrayRef<uint8_t>>
GetLiveProcessBinaryData(llvm::StringRef kind)168 Trace::GetLiveProcessBinaryData(llvm::StringRef kind) {
169 if (!m_live_process)
170 return createStringError(inconvertibleErrorCode(),
171 "Tracing requires a live process.");
172 llvm::Optional<size_t> size = GetLiveProcessBinaryDataSize(kind);
173 if (!size)
174 return createStringError(
175 inconvertibleErrorCode(),
176 "Tracing data \"%s\" is not available for the process.", kind.data());
177
178 TraceGetBinaryDataRequest request{GetPluginName().AsCString(), kind.str(),
179 None, 0, static_cast<int64_t>(*size)};
180 return m_live_process->TraceGetBinaryData(request);
181 }
182
RefreshLiveProcessState()183 void Trace::RefreshLiveProcessState() {
184 if (!m_live_process)
185 return;
186
187 uint32_t new_stop_id = m_live_process->GetStopID();
188 if (new_stop_id == m_stop_id)
189 return;
190
191 m_stop_id = new_stop_id;
192 m_live_thread_data.clear();
193
194 Expected<std::string> json_string = GetLiveProcessState();
195 if (!json_string) {
196 DoRefreshLiveProcessState(json_string.takeError());
197 return;
198 }
199 Expected<TraceGetStateResponse> live_process_state =
200 json::parse<TraceGetStateResponse>(*json_string, "TraceGetStateResponse");
201 if (!live_process_state) {
202 DoRefreshLiveProcessState(live_process_state.takeError());
203 return;
204 }
205
206 for (const TraceThreadState &thread_state :
207 live_process_state->tracedThreads) {
208 for (const TraceBinaryData &item : thread_state.binaryData)
209 m_live_thread_data[thread_state.tid][item.kind] = item.size;
210 }
211
212 for (const TraceBinaryData &item : live_process_state->processBinaryData)
213 m_live_process_data[item.kind] = item.size;
214
215 DoRefreshLiveProcessState(std::move(live_process_state));
216 }
217
GetStopID()218 uint32_t Trace::GetStopID() {
219 RefreshLiveProcessState();
220 return m_stop_id;
221 }
222