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/CommandOptionArgumentTable.h"
20 #include "lldb/Interpreter/CommandReturnObject.h"
21 #include "lldb/Interpreter/OptionArgParser.h"
22 #include "lldb/Interpreter/OptionGroupFormat.h"
23 #include "lldb/Interpreter/OptionValueBoolean.h"
24 #include "lldb/Interpreter/OptionValueLanguage.h"
25 #include "lldb/Interpreter/OptionValueString.h"
26 #include "lldb/Interpreter/Options.h"
27 #include "lldb/Target/Process.h"
28 #include "lldb/Target/Trace.h"
29 
30 using namespace lldb;
31 using namespace lldb_private;
32 using namespace llvm;
33 
34 // CommandObjectTraceSave
35 #define LLDB_OPTIONS_trace_save
36 #include "CommandOptions.inc"
37 
38 #pragma mark CommandObjectTraceSave
39 
40 class CommandObjectTraceSave : public CommandObjectParsed {
41 public:
42   class CommandOptions : public Options {
43   public:
CommandOptions()44     CommandOptions() { OptionParsingStarting(nullptr); }
45 
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)46     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
47                           ExecutionContext *execution_context) override {
48       Status error;
49       const int short_option = m_getopt_table[option_idx].val;
50 
51       switch (short_option) {
52       case 'c': {
53         m_compact = true;
54         break;
55       }
56       default:
57         llvm_unreachable("Unimplemented option");
58       }
59       return error;
60     }
61 
OptionParsingStarting(ExecutionContext * execution_context)62     void OptionParsingStarting(ExecutionContext *execution_context) override {
63       m_compact = false;
64     };
65 
GetDefinitions()66     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
67       return llvm::ArrayRef(g_trace_save_options);
68     };
69 
70     bool m_compact;
71   };
72 
GetOptions()73   Options *GetOptions() override { return &m_options; }
74 
CommandObjectTraceSave(CommandInterpreter & interpreter)75   CommandObjectTraceSave(CommandInterpreter &interpreter)
76       : CommandObjectParsed(
77             interpreter, "trace save",
78             "Save the trace of the current target in the specified directory, "
79             "which will be created if needed. "
80             "This directory will contain a trace bundle, with all the "
81             "necessary files the reconstruct the trace session even on a "
82             "different computer. "
83             "Part of this bundle is the bundle description file with the name "
84             "trace.json. This file can be used by the \"trace load\" command "
85             "to load this trace in LLDB."
86             "Note: if the current target contains information of multiple "
87             "processes or targets, they all will be included in the bundle.",
88             "trace save [<cmd-options>] <bundle_directory>",
89             eCommandRequiresProcess | eCommandTryTargetAPILock |
90                 eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
91                 eCommandProcessMustBeTraced) {
92     CommandArgumentData bundle_dir{eArgTypeDirectoryName, eArgRepeatPlain};
93     m_arguments.push_back({bundle_dir});
94   }
95 
96   void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)97   HandleArgumentCompletion(CompletionRequest &request,
98                            OptionElementVector &opt_element_vector) override {
99     lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
100         GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);
101   }
102 
103   ~CommandObjectTraceSave() override = default;
104 
105 protected:
DoExecute(Args & command,CommandReturnObject & result)106   void DoExecute(Args &command, CommandReturnObject &result) override {
107     if (command.size() != 1) {
108       result.AppendError("a single path to a directory where the trace bundle "
109                          "will be created is required");
110       return;
111     }
112 
113     FileSpec bundle_dir(command[0].ref());
114     FileSystem::Instance().Resolve(bundle_dir);
115 
116     ProcessSP process_sp = m_exe_ctx.GetProcessSP();
117 
118     TraceSP trace_sp = process_sp->GetTarget().GetTrace();
119 
120     if (llvm::Expected<FileSpec> desc_file =
121             trace_sp->SaveToDisk(bundle_dir, m_options.m_compact)) {
122       result.AppendMessageWithFormatv(
123           "Trace bundle description file written to: {0}", *desc_file);
124       result.SetStatus(eReturnStatusSuccessFinishResult);
125     } else {
126       result.AppendError(toString(desc_file.takeError()));
127     }
128   }
129 
130   CommandOptions m_options;
131 };
132 
133 // CommandObjectTraceLoad
134 #define LLDB_OPTIONS_trace_load
135 #include "CommandOptions.inc"
136 
137 #pragma mark CommandObjectTraceLoad
138 
139 class CommandObjectTraceLoad : public CommandObjectParsed {
140 public:
141   class CommandOptions : public Options {
142   public:
CommandOptions()143     CommandOptions() { OptionParsingStarting(nullptr); }
144 
145     ~CommandOptions() override = default;
146 
SetOptionValue(uint32_t option_idx,StringRef option_arg,ExecutionContext * execution_context)147     Status SetOptionValue(uint32_t option_idx, StringRef option_arg,
148                           ExecutionContext *execution_context) override {
149       Status error;
150       const int short_option = m_getopt_table[option_idx].val;
151 
152       switch (short_option) {
153       case 'v': {
154         m_verbose = true;
155         break;
156       }
157       default:
158         llvm_unreachable("Unimplemented option");
159       }
160       return error;
161     }
162 
OptionParsingStarting(ExecutionContext * execution_context)163     void OptionParsingStarting(ExecutionContext *execution_context) override {
164       m_verbose = false;
165     }
166 
GetDefinitions()167     ArrayRef<OptionDefinition> GetDefinitions() override {
168       return ArrayRef(g_trace_load_options);
169     }
170 
171     bool m_verbose; // Enable verbose logging for debugging purposes.
172   };
173 
CommandObjectTraceLoad(CommandInterpreter & interpreter)174   CommandObjectTraceLoad(CommandInterpreter &interpreter)
175       : CommandObjectParsed(
176             interpreter, "trace load",
177             "Load a post-mortem processor trace session from a trace bundle.",
178             "trace load <trace_description_file>") {
179     CommandArgumentData session_file_arg{eArgTypeFilename, eArgRepeatPlain};
180     m_arguments.push_back({session_file_arg});
181   }
182 
183   void
HandleArgumentCompletion(CompletionRequest & request,OptionElementVector & opt_element_vector)184   HandleArgumentCompletion(CompletionRequest &request,
185                            OptionElementVector &opt_element_vector) override {
186     lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
187         GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);
188   }
189 
190   ~CommandObjectTraceLoad() override = default;
191 
GetOptions()192   Options *GetOptions() override { return &m_options; }
193 
194 protected:
DoExecute(Args & command,CommandReturnObject & result)195   void DoExecute(Args &command, CommandReturnObject &result) override {
196     if (command.size() != 1) {
197       result.AppendError("a single path to a JSON file containing a the "
198                          "description of the trace bundle is required");
199       return;
200     }
201 
202     const FileSpec trace_description_file(command[0].ref());
203 
204     llvm::Expected<lldb::TraceSP> trace_or_err =
205         Trace::LoadPostMortemTraceFromFile(GetDebugger(),
206                                            trace_description_file);
207 
208     if (!trace_or_err) {
209       result.AppendErrorWithFormat(
210           "%s\n", llvm::toString(trace_or_err.takeError()).c_str());
211       return;
212     }
213 
214     if (m_options.m_verbose) {
215       result.AppendMessageWithFormatv("loading trace with plugin {0}\n",
216                                       trace_or_err.get()->GetPluginName());
217     }
218 
219     result.SetStatus(eReturnStatusSuccessFinishResult);
220   }
221 
222   CommandOptions m_options;
223 };
224 
225 // CommandObjectTraceDump
226 #define LLDB_OPTIONS_trace_dump
227 #include "CommandOptions.inc"
228 
229 #pragma mark CommandObjectTraceDump
230 
231 class CommandObjectTraceDump : public CommandObjectParsed {
232 public:
233   class CommandOptions : public Options {
234   public:
CommandOptions()235     CommandOptions() { OptionParsingStarting(nullptr); }
236 
237     ~CommandOptions() override = default;
238 
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)239     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
240                           ExecutionContext *execution_context) override {
241       Status error;
242       const int short_option = m_getopt_table[option_idx].val;
243 
244       switch (short_option) {
245       case 'v': {
246         m_verbose = true;
247         break;
248       }
249       default:
250         llvm_unreachable("Unimplemented option");
251       }
252       return error;
253     }
254 
OptionParsingStarting(ExecutionContext * execution_context)255     void OptionParsingStarting(ExecutionContext *execution_context) override {
256       m_verbose = false;
257     }
258 
GetDefinitions()259     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
260       return llvm::ArrayRef(g_trace_dump_options);
261     }
262 
263     bool m_verbose; // Enable verbose logging for debugging purposes.
264   };
265 
CommandObjectTraceDump(CommandInterpreter & interpreter)266   CommandObjectTraceDump(CommandInterpreter &interpreter)
267       : CommandObjectParsed(interpreter, "trace dump",
268                             "Dump the loaded processor trace data.",
269                             "trace dump") {}
270 
271   ~CommandObjectTraceDump() override = default;
272 
GetOptions()273   Options *GetOptions() override { return &m_options; }
274 
275 protected:
DoExecute(Args & command,CommandReturnObject & result)276   void DoExecute(Args &command, CommandReturnObject &result) override {
277     Status error;
278     // TODO: fill in the dumping code here!
279     if (error.Success()) {
280       result.SetStatus(eReturnStatusSuccessFinishResult);
281     } else {
282       result.AppendErrorWithFormat("%s\n", error.AsCString());
283     }
284   }
285 
286   CommandOptions m_options;
287 };
288 
289 // CommandObjectTraceSchema
290 #define LLDB_OPTIONS_trace_schema
291 #include "CommandOptions.inc"
292 
293 #pragma mark CommandObjectTraceSchema
294 
295 class CommandObjectTraceSchema : public CommandObjectParsed {
296 public:
297   class CommandOptions : public Options {
298   public:
CommandOptions()299     CommandOptions() { OptionParsingStarting(nullptr); }
300 
301     ~CommandOptions() override = default;
302 
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)303     Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
304                           ExecutionContext *execution_context) override {
305       Status error;
306       const int short_option = m_getopt_table[option_idx].val;
307 
308       switch (short_option) {
309       case 'v': {
310         m_verbose = true;
311         break;
312       }
313       default:
314         llvm_unreachable("Unimplemented option");
315       }
316       return error;
317     }
318 
OptionParsingStarting(ExecutionContext * execution_context)319     void OptionParsingStarting(ExecutionContext *execution_context) override {
320       m_verbose = false;
321     }
322 
GetDefinitions()323     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
324       return llvm::ArrayRef(g_trace_schema_options);
325     }
326 
327     bool m_verbose; // Enable verbose logging for debugging purposes.
328   };
329 
CommandObjectTraceSchema(CommandInterpreter & interpreter)330   CommandObjectTraceSchema(CommandInterpreter &interpreter)
331       : CommandObjectParsed(interpreter, "trace schema",
332                             "Show the schema of the given trace plugin.",
333                             "trace schema <plug-in>. Use the plug-in name "
334                             "\"all\" to see all schemas.\n") {
335     CommandArgumentData plugin_arg{eArgTypeNone, eArgRepeatPlain};
336     m_arguments.push_back({plugin_arg});
337   }
338 
339   ~CommandObjectTraceSchema() override = default;
340 
GetOptions()341   Options *GetOptions() override { return &m_options; }
342 
343 protected:
DoExecute(Args & command,CommandReturnObject & result)344   void DoExecute(Args &command, CommandReturnObject &result) override {
345     Status error;
346     if (command.empty()) {
347       result.AppendError(
348           "trace schema cannot be invoked without a plug-in as argument");
349       return;
350     }
351 
352     StringRef plugin_name(command[0].c_str());
353     if (plugin_name == "all") {
354       size_t index = 0;
355       while (true) {
356         StringRef schema = PluginManager::GetTraceSchema(index++);
357         if (schema.empty())
358           break;
359 
360         result.AppendMessage(schema);
361       }
362     } else {
363       if (Expected<StringRef> schemaOrErr =
364               Trace::FindPluginSchema(plugin_name))
365         result.AppendMessage(*schemaOrErr);
366       else
367         error = schemaOrErr.takeError();
368     }
369 
370     if (error.Success()) {
371       result.SetStatus(eReturnStatusSuccessFinishResult);
372     } else {
373       result.AppendErrorWithFormat("%s\n", error.AsCString());
374     }
375   }
376 
377   CommandOptions m_options;
378 };
379 
380 // CommandObjectTrace
381 
CommandObjectTrace(CommandInterpreter & interpreter)382 CommandObjectTrace::CommandObjectTrace(CommandInterpreter &interpreter)
383     : CommandObjectMultiword(interpreter, "trace",
384                              "Commands for loading and using processor "
385                              "trace information.",
386                              "trace [<sub-command-options>]") {
387   LoadSubCommand("load",
388                  CommandObjectSP(new CommandObjectTraceLoad(interpreter)));
389   LoadSubCommand("dump",
390                  CommandObjectSP(new CommandObjectTraceDump(interpreter)));
391   LoadSubCommand("save",
392                  CommandObjectSP(new CommandObjectTraceSave(interpreter)));
393   LoadSubCommand("schema",
394                  CommandObjectSP(new CommandObjectTraceSchema(interpreter)));
395 }
396 
397 CommandObjectTrace::~CommandObjectTrace() = default;
398 
DoGetProxyCommandObject()399 Expected<CommandObjectSP> CommandObjectTraceProxy::DoGetProxyCommandObject() {
400   ProcessSP process_sp = m_interpreter.GetExecutionContext().GetProcessSP();
401 
402   if (!process_sp)
403     return createStringError(inconvertibleErrorCode(),
404                              "Process not available.");
405   if (m_live_debug_session_only && !process_sp->IsLiveDebugSession())
406     return createStringError(inconvertibleErrorCode(),
407                              "Process must be alive.");
408 
409   if (Expected<TraceSP> trace_sp = process_sp->GetTarget().GetTraceOrCreate())
410     return GetDelegateCommand(**trace_sp);
411   else
412     return createStringError(inconvertibleErrorCode(),
413                              "Tracing is not supported. %s",
414                              toString(trace_sp.takeError()).c_str());
415 }
416 
GetProxyCommandObject()417 CommandObject *CommandObjectTraceProxy::GetProxyCommandObject() {
418   if (Expected<CommandObjectSP> delegate = DoGetProxyCommandObject()) {
419     m_delegate_sp = *delegate;
420     m_delegate_error.clear();
421     return m_delegate_sp.get();
422   } else {
423     m_delegate_sp.reset();
424     m_delegate_error = toString(delegate.takeError());
425     return nullptr;
426   }
427 }
428