Skip to content

Commit aaa3ab3

Browse files
committed
Add commands to list/enable/disable plugins
This commit adds three new commands for managing plugins. The `list` command will show which plugins are currently registered and their enabled state. The `enable` and `disable` commands can be used to enable or disable plugins. A disabled plugin will not show up to the PluginManager when it iterates over available plugins of a particular type. The purpose of these commands is to provide more visibility into registered plugins and allow users to disable plugins for experimental perf reasons. There are a few limitations to the current implementation 1. Only SystemRuntime plugins are currently supported. We can easily extend the existing implementation to support more types. 2. Only "statically" know plugin types are supported (i.e. those managed by the PluginManager and not from `plugin load`). It is possibly we could support dynamic plugins as well, but I have not looked into it yet.
1 parent 20e934a commit aaa3ab3

File tree

5 files changed

+445
-0
lines changed

5 files changed

+445
-0
lines changed

lldb/source/Commands/CommandObjectPlugin.cpp

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "CommandObjectPlugin.h"
10+
#include "lldb/Core/PluginManager.h"
11+
#include "lldb/Host/OptionParser.h"
1012
#include "lldb/Interpreter/CommandInterpreter.h"
1113
#include "lldb/Interpreter/CommandReturnObject.h"
14+
#include "llvm/Support/GlobPattern.h"
1215

1316
using namespace lldb;
1417
using namespace lldb_private;
@@ -46,12 +49,344 @@ class CommandObjectPluginLoad : public CommandObjectParsed {
4649
}
4750
};
4851

