1 //===-- CommandObjectTrace.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 "CommandObjectTrace.h"
10
11 #include "llvm/Support/JSON.h"
12 #include "llvm/Support/MemoryBuffer.h"
13
14 #include "lldb/Core/Debugger.h"
15 #include "lldb/Core/PluginManager.h"
16 #include "lldb/Host/OptionParser.h"
17 #include "lldb/Interpreter/CommandInterpreter.h"
18 #include "lldb/Interpreter/CommandObject.h"
19 #include "lldb/Interpreter/CommandReturnObject.h"
20 #include "lldb/Interpreter/OptionArgParser.h"
21 #include "lldb/Interpreter/OptionGroupFormat.h"
22 #include "lldb/Interpreter/OptionValueBoolean.h"
23 #include "lldb/Interpreter/OptionValueLanguage.h"
24 #include "lldb/Interpreter/OptionValueString.h"
25 #include "lldb/Interpreter/Options.h"
26 #include "lldb/Target/Process.h"
27 #include "lldb/Target/Trace.h"
28
29 using namespace lldb;
30 using namespace lldb_private;
31 using namespace llvm;
32
33 // CommandObjectTraceLoad
34 #define LLDB_OPTIONS_trace_load
35 #include "CommandOptions.inc"
36
37 #pragma mark CommandObjectTraceLoad
38
39 class CommandObjectTraceLoad : public CommandObjectParsed {
40 public:
41 class CommandOptions : public Options {
42 public:
CommandOptions()43 CommandOptions() : Options() { OptionParsingStarting(nullptr); }
44
45 ~CommandOptions() override = default;
46
SetOptionValue(uint32_t option_idx,StringRef option_arg,ExecutionContext * execution_context)47 Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
48 ExecutionContext *execution_context) override {
49 Status error;
50 const int short_option = m_getopt_table[option_idx].val;
51
52 switch (short_option) {
53 case 'v': {
54 m_verbose = true;
55 break;
56 }
57 default:
58 llvm_unreachable("Unimplemented option");
59 }
60 return error;
61 }
62
OptionParsingStarting(ExecutionContext * execution_context)63 void OptionParsingStarting(ExecutionContext *execution_context) override {
64 m_verbose = false;
65 }
66
GetDefinitions()67 ArrayRef<OptionDefinition> GetDefinitions() override {
68 return makeArrayRef(g_trace_load_options);
69 }
70
71 bool m_verbose; // Enable verbose logging for debugging purposes.
72 };
73
CommandObjectTraceLoad(CommandInterpreter & interpreter)74 CommandObjectTraceLoad(CommandInterpreter &interpreter)
75 : CommandObjectParsed(interpreter, "trace load",
76 "Load a processor trace session from a JSON file.",
77 "trace load"),
78 m_options() {}
79
80 ~CommandObjectTraceLoad() override = default;
81
GetOptions()82 Options *GetOptions() override { return &m_options; }
83
84 protected:
DoExecute(Args & command,CommandReturnObject & result)85 bool DoExecute(Args &command, CommandReturnObject &result) override {
86 if (command.size() != 1) {
87 result.AppendError(
88 "a single path to a JSON file containing a trace session"
89 "is required");
90 return false;
91 }
92
93 auto end_with_failure = [&result](llvm::Error err) -> bool {
94 result.AppendErrorWithFormat("%s\n",
95 llvm::toString(std::move(err)).c_str());
96 return false;
97 };
98
99 FileSpec json_file(command[0].ref());
100
101 auto buffer_or_error = llvm::MemoryBuffer::getFile(json_file.GetPath());
102 if (!buffer_or_error) {
103 return end_with_failure(llvm::createStringError(
104 std::errc::invalid_argument, "could not open input file: %s - %s.",
105 json_file.GetPath().c_str(),
106 buffer_or_error.getError().message().c_str()));
107 }
108
109 llvm::Expected<json::Value> session_file =
110 json::parse(buffer_or_error.get()->getBuffer().str());
111 if (!session_file)
112 return end_with_failure(session_file.takeError());
113
114 if (Expected<lldb::TraceSP> traceOrErr =
115 Trace::FindPluginForPostMortemProcess(
116 GetDebugger(), *session_file,
117 json_file.GetDirectory().AsCString())) {
118 lldb::TraceSP trace_sp = traceOrErr.get();
119 if (m_options.m_verbose && trace_sp)
120 result.AppendMessageWithFormat("loading trace with plugin %s\n",
121 trace_sp->GetPluginName().AsCString());
122 } else
123 return end_with_failure(traceOrErr.takeError());
124
125 result.SetStatus(eReturnStatusSuccessFinishResult);
126 return true;
127 }
128
129 CommandOptions m_options;
130 };
131
132 // CommandObjectTraceDump
133 #define LLDB_OPTIONS_trace_dump
134 #include "CommandOptions.inc"
135
136 #pragma mark CommandObjectTraceDump
137
138 class CommandObjectTraceDump : public CommandObjectParsed {
139 public:
140 class CommandOptions : public Options {
141 public:
CommandOptions()142 CommandOptions() : Options() { OptionParsingStarting(nullptr); }
143
144 ~CommandOptions() override = default;
145
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)146 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
147 ExecutionContext *execution_context) override {
148 Status error;
149 const int short_option = m_getopt_table[option_idx].val;
150
151 switch (short_option) {
152 case 'v': {
153 m_verbose = true;
154 break;
155 }
156 default:
157 llvm_unreachable("Unimplemented option");
158 }
159 return error;
160 }
161
OptionParsingStarting(ExecutionContext * execution_context)162 void OptionParsingStarting(ExecutionContext *execution_context) override {
163 m_verbose = false;
164 }
165
GetDefinitions()166 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
167 return llvm::makeArrayRef(g_trace_dump_options);
168 }
169
170 bool m_verbose; // Enable verbose logging for debugging purposes.
171 };
172
CommandObjectTraceDump(CommandInterpreter & interpreter)173 CommandObjectTraceDump(CommandInterpreter &interpreter)
174 : CommandObjectParsed(interpreter, "trace dump",
175 "Dump the loaded processor trace data.",
176 "trace dump"),
177 m_options() {}
178
179 ~CommandObjectTraceDump() override = default;
180
GetOptions()181 Options *GetOptions() override { return &m_options; }
182
183 protected:
DoExecute(Args & command,CommandReturnObject & result)184 bool DoExecute(Args &command, CommandReturnObject &result) override {
185 Status error;
186 // TODO: fill in the dumping code here!
187 if (error.Success()) {
188 result.SetStatus(eReturnStatusSuccessFinishResult);
189 } else {
190 result.AppendErrorWithFormat("%s\n", error.AsCString());
191 }
192 return result.Succeeded();
193 }
194
195 CommandOptions m_options;
196 };
197
198 // CommandObjectTraceSchema
199 #define LLDB_OPTIONS_trace_schema
200 #include "CommandOptions.inc"
201
202 #pragma mark CommandObjectTraceSchema
203
204 class CommandObjectTraceSchema : public CommandObjectParsed {
205 public:
206 class CommandOptions : public Options {
207 public:
CommandOptions()208 CommandOptions() : Options() { OptionParsingStarting(nullptr); }
209
210 ~CommandOptions() override = default;
211
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)212 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
213 ExecutionContext *execution_context) override {
214 Status error;
215 const int short_option = m_getopt_table[option_idx].val;
216
217 switch (short_option) {
218 case 'v': {
219 m_verbose = true;
220 break;
221 }
222 default:
223 llvm_unreachable("Unimplemented option");
224 }
225 return error;
226 }
227
OptionParsingStarting(ExecutionContext * execution_context)228 void OptionParsingStarting(ExecutionContext *execution_context) override {
229 m_verbose = false;
230 }
231
GetDefinitions()232 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
233 return llvm::makeArrayRef(g_trace_schema_options);
234 }
235
236 bool m_verbose; // Enable verbose logging for debugging purposes.
237 };
238
CommandObjectTraceSchema(CommandInterpreter & interpreter)239 CommandObjectTraceSchema(CommandInterpreter &interpreter)
240 : CommandObjectParsed(interpreter, "trace schema",
241 "Show the schema of the given trace plugin.",
242 "trace schema <plug-in>. Use the plug-in name "
243 "\"all\" to see all schemas.\n"),
244 m_options() {}
245
246 ~CommandObjectTraceSchema() override = default;
247
GetOptions()248 Options *GetOptions() override { return &m_options; }
249
250 protected:
DoExecute(Args & command,CommandReturnObject & result)251 bool DoExecute(Args &command, CommandReturnObject &result) override {
252 Status error;
253 if (command.empty()) {
254 result.AppendError(
255 "trace schema cannot be invoked without a plug-in as argument");
256 return false;
257 }
258
259 StringRef plugin_name(command[0].c_str());
260 if (plugin_name == "all") {
261 size_t index = 0;
262 while (true) {
263 StringRef schema = PluginManager::GetTraceSchema(index++);
264 if (schema.empty())
265 break;
266
267 result.AppendMessage(schema);
268 }
269 } else {
270 if (Expected<StringRef> schemaOrErr =
271 Trace::FindPluginSchema(plugin_name))
272 result.AppendMessage(*schemaOrErr);
273 else
274 error = schemaOrErr.takeError();
275 }
276
277 if (error.Success()) {
278 result.SetStatus(eReturnStatusSuccessFinishResult);
279 } else {
280 result.AppendErrorWithFormat("%s\n", error.AsCString());
281 }
282 return result.Succeeded();
283 }
284
285 CommandOptions m_options;
286 };
287
288 // CommandObjectTrace
289
CommandObjectTrace(CommandInterpreter & interpreter)290 CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter)
291 : CommandObjectMultiword(interpreter, "trace",
292 "Commands for loading and using processor "
293 "trace information.",
294 "trace [<sub-command-options>]") {
295 LoadSubCommand("load",
296 CommandObjectSP(new CommandObjectTraceLoad(interpreter)));
297 LoadSubCommand("dump",
298 CommandObjectSP(new CommandObjectTraceDump(interpreter)));
299 LoadSubCommand("schema",
300 CommandObjectSP(new CommandObjectTraceSchema(interpreter)));
301 }
302
303 CommandObjectTrace::~CommandObjectTrace() = default;
304
DoGetProxyCommandObject()305 Expected<CommandObjectSP> CommandObjectTraceProxy::DoGetProxyCommandObject() {
306 ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP();
307
308 if (!process_sp)
309 return createStringError(inconvertibleErrorCode(),
310 "Process not available.");
311 if (m_live_debug_session_only && !process_sp->IsLiveDebugSession())
312 return createStringError(inconvertibleErrorCode(),
313 "Process must be alive.");
314
315 if (Expected<TraceSP> trace_sp = process_sp->GetTarget().GetTraceOrCreate())
316 return GetDelegateCommand(**trace_sp);
317 else
318 return createStringError(inconvertibleErrorCode(),
319 "Tracing is not supported. %s",
320 toString(trace_sp.takeError()).c_str());
321 }
322
GetProxyCommandObject()323 CommandObject *CommandObjectTraceProxy::GetProxyCommandObject() {
324 if (Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) {
325 m_delegate_sp = *delegate;
326 m_delegate_error.clear();
327 return m_delegate_sp.get();
328 } else {
329 m_delegate_sp.reset();
330 m_delegate_error = toString(delegate.takeError());
331 return nullptr;
332 }
333 }
334