Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e240bda
Add commands to list/enable/disable plugins
dmpots Mar 11, 2025
e1c3d46
Apply suggestions from code review
dmpots Apr 4, 2025
681ceaa
Fix formatting and compile error
dmpots Apr 4, 2025
05bc4d4
Fix formatting
dmpots Apr 4, 2025
0f6515f
Move PluginNamespace array into PluginManager
dmpots Apr 11, 2025
779e727
Fix wording in comments and help
dmpots Apr 11, 2025
9e7a695
Remove glob logic for plugin names
dmpots Apr 12, 2025
314cb2d
Get rid of now unused plugin-list option
dmpots Apr 24, 2025
9b41e47
Get rid of unused include
dmpots Apr 24, 2025
0aad1f6
Include plugin info in stats
dmpots Apr 24, 2025
1f863a6
Fix formatting
dmpots Apr 24, 2025
ef976cc
Set success on plugin commands
dmpots Apr 25, 2025
a386406
Add plugin stats test
dmpots Apr 25, 2025
0e7e77f
Clean up formatting
dmpots Apr 25, 2025
3bc5395
[NFC] Refactor - move plugin name matching to PluginManager
dmpots May 31, 2025
4b8dcec
Move json generation into PluginManager
dmpots May 31, 2025
8f1cfdc
Require one argument for enable/disable
dmpots May 31, 2025
99ca1f4
Support json output format for plugin list
dmpots Jun 1, 2025
2633292
Add unit test for MatchPluginName
dmpots Jun 2, 2025
6ada430
Add unit test for PluginManager::GetJSON
dmpots Jun 2, 2025
6260522
Formatting
dmpots Jun 2, 2025
bfcde3d
cleanup uneeded check for argc == 0
dmpots Jun 5, 2025
29aa20d
Use -DAG on json check patterns
dmpots Jun 5, 2025
735dd1f
Add support for enable/disable instrumentation-runtime plugins
dmpots Jun 5, 2025
b31b8a8
Support multiple values in plugin list
dmpots Jun 5, 2025
8b66ff5
Support multiple values in plugin enable
dmpots Jun 6, 2025
aea2cfb
Support multiple values for plugin disable
dmpots Jun 6, 2025
b8d9ad7
Add comment for boolean parameter
dmpots Jun 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
335 changes: 335 additions & 0 deletions lldb/source/Commands/CommandObjectPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
//===----------------------------------------------------------------------===//

#include "CommandObjectPlugin.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Host/OptionParser.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "llvm/Support/GlobPattern.h"

using namespace lldb;
using namespace lldb_private;
Expand Down Expand Up @@ -46,12 +49,344 @@ class CommandObjectPluginLoad : public CommandObjectParsed {
}
};

namespace {
#define LLDB_OPTIONS_plugin_list
#include "CommandOptions.inc"

// These option definitions are shared by the plugin list/enable/disable
// commands.
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 'x':
m_exact_name_match = true;
break;
default:
llvm_unreachable("Unimplemented option");
}

return error;
}

void OptionParsingStarting(ExecutionContext *execution_context) override {
m_exact_name_match = false;
}

llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::ArrayRef(g_plugin_list_options);
}

// Instance variables to hold the values for command options.
bool m_exact_name_match = false;
};

// Define some data structures to describe known plugin "namespaces".
// The PluginManager is organized into a series of static functions
// that operate on different types of plugin. For example SystemRuntime
// and ObjectFile plugins.
//
// The namespace name is used a prefix when matching plugin names. For example,
// if we have an "elf" plugin in the "object-file" namespace then we will
// match a plugin name pattern against the "object-file.elf" name.
//
// The plugin namespace here is used so we can operate on all the plugins
// of a given type so it is easy to enable or disable them as a group.
using GetPluginInfo = std::function<std::vector<RegisteredPluginInfo>()>;
using SetPluginEnabled = std::function<bool(llvm::StringRef, bool)>;
struct PluginNamespace {
llvm::StringRef name;
GetPluginInfo get_info;
SetPluginEnabled set_enabled;
};

// Currently supported set of plugin namespaces. This will be expanded
// over time.
PluginNamespace PluginNamespaces[] = {
{"system-runtime", PluginManager::GetSystemRuntimePluginInfo,
PluginManager::SetSystemRuntimePluginEnabled}};

// 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(
llvm::GlobPattern pattern,
std::function<void(const PluginNamespace &plugin_namespace,
const std::vector<RegisteredPluginInfo> &plugin_info)>
action) {
int num_matching = 0;

for (const PluginNamespace &plugin_namespace : PluginNamespaces) {
std::vector<RegisteredPluginInfo> all_plugins = plugin_namespace.get_info();
std::vector<RegisteredPluginInfo> matching_plugins;
for (const RegisteredPluginInfo &plugin_info : all_plugins) {
std::string qualified_name =
(plugin_namespace.name + "." + plugin_info.name).str();
if (pattern.match(qualified_name)) {
matching_plugins.push_back(plugin_info);
}
}

if (!matching_plugins.empty()) {
num_matching += matching_plugins.size();
action(plugin_namespace, matching_plugins);
}
}

return num_matching;
}

// Return a string in glob syntax for matching plugins.
static std::string GetPluginNamePatternString(llvm::StringRef user_input,
bool add_default_glob) {
std::string pattern_str;
if (user_input.empty())
pattern_str = "*";
else
pattern_str = user_input;

if (add_default_glob && pattern_str != "*") {
pattern_str = "*" + pattern_str + "*";
}

return pattern_str;
}

