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: |
20 | CommandObjectPluginLoad(CommandInterpreter &interpreter) |
21 | : CommandObjectParsed(interpreter, "plugin load", |
22 | "Import a dylib that implements an LLDB plugin.", |
23 | nullptr) { |
24 | AddSimpleArgumentList(arg_type: eArgTypeFilename); |
25 | } |
26 | |
27 | ~CommandObjectPluginLoad() override = default; |
28 | |
29 | protected: |
30 | void DoExecute(Args &command, CommandReturnObject &result) override { |
31 | size_t argc = command.GetArgumentCount(); |
32 | |
33 | if (argc != 1) { |
34 | result.AppendError(in_string: "'plugin load' requires one argument"); |
35 | return; |
36 | } |
37 | |
38 | Status error; |
39 | |
40 | FileSpec dylib_fspec(command[0].ref()); |
41 | FileSystem::Instance().Resolve(file_spec&: dylib_fspec); |
42 | |
43 | if (GetDebugger().LoadPlugin(spec: dylib_fspec, error)) |
44 | result.SetStatus(eReturnStatusSuccessFinishResult); |
45 | else { |
46 | result.AppendError(in_string: 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. |
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_ns: plugin_namespace, |
69 | plugin: plugin_info)) |
70 | matching_plugins.push_back(x: 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. |
85 | int SetEnableOnMatchingPlugins(const llvm::StringRef &pattern, |
86 | CommandReturnObject &result, bool enabled) { |
87 | return ActOnMatchingPlugins( |
88 | pattern, action: [&](const PluginNamespace &plugin_namespace, |
89 | const std::vector<RegisteredPluginInfo> &plugins) { |
90 | result.AppendMessage(in_string: plugin_namespace.name); |
91 | for (const auto &plugin : plugins) { |
92 | if (!plugin_namespace.set_enabled(plugin.name, enabled)) { |
93 | result.AppendErrorWithFormat(format: "failed to enable plugin %s.%s", |
94 | plugin_namespace.name.data(), |
95 | plugin.name.data()); |
96 | continue; |
97 | } |
98 | |
99 | result.AppendMessageWithFormat( |
100 | format: " %s %-30s %s\n", enabled ? "[+]": "[-]", plugin.name.data(), |
101 | plugin.description.data()); |
102 | } |
103 | }); |
104 | } |
105 | |
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(Fmt: "{0:2}", Vals: 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 | |
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 | |
140 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
141 | m_json_format = false; |
142 | } |
143 | |
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: |
155 | CommandObjectPluginList(CommandInterpreter &interpreter) |
156 | : CommandObjectParsed(interpreter, "plugin list", |
157 | "Report info about registered LLDB plugins.", |
158 | nullptr) { |
159 | AddSimpleArgumentList(arg_type: 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 | |
195 | Options *GetOptions() override { return &m_options; } |
196 | |
197 | protected: |
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(n: argc == 0 ? 1 : argc); |
206 | if (argc == 0) |
207 | patterns.push_back(x: ""); |
208 | else |
209 | for (size_t i = 0; i < argc; ++i) |
210 | patterns.push_back(x: 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: |
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 | format: "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(in_string: ConvertJSONToPrettyString(json: std::move(obj))); |
237 | } |
238 | } |
239 | |
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, action: [&](const PluginNamespace &plugin_namespace, |
245 | const std::vector<RegisteredPluginInfo> &plugins) { |
246 | result.AppendMessage(in_string: plugin_namespace.name); |
247 | for (auto &plugin : plugins) { |
248 | result.AppendMessageWithFormat( |
249 | format: " %s %-30s %s\n", plugin.enabled ? "[+]": "[-]", |
250 | plugin.name.data(), plugin.description.data()); |
251 | } |
252 | }); |
253 | if (num_matching == 0) { |
254 | result.AppendErrorWithFormat( |
255 | format: "Found no matching plugins for pattern '%s'", pattern.data()); |
256 | break; |
257 | } |
258 | } |
259 | } |
260 | |
261 | PluginListCommandOptions m_options; |
262 | }; |
263 | |
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(format: "'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, enabled: enable); |
278 | |
279 | if (num_matching == 0) { |
280 | result.AppendErrorWithFormat( |
281 | format: "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: |
290 | CommandObjectPluginEnable(CommandInterpreter &interpreter) |
291 | : CommandObjectParsed(interpreter, "plugin enable", |
292 | "Enable registered LLDB plugins.", nullptr) { |
293 | AddSimpleArgumentList(arg_type: eArgTypeManagedPlugin); |
294 | } |
295 | |
296 | ~CommandObjectPluginEnable() override = default; |
297 | |
298 | protected: |
299 | void DoExecute(Args &command, CommandReturnObject &result) override { |
300 | DoPluginEnableDisable(command, result, /*enable=*/true); |
301 | } |
302 | }; |
303 | |
304 | class CommandObjectPluginDisable : public CommandObjectParsed { |
305 | public: |
306 | CommandObjectPluginDisable(CommandInterpreter &interpreter) |
307 | : CommandObjectParsed(interpreter, "plugin disable", |
308 | "Disable registered LLDB plugins.", nullptr) { |
309 | AddSimpleArgumentList(arg_type: eArgTypeManagedPlugin); |
310 | } |
311 | |
312 | ~CommandObjectPluginDisable() override = default; |
313 | |
314 | protected: |
315 | void DoExecute(Args &command, CommandReturnObject &result) override { |
316 | DoPluginEnableDisable(command, result, /*enable=*/false); |
317 | } |
318 | }; |
319 | |
320 | CommandObjectPlugin::CommandObjectPlugin(CommandInterpreter &interpreter) |
321 | : CommandObjectMultiword(interpreter, "plugin", |
322 | "Commands for managing LLDB plugins.", |
323 | "plugin <subcommand> [<subcommand-options>]") { |
324 | LoadSubCommand(cmd_name: "load", |
325 | command_obj: CommandObjectSP(new CommandObjectPluginLoad(interpreter))); |
326 | LoadSubCommand(cmd_name: "list", |
327 | command_obj: CommandObjectSP(new CommandObjectPluginList(interpreter))); |
328 | LoadSubCommand(cmd_name: "enable", |
329 | command_obj: CommandObjectSP(new CommandObjectPluginEnable(interpreter))); |
330 | LoadSubCommand(cmd_name: "disable", |
331 | command_obj: CommandObjectSP(new CommandObjectPluginDisable(interpreter))); |
332 | } |
333 | |
334 | CommandObjectPlugin::~CommandObjectPlugin() = default; |
335 |
Definitions
- CommandObjectPluginLoad
- CommandObjectPluginLoad
- ~CommandObjectPluginLoad
- DoExecute
- ActOnMatchingPlugins
- SetEnableOnMatchingPlugins
- ConvertJSONToPrettyString
- PluginListCommandOptions
- PluginListCommandOptions
- ~PluginListCommandOptions
- SetOptionValue
- OptionParsingStarting
- GetDefinitions
- CommandObjectPluginList
- CommandObjectPluginList
- ~CommandObjectPluginList
- GetOptions
- DoExecute
- OutputJsonFormat
- OutputTextFormat
- DoPluginEnableDisable
- CommandObjectPluginEnable
- CommandObjectPluginEnable
- ~CommandObjectPluginEnable
- DoExecute
- CommandObjectPluginDisable
- CommandObjectPluginDisable
- ~CommandObjectPluginDisable
- DoExecute
- CommandObjectPlugin
Learn to use CMake with our Intro Training
Find out more