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