
This commit adds completion support for the plugin commands. It will try to complete partial namespaces to the full namespace string. If the completion input is already a full namespace string then it will add all the matching plugins in that namespace as completions. This lets the user complete to the namespace first and then tab-complete to the next level if desired. ``` (lldb) plugin list a<tab> Available completions: abi architecture (lldb) plugin list ab<tab> (lldb) plugin list abi<tab> (lldb) plugin list abi.<tab> Available completions: abi.SysV-arm64 abi.ABIMacOSX_arm64 abi.SysV-arm ... ```
359 lines
12 KiB
C++
359 lines
12 KiB
C++
//===-- 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/CommandReturnObject.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) {
|
|
return ActOnMatchingPlugins(
|
|
pattern, [&](const PluginNamespace &plugin_namespace,
|
|
const std::vector<RegisteredPluginInfo> &plugins) {
|
|
result.AppendMessage(plugin_namespace.name);
|
|
for (const auto &plugin : plugins) {
|
|
if (!plugin_namespace.set_enabled(plugin.name, enabled)) {
|
|
result.AppendErrorWithFormat("failed to enable plugin %s.%s",
|
|
plugin_namespace.name.data(),
|
|
plugin.name.data());
|
|
continue;
|
|
}
|
|
|
|
result.AppendMessageWithFormat(
|
|
" %s %-30s %s\n", enabled ? "[+]" : "[-]", plugin.name.data(),
|
|
plugin.description.data());
|
|
}
|
|
});
|
|
}
|
|
|
|
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 {
|
|
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;
|
|
default:
|
|
llvm_unreachable("Unimplemented option");
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void OptionParsingStarting(ExecutionContext *execution_context) override {
|
|
m_json_format = false;
|
|
}
|
|
|
|
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;
|
|
};
|
|
} // 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);
|
|
else
|
|
OutputTextFormat(patterns, result);
|
|
}
|
|
|
|
private:
|
|
void OutputJsonFormat(const std::vector<llvm::StringRef> &patterns,
|
|
CommandReturnObject &result) {
|
|
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) {
|
|
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.AppendMessageWithFormat(
|
|
" %s %-30s %s\n", plugin.enabled ? "[+]" : "[-]",
|
|
plugin.name.data(), plugin.description.data());
|
|
}
|
|
});
|
|
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) {
|
|
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);
|
|
|
|
if (num_matching == 0) {
|
|
result.AppendErrorWithFormat(
|
|
"Found no matching plugins to %s for pattern '%s'", name,
|
|
pattern.data());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
class CommandObjectPluginEnable : public CommandObjectParsed {
|
|
public:
|
|
CommandObjectPluginEnable(CommandInterpreter &interpreter)
|
|
: CommandObjectParsed(interpreter, "plugin enable",
|
|
"Enable registered LLDB plugins.", nullptr) {
|
|
AddSimpleArgumentList(eArgTypeManagedPlugin);
|
|
}
|
|
|
|
void
|
|
HandleArgumentCompletion(CompletionRequest &request,
|
|
OptionElementVector &opt_element_vector) override {
|
|
lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
|
|
GetCommandInterpreter(), lldb::eManagedPluginCompletion, request,
|
|
nullptr);
|
|
}
|
|
|
|
~CommandObjectPluginEnable() override = default;
|
|
|
|
protected:
|
|
void DoExecute(Args &command, CommandReturnObject &result) override {
|
|
DoPluginEnableDisable(command, result, /*enable=*/true);
|
|
}
|
|
};
|
|
|
|
class CommandObjectPluginDisable : public CommandObjectParsed {
|
|
public:
|
|
CommandObjectPluginDisable(CommandInterpreter &interpreter)
|
|
: CommandObjectParsed(interpreter, "plugin disable",
|
|
"Disable registered LLDB plugins.", nullptr) {
|
|
AddSimpleArgumentList(eArgTypeManagedPlugin);
|
|
}
|
|
|
|
void
|
|
HandleArgumentCompletion(CompletionRequest &request,
|
|
OptionElementVector &opt_element_vector) override {
|
|
lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
|
|
GetCommandInterpreter(), lldb::eManagedPluginCompletion, request,
|
|
nullptr);
|
|
}
|
|
|
|
~CommandObjectPluginDisable() override = default;
|
|
|
|
protected:
|
|
void DoExecute(Args &command, CommandReturnObject &result) override {
|
|
DoPluginEnableDisable(command, result, /*enable=*/false);
|
|
}
|
|
};
|
|
|
|
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;
|