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