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() { 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(
76             interpreter, "trace load",
77             "Load a post-mortem processor trace session from a trace bundle.",
78             "trace load") {}
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("a single path to a JSON file containing a the "
88                          "description of the trace bundle is required");
89       return false;
90     }
91 
92     const FileSpec trace_description_file(command[0].ref());
93 
94     llvm::Expected<lldb::TraceSP> trace_or_err =
95         Trace::LoadPostMortemTraceFromFile(GetDebugger(),
96                                            trace_description_file);
97 
98     if (!trace_or_err) {
99       result.AppendErrorWithFormat(
100           "%s\n", llvm::toString(trace_or_err.takeError()).c_str());
101       return false;
102     }
103 
104     if (m_options.m_verbose) {
105       result.AppendMessageWithFormatv("loading trace with plugin {0}\n",
106                                       trace_or_err.get()->GetPluginName());
107     }
108 
109     result.SetStatus(eReturnStatusSuccessFinishResult);
110     return true;
111   }
112 
113   CommandOptions m_options;
114 };
115 
116 // CommandObjectTraceDump
117 #define LLDB_OPTIONS_trace_dump
118 #include "CommandOptions.inc"
119 
120 #pragma mark CommandObjectTraceDump
121 
122 class CommandObjectTraceDump : public CommandObjectParsed {
123 public:
124   class CommandOptions : public Options {
125   public:
126     CommandOptions() { OptionParsingStarting(nullptr); }
127 
128     ~CommandOptions() override = default;
129 
130     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
131                           ExecutionContext *execution_context) override {
132       Status error;
133       const int short_option = m_getopt_table[option_idx].val;
134 
135       switch (short_option) {
136       case 'v': {
137         m_verbose = true;
138         break;
139       }
140       default:
141         llvm_unreachable("Unimplemented option");
142       }
143       return error;
144     }
145 
146     void OptionParsingStarting(ExecutionContext *execution_context) override {
147       m_verbose = false;
148     }
149 
150     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
151       return llvm::makeArrayRef(g_trace_dump_options);
152     }
153 
154     bool m_verbose; // Enable verbose logging for debugging purposes.
155   };
156 
157   CommandObjectTraceDump(CommandInterpreter &interpreter)
158       : CommandObjectParsed(interpreter, "trace dump",
159                             "Dump the loaded processor trace data.",
160                             "trace dump") {}
161 
162   ~CommandObjectTraceDump() override = default;
163 
164   Options *GetOptions() override { return &m_options; }
165 
166 protected:
167   bool DoExecute(Args &command, CommandReturnObject &result) override {
168     Status error;
169     // TODO: fill in the dumping code here!
170     if (error.Success()) {
171       result.SetStatus(eReturnStatusSuccessFinishResult);
172     } else {
173       result.AppendErrorWithFormat("%s\n", error.AsCString());
174     }
175     return result.Succeeded();
176   }
177 
178   CommandOptions m_options;
179 };
180 
181 // CommandObjectTraceSchema
182 #define LLDB_OPTIONS_trace_schema
183 #include "CommandOptions.inc"
184 
185 #pragma mark CommandObjectTraceSchema
186 
187 class CommandObjectTraceSchema : public CommandObjectParsed {
188 public:
189   class CommandOptions : public Options {
190   public:
191     CommandOptions() { OptionParsingStarting(nullptr); }
192 
193     ~CommandOptions() override = default;
194 
195     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
196                           ExecutionContext *execution_context) override {
197       Status error;
198       const int short_option = m_getopt_table[option_idx].val;
199 
200       switch (short_option) {
201       case 'v': {
202         m_verbose = true;
203         break;
204       }
205       default:
206         llvm_unreachable("Unimplemented option");
207       }
208       return error;
209     }
210 
211     void OptionParsingStarting(ExecutionContext *execution_context) override {
212       m_verbose = false;
213     }
214 
215     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
216       return llvm::makeArrayRef(g_trace_schema_options);
217     }
218 
219     bool m_verbose; // Enable verbose logging for debugging purposes.
220   };
221 
222   CommandObjectTraceSchema(CommandInterpreter &interpreter)
223       : CommandObjectParsed(interpreter, "trace schema",
224                             "Show the schema of the given trace plugin.",
225                             "trace schema <plug-in>. Use the plug-in name "
226                             "\"all\" to see all schemas.\n") {}
227 
228   ~CommandObjectTraceSchema() override = default;
229 
230   Options *GetOptions() override { return &m_options; }
231 
232 protected:
233   bool DoExecute(Args &command, CommandReturnObject &result) override {
234     Status error;
235     if (command.empty()) {
236       result.AppendError(
237           "trace schema cannot be invoked without a plug-in as argument");
238       return false;
239     }
240 
241     StringRef plugin_name(command[0].c_str());
242     if (plugin_name == "all") {
243       size_t index = 0;
244       while (true) {
245         StringRef schema = PluginManager::GetTraceSchema(index++);
246         if (schema.empty())
247           break;
248 
249         result.AppendMessage(schema);
250       }
251     } else {
252       if (Expected<StringRef> schemaOrErr =
253               Trace::FindPluginSchema(plugin_name))
254         result.AppendMessage(*schemaOrErr);
255       else
256         error = schemaOrErr.takeError();
257     }
258 
259     if (error.Success()) {
260       result.SetStatus(eReturnStatusSuccessFinishResult);
261     } else {
262       result.AppendErrorWithFormat("%s\n", error.AsCString());
263     }
264     return result.Succeeded();
265   }
266 
267   CommandOptions m_options;
268 };
269 
270 // CommandObjectTrace
271 
272 CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter)
273     : CommandObjectMultiword(interpreter, "trace",
274                              "Commands for loading and using processor "
275                              "trace information.",
276                              "trace [<sub-command-options>]") {
277   LoadSubCommand("load",
278                  CommandObjectSP(new CommandObjectTraceLoad(interpreter)));
279   LoadSubCommand("dump",
280                  CommandObjectSP(new CommandObjectTraceDump(interpreter)));
281   LoadSubCommand("schema",
282                  CommandObjectSP(new CommandObjectTraceSchema(interpreter)));
283 }
284 
285 CommandObjectTrace::~CommandObjectTrace() = default;
286 
287 Expected<CommandObjectSP> CommandObjectTraceProxy::DoGetProxyCommandObject() {
288   ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP();
289 
290   if (!process_sp)
291     return createStringError(inconvertibleErrorCode(),
292                              "Process not available.");
293   if (m_live_debug_session_only && !process_sp->IsLiveDebugSession())
294     return createStringError(inconvertibleErrorCode(),
295                              "Process must be alive.");
296 
297   if (Expected<TraceSP> trace_sp = process_sp->GetTarget().GetTraceOrCreate())
298     return GetDelegateCommand(**trace_sp);
299   else
300     return createStringError(inconvertibleErrorCode(),
301                              "Tracing is not supported. %s",
302                              toString(trace_sp.takeError()).c_str());
303 }
304 
305 CommandObject *CommandObjectTraceProxy::GetProxyCommandObject() {
306   if (Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) {
307     m_delegate_sp = *delegate;
308     m_delegate_error.clear();
309     return m_delegate_sp.get();
310   } else {
311     m_delegate_sp.reset();
312     m_delegate_error = toString(delegate.takeError());
313     return nullptr;
314   }
315 }
316