xref: /freebsd/contrib/llvm-project/lldb/source/Commands/CommandObjectPlugin.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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