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(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("'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. 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. 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 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 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(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(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: 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 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 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: 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: 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(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("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