xref: /freebsd-src/contrib/llvm-project/lldb/source/Commands/CommandObjectTrace.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
1e8d8bef9SDimitry Andric //===-- CommandObjectTrace.cpp --------------------------------------------===//
2e8d8bef9SDimitry Andric //
3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e8d8bef9SDimitry Andric //
7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8e8d8bef9SDimitry Andric 
9e8d8bef9SDimitry Andric #include "CommandObjectTrace.h"
10e8d8bef9SDimitry Andric 
11e8d8bef9SDimitry Andric #include "llvm/Support/JSON.h"
12e8d8bef9SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
13e8d8bef9SDimitry Andric 
14e8d8bef9SDimitry Andric #include "lldb/Core/Debugger.h"
15e8d8bef9SDimitry Andric #include "lldb/Core/PluginManager.h"
16e8d8bef9SDimitry Andric #include "lldb/Host/OptionParser.h"
17e8d8bef9SDimitry Andric #include "lldb/Interpreter/CommandInterpreter.h"
18e8d8bef9SDimitry Andric #include "lldb/Interpreter/CommandObject.h"
19e8d8bef9SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h"
20e8d8bef9SDimitry Andric #include "lldb/Interpreter/OptionArgParser.h"
21e8d8bef9SDimitry Andric #include "lldb/Interpreter/OptionGroupFormat.h"
22e8d8bef9SDimitry Andric #include "lldb/Interpreter/OptionValueBoolean.h"
23e8d8bef9SDimitry Andric #include "lldb/Interpreter/OptionValueLanguage.h"
24e8d8bef9SDimitry Andric #include "lldb/Interpreter/OptionValueString.h"
25e8d8bef9SDimitry Andric #include "lldb/Interpreter/Options.h"
26*fe6060f1SDimitry Andric #include "lldb/Target/Process.h"
27e8d8bef9SDimitry Andric #include "lldb/Target/Trace.h"
28e8d8bef9SDimitry Andric 
29e8d8bef9SDimitry Andric using namespace lldb;
30e8d8bef9SDimitry Andric using namespace lldb_private;
31e8d8bef9SDimitry Andric using namespace llvm;
32e8d8bef9SDimitry Andric 
33e8d8bef9SDimitry Andric // CommandObjectTraceLoad
34e8d8bef9SDimitry Andric #define LLDB_OPTIONS_trace_load
35e8d8bef9SDimitry Andric #include "CommandOptions.inc"
36e8d8bef9SDimitry Andric 
37e8d8bef9SDimitry Andric #pragma mark CommandObjectTraceLoad
38e8d8bef9SDimitry Andric 
39e8d8bef9SDimitry Andric class CommandObjectTraceLoad : public CommandObjectParsed {
40e8d8bef9SDimitry Andric public:
41e8d8bef9SDimitry Andric   class CommandOptions : public Options {
42e8d8bef9SDimitry Andric   public:
43e8d8bef9SDimitry Andric     CommandOptions() : Options() { OptionParsingStarting(nullptr); }
44e8d8bef9SDimitry Andric 
45e8d8bef9SDimitry Andric     ~CommandOptions() override = default;
46e8d8bef9SDimitry Andric 
47e8d8bef9SDimitry Andric     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
48e8d8bef9SDimitry Andric                           ExecutionContext *execution_context) override {
49e8d8bef9SDimitry Andric       Status error;
50e8d8bef9SDimitry Andric       const int short_option = m_getopt_table[option_idx].val;
51e8d8bef9SDimitry Andric 
52e8d8bef9SDimitry Andric       switch (short_option) {
53e8d8bef9SDimitry Andric       case 'v': {
54e8d8bef9SDimitry Andric         m_verbose = true;
55e8d8bef9SDimitry Andric         break;
56e8d8bef9SDimitry Andric       }
57e8d8bef9SDimitry Andric       default:
58e8d8bef9SDimitry Andric         llvm_unreachable("Unimplemented option");
59e8d8bef9SDimitry Andric       }
60e8d8bef9SDimitry Andric       return error;
61e8d8bef9SDimitry Andric     }
62e8d8bef9SDimitry Andric 
63e8d8bef9SDimitry Andric     void OptionParsingStarting(ExecutionContext *execution_context) override {
64e8d8bef9SDimitry Andric       m_verbose = false;
65e8d8bef9SDimitry Andric     }
66e8d8bef9SDimitry Andric 
67e8d8bef9SDimitry Andric     ArrayRef<OptionDefinition> GetDefinitions() override {
68e8d8bef9SDimitry Andric       return makeArrayRef(g_trace_load_options);
69e8d8bef9SDimitry Andric     }
70e8d8bef9SDimitry Andric 
71e8d8bef9SDimitry Andric     bool m_verbose; // Enable verbose logging for debugging purposes.
72e8d8bef9SDimitry Andric   };
73e8d8bef9SDimitry Andric 
74e8d8bef9SDimitry Andric   CommandObjectTraceLoad(CommandInterpreter &interpreter)
75e8d8bef9SDimitry Andric       : CommandObjectParsed(interpreter, "trace load",
76e8d8bef9SDimitry Andric                             "Load a processor trace session from a JSON file.",
77e8d8bef9SDimitry Andric                             "trace load"),
78e8d8bef9SDimitry Andric         m_options() {}
79e8d8bef9SDimitry Andric 
80e8d8bef9SDimitry Andric   ~CommandObjectTraceLoad() override = default;
81e8d8bef9SDimitry Andric 
82e8d8bef9SDimitry Andric   Options *GetOptions() override { return &m_options; }
83e8d8bef9SDimitry Andric 
84e8d8bef9SDimitry Andric protected:
85e8d8bef9SDimitry Andric   bool DoExecute(Args &command, CommandReturnObject &result) override {
86e8d8bef9SDimitry Andric     if (command.size() != 1) {
87e8d8bef9SDimitry Andric       result.AppendError(
88e8d8bef9SDimitry Andric           "a single path to a JSON file containing a trace session"
89e8d8bef9SDimitry Andric           "is required");
90e8d8bef9SDimitry Andric       return false;
91e8d8bef9SDimitry Andric     }
92e8d8bef9SDimitry Andric 
93e8d8bef9SDimitry Andric     auto end_with_failure = [&result](llvm::Error err) -> bool {
94e8d8bef9SDimitry Andric       result.AppendErrorWithFormat("%s\n",
95e8d8bef9SDimitry Andric                                    llvm::toString(std::move(err)).c_str());
96e8d8bef9SDimitry Andric       return false;
97e8d8bef9SDimitry Andric     };
98e8d8bef9SDimitry Andric 
99e8d8bef9SDimitry Andric     FileSpec json_file(command[0].ref());
100e8d8bef9SDimitry Andric 
101e8d8bef9SDimitry Andric     auto buffer_or_error = llvm::MemoryBuffer::getFile(json_file.GetPath());
102e8d8bef9SDimitry Andric     if (!buffer_or_error) {
103e8d8bef9SDimitry Andric       return end_with_failure(llvm::createStringError(
104e8d8bef9SDimitry Andric           std::errc::invalid_argument, "could not open input file: %s - %s.",
105e8d8bef9SDimitry Andric           json_file.GetPath().c_str(),
106e8d8bef9SDimitry Andric           buffer_or_error.getError().message().c_str()));
107e8d8bef9SDimitry Andric     }
108e8d8bef9SDimitry Andric 
109e8d8bef9SDimitry Andric     llvm::Expected<json::Value> session_file =
110e8d8bef9SDimitry Andric         json::parse(buffer_or_error.get()->getBuffer().str());
111e8d8bef9SDimitry Andric     if (!session_file)
112e8d8bef9SDimitry Andric       return end_with_failure(session_file.takeError());
113e8d8bef9SDimitry Andric 
114e8d8bef9SDimitry Andric     if (Expected<lldb::TraceSP> traceOrErr =
115*fe6060f1SDimitry Andric             Trace::FindPluginForPostMortemProcess(
116*fe6060f1SDimitry Andric                 GetDebugger(), *session_file,
117e8d8bef9SDimitry Andric                 json_file.GetDirectory().AsCString())) {
118e8d8bef9SDimitry Andric       lldb::TraceSP trace_sp = traceOrErr.get();
119*fe6060f1SDimitry Andric       if (m_options.m_verbose && trace_sp)
120e8d8bef9SDimitry Andric         result.AppendMessageWithFormat("loading trace with plugin %s\n",
121e8d8bef9SDimitry Andric                                        trace_sp->GetPluginName().AsCString());
122e8d8bef9SDimitry Andric     } else
123e8d8bef9SDimitry Andric       return end_with_failure(traceOrErr.takeError());
124e8d8bef9SDimitry Andric 
125e8d8bef9SDimitry Andric     result.SetStatus(eReturnStatusSuccessFinishResult);
126e8d8bef9SDimitry Andric     return true;
127e8d8bef9SDimitry Andric   }
128e8d8bef9SDimitry Andric 
129e8d8bef9SDimitry Andric   CommandOptions m_options;
130e8d8bef9SDimitry Andric };
131e8d8bef9SDimitry Andric 
132e8d8bef9SDimitry Andric // CommandObjectTraceDump
133e8d8bef9SDimitry Andric #define LLDB_OPTIONS_trace_dump
134e8d8bef9SDimitry Andric #include "CommandOptions.inc"
135e8d8bef9SDimitry Andric 
136e8d8bef9SDimitry Andric #pragma mark CommandObjectTraceDump
137e8d8bef9SDimitry Andric 
138e8d8bef9SDimitry Andric class CommandObjectTraceDump : public CommandObjectParsed {
139e8d8bef9SDimitry Andric public:
140e8d8bef9SDimitry Andric   class CommandOptions : public Options {
141e8d8bef9SDimitry Andric   public:
142e8d8bef9SDimitry Andric     CommandOptions() : Options() { OptionParsingStarting(nullptr); }
143e8d8bef9SDimitry Andric 
144e8d8bef9SDimitry Andric     ~CommandOptions() override = default;
145e8d8bef9SDimitry Andric 
146e8d8bef9SDimitry Andric     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
147e8d8bef9SDimitry Andric                           ExecutionContext *execution_context) override {
148e8d8bef9SDimitry Andric       Status error;
149e8d8bef9SDimitry Andric       const int short_option = m_getopt_table[option_idx].val;
150e8d8bef9SDimitry Andric 
151e8d8bef9SDimitry Andric       switch (short_option) {
152e8d8bef9SDimitry Andric       case 'v': {
153e8d8bef9SDimitry Andric         m_verbose = true;
154e8d8bef9SDimitry Andric         break;
155e8d8bef9SDimitry Andric       }
156e8d8bef9SDimitry Andric       default:
157e8d8bef9SDimitry Andric         llvm_unreachable("Unimplemented option");
158e8d8bef9SDimitry Andric       }
159e8d8bef9SDimitry Andric       return error;
160e8d8bef9SDimitry Andric     }
161e8d8bef9SDimitry Andric 
162e8d8bef9SDimitry Andric     void OptionParsingStarting(ExecutionContext *execution_context) override {
163e8d8bef9SDimitry Andric       m_verbose = false;
164e8d8bef9SDimitry Andric     }
165e8d8bef9SDimitry Andric 
166e8d8bef9SDimitry Andric     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
167e8d8bef9SDimitry Andric       return llvm::makeArrayRef(g_trace_dump_options);
168e8d8bef9SDimitry Andric     }
169e8d8bef9SDimitry Andric 
170e8d8bef9SDimitry Andric     bool m_verbose; // Enable verbose logging for debugging purposes.
171e8d8bef9SDimitry Andric   };
172e8d8bef9SDimitry Andric 
173e8d8bef9SDimitry Andric   CommandObjectTraceDump(CommandInterpreter &interpreter)
174e8d8bef9SDimitry Andric       : CommandObjectParsed(interpreter, "trace dump",
175e8d8bef9SDimitry Andric                             "Dump the loaded processor trace data.",
176e8d8bef9SDimitry Andric                             "trace dump"),
177e8d8bef9SDimitry Andric         m_options() {}
178e8d8bef9SDimitry Andric 
179e8d8bef9SDimitry Andric   ~CommandObjectTraceDump() override = default;
180e8d8bef9SDimitry Andric 
181e8d8bef9SDimitry Andric   Options *GetOptions() override { return &m_options; }
182e8d8bef9SDimitry Andric 
183e8d8bef9SDimitry Andric protected:
184e8d8bef9SDimitry Andric   bool DoExecute(Args &command, CommandReturnObject &result) override {
185e8d8bef9SDimitry Andric     Status error;
186e8d8bef9SDimitry Andric     // TODO: fill in the dumping code here!
187e8d8bef9SDimitry Andric     if (error.Success()) {
188e8d8bef9SDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
189e8d8bef9SDimitry Andric     } else {
190e8d8bef9SDimitry Andric       result.AppendErrorWithFormat("%s\n", error.AsCString());
191e8d8bef9SDimitry Andric     }
192e8d8bef9SDimitry Andric     return result.Succeeded();
193e8d8bef9SDimitry Andric   }
194e8d8bef9SDimitry Andric 
195e8d8bef9SDimitry Andric   CommandOptions m_options;
196e8d8bef9SDimitry Andric };
197e8d8bef9SDimitry Andric 
198e8d8bef9SDimitry Andric // CommandObjectTraceSchema
199e8d8bef9SDimitry Andric #define LLDB_OPTIONS_trace_schema
200e8d8bef9SDimitry Andric #include "CommandOptions.inc"
201e8d8bef9SDimitry Andric 
202e8d8bef9SDimitry Andric #pragma mark CommandObjectTraceSchema
203e8d8bef9SDimitry Andric 
204e8d8bef9SDimitry Andric class CommandObjectTraceSchema : public CommandObjectParsed {
205e8d8bef9SDimitry Andric public:
206e8d8bef9SDimitry Andric   class CommandOptions : public Options {
207e8d8bef9SDimitry Andric   public:
208e8d8bef9SDimitry Andric     CommandOptions() : Options() { OptionParsingStarting(nullptr); }
209e8d8bef9SDimitry Andric 
210e8d8bef9SDimitry Andric     ~CommandOptions() override = default;
211e8d8bef9SDimitry Andric 
212e8d8bef9SDimitry Andric     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
213e8d8bef9SDimitry Andric                           ExecutionContext *execution_context) override {
214e8d8bef9SDimitry Andric       Status error;
215e8d8bef9SDimitry Andric       const int short_option = m_getopt_table[option_idx].val;
216e8d8bef9SDimitry Andric 
217e8d8bef9SDimitry Andric       switch (short_option) {
218e8d8bef9SDimitry Andric       case 'v': {
219e8d8bef9SDimitry Andric         m_verbose = true;
220e8d8bef9SDimitry Andric         break;
221e8d8bef9SDimitry Andric       }
222e8d8bef9SDimitry Andric       default:
223e8d8bef9SDimitry Andric         llvm_unreachable("Unimplemented option");
224e8d8bef9SDimitry Andric       }
225e8d8bef9SDimitry Andric       return error;
226e8d8bef9SDimitry Andric     }
227e8d8bef9SDimitry Andric 
228e8d8bef9SDimitry Andric     void OptionParsingStarting(ExecutionContext *execution_context) override {
229e8d8bef9SDimitry Andric       m_verbose = false;
230e8d8bef9SDimitry Andric     }
231e8d8bef9SDimitry Andric 
232e8d8bef9SDimitry Andric     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
233e8d8bef9SDimitry Andric       return llvm::makeArrayRef(g_trace_schema_options);
234e8d8bef9SDimitry Andric     }
235e8d8bef9SDimitry Andric 
236e8d8bef9SDimitry Andric     bool m_verbose; // Enable verbose logging for debugging purposes.
237e8d8bef9SDimitry Andric   };
238e8d8bef9SDimitry Andric 
239e8d8bef9SDimitry Andric   CommandObjectTraceSchema(CommandInterpreter &interpreter)
240e8d8bef9SDimitry Andric       : CommandObjectParsed(interpreter, "trace schema",
241e8d8bef9SDimitry Andric                             "Show the schema of the given trace plugin.",
242e8d8bef9SDimitry Andric                             "trace schema <plug-in>. Use the plug-in name "
243e8d8bef9SDimitry Andric                             "\"all\" to see all schemas.\n"),
244e8d8bef9SDimitry Andric         m_options() {}
245e8d8bef9SDimitry Andric 
246e8d8bef9SDimitry Andric   ~CommandObjectTraceSchema() override = default;
247e8d8bef9SDimitry Andric 
248e8d8bef9SDimitry Andric   Options *GetOptions() override { return &m_options; }
249e8d8bef9SDimitry Andric 
250e8d8bef9SDimitry Andric protected:
251e8d8bef9SDimitry Andric   bool DoExecute(Args &command, CommandReturnObject &result) override {
252e8d8bef9SDimitry Andric     Status error;
253e8d8bef9SDimitry Andric     if (command.empty()) {
254*fe6060f1SDimitry Andric       result.AppendError(
255e8d8bef9SDimitry Andric           "trace schema cannot be invoked without a plug-in as argument");
256e8d8bef9SDimitry Andric       return false;
257e8d8bef9SDimitry Andric     }
258e8d8bef9SDimitry Andric 
259e8d8bef9SDimitry Andric     StringRef plugin_name(command[0].c_str());
260e8d8bef9SDimitry Andric     if (plugin_name == "all") {
261e8d8bef9SDimitry Andric       size_t index = 0;
262e8d8bef9SDimitry Andric       while (true) {
263e8d8bef9SDimitry Andric         StringRef schema = PluginManager::GetTraceSchema(index++);
264e8d8bef9SDimitry Andric         if (schema.empty())
265e8d8bef9SDimitry Andric           break;
266e8d8bef9SDimitry Andric 
267e8d8bef9SDimitry Andric         result.AppendMessage(schema);
268e8d8bef9SDimitry Andric       }
269e8d8bef9SDimitry Andric     } else {
270e8d8bef9SDimitry Andric       if (Expected<StringRef> schemaOrErr =
271e8d8bef9SDimitry Andric               Trace::FindPluginSchema(plugin_name))
272e8d8bef9SDimitry Andric         result.AppendMessage(*schemaOrErr);
273e8d8bef9SDimitry Andric       else
274e8d8bef9SDimitry Andric         error = schemaOrErr.takeError();
275e8d8bef9SDimitry Andric     }
276e8d8bef9SDimitry Andric 
277e8d8bef9SDimitry Andric     if (error.Success()) {
278e8d8bef9SDimitry Andric       result.SetStatus(eReturnStatusSuccessFinishResult);
279e8d8bef9SDimitry Andric     } else {
280e8d8bef9SDimitry Andric       result.AppendErrorWithFormat("%s\n", error.AsCString());
281e8d8bef9SDimitry Andric     }
282e8d8bef9SDimitry Andric     return result.Succeeded();
283e8d8bef9SDimitry Andric   }
284e8d8bef9SDimitry Andric 
285e8d8bef9SDimitry Andric   CommandOptions m_options;
286e8d8bef9SDimitry Andric };
287e8d8bef9SDimitry Andric 
288e8d8bef9SDimitry Andric // CommandObjectTrace
289e8d8bef9SDimitry Andric 
290e8d8bef9SDimitry Andric CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter)
291e8d8bef9SDimitry Andric     : CommandObjectMultiword(interpreter, "trace",
292e8d8bef9SDimitry Andric                              "Commands for loading and using processor "
293e8d8bef9SDimitry Andric                              "trace information.",
294e8d8bef9SDimitry Andric                              "trace [<sub-command-options>]") {
295e8d8bef9SDimitry Andric   LoadSubCommand("load",
296e8d8bef9SDimitry Andric                  CommandObjectSP(new CommandObjectTraceLoad(interpreter)));
297e8d8bef9SDimitry Andric   LoadSubCommand("dump",
298e8d8bef9SDimitry Andric                  CommandObjectSP(new CommandObjectTraceDump(interpreter)));
299e8d8bef9SDimitry Andric   LoadSubCommand("schema",
300e8d8bef9SDimitry Andric                  CommandObjectSP(new CommandObjectTraceSchema(interpreter)));
301e8d8bef9SDimitry Andric }
302e8d8bef9SDimitry Andric 
303e8d8bef9SDimitry Andric CommandObjectTrace::~CommandObjectTrace() = default;
304*fe6060f1SDimitry Andric 
305*fe6060f1SDimitry Andric Expected<CommandObjectSP> CommandObjectTraceProxy::DoGetProxyCommandObject() {
306*fe6060f1SDimitry Andric   ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP();
307*fe6060f1SDimitry Andric 
308*fe6060f1SDimitry Andric   if (!process_sp)
309*fe6060f1SDimitry Andric     return createStringError(inconvertibleErrorCode(),
310*fe6060f1SDimitry Andric                              "Process not available.");
311*fe6060f1SDimitry Andric   if (m_live_debug_session_only && !process_sp->IsLiveDebugSession())
312*fe6060f1SDimitry Andric     return createStringError(inconvertibleErrorCode(),
313*fe6060f1SDimitry Andric                              "Process must be alive.");
314*fe6060f1SDimitry Andric 
315*fe6060f1SDimitry Andric   if (Expected<TraceSP> trace_sp = process_sp->GetTarget().GetTraceOrCreate())
316*fe6060f1SDimitry Andric     return GetDelegateCommand(**trace_sp);
317*fe6060f1SDimitry Andric   else
318*fe6060f1SDimitry Andric     return createStringError(inconvertibleErrorCode(),
319*fe6060f1SDimitry Andric                              "Tracing is not supported. %s",
320*fe6060f1SDimitry Andric                              toString(trace_sp.takeError()).c_str());
321*fe6060f1SDimitry Andric }
322*fe6060f1SDimitry Andric 
323*fe6060f1SDimitry Andric CommandObject *CommandObjectTraceProxy::GetProxyCommandObject() {
324*fe6060f1SDimitry Andric   if (Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) {
325*fe6060f1SDimitry Andric     m_delegate_sp = *delegate;
326*fe6060f1SDimitry Andric     m_delegate_error.clear();
327*fe6060f1SDimitry Andric     return m_delegate_sp.get();
328*fe6060f1SDimitry Andric   } else {
329*fe6060f1SDimitry Andric     m_delegate_sp.reset();
330*fe6060f1SDimitry Andric     m_delegate_error = toString(delegate.takeError());
331*fe6060f1SDimitry Andric     return nullptr;
332*fe6060f1SDimitry Andric   }
333*fe6060f1SDimitry Andric }
334