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