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:
43     CommandOptions() : Options() { OptionParsingStarting(nullptr); }
44 
45     ~CommandOptions() override = default;
46 
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 
63     void OptionParsingStarting(ExecutionContext *execution_context) override {
64       m_verbose = false;
65     }
66 
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 
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 
82   Options *GetOptions() override { return &m_options; }
83 
84 protected:
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:
142     CommandOptions() : Options() { OptionParsingStarting(nullptr); }
143 
144     ~CommandOptions() override = default;
145 
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 
162     void OptionParsingStarting(ExecutionContext *execution_context) override {
163       m_verbose = false;
164     }
165 
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 
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 
181   Options *GetOptions() override { return &m_options; }
182 
183 protected:
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:
208     CommandOptions() : Options() { OptionParsingStarting(nullptr); }
209 
210     ~CommandOptions() override = default;
211 
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 
228     void OptionParsingStarting(ExecutionContext *execution_context) override {
229       m_verbose = false;
230     }
231 
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 
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 
248   Options *GetOptions() override { return &m_options; }
249 
250 protected:
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 
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 
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 
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