1 //===-- CommandObjectPlugin.cpp -------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "CommandObjectPlugin.h"
10 #include "lldb/Core/PluginManager.h"
11 #include "lldb/Host/OptionParser.h"
12 #include "lldb/Interpreter/CommandInterpreter.h"
13 #include "lldb/Interpreter/CommandReturnObject.h"
14
15 using namespace lldb;
16 using namespace lldb_private;
17
18 class CommandObjectPluginLoad : public CommandObjectParsed {
19 public:
CommandObjectPluginLoad(CommandInterpreter & interpreter)20 CommandObjectPluginLoad(CommandInterpreter &interpreter)
21 : CommandObjectParsed(interpreter, "plugin load",
22 "Import a dylib that implements an LLDB plugin.",
23 nullptr) {
24 AddSimpleArgumentList(eArgTypeFilename);
25 }
26
27 ~CommandObjectPluginLoad() override = default;
28
29 protected:
DoExecute(Args & command,CommandReturnObject & result)30 void DoExecute(Args &command, CommandReturnObject &result) override {
31 size_t argc = command.GetArgumentCount();
32
33 if (argc != 1) {
34 result.AppendError("'plugin load' requires one argument");
35 return;
36 }
37
38 Status error;
39
40 FileSpec dylib_fspec(command[0].ref());
41 FileSystem::Instance().Resolve(dylib_fspec);
42
43 if (GetDebugger().LoadPlugin(dylib_fspec, error))
44 result.SetStatus(eReturnStatusSuccessFinishResult);
45 else {
46 result.AppendError(error.AsCString());
47 }
48 }
49 };
50
51 namespace {
52 // Helper function to perform an action on each matching plugin.
53 // The action callback is given the containing namespace along with plugin info
54 // for each matching plugin.
ActOnMatchingPlugins(const llvm::StringRef pattern,std::function<void (const PluginNamespace & plugin_namespace,const std::vector<RegisteredPluginInfo> & plugin_info)> action)55 static int ActOnMatchingPlugins(
56 const llvm::StringRef pattern,
57 std::function<void(const PluginNamespace &plugin_namespace,
58 const std::vector<RegisteredPluginInfo> &plugin_info)>
59 action) {
60 int num_matching = 0;
61
62 for (const PluginNamespace &plugin_namespace :
63 PluginManager::GetPluginNamespaces()) {
64
65 std::vector<RegisteredPluginInfo> matching_plugins;
66 for (const RegisteredPluginInfo &plugin_info :
67 plugin_namespace.get_info()) {
68 if (PluginManager::MatchPluginName(pattern, plugin_namespace,
69 plugin_info))
70 matching_plugins.push_back(plugin_info);
71 }
72
73 if (!matching_plugins.empty()) {
74 num_matching += matching_plugins.size();
75 action(plugin_namespace, matching_plugins);
76 }
77 }
78
79 return num_matching;
80 }
81
82 // Call the "SetEnable" function for each matching plugins.
83 // Used to share the majority of the code between the enable
84 // and disable commands.
SetEnableOnMatchingPlugins(const llvm::StringRef & pattern,CommandReturnObject & result,bool enabled)85 int SetEnableOnMatchingPlugins(const llvm::StringRef &pattern,
86 CommandReturnObject &result, bool enabled) {
87 return ActOnMatchingPlugins(
88 pattern, [&](const PluginNamespace &plugin_namespace,
89 const std::vector<RegisteredPluginInfo> &plugins) {
90 result.AppendMessage(plugin_namespace.name);
91 for (const auto &plugin : plugins) {
92 if (!plugin_namespace.set_enabled(plugin.name, enabled)) {
93 result.AppendErrorWithFormat("failed to enable plugin %s.%s",
94 plugin_namespace.name.data(),
95 plugin.name.data());
96 continue;
97 }
98
99 result.AppendMessageWithFormat(
100 " %s %-30s %s\n", enabled ? "[+]" : "[-]", plugin.name.data(),
101 plugin.description.data());
102 }
103 });
104 }
105
ConvertJSONToPrettyString(const llvm::json::Value & json)106 static std::string ConvertJSONToPrettyString(const llvm::json::Value &json) {
107 std::string str;
108 llvm::raw_string_ostream os(str);
109 os << llvm::formatv("{0:2}", json).str();
110 os.flush();
111 return str;
112 }
113
114 #define LLDB_OPTIONS_plugin_list
115 #include "CommandOptions.inc"
116
117 // These option definitions are used by the plugin list command.
118 class PluginListCommandOptions : public Options {
119 public:
120 PluginListCommandOptions() = default;
121
122 ~PluginListCommandOptions() override = default;
123
SetOptionValue(uint32_t option_idx,llvm::StringRef option_arg,ExecutionContext * execution_context)124 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
125 ExecutionContext *execution_context) override {
126 Status error;
127 const int short_option = m_getopt_table[option_idx].val;
128
129 switch (short_option) {
130 case 'j':
131 m_json_format = true;
132 break;
133 default:
134 llvm_unreachable("Unimplemented option");
135 }
136
137 return error;
138 }
139
OptionParsingStarting(ExecutionContext * execution_context)140 void OptionParsingStarting(ExecutionContext *execution_context) override {
141 m_json_format = false;
142 }
143
GetDefinitions()144 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
145 return llvm::ArrayRef(g_plugin_list_options);
146 }
147
148 // Instance variables to hold the values for command options.
149 bool m_json_format = false;
150 };
151 } // namespace
152
153 class CommandObjectPluginList : public CommandObjectParsed {
154 public:
CommandObjectPluginList(CommandInterpreter & interpreter)155 CommandObjectPluginList(CommandInterpreter &interpreter)
156 : CommandObjectParsed(interpreter, "plugin list",
157 "Report info about registered LLDB plugins.",
158 nullptr) {
159 AddSimpleArgumentList(eArgTypeManagedPlugin);
160 SetHelpLong(R"(
161 Display information about registered plugins.
162 The plugin information is formatted as shown below:
163
164 <plugin-namespace>
165 [+] <plugin-name> Plugin #1 description
166 [-] <plugin-name> Plugin #2 description
167
168 An enabled plugin is marked with [+] and a disabled plugin is marked with [-].
169
170 Plugins can be listed by namespace and name with:
171
172 plugin list <plugin-namespace>[.<plugin-name>]
173
174 Plugins can be listed by namespace alone or with a fully qualified name. When listed
175 with just a namespace all plugins in that namespace are listed. When no arguments
176 are given all plugins are listed.
177
178 Examples:
179 List all plugins
180
181 (lldb) plugin list
182
183 List all plugins in the system-runtime namespace
184
185 (lldb) plugin list system-runtime
186
187 List only the plugin 'foo' matching a fully qualified name exactly
188
189 (lldb) plugin list system-runtime.foo
190 )");
191 }
192
193 ~CommandObjectPluginList() override = default;
194
GetOptions()195 Options *GetOptions() override { return &m_options; }
196
197 protected:
DoExecute(Args & command,CommandReturnObject & result)198 void DoExecute(Args &command, CommandReturnObject &result) override {
199 size_t argc = command.GetArgumentCount();
200 result.SetStatus(eReturnStatusSuccessFinishResult);
201
202 // Create a temporary vector to hold the patterns to simplify the logic
203 // for the case when the user passes no patterns
204 std::vector<llvm::StringRef> patterns;
205 patterns.reserve(argc == 0 ? 1 : argc);
206 if (argc == 0)
207 patterns.push_back("");
208 else
209 for (size_t i = 0; i < argc; ++i)
210 patterns.push_back(command[i].ref());
211
212 if (m_options.m_json_format)
213 OutputJsonFormat(patterns, result);
214 else
215 OutputTextFormat(patterns, result);
216 }
217
218 private:
OutputJsonFormat(const std::vector<llvm::StringRef> & patterns,CommandReturnObject & result)219 void OutputJsonFormat(const std::vector<llvm::StringRef> &patterns,
220 CommandReturnObject &result) {
221 llvm::json::Object obj;
222 bool found_empty = false;
223 for (const llvm::StringRef pattern : patterns) {
224 llvm::json::Object pat_obj = PluginManager::GetJSON(pattern);
225 if (pat_obj.empty()) {
226 found_empty = true;
227 result.AppendErrorWithFormat(
228 "Found no matching plugins for pattern '%s'", pattern.data());
229 break;
230 }
231 for (auto &entry : pat_obj) {
232 obj[entry.first] = std::move(entry.second);
233 }
234 }
235 if (!found_empty) {
236 result.AppendMessage(ConvertJSONToPrettyString(std::move(obj)));
237 }
238 }
239
OutputTextFormat(const std::vector<llvm::StringRef> & patterns,CommandReturnObject & result)240 void OutputTextFormat(const std::vector<llvm::StringRef> &patterns,
241 CommandReturnObject &result) {
242 for (const llvm::StringRef pattern : patterns) {
243 int num_matching = ActOnMatchingPlugins(
244 pattern, [&](const PluginNamespace &plugin_namespace,
245 const std::vector<RegisteredPluginInfo> &plugins) {
246 result.AppendMessage(plugin_namespace.name);
247 for (auto &plugin : plugins) {
248 result.AppendMessageWithFormat(
249 " %s %-30s %s\n", plugin.enabled ? "[+]" : "[-]",
250 plugin.name.data(), plugin.description.data());
251 }
252 });
253 if (num_matching == 0) {
254 result.AppendErrorWithFormat(
255 "Found no matching plugins for pattern '%s'", pattern.data());
256 break;
257 }
258 }
259 }
260
261 PluginListCommandOptions m_options;
262 };
263
DoPluginEnableDisable(Args & command,CommandReturnObject & result,bool enable)264 static void DoPluginEnableDisable(Args &command, CommandReturnObject &result,
265 bool enable) {
266 const char *name = enable ? "enable" : "disable";
267 size_t argc = command.GetArgumentCount();
268 if (argc == 0) {
269 result.AppendErrorWithFormat("'plugin %s' requires one or more arguments",
270 name);
271 return;
272 }
273 result.SetStatus(eReturnStatusSuccessFinishResult);
274
275 for (size_t i = 0; i < argc; ++i) {
276 llvm::StringRef pattern = command[i].ref();
277 int num_matching = SetEnableOnMatchingPlugins(pattern, result, enable);
278
279 if (num_matching == 0) {
280 result.AppendErrorWithFormat(
281 "Found no matching plugins to %s for pattern '%s'", name,
282 pattern.data());
283 break;
284 }
285 }
286 }
287
288 class CommandObjectPluginEnable : public CommandObjectParsed {
289 public:
CommandObjectPluginEnable(CommandInterpreter & interpreter)290 CommandObjectPluginEnable(CommandInterpreter &interpreter)
291 : CommandObjectParsed(interpreter, "plugin enable",
292 "Enable registered LLDB plugins.", nullptr) {
293 AddSimpleArgumentList(eArgTypeManagedPlugin);
294 }
295
296 ~CommandObjectPluginEnable() override = default;
297
298 protected:
DoExecute(Args & command,CommandReturnObject & result)299 void DoExecute(Args &command, CommandReturnObject &result) override {
300 DoPluginEnableDisable(command, result, /*enable=*/true);
301 }
302 };
303
304 class CommandObjectPluginDisable : public CommandObjectParsed {
305 public:
CommandObjectPluginDisable(CommandInterpreter & interpreter)306 CommandObjectPluginDisable(CommandInterpreter &interpreter)
307 : CommandObjectParsed(interpreter, "plugin disable",
308 "Disable registered LLDB plugins.", nullptr) {
309 AddSimpleArgumentList(eArgTypeManagedPlugin);
310 }
311
312 ~CommandObjectPluginDisable() override = default;
313
314 protected:
DoExecute(Args & command,CommandReturnObject & result)315 void DoExecute(Args &command, CommandReturnObject &result) override {
316 DoPluginEnableDisable(command, result, /*enable=*/false);
317 }
318 };
319
CommandObjectPlugin(CommandInterpreter & interpreter)320 CommandObjectPlugin::CommandObjectPlugin(CommandInterpreter &interpreter)
321 : CommandObjectMultiword(interpreter, "plugin",
322 "Commands for managing LLDB plugins.",
323 "plugin <subcommand> [<subcommand-options>]") {
324 LoadSubCommand("load",
325 CommandObjectSP(new CommandObjectPluginLoad(interpreter)));
326 LoadSubCommand("list",
327 CommandObjectSP(new CommandObjectPluginList(interpreter)));
328 LoadSubCommand("enable",
329 CommandObjectSP(new CommandObjectPluginEnable(interpreter)));
330 LoadSubCommand("disable",
331 CommandObjectSP(new CommandObjectPluginDisable(interpreter)));
332 }
333
334 CommandObjectPlugin::~CommandObjectPlugin() = default;
335