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