blob: 396910abfb8b8cc57918a7396ef4ab0df7194979 [file]
//===-- CommandObjectPlugin.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 "CommandObjectPlugin.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Host/OptionParser.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandOptionArgumentTable.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/OptionArgParser.h"
using namespace lldb;
using namespace lldb_private;
class CommandObjectPluginLoad : public CommandObjectParsed {
public:
CommandObjectPluginLoad(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "plugin load",
"Import a dylib that implements an LLDB plugin.",
nullptr) {
AddSimpleArgumentList(eArgTypeFilename);
}
~CommandObjectPluginLoad() override = default;
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
size_t argc = command.GetArgumentCount();
if (argc != 1) {
result.AppendError("'plugin load' requires one argument");
return;
}
Status error;
FileSpec dylib_fspec(command[0].ref());
FileSystem::Instance().Resolve(dylib_fspec);
if (GetDebugger().LoadPlugin(dylib_fspec, error))
result.SetStatus(eReturnStatusSuccessFinishResult);
else {
result.AppendError(error.AsCString());
}
}
};
namespace {
// Helper function to perform an action on each matching plugin.
// The action callback is given the containing namespace along with plugin info
// for each matching plugin.
static int ActOnMatchingPlugins(
const llvm::StringRef pattern,
std::function<void(const PluginNamespace &plugin_namespace,
const std::vector<RegisteredPluginInfo> &plugin_info)>
action) {
int num_matching = 0;
for (const PluginNamespace &plugin_namespace :
PluginManager::GetPluginNamespaces()) {
std::vector<RegisteredPluginInfo> matching_plugins;
for (const RegisteredPluginInfo &plugin_info :
plugin_namespace.get_info()) {
if (PluginManager::MatchPluginName(pattern, plugin_namespace,
plugin_info))
matching_plugins.push_back(plugin_info);
}
if (!matching_plugins.empty()) {
num_matching += matching_plugins.size();
action(plugin_namespace, matching_plugins);
}
}
return num_matching;
}
// Call the "SetEnable" function for each matching plugins.
// Used to share the majority of the code between the enable
// and disable commands.
int SetEnableOnMatchingPlugins(const llvm::StringRef &pattern,
CommandReturnObject &result, bool enabled,
Debugger &requesting_debugger,
PluginDomainKind domain) {
return ActOnMatchingPlugins(
pattern, [&](const PluginNamespace &plugin_namespace,
const std::vector<RegisteredPluginInfo> &plugins) {
auto PrintEnablement = [enabled,
&result](const RegisteredPluginInfo plugin) {
result.AppendMessageWithFormatv(" {0} {1, -30} {2}",
enabled ? "[+]" : "[-]", plugin.name,
plugin.description);
};
result.AppendMessage(plugin_namespace.name);
for (const auto &plugin : plugins) {
if (plugin_namespace.SupportsOnlyDomain(
PluginDomainKind::ePluginDomainKindGlobal)) {
bool success = true;
if (domain != ePluginDomainKindGlobal) {
result.AppendErrorWithFormatv(
"failed to {} plugin {}.{}: {} domain is not supported",
enabled ? "enable" : "disable", plugin_namespace.name,
plugin.name, PluginManager::PluginDomainKindToStr(domain));
continue;
}
success = (*plugin_namespace.GetSetEnabledGlobalFn())(plugin.name,
enabled);
if (!success) {
result.AppendErrorWithFormatv("failed to {} plugin {}.{}",
enabled ? "enable" : "disable",
plugin_namespace.name, plugin.name);
continue;
}
PrintEnablement(plugin);
continue;
}
// Handle plugin namespace that supports more than just the global
// domain. Currently this is just the instrumentation-runtime
// namespace.
if (!plugin_namespace.SupportsDomain(domain)) {
result.AppendErrorWithFormatv(
"failed to {0} plugin {1}.{2}: the {1} namespace "
"does not support the {3} domain",
enabled ? "enable" : "disable", plugin_namespace.name,
plugin.name, PluginManager::PluginDomainKindToStr(domain));
continue;
}
assert(plugin_namespace.GetSetEnabledAllDomainsFn().has_value());
llvm::Error error = (*plugin_namespace.GetSetEnabledAllDomainsFn())(
plugin.name, enabled, requesting_debugger, domain);
if (error) {
result.AppendErrorWithFormatv("failed to {} plugin {}.{}: {}",
enabled ? "enable" : "disable",
plugin_namespace.name, plugin.name,
llvm::toString(std::move(error)));
continue;
}
PrintEnablement(plugin);
}
});
}
static std::string ConvertJSONToPrettyString(const llvm::json::Value &json) {
std::string str;
llvm::raw_string_ostream os(str);
os << llvm::formatv("{0:2}", json).str();
os.flush();
return str;
}
#define LLDB_OPTIONS_plugin_list
#include "CommandOptions.inc"
// These option definitions are used by the plugin list command.
class PluginListCommandOptions : public Options {
static constexpr const PluginDomainKind kDefaultDomain =
ePluginDomainKindGlobal;
public:
PluginListCommandOptions() = default;
~PluginListCommandOptions() override = default;
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) override {
Status error;
const int short_option = m_getopt_table[option_idx].val;
switch (short_option) {
case 'j':
m_json_format = true;
break;
case 'd':
m_domain = static_cast<PluginDomainKind>(OptionArgParser::ToOptionEnum(
option_arg, GetDefinitions()[option_idx].enum_values, kDefaultDomain,
error));
break;
default:
llvm_unreachable("Unimplemented option");
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_json_format = false;
m_domain = kDefaultDomain;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_plugin_list_options);
}
// Instance variables to hold the values for command options.
bool m_json_format = false;
PluginDomainKind m_domain = kDefaultDomain;
};
} // namespace
class CommandObjectPluginList : public CommandObjectParsed {
public:
CommandObjectPluginList(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "plugin list",
"Report info about registered LLDB plugins.",
nullptr) {
AddSimpleArgumentList(eArgTypeManagedPlugin);
SetHelpLong(R"(
Display information about registered plugins.
The plugin information is formatted as shown below:
<plugin-namespace>
[+] <plugin-name> Plugin #1 description
[-] <plugin-name> Plugin #2 description
An enabled plugin is marked with [+] and a disabled plugin is marked with [-].
Plugins can be listed by namespace and name with:
plugin list <plugin-namespace>[.<plugin-name>]
Plugins can be listed by namespace alone or with a fully qualified name. When listed
with just a namespace all plugins in that namespace are listed. When no arguments
are given all plugins are listed.
Examples:
List all plugins
(lldb) plugin list
List all plugins in the system-runtime namespace
(lldb) plugin list system-runtime
List only the plugin 'foo' matching a fully qualified name exactly
(lldb) plugin list system-runtime.foo
)");
}
~CommandObjectPluginList() override = default;
Options *GetOptions() override { return &m_options; }
void
HandleArgumentCompletion(CompletionRequest &request,
OptionElementVector &opt_element_vector) override {
lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
GetCommandInterpreter(), lldb::eManagedPluginCompletion, request,
nullptr);
}
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
size_t argc = command.GetArgumentCount();
result.SetStatus(eReturnStatusSuccessFinishResult);
// Create a temporary vector to hold the patterns to simplify the logic
// for the case when the user passes no patterns
std::vector<llvm::StringRef> patterns;
patterns.reserve(argc == 0 ? 1 : argc);
if (argc == 0)
patterns.push_back("");
else
for (size_t i = 0; i < argc; ++i)
patterns.push_back(command[i].ref());
if (m_options.m_json_format)
OutputJsonFormat(patterns, result, GetDebugger(), m_options.m_domain);
else
OutputTextFormat(patterns, result, GetDebugger(), m_options.m_domain);
}
private:
void OutputJsonFormat(const std::vector<llvm::StringRef> &patterns,
CommandReturnObject &result,
Debugger &requesting_debugger,
PluginDomainKind domain) {
if (domain != PluginDomainKind::ePluginDomainKindGlobal) {
result.AppendErrorWithFormatv(
"{} domain is not supported",
PluginManager::PluginDomainKindToStr(domain));
return;
}
llvm::json::Object obj;
bool found_empty = false;
for (const llvm::StringRef pattern : patterns) {
llvm::json::Object pat_obj = PluginManager::GetJSON(pattern);
if (pat_obj.empty()) {
found_empty = true;
result.AppendErrorWithFormat(
"Found no matching plugins for pattern '%s'", pattern.data());
break;
}
for (auto &entry : pat_obj) {
obj[entry.first] = std::move(entry.second);
}
}
if (!found_empty) {
result.AppendMessage(ConvertJSONToPrettyString(std::move(obj)));
}
}
void OutputTextFormat(const std::vector<llvm::StringRef> &patterns,
CommandReturnObject &result,
Debugger &requesting_debugger,
PluginDomainKind domain) {
if (domain != PluginDomainKind::ePluginDomainKindGlobal) {
result.AppendErrorWithFormatv(
"{} domain is not supported",
PluginManager::PluginDomainKindToStr(domain));
return;
}
for (const llvm::StringRef pattern : patterns) {
int num_matching = ActOnMatchingPlugins(
pattern, [&](const PluginNamespace &plugin_namespace,
const std::vector<RegisteredPluginInfo> &plugins) {
result.AppendMessage(plugin_namespace.name);
for (auto &plugin : plugins) {
result.AppendMessageWithFormatv(" {0} {1, -30} {2}",
plugin.enabled ? "[+]" : "[-]",
plugin.name, plugin.description);
}
});
if (num_matching == 0) {
result.AppendErrorWithFormat(
"Found no matching plugins for pattern '%s'", pattern.data());
break;
}
}
}
PluginListCommandOptions m_options;
};
static void DoPluginEnableDisable(Args &command, CommandReturnObject &result,
bool enable, Debugger &requesting_debugger,
PluginDomainKind domain) {
const char *name = enable ? "enable" : "disable";
size_t argc = command.GetArgumentCount();
if (argc == 0) {
result.AppendErrorWithFormat("'plugin %s' requires one or more arguments",
name);
return;
}
result.SetStatus(eReturnStatusSuccessFinishResult);
for (size_t i = 0; i < argc; ++i) {
llvm::StringRef pattern = command[i].ref();
int num_matching = SetEnableOnMatchingPlugins(pattern, result, enable,
requesting_debugger, domain);
if (num_matching == 0) {
result.AppendErrorWithFormat(
"Found no matching plugins to %s for pattern '%s'", name,
pattern.data());
break;
}
}
}
#define LLDB_OPTIONS_plugin_enable
#include "CommandOptions.inc"
#define LLDB_OPTIONS_plugin_disable
#include "CommandOptions.inc"
// Options class for the --domain flag, shared by plugin enable and
// plugin disable (and reusable by plugin status in the future).
class PluginDomainOptions : public Options {
static constexpr const PluginDomainKind kDefaultDomain =
ePluginDomainKindGlobal;
public:
PluginDomainOptions(llvm::ArrayRef<OptionDefinition> definitions)
: m_definitions(definitions) {}
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *execution_context) override {
Status error;
const int short_option = m_getopt_table[option_idx].val;
switch (short_option) {
case 'd':
m_domain = static_cast<PluginDomainKind>(OptionArgParser::ToOptionEnum(
option_arg, GetDefinitions()[option_idx].enum_values, kDefaultDomain,
error));
break;
default:
llvm_unreachable("Unimplemented option");
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
m_domain = kDefaultDomain;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return m_definitions;
}
PluginDomainKind m_domain = kDefaultDomain;
private:
llvm::ArrayRef<OptionDefinition> m_definitions;
};
class CommandObjectPluginEnable : public CommandObjectParsed {
public:
CommandObjectPluginEnable(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "plugin enable",
"Enable registered LLDB plugins.", nullptr),
m_options(llvm::ArrayRef(g_plugin_enable_options)) {
AddSimpleArgumentList(eArgTypeManagedPlugin);
}
void
HandleArgumentCompletion(CompletionRequest &request,
OptionElementVector &opt_element_vector) override {
lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
GetCommandInterpreter(), lldb::eManagedPluginCompletion, request,
nullptr);
}
~CommandObjectPluginEnable() override = default;
Options *GetOptions() override { return &m_options; }
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
DoPluginEnableDisable(command, result, /*enable=*/true, GetDebugger(),
m_options.m_domain);
}
PluginDomainOptions m_options;
};
class CommandObjectPluginDisable : public CommandObjectParsed {
public:
CommandObjectPluginDisable(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "plugin disable",
"Disable registered LLDB plugins.", nullptr),
m_options(llvm::ArrayRef(g_plugin_disable_options)) {
AddSimpleArgumentList(eArgTypeManagedPlugin);
}
void
HandleArgumentCompletion(CompletionRequest &request,
OptionElementVector &opt_element_vector) override {
lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
GetCommandInterpreter(), lldb::eManagedPluginCompletion, request,
nullptr);
}
~CommandObjectPluginDisable() override = default;
Options *GetOptions() override { return &m_options; }
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
DoPluginEnableDisable(command, result, /*enable=*/false, GetDebugger(),
m_options.m_domain);
}
PluginDomainOptions m_options;
};
CommandObjectPlugin::CommandObjectPlugin(CommandInterpreter &interpreter)
: CommandObjectMultiword(interpreter, "plugin",
"Commands for managing LLDB plugins.",
"plugin <subcommand> [<subcommand-options>]") {
LoadSubCommand("load",
CommandObjectSP(new CommandObjectPluginLoad(interpreter)));
LoadSubCommand("list",
CommandObjectSP(new CommandObjectPluginList(interpreter)));
LoadSubCommand("enable",
CommandObjectSP(new CommandObjectPluginEnable(interpreter)));
LoadSubCommand("disable",
CommandObjectSP(new CommandObjectPluginDisable(interpreter)));
}
CommandObjectPlugin::~CommandObjectPlugin() = default;