llvm-project/lldb/source/API/SBCommandInterpreter.cpp
royitaqi e8dc8d614a
Add new Python API SBCommandInterpreter::GetTranscript() (#90703)
# Motivation

Currently, the user can already get the "transcript" (for "what is the
transcript", see `CommandInterpreter::SaveTranscript`). However, the
only way to obtain the transcript data as a user is to first destroy the
debugger, then read the save directory. Note that destroy-callbacks
cannot be used, because 1\ transcript data is private to the command
interpreter (see `CommandInterpreter.h`), and 2\ the writing of the
transcript is *after* the invocation of destory-callbacks (see
`Debugger::Destroy`).

So basically, there is no way to obtain the transcript:
* during the lifetime of a debugger (including the destroy-callbacks,
which often performs logging tasks, where the transcript can be useful)
* without relying on external storage

In theory, there are other ways for user to obtain transcript data
during a debugger's life cycle:
* Use Python API and intercept commands and results.
* Use CLI and record console input/output.

However, such ways rely on the client's setup and are not supported
natively by LLDB.


# Proposal

Add a new Python API `SBCommandInterpreter::GetTranscript()`.

Goals:
* It can be called at any time during the debugger's life cycle,
including in destroy-callbacks.
* It returns data in-memory.

Structured data:
* To make data processing easier, the return type is `SBStructuredData`.
See comments in code for how the data is organized.
* In the future, `SaveTranscript` can be updated to write different
formats using such data (e.g. JSON). This is probably accompanied by a
new setting (e.g. `interpreter.save-session-format`).

# Alternatives

The return type can also be `std::vector<std::pair<std::string,
SBCommandReturnObject>>`. This will make implementation easier, without
having to translate it to `SBStructuredData`. On the other hand,
`SBStructuredData` can convert to JSON easily, so it's more convenient
for user to process.

# Privacy

Both user commands and output/error in the transcript can contain
privacy data. However, as mentioned, the transcript is already available
to the user. The addition of the new API doesn't increase the level of
risk. In fact, it _lowers_ the risk of privacy data being leaked later
on, by avoiding writing such data to external storage.

Once the user (or their code) gets the transcript, it will be their
responsibility to make sure that any required privacy policies are
guaranteed.

# Tests

```
bin/llvm-lit -sv ../external/llvm-project/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
```

```
bin/llvm-lit -sv ../external/llvm-project/lldb/test/API/commands/session/save/TestSessionSave.py
```

---------

Co-authored-by: Roy Shi <royshi@meta.com>
Co-authored-by: Med Ismail Bennani <ismail@bennani.ma>
2024-05-20 15:49:46 -07:00

746 lines
24 KiB
C++

//===-- SBCommandInterpreter.cpp ------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb/Utility/StructuredData.h"
#include "lldb/lldb-types.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandObjectMultiword.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/Instrumentation.h"
#include "lldb/Utility/Listener.h"
#include "lldb/API/SBBroadcaster.h"
#include "lldb/API/SBCommandInterpreter.h"
#include "lldb/API/SBCommandInterpreterRunOptions.h"
#include "lldb/API/SBCommandReturnObject.h"
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBExecutionContext.h"
#include "lldb/API/SBListener.h"
#include "lldb/API/SBProcess.h"
#include "lldb/API/SBStream.h"
#include "lldb/API/SBStringList.h"
#include "lldb/API/SBTarget.h"
#include <memory>
#include <optional>
using namespace lldb;
using namespace lldb_private;
namespace lldb_private {
class CommandPluginInterfaceImplementation : public CommandObjectParsed {
public:
CommandPluginInterfaceImplementation(CommandInterpreter &interpreter,
const char *name,
lldb::SBCommandPluginInterface *backend,
const char *help = nullptr,
const char *syntax = nullptr,
uint32_t flags = 0,
const char *auto_repeat_command = "")
: CommandObjectParsed(interpreter, name, help, syntax, flags),
m_backend(backend) {
m_auto_repeat_command =
auto_repeat_command == nullptr
? std::nullopt
: std::optional<std::string>(auto_repeat_command);
// We don't know whether any given command coming from this interface takes
// arguments or not so here we're just disabling the basic args check.
CommandArgumentData none_arg{eArgTypeNone, eArgRepeatStar};
m_arguments.push_back({none_arg});
}
bool IsRemovable() const override { return true; }
/// More documentation is available in lldb::CommandObject::GetRepeatCommand,
/// but in short, if std::nullopt is returned, the previous command will be
/// repeated, and if an empty string is returned, no commands will be
/// executed.
std::optional<std::string> GetRepeatCommand(Args &current_command_args,
uint32_t index) override {
if (!m_auto_repeat_command)
return std::nullopt;
else
return m_auto_repeat_command;
}
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
SBCommandReturnObject sb_return(result);
SBCommandInterpreter sb_interpreter(&m_interpreter);
SBDebugger debugger_sb(m_interpreter.GetDebugger().shared_from_this());
m_backend->DoExecute(debugger_sb, command.GetArgumentVector(), sb_return);
}
std::shared_ptr<lldb::SBCommandPluginInterface> m_backend;
std::optional<std::string> m_auto_repeat_command;
};
} // namespace lldb_private
SBCommandInterpreter::SBCommandInterpreter() : m_opaque_ptr() {
LLDB_INSTRUMENT_VA(this);
}
SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter)
: m_opaque_ptr(interpreter) {
LLDB_INSTRUMENT_VA(this, interpreter);
}
SBCommandInterpreter::SBCommandInterpreter(const SBCommandInterpreter &rhs)
: m_opaque_ptr(rhs.m_opaque_ptr) {
LLDB_INSTRUMENT_VA(this, rhs);
}
SBCommandInterpreter::~SBCommandInterpreter() = default;
const SBCommandInterpreter &SBCommandInterpreter::
operator=(const SBCommandInterpreter &rhs) {
LLDB_INSTRUMENT_VA(this, rhs);
m_opaque_ptr = rhs.m_opaque_ptr;
return *this;
}
bool SBCommandInterpreter::IsValid() const {
LLDB_INSTRUMENT_VA(this);
return this->operator bool();
}
SBCommandInterpreter::operator bool() const {
LLDB_INSTRUMENT_VA(this);
return m_opaque_ptr != nullptr;
}
bool SBCommandInterpreter::CommandExists(const char *cmd) {
LLDB_INSTRUMENT_VA(this, cmd);
return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->CommandExists(cmd)
: false);
}
bool SBCommandInterpreter::UserCommandExists(const char *cmd) {
LLDB_INSTRUMENT_VA(this, cmd);
return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->UserCommandExists(cmd)
: false);
}
bool SBCommandInterpreter::AliasExists(const char *cmd) {
LLDB_INSTRUMENT_VA(this, cmd);
return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->AliasExists(cmd)
: false);
}
bool SBCommandInterpreter::IsActive() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? m_opaque_ptr->IsActive() : false);
}
bool SBCommandInterpreter::WasInterrupted() const {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? m_opaque_ptr->GetDebugger().InterruptRequested() : false);
}
bool SBCommandInterpreter::InterruptCommand() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? m_opaque_ptr->InterruptCommand() : false);
}
const char *SBCommandInterpreter::GetIOHandlerControlSequence(char ch) {
LLDB_INSTRUMENT_VA(this, ch);
if (!IsValid())
return nullptr;
return ConstString(
m_opaque_ptr->GetDebugger().GetTopIOHandlerControlSequence(ch))
.GetCString();
}
lldb::ReturnStatus
SBCommandInterpreter::HandleCommand(const char *command_line,
SBCommandReturnObject &result,
bool add_to_history) {
LLDB_INSTRUMENT_VA(this, command_line, result, add_to_history);
SBExecutionContext sb_exe_ctx;
return HandleCommand(command_line, sb_exe_ctx, result, add_to_history);
}
lldb::ReturnStatus SBCommandInterpreter::HandleCommand(
const char *command_line, SBExecutionContext &override_context,
SBCommandReturnObject &result, bool add_to_history) {
LLDB_INSTRUMENT_VA(this, command_line, override_context, result,
add_to_history);
result.Clear();
if (command_line && IsValid()) {
result.ref().SetInteractive(false);
auto do_add_to_history = add_to_history ? eLazyBoolYes : eLazyBoolNo;
if (override_context.get())
m_opaque_ptr->HandleCommand(command_line, do_add_to_history,
override_context.get()->Lock(true),
result.ref());
else
m_opaque_ptr->HandleCommand(command_line, do_add_to_history,
result.ref());
} else {
result->AppendError(
"SBCommandInterpreter or the command line is not valid");
}
return result.GetStatus();
}
void SBCommandInterpreter::HandleCommandsFromFile(
lldb::SBFileSpec &file, lldb::SBExecutionContext &override_context,
lldb::SBCommandInterpreterRunOptions &options,
lldb::SBCommandReturnObject result) {
LLDB_INSTRUMENT_VA(this, file, override_context, options, result);
if (!IsValid()) {
result->AppendError("SBCommandInterpreter is not valid.");
return;
}
if (!file.IsValid()) {
SBStream s;
file.GetDescription(s);
result->AppendErrorWithFormat("File is not valid: %s.", s.GetData());
}
FileSpec tmp_spec = file.ref();
if (override_context.get())
m_opaque_ptr->HandleCommandsFromFile(tmp_spec,
override_context.get()->Lock(true),
options.ref(),
result.ref());
else
m_opaque_ptr->HandleCommandsFromFile(tmp_spec, options.ref(), result.ref());
}
int SBCommandInterpreter::HandleCompletion(
const char *current_line, const char *cursor, const char *last_char,
int match_start_point, int max_return_elements, SBStringList &matches) {
LLDB_INSTRUMENT_VA(this, current_line, cursor, last_char, match_start_point,
max_return_elements, matches);
SBStringList dummy_descriptions;
return HandleCompletionWithDescriptions(
current_line, cursor, last_char, match_start_point, max_return_elements,
matches, dummy_descriptions);
}
int SBCommandInterpreter::HandleCompletionWithDescriptions(
const char *current_line, const char *cursor, const char *last_char,
int match_start_point, int max_return_elements, SBStringList &matches,
SBStringList &descriptions) {
LLDB_INSTRUMENT_VA(this, current_line, cursor, last_char, match_start_point,
max_return_elements, matches, descriptions);
// Sanity check the arguments that are passed in: cursor & last_char have to
// be within the current_line.
if (current_line == nullptr || cursor == nullptr || last_char == nullptr)
return 0;
if (cursor < current_line || last_char < current_line)
return 0;
size_t current_line_size = strlen(current_line);
if (cursor - current_line > static_cast<ptrdiff_t>(current_line_size) ||
last_char - current_line > static_cast<ptrdiff_t>(current_line_size))
return 0;
if (!IsValid())
return 0;
lldb_private::StringList lldb_matches, lldb_descriptions;
CompletionResult result;
CompletionRequest request(current_line, cursor - current_line, result);
m_opaque_ptr->HandleCompletion(request);
result.GetMatches(lldb_matches);
result.GetDescriptions(lldb_descriptions);
// Make the result array indexed from 1 again by adding the 'common prefix'
// of all completions as element 0. This is done to emulate the old API.
if (request.GetParsedLine().GetArgumentCount() == 0) {
// If we got an empty string, insert nothing.
lldb_matches.InsertStringAtIndex(0, "");
lldb_descriptions.InsertStringAtIndex(0, "");
} else {
// Now figure out if there is a common substring, and if so put that in
// element 0, otherwise put an empty string in element 0.
std::string command_partial_str = request.GetCursorArgumentPrefix().str();
std::string common_prefix = lldb_matches.LongestCommonPrefix();
const size_t partial_name_len = command_partial_str.size();
common_prefix.erase(0, partial_name_len);
// If we matched a unique single command, add a space... Only do this if
// the completer told us this was a complete word, however...
if (lldb_matches.GetSize() == 1) {
char quote_char = request.GetParsedArg().GetQuoteChar();
common_prefix =
Args::EscapeLLDBCommandArgument(common_prefix, quote_char);
if (request.GetParsedArg().IsQuoted())
common_prefix.push_back(quote_char);
common_prefix.push_back(' ');
}
lldb_matches.InsertStringAtIndex(0, common_prefix.c_str());
lldb_descriptions.InsertStringAtIndex(0, "");
}
SBStringList temp_matches_list(&lldb_matches);
matches.AppendList(temp_matches_list);
SBStringList temp_descriptions_list(&lldb_descriptions);
descriptions.AppendList(temp_descriptions_list);
return result.GetNumberOfResults();
}
int SBCommandInterpreter::HandleCompletionWithDescriptions(
const char *current_line, uint32_t cursor_pos, int match_start_point,
int max_return_elements, SBStringList &matches,
SBStringList &descriptions) {
LLDB_INSTRUMENT_VA(this, current_line, cursor_pos, match_start_point,
max_return_elements, matches, descriptions);
const char *cursor = current_line + cursor_pos;
const char *last_char = current_line + strlen(current_line);
return HandleCompletionWithDescriptions(
current_line, cursor, last_char, match_start_point, max_return_elements,
matches, descriptions);
}
int SBCommandInterpreter::HandleCompletion(const char *current_line,
uint32_t cursor_pos,
int match_start_point,
int max_return_elements,
lldb::SBStringList &matches) {
LLDB_INSTRUMENT_VA(this, current_line, cursor_pos, match_start_point,
max_return_elements, matches);
const char *cursor = current_line + cursor_pos;
const char *last_char = current_line + strlen(current_line);
return HandleCompletion(current_line, cursor, last_char, match_start_point,
max_return_elements, matches);
}
bool SBCommandInterpreter::HasCommands() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? m_opaque_ptr->HasCommands() : false);
}
bool SBCommandInterpreter::HasAliases() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? m_opaque_ptr->HasAliases() : false);
}
bool SBCommandInterpreter::HasAliasOptions() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? m_opaque_ptr->HasAliasOptions() : false);
}
bool SBCommandInterpreter::IsInteractive() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? m_opaque_ptr->IsInteractive() : false);
}
SBProcess SBCommandInterpreter::GetProcess() {
LLDB_INSTRUMENT_VA(this);
SBProcess sb_process;
ProcessSP process_sp;
if (IsValid()) {
TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
if (target_sp) {
std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
process_sp = target_sp->GetProcessSP();
sb_process.SetSP(process_sp);
}
}
return sb_process;
}
SBDebugger SBCommandInterpreter::GetDebugger() {
LLDB_INSTRUMENT_VA(this);
SBDebugger sb_debugger;
if (IsValid())
sb_debugger.reset(m_opaque_ptr->GetDebugger().shared_from_this());
return sb_debugger;
}
bool SBCommandInterpreter::GetPromptOnQuit() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? m_opaque_ptr->GetPromptOnQuit() : false);
}
void SBCommandInterpreter::SetPromptOnQuit(bool b) {
LLDB_INSTRUMENT_VA(this, b);
if (IsValid())
m_opaque_ptr->SetPromptOnQuit(b);
}
void SBCommandInterpreter::AllowExitCodeOnQuit(bool allow) {
LLDB_INSTRUMENT_VA(this, allow);
if (m_opaque_ptr)
m_opaque_ptr->AllowExitCodeOnQuit(allow);
}
bool SBCommandInterpreter::HasCustomQuitExitCode() {
LLDB_INSTRUMENT_VA(this);
bool exited = false;
if (m_opaque_ptr)
m_opaque_ptr->GetQuitExitCode(exited);
return exited;
}
int SBCommandInterpreter::GetQuitStatus() {
LLDB_INSTRUMENT_VA(this);
bool exited = false;
return (m_opaque_ptr ? m_opaque_ptr->GetQuitExitCode(exited) : 0);
}
void SBCommandInterpreter::ResolveCommand(const char *command_line,
SBCommandReturnObject &result) {
LLDB_INSTRUMENT_VA(this, command_line, result);
result.Clear();
if (command_line && IsValid()) {
m_opaque_ptr->ResolveCommand(command_line, result.ref());
} else {
result->AppendError(
"SBCommandInterpreter or the command line is not valid");
}
}
CommandInterpreter *SBCommandInterpreter::get() { return m_opaque_ptr; }
CommandInterpreter &SBCommandInterpreter::ref() {
assert(m_opaque_ptr);
return *m_opaque_ptr;
}
void SBCommandInterpreter::reset(
lldb_private::CommandInterpreter *interpreter) {
m_opaque_ptr = interpreter;
}
void SBCommandInterpreter::SourceInitFileInGlobalDirectory(
SBCommandReturnObject &result) {
LLDB_INSTRUMENT_VA(this, result);
result.Clear();
if (IsValid()) {
TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
std::unique_lock<std::recursive_mutex> lock;
if (target_sp)
lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
m_opaque_ptr->SourceInitFileGlobal(result.ref());
} else {
result->AppendError("SBCommandInterpreter is not valid");
}
}
void SBCommandInterpreter::SourceInitFileInHomeDirectory(
SBCommandReturnObject &result) {
LLDB_INSTRUMENT_VA(this, result);
SourceInitFileInHomeDirectory(result, /*is_repl=*/false);
}
void SBCommandInterpreter::SourceInitFileInHomeDirectory(
SBCommandReturnObject &result, bool is_repl) {
LLDB_INSTRUMENT_VA(this, result, is_repl);
result.Clear();
if (IsValid()) {
TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
std::unique_lock<std::recursive_mutex> lock;
if (target_sp)
lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
m_opaque_ptr->SourceInitFileHome(result.ref(), is_repl);
} else {
result->AppendError("SBCommandInterpreter is not valid");
}
}
void SBCommandInterpreter::SourceInitFileInCurrentWorkingDirectory(
SBCommandReturnObject &result) {
LLDB_INSTRUMENT_VA(this, result);
result.Clear();
if (IsValid()) {
TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
std::unique_lock<std::recursive_mutex> lock;
if (target_sp)
lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
m_opaque_ptr->SourceInitFileCwd(result.ref());
} else {
result->AppendError("SBCommandInterpreter is not valid");
}
}
SBBroadcaster SBCommandInterpreter::GetBroadcaster() {
LLDB_INSTRUMENT_VA(this);
SBBroadcaster broadcaster(m_opaque_ptr, false);
return broadcaster;
}
const char *SBCommandInterpreter::GetBroadcasterClass() {
LLDB_INSTRUMENT();
return ConstString(CommandInterpreter::GetStaticBroadcasterClass())
.AsCString();
}
const char *SBCommandInterpreter::GetArgumentTypeAsCString(
const lldb::CommandArgumentType arg_type) {
LLDB_INSTRUMENT_VA(arg_type);
return ConstString(CommandObject::GetArgumentTypeAsCString(arg_type))
.GetCString();
}
const char *SBCommandInterpreter::GetArgumentDescriptionAsCString(
const lldb::CommandArgumentType arg_type) {
LLDB_INSTRUMENT_VA(arg_type);
return ConstString(CommandObject::GetArgumentDescriptionAsCString(arg_type))
.GetCString();
}
bool SBCommandInterpreter::EventIsCommandInterpreterEvent(
const lldb::SBEvent &event) {
LLDB_INSTRUMENT_VA(event);
return event.GetBroadcasterClass() ==
SBCommandInterpreter::GetBroadcasterClass();
}
bool SBCommandInterpreter::SetCommandOverrideCallback(
const char *command_name, lldb::CommandOverrideCallback callback,
void *baton) {
LLDB_INSTRUMENT_VA(this, command_name, callback, baton);
if (command_name && command_name[0] && IsValid()) {
llvm::StringRef command_name_str = command_name;
CommandObject *cmd_obj =
m_opaque_ptr->GetCommandObjectForCommand(command_name_str);
if (cmd_obj) {
assert(command_name_str.empty());
cmd_obj->SetOverrideCallback(callback, baton);
return true;
}
}
return false;
}
SBStructuredData SBCommandInterpreter::GetStatistics() {
LLDB_INSTRUMENT_VA(this);
SBStructuredData data;
if (!IsValid())
return data;
std::string json_str =
llvm::formatv("{0:2}", m_opaque_ptr->GetStatistics()).str();
data.m_impl_up->SetObjectSP(StructuredData::ParseJSON(json_str));
return data;
}
SBStructuredData SBCommandInterpreter::GetTranscript() {
LLDB_INSTRUMENT_VA(this);
SBStructuredData data;
if (IsValid())
// A deep copy is performed by `std::make_shared` on the
// `StructuredData::Array`, via its implicitly-declared copy constructor.
// This ensures thread-safety between the user changing the returned
// `SBStructuredData` and the `CommandInterpreter` changing its internal
// `m_transcript`.
data.m_impl_up->SetObjectSP(
std::make_shared<StructuredData::Array>(m_opaque_ptr->GetTranscript()));
return data;
}
lldb::SBCommand SBCommandInterpreter::AddMultiwordCommand(const char *name,
const char *help) {
LLDB_INSTRUMENT_VA(this, name, help);
lldb::CommandObjectSP new_command_sp(
new CommandObjectMultiword(*m_opaque_ptr, name, help));
new_command_sp->GetAsMultiwordCommand()->SetRemovable(true);
Status add_error = m_opaque_ptr->AddUserCommand(name, new_command_sp, true);
if (add_error.Success())
return lldb::SBCommand(new_command_sp);
return lldb::SBCommand();
}
lldb::SBCommand SBCommandInterpreter::AddCommand(
const char *name, lldb::SBCommandPluginInterface *impl, const char *help) {
LLDB_INSTRUMENT_VA(this, name, impl, help);
return AddCommand(name, impl, help, /*syntax=*/nullptr,
/*auto_repeat_command=*/"");
}
lldb::SBCommand
SBCommandInterpreter::AddCommand(const char *name,
lldb::SBCommandPluginInterface *impl,
const char *help, const char *syntax) {
LLDB_INSTRUMENT_VA(this, name, impl, help, syntax);
return AddCommand(name, impl, help, syntax, /*auto_repeat_command=*/"");
}
lldb::SBCommand SBCommandInterpreter::AddCommand(
const char *name, lldb::SBCommandPluginInterface *impl, const char *help,
const char *syntax, const char *auto_repeat_command) {
LLDB_INSTRUMENT_VA(this, name, impl, help, syntax, auto_repeat_command);
lldb::CommandObjectSP new_command_sp;
new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
*m_opaque_ptr, name, impl, help, syntax, /*flags=*/0,
auto_repeat_command);
Status add_error = m_opaque_ptr->AddUserCommand(name, new_command_sp, true);
if (add_error.Success())
return lldb::SBCommand(new_command_sp);
return lldb::SBCommand();
}
SBCommand::SBCommand() { LLDB_INSTRUMENT_VA(this); }
SBCommand::SBCommand(lldb::CommandObjectSP cmd_sp) : m_opaque_sp(cmd_sp) {}
bool SBCommand::IsValid() {
LLDB_INSTRUMENT_VA(this);
return this->operator bool();
}
SBCommand::operator bool() const {
LLDB_INSTRUMENT_VA(this);
return m_opaque_sp.get() != nullptr;
}
const char *SBCommand::GetName() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? ConstString(m_opaque_sp->GetCommandName()).AsCString() : nullptr);
}
const char *SBCommand::GetHelp() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? ConstString(m_opaque_sp->GetHelp()).AsCString()
: nullptr);
}
const char *SBCommand::GetHelpLong() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? ConstString(m_opaque_sp->GetHelpLong()).AsCString()
: nullptr);
}
void SBCommand::SetHelp(const char *help) {
LLDB_INSTRUMENT_VA(this, help);
if (IsValid())
m_opaque_sp->SetHelp(help);
}
void SBCommand::SetHelpLong(const char *help) {
LLDB_INSTRUMENT_VA(this, help);
if (IsValid())
m_opaque_sp->SetHelpLong(help);
}
lldb::SBCommand SBCommand::AddMultiwordCommand(const char *name,
const char *help) {
LLDB_INSTRUMENT_VA(this, name, help);
if (!IsValid())
return lldb::SBCommand();
if (!m_opaque_sp->IsMultiwordObject())
return lldb::SBCommand();
CommandObjectMultiword *new_command = new CommandObjectMultiword(
m_opaque_sp->GetCommandInterpreter(), name, help);
new_command->SetRemovable(true);
lldb::CommandObjectSP new_command_sp(new_command);
if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
return lldb::SBCommand(new_command_sp);
return lldb::SBCommand();
}
lldb::SBCommand SBCommand::AddCommand(const char *name,
lldb::SBCommandPluginInterface *impl,
const char *help) {
LLDB_INSTRUMENT_VA(this, name, impl, help);
return AddCommand(name, impl, help, /*syntax=*/nullptr,
/*auto_repeat_command=*/"");
}
lldb::SBCommand SBCommand::AddCommand(const char *name,
lldb::SBCommandPluginInterface *impl,
const char *help, const char *syntax) {
LLDB_INSTRUMENT_VA(this, name, impl, help, syntax);
return AddCommand(name, impl, help, syntax, /*auto_repeat_command=*/"");
}
lldb::SBCommand SBCommand::AddCommand(const char *name,
lldb::SBCommandPluginInterface *impl,
const char *help, const char *syntax,
const char *auto_repeat_command) {
LLDB_INSTRUMENT_VA(this, name, impl, help, syntax, auto_repeat_command);
if (!IsValid())
return lldb::SBCommand();
if (!m_opaque_sp->IsMultiwordObject())
return lldb::SBCommand();
lldb::CommandObjectSP new_command_sp;
new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
m_opaque_sp->GetCommandInterpreter(), name, impl, help, syntax,
/*flags=*/0, auto_repeat_command);
if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
return lldb::SBCommand(new_command_sp);
return lldb::SBCommand();
}
uint32_t SBCommand::GetFlags() {
LLDB_INSTRUMENT_VA(this);
return (IsValid() ? m_opaque_sp->GetFlags().Get() : 0);
}
void SBCommand::SetFlags(uint32_t flags) {
LLDB_INSTRUMENT_VA(this, flags);
if (IsValid())
m_opaque_sp->GetFlags().Set(flags);
}