52+
namespace {
53+
#define LLDB_OPTIONS_plugin_list
54+
#include "CommandOptions.inc"
55+
56+
// These option definitions are shared by the plugin list/enable/disable
57+
// commands.
58+
class PluginListCommandOptions : public Options {
59+
public:
60+
PluginListCommandOptions() = default;
61+
62+
~PluginListCommandOptions() override = default;
63+
64+
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
65+
ExecutionContext *execution_context) override {
66+
Status error;
67+
const int short_option = m_getopt_table[option_idx].val;
68+
69+
switch (short_option) {
70+
case 'x':
71+
m_exact_name_match = true;
72+
break;
73+
default:
74+
llvm_unreachable("Unimplemented option");
75+
}
76+
77+
return error;
78+
}
79+
80+
void OptionParsingStarting(ExecutionContext *execution_context) override {
81+
m_exact_name_match = false;
82+
}
83+
84+
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
85+
return llvm::ArrayRef(g_plugin_list_options);
86+
}
87+
88+
// Instance variables to hold the values for command options.
89+
bool m_exact_name_match = false;
90+
};
91+
92+
// Define some data structures to describe known plugin "namespaces".
93+
// The PluginManager is organized into a series of static functions
94+
// that operate on different types of plugin. For example SystemRuntime
95+
// and ObjectFile plugins.
96+
//
97+
// The namespace name is used a prefix when matching plugin names. For example,
98+
// if we have an "elf" plugin in the "object-file" namespace then we will
99+
// match a plugin name pattern against the "object-file.elf" name.
100+
//
101+
// The plugin namespace here is used so we can operate on all the plugins
102+
// of a given type so it is easy to enable or disable them as a group.
103+
using GetPluginInfo = std::function<std::vector<RegisteredPluginInfo>()>;
104+
using SetPluginEnabled = std::function<bool(llvm::StringRef, bool)>;
105+
struct PluginNamespace {
106+
llvm::StringRef name;
107+
GetPluginInfo get_info;
108+
SetPluginEnabled set_enabled;
109+
};
110+
111+
// Currently supported set of plugin namespaces. This will be expanded
112+
// over time.
113+
PluginNamespace PluginNamespaces[] = {
114+
{"system-runtime", PluginManager::GetSystemRuntimePluginInfo,
115+
PluginManager::SetSystemRuntimePluginEnabled}};
116+
117+
// Helper function to perform an action on each matching plugin.
118+
// The action callback is given the containing namespace along with plugin info
119+
// for each matching plugin.
120+
static int ActOnMatchingPlugins(
121+
llvm::GlobPattern pattern,
122+
std::function<void(const PluginNamespace &plugin_namespace,
123+
const std::vector<RegisteredPluginInfo> &plugin_info)>
124+
action) {
125+
int num_matching = 0;
126+
127+
for (const PluginNamespace &plugin_namespace : PluginNamespaces) {
128+
std::vector<RegisteredPluginInfo> all_plugins = plugin_namespace.get_info();
129+
std::vector<RegisteredPluginInfo> matching_plugins;
130+
for (const RegisteredPluginInfo &plugin_info : all_plugins) {
131+
std::string qualified_name =
132+
(plugin_namespace.name + "." + plugin_info.name).str();
133+
if (pattern.match(qualified_name)) {
134+
matching_plugins.push_back(plugin_info);
135+
}
136+
}
137+
138+
if (!matching_plugins.empty()) {
139+
num_matching += matching_plugins.size();
140+
action(plugin_namespace, matching_plugins);
141+
}
142+
}
143+
144+
return num_matching;
145+
}
146+
147+
// Return a string in glob syntax for matching plugins.
148+
static std::string GetPluginNamePatternString(llvm::StringRef user_input,
149+
bool add_default_glob) {
150+
std::string pattern_str;
151+
if (user_input.empty())
152+
pattern_str = "*";
153+
else
154+
pattern_str = user_input;
155+
156+
if (add_default_glob && pattern_str != "*") {
157+
pattern_str = "*" + pattern_str + "*";
158+
}
159+
160+
return pattern_str;
161+
}
162+
163+
// Attempts to create a glob pattern for a plugin name based on plugin command
164+
// input. Writes an error message to the `result` object if the glob cannot be
165+
// created successfully.
166+
//
167+
// The `glob_storage` is used to hold the string data for the glob pattern. The
168+
// llvm::GlobPattern only contains pointers into the string data so we need a
169+
// stable location that can outlive the glob pattern itself.
170+
std::optional<llvm::GlobPattern>
171+
TryCreatePluginPattern(const char *plugin_command_name, const Args &command,
172+
const PluginListCommandOptions &options,
173+
CommandReturnObject &result, std::string &glob_storage) {
174+
size_t argc = command.GetArgumentCount();
175+
if (argc > 1) {
176+
result.AppendErrorWithFormat("'%s' requires one argument",
177+
plugin_command_name);
178+
return {};
179+
}
180+
181+
llvm::StringRef user_pattern;
182+
if (argc == 1) {
183+
user_pattern = command[0].ref();
184+
}
185+
186+
glob_storage =
187+
GetPluginNamePatternString(user_pattern, !options.m_exact_name_match);
188+
189+
auto glob_pattern = llvm::GlobPattern::create(glob_storage);
190+
191+
if (auto error = glob_pattern.takeError()) {
192+
std::string error_message =
193+
(llvm::Twine("Invalid plugin glob pattern: '") + glob_storage +
194+
"': " + llvm::toString(std::move(error)))
195+
.str();
196+
result.AppendError(error_message);
197+
return {};
198+
}
199+
200+
return *glob_pattern;
201+
}
202+
203+
// Call the "SetEnable" function for each matching plugins.
204+
// Used to share the majority of the code between the enable
205+
// and disable commands.
206+
int SetEnableOnMatchingPlugins(const llvm::GlobPattern &pattern,
207+
CommandReturnObject &result, bool enabled) {
208+
return ActOnMatchingPlugins(
209+
pattern, [&](const PluginNamespace &plugin_namespace,
210+
const std::vector<RegisteredPluginInfo> &plugins) {
211+
result.AppendMessage(plugin_namespace.name);
212+
for (const auto &plugin : plugins) {
213+
if (!plugin_namespace.set_enabled(plugin.name, enabled)) {
214+
result.AppendErrorWithFormat("failed to enable plugin %s.%s",
215+
plugin_namespace.name.data(),
216+
plugin.name.data());
217+
continue;
218+
}
219+
220+
result.AppendMessageWithFormat(
221+
" %s %-30s %s\n", enabled ? "[+]" : "[-]", plugin.name.data(),
222+
plugin.description.data());
223+
}
224+
});
225+
}
226+
} // namespace
227+
228+
class CommandObjectPluginList : public CommandObjectParsed {
229+
public:
230+
CommandObjectPluginList(CommandInterpreter &interpreter)
231+
: CommandObjectParsed(interpreter, "plugin list",
232+
"Report info about registered LLDB plugins.",
233+
nullptr) {
234+
AddSimpleArgumentList(eArgTypePlugin);
235+
SetHelpLong(R"(
236+
Display information about registered plugins.
237+
The plugin information is formatted as shown below
238+
239+
<plugin-namespace>
240+
[+] <plugin-name> Plugin #1 description
241+
[-] <plugin-name> Plugin #2 description
242+
243+
An enabled plugin is marked with [+] and a disabled plugin is marked with [-].
244+
245+
Selecting plugins
246+
------------------
247+
plugin list [<plugin-namespace>.][<plugin-name>]
248+
249+
Plugin names are specified using glob patterns. The pattern will be matched
250+
against the plugins fully qualified name, which is composed of the namespace,
251+
followed by a '.', followed by the plugin name.
252+
253+
When no arguments are given the plugin selection string is the wildcard '*'.
254+
By default wildcards are added around the input to enable searching by
255+
substring. You can prevent these implicit wild cards by using the
256+
-x flag.
257+
258+
Examples
259+
-----------------
260+
List all plugins in the system-runtime namespace
261+
262+
(lldb) plugin list system-runtime.*
263+
264+
List all plugins containing the string foo
265+
266+
(lldb) plugin list foo
267+
268+
This is equivalent to
269+
270+
(lldb) plugin list *foo*
271+
272+
List only a plugin matching a fully qualified name exactly
273+
274+
(lldb) plugin list -x system-runtime.foo
275+
)");
276+
}
277+
278+
~CommandObjectPluginList() override = default;
279+
280+
Options *GetOptions() override { return &m_options; }
281+
282+
protected:
283+
void DoExecute(Args &command, CommandReturnObject &result) override {
284+
std::string glob_storage;
285+
std::optional<llvm::GlobPattern> plugin_glob = TryCreatePluginPattern(
286+
"plugin list", command, m_options, result, glob_storage);
287+
288+
if (!plugin_glob) {
289+
assert(!result.Succeeded());
290+
return;
291+
}
292+
293+
int num_matching = ActOnMatchingPlugins(
294+
*plugin_glob, [&](const PluginNamespace &plugin_namespace,
295+
const std::vector<RegisteredPluginInfo> &plugins) {
296+
result.AppendMessage(plugin_namespace.name);
297+
for (auto &plugin : plugins) {
298+
result.AppendMessageWithFormat(
299+
" %s %-30s %s\n", plugin.enabled ? "[+]" : "[-]",
300+
plugin.name.data(), plugin.description.data());
301+
}
302+
});
303+
304+
if (num_matching == 0) {
305+
result.AppendErrorWithFormat("Found no matching plugins");
306+
}
307+
}
308+
309+
PluginListCommandOptions m_options;
310+
};
311+
312+
class CommandObjectPluginEnable : public CommandObjectParsed {
313+
public:
314+
CommandObjectPluginEnable(CommandInterpreter &interpreter)
315+
: CommandObjectParsed(interpreter, "plugin enable",
316+
"Enable registered LLDB plugins.", nullptr) {
317+
AddSimpleArgumentList(eArgTypePlugin);
318+
}
319+
320+
~CommandObjectPluginEnable() override = default;
321+
322+
Options *GetOptions() override { return &m_options; }
323+
324+
protected:
325+
void DoExecute(Args &command, CommandReturnObject &result) override {
326+
std::string glob_storage;
327+
std::optional<llvm::GlobPattern> plugin_glob = TryCreatePluginPattern(
328+
"plugin enable", command, m_options, result, glob_storage);
329+
330+
if (!plugin_glob) {
331+
assert(!result.Succeeded());
332+
return;
333+
}
334+
335+
int num_matching = SetEnableOnMatchingPlugins(*plugin_glob, result, true);
336+
337+
if (num_matching == 0) {
338+
result.AppendErrorWithFormat("Found no matching plugins to enable");
339+
}
340+
}
341+
342+
PluginListCommandOptions m_options;
343+
};
344+
345+
class CommandObjectPluginDisable : public CommandObjectParsed {
346+
public:
347+
CommandObjectPluginDisable(CommandInterpreter &interpreter)
348+
: CommandObjectParsed(interpreter, "plugin disable",
349+
"Disable registered LLDB plugins.", nullptr) {
350+
AddSimpleArgumentList(eArgTypePlugin);
351+
}
352+
353+
~CommandObjectPluginDisable() override = default;
354+
355+
Options *GetOptions() override { return &m_options; }
356+
357+
protected:
358+
void DoExecute(Args &command, CommandReturnObject &result) override {
359+
std::string glob_storage;
360+
std::optional<llvm::GlobPattern> plugin_glob = TryCreatePluginPattern(
361+
"plugin disable", command, m_options, result, glob_storage);
362+
363+
if (!plugin_glob) {
364+
assert(!result.Succeeded());
365+
return;
366+
}
367+
368+
int num_matching = SetEnableOnMatchingPlugins(*plugin_glob, result, false);
369+
370+
if (num_matching == 0) {
371+
result.AppendErrorWithFormat("Found no matching plugins to disable");
372+
}
373+
}
374+
375+
PluginListCommandOptions m_options;
376+
};
377+
49378
CommandObjectPlugin::CommandObjectPlugin(CommandInterpreter &interpreter)
50379
: CommandObjectMultiword(interpreter, "plugin",
51380
"Commands for managing LLDB plugins.",
52381
"plugin <subcommand> [<subcommand-options>]") {
53382
LoadSubCommand("load",
54383
CommandObjectSP(new CommandObjectPluginLoad(interpreter)));
384+
LoadSubCommand("list",
385+
CommandObjectSP(new CommandObjectPluginList(interpreter)));
386+
LoadSubCommand("enable",
387+
CommandObjectSP(new CommandObjectPluginEnable(interpreter)));
388+
LoadSubCommand("disable",
389+
CommandObjectSP(new CommandObjectPluginDisable(interpreter)));
55390
}
56391

57392
CommandObjectPlugin::~CommandObjectPlugin() = default;

lldb/source/Commands/CommandObjectSettings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "lldb/Interpreter/CommandInterpreter.h"
1616
#include "lldb/Interpreter/CommandOptionArgumentTable.h"
1717
#include "lldb/Interpreter/CommandReturnObject.h"
18+
#include "lldb/Interpreter/OptionValue.h"
1819
#include "lldb/Interpreter/OptionValueProperties.h"
1920

2021
using namespace lldb;

lldb/source/Commands/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,11 @@ let Command = "platform shell" in {
683683
Desc<"Shell interpreter path. This is the binary used to run the command.">;
684684
}
685685

686+
let Command = "plugin list" in {
687+
def plugin_info_exact : Option<"exact", "x">,
688+
Desc<"Do not add implicit * glob around plugin name">;
689+
}
690+
686691
let Command = "process launch" in {
687692
def process_launch_stop_at_entry : Option<"stop-at-entry", "s">,
688693
Desc<"Stop at the entry point of the program when launching a process.">;

0 commit comments

Comments
 (0)