Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
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
23 changes: 23 additions & 0 deletions lldb/include/lldb/Core/PluginManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-private-interfaces.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"

#include <cstddef>
#include <cstdint>
#include <functional>
#include <vector>

#define LLDB_PLUGIN_DEFINE_ADV(ClassName, PluginName) \
Expand Down Expand Up @@ -54,12 +56,33 @@ struct RegisteredPluginInfo {
bool enabled = 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 plugins. For example SystemRuntime
// and ObjectFile plugins.
//
// The namespace name is used a prefix when matching plugin names. For example,
// if we have an "macosx" plugin in the "system-runtime" namespace then we will
// match a plugin name pattern against the "system-runtime.macosx" 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;
};

class PluginManager {
public:
static void Initialize();

static void Terminate();

static llvm::ArrayRef<PluginNamespace> GetPluginNamespaces();

// ABI
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
ABICreateInstance create_callback);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ static constexpr CommandObject::ArgumentTableEntry g_argument_table[] = {
{ lldb::eArgTypeModule, "module", lldb::CompletionType::eModuleCompletion, {}, { nullptr, false }, "The name of a module loaded into the current target." },
{ lldb::eArgTypeCPUName, "cpu-name", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "The name of a CPU." },
{ lldb::eArgTypeCPUFeatures, "cpu-features", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "The CPU feature string." },
{ lldb::eArgTypeManagedPlugin, "managed-plugin", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "Plugins managed by the PluginManager" },
// clang-format on
};

Expand Down
9 changes: 9 additions & 0 deletions lldb/include/lldb/Target/Statistics.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,21 @@ struct StatisticsOptions {
return !GetSummaryOnly();
}

void SetIncludePlugins(bool value) { m_include_plugins = value; }
bool GetIncludePlugins() const {
if (m_include_plugins.has_value())
return m_include_plugins.value();
// Default to true in both default mode and summary mode.
return true;
}

private:
std::optional<bool> m_summary_only;
std::optional<bool> m_load_all_debug_info;
std::optional<bool> m_include_targets;
std::optional<bool> m_include_modules;
std::optional<bool> m_include_transcript;
std::optional<bool> m_include_plugins;
};

/// A class that represents statistics about a TypeSummaryProviders invocations
Expand Down
1 change: 1 addition & 0 deletions lldb/include/lldb/lldb-enumerations.h
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@ enum CommandArgumentType {
eArgTypeModule,
eArgTypeCPUName,
eArgTypeCPUFeatures,
eArgTypeManagedPlugin,
eArgTypeLastArg // Always keep this entry as the last entry in this
// enumeration!!
};
Expand Down
197 changes: 197 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,206 @@ class CommandObjectPluginLoad : public CommandObjectParsed {
}
};

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()) {
const bool match_namespace =
pattern.empty() || pattern == plugin_namespace.name;

std::vector<RegisteredPluginInfo> matching_plugins;
for (const RegisteredPluginInfo &plugin_info :
plugin_namespace.get_info()) {

// If we match the namespace, we can skip the plugin name check.
bool match_qualified_name = false;
if (!match_namespace) {
std::string qualified_name =
(plugin_namespace.name + "." + plugin_info.name).str();
match_qualified_name = pattern == qualified_name;
}

if (match_namespace || 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;
}

// 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());
}
});
}
} // 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;

protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
size_t argc = command.GetArgumentCount();
if (argc > 1) {
result.AppendError("'plugin load' requires one argument");
return;
}
llvm::StringRef pattern = argc ? command[0].ref() : "";
result.SetStatus(eReturnStatusSuccessFinishResult);

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");
}
};

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

~CommandObjectPluginEnable() override = default;

protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
size_t argc = command.GetArgumentCount();
if (argc > 1) {
result.AppendError("'plugin enable' requires one argument");
return;
}
llvm::StringRef pattern = argc ? command[0].ref() : "";
result.SetStatus(eReturnStatusSuccessFinishResult);

int num_matching = SetEnableOnMatchingPlugins(pattern, result, true);

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

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

~CommandObjectPluginDisable() override = default;

protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
size_t argc = command.GetArgumentCount();
if (argc > 1) {
result.AppendError("'plugin disable' requires one argument");
return;
}
llvm::StringRef pattern = argc ? command[0].ref() : "";
result.SetStatus(eReturnStatusSuccessFinishResult);

int num_matching = SetEnableOnMatchingPlugins(pattern, result, false);

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

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;
7 changes: 7 additions & 0 deletions lldb/source/Commands/CommandObjectStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ class CommandObjectStatsDump : public CommandObjectParsed {
else
error = Status::FromError(bool_or_error.takeError());
break;
case 'p':
if (llvm::Expected<bool> bool_or_error =
OptionArgParser::ToBoolean("--plugins", option_arg))
m_stats_options.SetIncludePlugins(*bool_or_error);
else
error = Status::FromError(bool_or_error.takeError());
break;
default:
llvm_unreachable("Unimplemented option");
}
Expand Down
8 changes: 7 additions & 1 deletion lldb/source/Commands/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1467,5 +1467,11 @@ let Command = "statistics dump" in {
"scripts executed during a debug session. "
"Defaults to true, unless the '--summary' mode is enabled, in which case "
"this is turned off unless specified.">;

def statistics_dump_plugins
: Option<"plugins", "p">,
Group<1>,
Arg<"Boolean">,
Desc<"Dump statistics for known plugins including name, order, and "
"enabled state. Defaults to true for both summary and default "
"mode.">;
}
10 changes: 10 additions & 0 deletions lldb/source/Core/PluginManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ void PluginManager::Terminate() {
plugin_map.clear();
}

llvm::ArrayRef<PluginNamespace> PluginManager::GetPluginNamespaces() {
// Currently supported set of plugin namespaces. This will be expanded
// over time.
static PluginNamespace PluginNamespaces[] = {
{"system-runtime", PluginManager::GetSystemRuntimePluginInfo,
PluginManager::SetSystemRuntimePluginEnabled}};

return PluginNamespaces;
}

template <typename Callback> struct PluginInstance {
typedef Callback CallbackType;

Expand Down
20 changes: 20 additions & 0 deletions lldb/source/Target/Statistics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Target/DynamicLoader.h"
Expand Down Expand Up @@ -285,6 +286,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
const bool include_targets = options.GetIncludeTargets();
const bool include_modules = options.GetIncludeModules();
const bool include_transcript = options.GetIncludeTranscript();
const bool include_plugins = options.GetIncludePlugins();

json::Array json_targets;
json::Array json_modules;
Expand Down Expand Up @@ -464,6 +466,24 @@ llvm::json::Value DebuggerStats::ReportStatistics(
}
}

if (include_plugins) {
json::Object plugin_stats;
for (const PluginNamespace &plugin_ns :
PluginManager::GetPluginNamespaces()) {
json::Array namespace_stats;

for (const RegisteredPluginInfo &plugin : plugin_ns.get_info()) {
json::Object plugin_json;
plugin_json.try_emplace("name", plugin.name);
plugin_json.try_emplace("enabled", plugin.enabled);

namespace_stats.emplace_back(std::move(plugin_json));
}
plugin_stats.try_emplace(plugin_ns.name, std::move(namespace_stats));
}
global_stats.try_emplace("plugins", std::move(plugin_stats));
}

return std::move(global_stats);
}

Expand Down
Loading
Loading