// Attempts to create a glob pattern for a plugin name based on plugin command
// input. Writes an error message to the `result` object if the glob cannot be
// created successfully.
//
// The `glob_storage` is used to hold the string data for the glob pattern. The
// llvm::GlobPattern only contains pointers into the string data so we need a
// stable location that can outlive the glob pattern itself.
std::optional<llvm::GlobPattern>
TryCreatePluginPattern(const char *plugin_command_name, const Args &command,
const PluginListCommandOptions &options,
CommandReturnObject &result, std::string &glob_storage) {
size_t argc = command.GetArgumentCount();
if (argc > 1) {
result.AppendErrorWithFormat("'%s' requires one argument",
plugin_command_name);
return {};
}

llvm::StringRef user_pattern;
if (argc == 1) {
user_pattern = command[0].ref();
}

glob_storage =
GetPluginNamePatternString(user_pattern, !options.m_exact_name_match);

auto glob_pattern = llvm::GlobPattern::create(glob_storage);

if (auto error = glob_pattern.takeError()) {
std::string error_message =
(llvm::Twine("Invalid plugin glob pattern: '") + glob_storage +
"': " + llvm::toString(std::move(error)))
.str();
result.AppendError(error_message);
return {};
}

return *glob_pattern;
}

// 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::GlobPattern &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());
}
});
}
} // namespace

class CommandObjectPluginList : public CommandObjectParsed {
public:
CommandObjectPluginList(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "plugin list",
"Report info about registered LLDB plugins.",
nullptr) {
AddSimpleArgumentList(eArgTypePlugin);
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 [-].

Selecting plugins
------------------
plugin list [<plugin-namespace>.][<plugin-name>]

Plugin names are specified using glob patterns. The pattern will be matched
against the plugins fully qualified name, which is composed of the namespace,
followed by a '.', followed by the plugin name.

When no arguments are given the plugin selection string is the wildcard '*'.
By default wildcards are added around the input to enable searching by
substring. You can prevent these implicit wild cards by using the
-x flag.

Examples
-----------------
List all plugins in the system-runtime namespace

(lldb) plugin list system-runtime.*

List all plugins containing the string foo

(lldb) plugin list foo

This is equivalent to

(lldb) plugin list *foo*

List only a plugin matching a fully qualified name exactly

(lldb) plugin list -x system-runtime.foo
)");
}

~CommandObjectPluginList() override = default;

Options *GetOptions() override { return &m_options; }

protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
std::string glob_storage;
std::optional<llvm::GlobPattern> plugin_glob = TryCreatePluginPattern(
"plugin list", command, m_options, result, glob_storage);

if (!plugin_glob) {
assert(!result.Succeeded());
return;
}

int num_matching = ActOnMatchingPlugins(
*plugin_glob, [&](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");
}
}

PluginListCommandOptions m_options;
};

class CommandObjectPluginEnable : public CommandObjectParsed {
public:
CommandObjectPluginEnable(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "plugin enable",
"Enable registered LLDB plugins.", nullptr) {
AddSimpleArgumentList(eArgTypePlugin);
}

~CommandObjectPluginEnable() override = default;

Options *GetOptions() override { return &m_options; }

protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
std::string glob_storage;
std::optional<llvm::GlobPattern> plugin_glob = TryCreatePluginPattern(
"plugin enable", command, m_options, result, glob_storage);

if (!plugin_glob) {
assert(!result.Succeeded());
return;
}

int num_matching = SetEnableOnMatchingPlugins(*plugin_glob, result, true);

if (num_matching == 0) {
result.AppendErrorWithFormat("Found no matching plugins to enable");
}
}

PluginListCommandOptions m_options;
};

class CommandObjectPluginDisable : public CommandObjectParsed {
public:
CommandObjectPluginDisable(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "plugin disable",
"Disable registered LLDB plugins.", nullptr) {
AddSimpleArgumentList(eArgTypePlugin);
}

~CommandObjectPluginDisable() override = default;

Options *GetOptions() override { return &m_options; }

protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
std::string glob_storage;
std::optional<llvm::GlobPattern> plugin_glob = TryCreatePluginPattern(
"plugin disable", command, m_options, result, glob_storage);

if (!plugin_glob) {
assert(!result.Succeeded());
return;
}

int num_matching = SetEnableOnMatchingPlugins(*plugin_glob, result, false);

if (num_matching == 0) {
result.AppendErrorWithFormat("Found no matching plugins to disable");
}
}

PluginListCommandOptions 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;
1 change: 1 addition & 0 deletions lldb/source/Commands/CommandObjectSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandOptionArgumentTable.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Interpreter/OptionValueProperties.h"

using namespace lldb;
Expand Down
5 changes: 5 additions & 0 deletions lldb/source/Commands/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,11 @@ let Command = "platform shell" in {
Desc<"Shell interpreter path. This is the binary used to run the command.">;
}

let Command = "plugin list" in {
def plugin_info_exact : Option<"exact", "x">,
Desc<"Do not add implicit * glob around plugin name">;
}

let Command = "process launch" in {
def process_launch_stop_at_entry : Option<"stop-at-entry", "s">,
Desc<"Stop at the entry point of the program when launching a process.">;
Expand Down
Loading