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/Trace.h"
27 
28 using namespace lldb;
29 using namespace lldb_private;
30 using namespace llvm;
31 
32 // CommandObjectTraceLoad
33 #define LLDB_OPTIONS_trace_load
34 #include "CommandOptions.inc"
35 
36 #pragma mark CommandObjectTraceLoad
37 
38 class CommandObjectTraceLoad : public CommandObjectParsed {
39 public:
40   class CommandOptions : public Options {
41   public:
CommandOptions()42     CommandOptions() : Options() { OptionParsingStarting(nullptr); }
43 
44     ~CommandOptions() override = default;
45 
SetOptionValue(uint32_t option_idx,StringRef option_arg,ExecutionContext * execution_context)46     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
47                           ExecutionContext *execution_context) override {
48       Status error;
49       const int short_option = m_getopt_table[option_idx].val;
50 
51       switch (short_option) {
52       case 'v': {
53         m_verbose = true;
54         break;
55       }
56       default:
57         llvm_unreachable("Unimplemented option");
58       }
59       return error;
60     }
61 
OptionParsingStarting(ExecutionContext * execution_context)62     void OptionParsingStarting(ExecutionContext *execution_context) override {
63       m_verbose = false;
64     }
65 
GetDefinitions()66     ArrayRef<OptionDefinition> GetDefinitions() override {
67       return makeArrayRef(g_trace_load_options);
68     }
69 
70     bool m_verbose; // Enable verbose logging for debugging purposes.
71   };
72 
CommandObjectTraceLoad(CommandInterpreter & interpreter)73   CommandObjectTraceLoad(CommandInterpreter &interpreter)
74       : CommandObjectParsed(interpreter, "trace load",
75                             "Load a processor trace session from a JSON file.",
76                             "trace load"),
77         m_options() {}
78 
79   ~CommandObjectTraceLoad() override = default;
80 
GetOptions()81   Options *GetOptions() override { return &m_options; }
82 
83 protected:
DoExecute(Args & command,CommandReturnObject & result)84   bool DoExecute(Args &command, CommandReturnObject &result) override {
85     if (command.size() != 1) {
86       result.AppendError(
87           "a single path to a JSON file containing a trace session"
88           "is required");
89       result.SetStatus(eReturnStatusFailed);
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       result.SetStatus(eReturnStatusFailed);
97       return false;
98     };
99 
100     FileSpec json_file(command[0].ref());
101 
102     auto buffer_or_error = llvm::MemoryBuffer::getFile(json_file.GetPath());
103     if (!buffer_or_error) {
104       return end_with_failure(llvm::createStringError(
105           std::errc::invalid_argument, "could not open input file: %s - %s.",
106           json_file.GetPath().c_str(),
107           buffer_or_error.getError().message().c_str()));
108     }
109 
110     llvm::Expected<json::Value> session_file =
111         json::parse(buffer_or_error.get()->getBuffer().str());
112     if (!session_file)
113       return end_with_failure(session_file.takeError());
114 
115     if (Expected<lldb::TraceSP> traceOrErr =
116             Trace::FindPlugin(GetDebugger(), *session_file,
117                               json_file.GetDirectory().AsCString())) {
118       lldb::TraceSP trace_sp = traceOrErr.get();
119       if (m_options.m_verbose)
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       result.SetStatus(eReturnStatusFailed);
192     }
193     return result.Succeeded();
194   }
195 
196   CommandOptions m_options;
197 };
198 
199 // CommandObjectTraceSchema
200 #define LLDB_OPTIONS_trace_schema
201 #include "CommandOptions.inc"
202 
203 #pragma mark CommandObjectTraceSchema
204 
205 class CommandObjectTraceSchema : public CommandObjectParsed {
206 public:
207   class CommandOptions : public Options {
208   public:
CommandOptions()209     CommandOptions() : Options() { OptionParsingStarting(nullptr); }
210 
211     ~CommandOptions() override = default;
212 
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)213     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
214                           ExecutionContext *execution_context) override {
215       Status error;
216       const int short_option = m_getopt_table[option_idx].val;
217 
218       switch (short_option) {
219       case 'v': {
220         m_verbose = true;
221         break;
222       }
223       default:
224         llvm_unreachable("Unimplemented option");
225       }
226       return error;
227     }
228 
OptionParsingStarting(ExecutionContext * execution_context)229     void OptionParsingStarting(ExecutionContext *execution_context) override {
230       m_verbose = false;
231     }
232 
GetDefinitions()233     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
234       return llvm::makeArrayRef(g_trace_schema_options);
235     }
236 
237     bool m_verbose; // Enable verbose logging for debugging purposes.
238   };
239 
CommandObjectTraceSchema(CommandInterpreter & interpreter)240   CommandObjectTraceSchema(CommandInterpreter &interpreter)
241       : CommandObjectParsed(interpreter, "trace schema",
242                             "Show the schema of the given trace plugin.",
243                             "trace schema <plug-in>. Use the plug-in name "
244                             "\"all\" to see all schemas.\n"),
245         m_options() {}
246 
247   ~CommandObjectTraceSchema() override = default;
248 
GetOptions()249   Options *GetOptions() override { return &m_options; }
250 
251 protected:
DoExecute(Args & command,CommandReturnObject & result)252   bool DoExecute(Args &command, CommandReturnObject &result) override {
253     Status error;
254     if (command.empty()) {
255       result.SetError(
256           "trace schema cannot be invoked without a plug-in as argument");
257       return false;
258     }
259 
260     StringRef plugin_name(command[0].c_str());
261     if (plugin_name == "all") {
262       size_t index = 0;
263       while (true) {
264         StringRef schema = PluginManager::GetTraceSchema(index++);
265         if (schema.empty())
266           break;
267 
268         result.AppendMessage(schema);
269       }
270     } else {
271       if (Expected<StringRef> schemaOrErr =
272               Trace::FindPluginSchema(plugin_name))
273         result.AppendMessage(*schemaOrErr);
274       else
275         error = schemaOrErr.takeError();
276     }
277 
278     if (error.Success()) {
279       result.SetStatus(eReturnStatusSuccessFinishResult);
280     } else {
281       result.AppendErrorWithFormat("%s\n", error.AsCString());
282       result.SetStatus(eReturnStatusFailed);
283     }
284     return result.Succeeded();
285   }
286 
287   CommandOptions m_options;
288 };
289 
290 // CommandObjectTrace
291 
CommandObjectTrace(CommandInterpreter & interpreter)292 CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter)
293     : CommandObjectMultiword(interpreter, "trace",
294                              "Commands for loading and using processor "
295                              "trace information.",
296                              "trace [<sub-command-options>]") {
297   LoadSubCommand("load",
298                  CommandObjectSP(new CommandObjectTraceLoad(interpreter)));
299   LoadSubCommand("dump",
300                  CommandObjectSP(new CommandObjectTraceDump(interpreter)));
301   LoadSubCommand("schema",
302                  CommandObjectSP(new CommandObjectTraceSchema(interpreter)));
303 }
304 
305 CommandObjectTrace::~CommandObjectTrace() = default;
306