1 //===-- CommandObjectTrace.cpp --------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "CommandObjectTrace.h"
11 #include "llvm/Support/JSON.h"
12 #include "llvm/Support/MemoryBuffer.h"
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"
31 using namespace lldb_private
;
34 // CommandObjectTraceSave
35 #define LLDB_OPTIONS_trace_save
36 #include "CommandOptions.inc"
38 #pragma mark CommandObjectTraceSave
40 class CommandObjectTraceSave
: public CommandObjectParsed
{
42 class CommandOptions
: public Options
{
44 CommandOptions() { OptionParsingStarting(nullptr); }
46 Status
SetOptionValue(uint32_t option_idx
, llvm::StringRef option_arg
,
47 ExecutionContext
*execution_context
) override
{
49 const int short_option
= m_getopt_table
[option_idx
].val
;
51 switch (short_option
) {
57 llvm_unreachable("Unimplemented option");
62 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
66 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
67 return llvm::ArrayRef(g_trace_save_options
);
73 Options
*GetOptions() override
{ return &m_options
; }
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 AddSimpleArgumentList(eArgTypeDirectoryName
);
96 HandleArgumentCompletion(CompletionRequest
&request
,
97 OptionElementVector
&opt_element_vector
) override
{
98 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
99 GetCommandInterpreter(), lldb::eDiskFileCompletion
, request
, nullptr);
102 ~CommandObjectTraceSave() override
= default;
105 void DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
106 if (command
.size() != 1) {
107 result
.AppendError("a single path to a directory where the trace bundle "
108 "will be created is required");
112 FileSpec
bundle_dir(command
[0].ref());
113 FileSystem::Instance().Resolve(bundle_dir
);
115 ProcessSP process_sp
= m_exe_ctx
.GetProcessSP();
117 TraceSP trace_sp
= process_sp
->GetTarget().GetTrace();
119 if (llvm::Expected
<FileSpec
> desc_file
=
120 trace_sp
->SaveToDisk(bundle_dir
, m_options
.m_compact
)) {
121 result
.AppendMessageWithFormatv(
122 "Trace bundle description file written to: {0}", *desc_file
);
123 result
.SetStatus(eReturnStatusSuccessFinishResult
);
125 result
.AppendError(toString(desc_file
.takeError()));
129 CommandOptions m_options
;
132 // CommandObjectTraceLoad
133 #define LLDB_OPTIONS_trace_load
134 #include "CommandOptions.inc"
136 #pragma mark CommandObjectTraceLoad
138 class CommandObjectTraceLoad
: public CommandObjectParsed
{
140 class CommandOptions
: public Options
{
142 CommandOptions() { OptionParsingStarting(nullptr); }
144 ~CommandOptions() override
= default;
146 Status
SetOptionValue(uint32_t option_idx
, StringRef option_arg
,
147 ExecutionContext
*execution_context
) override
{
149 const int short_option
= m_getopt_table
[option_idx
].val
;
151 switch (short_option
) {
157 llvm_unreachable("Unimplemented option");
162 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
166 ArrayRef
<OptionDefinition
> GetDefinitions() override
{
167 return ArrayRef(g_trace_load_options
);
170 bool m_verbose
; // Enable verbose logging for debugging purposes.
173 CommandObjectTraceLoad(CommandInterpreter
&interpreter
)
174 : CommandObjectParsed(
175 interpreter
, "trace load",
176 "Load a post-mortem processor trace session from a trace bundle.",
177 "trace load <trace_description_file>") {
178 AddSimpleArgumentList(eArgTypeFilename
);
182 HandleArgumentCompletion(CompletionRequest
&request
,
183 OptionElementVector
&opt_element_vector
) override
{
184 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
185 GetCommandInterpreter(), lldb::eDiskFileCompletion
, request
, nullptr);
188 ~CommandObjectTraceLoad() override
= default;
190 Options
*GetOptions() override
{ return &m_options
; }
193 void DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
194 if (command
.size() != 1) {
195 result
.AppendError("a single path to a JSON file containing a the "
196 "description of the trace bundle is required");
200 const FileSpec
trace_description_file(command
[0].ref());
202 llvm::Expected
<lldb::TraceSP
> trace_or_err
=
203 Trace::LoadPostMortemTraceFromFile(GetDebugger(),
204 trace_description_file
);
207 result
.AppendErrorWithFormat(
208 "%s\n", llvm::toString(trace_or_err
.takeError()).c_str());
212 if (m_options
.m_verbose
) {
213 result
.AppendMessageWithFormatv("loading trace with plugin {0}\n",
214 trace_or_err
.get()->GetPluginName());
217 result
.SetStatus(eReturnStatusSuccessFinishResult
);
220 CommandOptions m_options
;
223 // CommandObjectTraceDump
224 #define LLDB_OPTIONS_trace_dump
225 #include "CommandOptions.inc"
227 #pragma mark CommandObjectTraceDump
229 class CommandObjectTraceDump
: public CommandObjectParsed
{
231 class CommandOptions
: public Options
{
233 CommandOptions() { OptionParsingStarting(nullptr); }
235 ~CommandOptions() override
= default;
237 Status
SetOptionValue(uint32_t option_idx
, llvm::StringRef option_arg
,
238 ExecutionContext
*execution_context
) override
{
240 const int short_option
= m_getopt_table
[option_idx
].val
;
242 switch (short_option
) {
248 llvm_unreachable("Unimplemented option");
253 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
257 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
258 return llvm::ArrayRef(g_trace_dump_options
);
261 bool m_verbose
; // Enable verbose logging for debugging purposes.
264 CommandObjectTraceDump(CommandInterpreter
&interpreter
)
265 : CommandObjectParsed(interpreter
, "trace dump",
266 "Dump the loaded processor trace data.",
269 ~CommandObjectTraceDump() override
= default;
271 Options
*GetOptions() override
{ return &m_options
; }
274 void DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
276 // TODO: fill in the dumping code here!
277 if (error
.Success()) {
278 result
.SetStatus(eReturnStatusSuccessFinishResult
);
280 result
.AppendErrorWithFormat("%s\n", error
.AsCString());
284 CommandOptions m_options
;
287 // CommandObjectTraceSchema
288 #define LLDB_OPTIONS_trace_schema
289 #include "CommandOptions.inc"
291 #pragma mark CommandObjectTraceSchema
293 class CommandObjectTraceSchema
: public CommandObjectParsed
{
295 class CommandOptions
: public Options
{
297 CommandOptions() { OptionParsingStarting(nullptr); }
299 ~CommandOptions() override
= default;
301 Status
SetOptionValue(uint32_t option_idx
, llvm::StringRef option_arg
,
302 ExecutionContext
*execution_context
) override
{
304 const int short_option
= m_getopt_table
[option_idx
].val
;
306 switch (short_option
) {
312 llvm_unreachable("Unimplemented option");
317 void OptionParsingStarting(ExecutionContext
*execution_context
) override
{
321 llvm::ArrayRef
<OptionDefinition
> GetDefinitions() override
{
322 return llvm::ArrayRef(g_trace_schema_options
);
325 bool m_verbose
; // Enable verbose logging for debugging purposes.
328 CommandObjectTraceSchema(CommandInterpreter
&interpreter
)
329 : CommandObjectParsed(interpreter
, "trace schema",
330 "Show the schema of the given trace plugin.",
331 "trace schema <plug-in>. Use the plug-in name "
332 "\"all\" to see all schemas.\n") {
333 AddSimpleArgumentList(eArgTypeNone
);
336 ~CommandObjectTraceSchema() override
= default;
338 Options
*GetOptions() override
{ return &m_options
; }
341 void DoExecute(Args
&command
, CommandReturnObject
&result
) override
{
343 if (command
.empty()) {
345 "trace schema cannot be invoked without a plug-in as argument");
349 StringRef
plugin_name(command
[0].c_str());
350 if (plugin_name
== "all") {
353 StringRef schema
= PluginManager::GetTraceSchema(index
++);
357 result
.AppendMessage(schema
);
360 if (Expected
<StringRef
> schemaOrErr
=
361 Trace::FindPluginSchema(plugin_name
))
362 result
.AppendMessage(*schemaOrErr
);
364 error
= Status::FromError(schemaOrErr
.takeError());
367 if (error
.Success()) {
368 result
.SetStatus(eReturnStatusSuccessFinishResult
);
370 result
.AppendErrorWithFormat("%s\n", error
.AsCString());
374 CommandOptions m_options
;
377 // CommandObjectTrace
379 CommandObjectTrace::CommandObjectTrace(CommandInterpreter
&interpreter
)
380 : CommandObjectMultiword(interpreter
, "trace",
381 "Commands for loading and using processor "
382 "trace information.",
383 "trace [<sub-command-options>]") {
384 LoadSubCommand("load",
385 CommandObjectSP(new CommandObjectTraceLoad(interpreter
)));
386 LoadSubCommand("dump",
387 CommandObjectSP(new CommandObjectTraceDump(interpreter
)));
388 LoadSubCommand("save",
389 CommandObjectSP(new CommandObjectTraceSave(interpreter
)));
390 LoadSubCommand("schema",
391 CommandObjectSP(new CommandObjectTraceSchema(interpreter
)));
394 CommandObjectTrace::~CommandObjectTrace() = default;
396 Expected
<CommandObjectSP
> CommandObjectTraceProxy::DoGetProxyCommandObject() {
397 ProcessSP process_sp
= m_interpreter
.GetExecutionContext().GetProcessSP();
400 return createStringError(inconvertibleErrorCode(),
401 "Process not available.");
402 if (m_live_debug_session_only
&& !process_sp
->IsLiveDebugSession())
403 return createStringError(inconvertibleErrorCode(),
404 "Process must be alive.");
406 if (Expected
<TraceSP
> trace_sp
= process_sp
->GetTarget().GetTraceOrCreate())
407 return GetDelegateCommand(**trace_sp
);
409 return createStringError(inconvertibleErrorCode(),
410 "Tracing is not supported. %s",
411 toString(trace_sp
.takeError()).c_str());
414 CommandObject
*CommandObjectTraceProxy::GetProxyCommandObject() {
415 if (Expected
<CommandObjectSP
> delegate
= DoGetProxyCommandObject()) {
416 m_delegate_sp
= *delegate
;
417 m_delegate_error
.clear();
418 return m_delegate_sp
.get();
420 m_delegate_sp
.reset();
421 m_delegate_error
= toString(delegate
.takeError());