Author: David Peixotto Date: 2025-06-09T13:30:13-07:00 New Revision: d4fe522eb4ae710e90107a682911fc75f2388a87
URL: https://github.com/llvm/llvm-project/commit/d4fe522eb4ae710e90107a682911fc75f2388a87 DIFF: https://github.com/llvm/llvm-project/commit/d4fe522eb4ae710e90107a682911fc75f2388a87.diff LOG: Add commands to list/enable/disable plugins (#134418) 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 and InstrumentationRuntime plugins are currently supported. We can easily extend the existing implementation to support more types. The scope was limited to these plugins to keep the PR size manageable. 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. Added: lldb/test/Shell/Commands/command-plugin-enable+disable.test lldb/test/Shell/Commands/command-plugin-list.test Modified: lldb/include/lldb/Core/PluginManager.h lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h lldb/include/lldb/Target/Statistics.h lldb/include/lldb/lldb-enumerations.h lldb/source/Commands/CommandObjectPlugin.cpp lldb/source/Commands/CommandObjectStats.cpp lldb/source/Commands/Options.td lldb/source/Core/PluginManager.cpp lldb/source/Target/Statistics.cpp lldb/test/API/commands/statistics/basic/TestStats.py lldb/unittests/Core/PluginManagerTest.cpp Removed: ################################################################################ diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h index e2f709ecd2ff7..e7b1691031111 100644 --- a/lldb/include/lldb/Core/PluginManager.h +++ b/lldb/include/lldb/Core/PluginManager.h @@ -19,10 +19,13 @@ #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-private-interfaces.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" #include <cstddef> #include <cstdint> +#include <functional> #include <vector> #define LLDB_PLUGIN_DEFINE_ADV(ClassName, PluginName) \ @@ -55,12 +58,67 @@ struct RegisteredPluginInfo { bool enabled = false; }; +// Define some data structures to describe known plugin "namespaces". +// The PluginManager is organized into a series of static functions +// that operate on diff erent types of plugins. For example SystemRuntime +// and ObjectFile plugins. +// +// The namespace name is used a prefix when matching plugin names. For example, +// if we have an "macosx" plugin in the "system-runtime" namespace then we will +// match a plugin name pattern against the "system-runtime.macosx" name. +// +// The plugin namespace here is used so we can operate on all the plugins +// of a given type so it is easy to enable or disable them as a group. +using GetPluginInfo = std::function<std::vector<RegisteredPluginInfo>()>; +using SetPluginEnabled = std::function<bool(llvm::StringRef, bool)>; +struct PluginNamespace { + llvm::StringRef name; + GetPluginInfo get_info; + SetPluginEnabled set_enabled; +}; + class PluginManager { public: static void Initialize(); static void Terminate(); + // Support for enabling and disabling plugins. + + // Return the plugins that can be enabled or disabled by the user. + static llvm::ArrayRef<PluginNamespace> GetPluginNamespaces(); + + // Generate a json object that describes the plugins that are available. + // This is a json representation of the plugin info returned by + // GetPluginNamespaces(). + // + // { + // <plugin-namespace>: [ + // { + // "enabled": <bool>, + // "name": <plugin-name>, + // }, + // ... + // ], + // ... + // } + // + // If pattern is given it will be used to filter the plugins that are + // are returned. The pattern filters the plugin names using the + // PluginManager::MatchPluginName() function. + static llvm::json::Object GetJSON(llvm::StringRef pattern = ""); + + // Return true if the pattern matches the plugin name. + // + // The pattern matches the name if it is exactly equal to the namespace name + // or if it is equal to the qualified name, which is the namespace name + // followed by a dot and the plugin name (e.g. "system-runtime.foo"). + // + // An empty pattern matches all plugins. + static bool MatchPluginName(llvm::StringRef pattern, + const PluginNamespace &plugin_ns, + const RegisteredPluginInfo &plugin); + // ABI static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback); @@ -491,6 +549,12 @@ class PluginManager { static InstrumentationRuntimeCreateInstance GetInstrumentationRuntimeCreateCallbackAtIndex(uint32_t idx); + static std::vector<RegisteredPluginInfo> + GetInstrumentationRuntimePluginInfo(); + + static bool SetInstrumentationRuntimePluginEnabled(llvm::StringRef name, + bool enabled); + // TypeSystem static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, TypeSystemCreateInstance create_callback, diff --git a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h index 1875ff6a048d4..8535dfcf46da5 100644 --- a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h +++ b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h @@ -314,6 +314,7 @@ static constexpr CommandObject::ArgumentTableEntry g_argument_table[] = { { lldb::eArgTypeModule, "module", lldb::CompletionType::eModuleCompletion, {}, { nullptr, false }, "The name of a module loaded into the current target." }, { lldb::eArgTypeCPUName, "cpu-name", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "The name of a CPU." }, { lldb::eArgTypeCPUFeatures, "cpu-features", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "The CPU feature string." }, + { lldb::eArgTypeManagedPlugin, "managed-plugin", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "Plugins managed by the PluginManager" }, // clang-format on }; diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h index 9ac32172e8002..2d0d25cd3c753 100644 --- a/lldb/include/lldb/Target/Statistics.h +++ b/lldb/include/lldb/Target/Statistics.h @@ -196,12 +196,21 @@ struct StatisticsOptions { return !GetSummaryOnly(); } + void SetIncludePlugins(bool value) { m_include_plugins = value; } + bool GetIncludePlugins() const { + if (m_include_plugins.has_value()) + return m_include_plugins.value(); + // Default to true in both default mode and summary mode. + return true; + } + private: std::optional<bool> m_summary_only; std::optional<bool> m_load_all_debug_info; std::optional<bool> m_include_targets; std::optional<bool> m_include_modules; std::optional<bool> m_include_transcript; + std::optional<bool> m_include_plugins; }; /// A class that represents statistics about a TypeSummaryProviders invocations diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 6d10cc8bcffcb..eeb7299a354e1 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -663,6 +663,7 @@ enum CommandArgumentType { eArgTypeModule, eArgTypeCPUName, eArgTypeCPUFeatures, + eArgTypeManagedPlugin, eArgTypeLastArg // Always keep this entry as the last entry in this // enumeration!! }; diff --git a/lldb/source/Commands/CommandObjectPlugin.cpp b/lldb/source/Commands/CommandObjectPlugin.cpp index f3108b8a768d2..cdc9006bf5261 100644 --- a/lldb/source/Commands/CommandObjectPlugin.cpp +++ b/lldb/source/Commands/CommandObjectPlugin.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "CommandObjectPlugin.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/OptionParser.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" @@ -46,12 +48,287 @@ class CommandObjectPluginLoad : public CommandObjectParsed { } }; +namespace { +// Helper function to perform an action on each matching plugin. +// The action callback is given the containing namespace along with plugin info +// for each matching plugin. +static int ActOnMatchingPlugins( + const llvm::StringRef pattern, + std::function<void(const PluginNamespace &plugin_namespace, + const std::vector<RegisteredPluginInfo> &plugin_info)> + action) { + int num_matching = 0; + + for (const PluginNamespace &plugin_namespace : + PluginManager::GetPluginNamespaces()) { + + std::vector<RegisteredPluginInfo> matching_plugins; + for (const RegisteredPluginInfo &plugin_info : + plugin_namespace.get_info()) { + if (PluginManager::MatchPluginName(pattern, plugin_namespace, + plugin_info)) + matching_plugins.push_back(plugin_info); + } + + if (!matching_plugins.empty()) { + num_matching += matching_plugins.size(); + action(plugin_namespace, matching_plugins); + } + } + + return num_matching; +} + +// Call the "SetEnable" function for each matching plugins. +// Used to share the majority of the code between the enable +// and disable commands. +int SetEnableOnMatchingPlugins(const llvm::StringRef &pattern, + CommandReturnObject &result, bool enabled) { + return ActOnMatchingPlugins( + pattern, [&](const PluginNamespace &plugin_namespace, + const std::vector<RegisteredPluginInfo> &plugins) { + result.AppendMessage(plugin_namespace.name); + for (const auto &plugin : plugins) { + if (!plugin_namespace.set_enabled(plugin.name, enabled)) { + result.AppendErrorWithFormat("failed to enable plugin %s.%s", + plugin_namespace.name.data(), + plugin.name.data()); + continue; + } + + result.AppendMessageWithFormat( + " %s %-30s %s\n", enabled ? "[+]" : "[-]", plugin.name.data(), + plugin.description.data()); + } + }); +} + +static std::string ConvertJSONToPrettyString(const llvm::json::Value &json) { + std::string str; + llvm::raw_string_ostream os(str); + os << llvm::formatv("{0:2}", json).str(); + os.flush(); + return str; +} + +#define LLDB_OPTIONS_plugin_list +#include "CommandOptions.inc" + +// These option definitions are used by the plugin list command. +class PluginListCommandOptions : public Options { +public: + PluginListCommandOptions() = default; + + ~PluginListCommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + Status error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) { + case 'j': + m_json_format = true; + break; + default: + llvm_unreachable("Unimplemented option"); + } + + return error; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_json_format = false; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return llvm::ArrayRef(g_plugin_list_options); + } + + // Instance variables to hold the values for command options. + bool m_json_format = false; +}; +} // namespace + +class CommandObjectPluginList : public CommandObjectParsed { +public: + CommandObjectPluginList(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "plugin list", + "Report info about registered LLDB plugins.", + nullptr) { + AddSimpleArgumentList(eArgTypeManagedPlugin); + SetHelpLong(R"( +Display information about registered plugins. +The plugin information is formatted as shown below: + + <plugin-namespace> + [+] <plugin-name> Plugin #1 description + [-] <plugin-name> Plugin #2 description + +An enabled plugin is marked with [+] and a disabled plugin is marked with [-]. + +Plugins can be listed by namespace and name with: + + plugin list <plugin-namespace>[.<plugin-name>] + +Plugins can be listed by namespace alone or with a fully qualified name. When listed +with just a namespace all plugins in that namespace are listed. When no arguments +are given all plugins are listed. + +Examples: +List all plugins + + (lldb) plugin list + +List all plugins in the system-runtime namespace + + (lldb) plugin list system-runtime + +List only the plugin 'foo' matching a fully qualified name exactly + + (lldb) plugin list system-runtime.foo +)"); + } + + ~CommandObjectPluginList() override = default; + + Options *GetOptions() override { return &m_options; } + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + size_t argc = command.GetArgumentCount(); + result.SetStatus(eReturnStatusSuccessFinishResult); + + // Create a temporary vector to hold the patterns to simplify the logic + // for the case when the user passes no patterns + std::vector<llvm::StringRef> patterns; + patterns.reserve(argc == 0 ? 1 : argc); + if (argc == 0) + patterns.push_back(""); + else + for (size_t i = 0; i < argc; ++i) + patterns.push_back(command[i].ref()); + + if (m_options.m_json_format) + OutputJsonFormat(patterns, result); + else + OutputTextFormat(patterns, result); + } + +private: + void OutputJsonFormat(const std::vector<llvm::StringRef> &patterns, + CommandReturnObject &result) { + llvm::json::Object obj; + bool found_empty = false; + for (const llvm::StringRef pattern : patterns) { + llvm::json::Object pat_obj = PluginManager::GetJSON(pattern); + if (pat_obj.empty()) { + found_empty = true; + result.AppendErrorWithFormat( + "Found no matching plugins for pattern '%s'", pattern.data()); + break; + } + for (auto &entry : pat_obj) { + obj[entry.first] = std::move(entry.second); + } + } + if (!found_empty) { + result.AppendMessage(ConvertJSONToPrettyString(std::move(obj))); + } + } + + void OutputTextFormat(const std::vector<llvm::StringRef> &patterns, + CommandReturnObject &result) { + for (const llvm::StringRef pattern : patterns) { + int num_matching = ActOnMatchingPlugins( + pattern, [&](const PluginNamespace &plugin_namespace, + const std::vector<RegisteredPluginInfo> &plugins) { + result.AppendMessage(plugin_namespace.name); + for (auto &plugin : plugins) { + result.AppendMessageWithFormat( + " %s %-30s %s\n", plugin.enabled ? "[+]" : "[-]", + plugin.name.data(), plugin.description.data()); + } + }); + if (num_matching == 0) { + result.AppendErrorWithFormat( + "Found no matching plugins for pattern '%s'", pattern.data()); + break; + } + } + } + + PluginListCommandOptions m_options; +}; + +static void DoPluginEnableDisable(Args &command, CommandReturnObject &result, + bool enable) { + const char *name = enable ? "enable" : "disable"; + size_t argc = command.GetArgumentCount(); + if (argc == 0) { + result.AppendErrorWithFormat("'plugin %s' requires one or more arguments", + name); + return; + } + result.SetStatus(eReturnStatusSuccessFinishResult); + + for (size_t i = 0; i < argc; ++i) { + llvm::StringRef pattern = command[i].ref(); + int num_matching = SetEnableOnMatchingPlugins(pattern, result, enable); + + if (num_matching == 0) { + result.AppendErrorWithFormat( + "Found no matching plugins to %s for pattern '%s'", name, + pattern.data()); + break; + } + } +} + +class CommandObjectPluginEnable : public CommandObjectParsed { +public: + CommandObjectPluginEnable(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "plugin enable", + "Enable registered LLDB plugins.", nullptr) { + AddSimpleArgumentList(eArgTypeManagedPlugin); + } + + ~CommandObjectPluginEnable() override = default; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + DoPluginEnableDisable(command, result, /*enable=*/true); + } +}; + +class CommandObjectPluginDisable : public CommandObjectParsed { +public: + CommandObjectPluginDisable(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "plugin disable", + "Disable registered LLDB plugins.", nullptr) { + AddSimpleArgumentList(eArgTypeManagedPlugin); + } + + ~CommandObjectPluginDisable() override = default; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + DoPluginEnableDisable(command, result, /*enable=*/false); + } +}; + CommandObjectPlugin::CommandObjectPlugin(CommandInterpreter &interpreter) : CommandObjectMultiword(interpreter, "plugin", "Commands for managing LLDB plugins.", "plugin <subcommand> [<subcommand-options>]") { LoadSubCommand("load", CommandObjectSP(new CommandObjectPluginLoad(interpreter))); + LoadSubCommand("list", + CommandObjectSP(new CommandObjectPluginList(interpreter))); + LoadSubCommand("enable", + CommandObjectSP(new CommandObjectPluginEnable(interpreter))); + LoadSubCommand("disable", + CommandObjectSP(new CommandObjectPluginDisable(interpreter))); } CommandObjectPlugin::~CommandObjectPlugin() = default; diff --git a/lldb/source/Commands/CommandObjectStats.cpp b/lldb/source/Commands/CommandObjectStats.cpp index 7d333afc231ba..b77c44bdf5d09 100644 --- a/lldb/source/Commands/CommandObjectStats.cpp +++ b/lldb/source/Commands/CommandObjectStats.cpp @@ -103,6 +103,13 @@ class CommandObjectStatsDump : public CommandObjectParsed { else error = Status::FromError(bool_or_error.takeError()); break; + case 'p': + if (llvm::Expected<bool> bool_or_error = + OptionArgParser::ToBoolean("--plugins", option_arg)) + m_stats_options.SetIncludePlugins(*bool_or_error); + else + error = Status::FromError(bool_or_error.takeError()); + break; default: llvm_unreachable("Unimplemented option"); } diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index 848d42daeb2ad..75bdffe16c52e 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -683,6 +683,11 @@ let Command = "platform shell" in { Desc<"Shell interpreter path. This is the binary used to run the command.">; } +let Command = "plugin list" in { + def plugin_list_json : Option<"json", "j">, + Desc<"Output the plugin list in json format.">; +} + let Command = "process launch" in { def process_launch_stop_at_entry : Option<"stop-at-entry", "s">, Desc<"Stop at the entry point of the program when launching a process.">; @@ -1484,5 +1489,11 @@ let Command = "statistics dump" in { "scripts executed during a debug session. " "Defaults to true, unless the '--summary' mode is enabled, in which case " "this is turned off unless specified.">; - + def statistics_dump_plugins + : Option<"plugins", "p">, + Group<1>, + Arg<"Boolean">, + Desc<"Dump statistics for known plugins including name, order, and " + "enabled state. Defaults to true for both summary and default " + "mode.">; } diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp index de815e6308838..5d44434033c55 100644 --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -181,6 +181,56 @@ void PluginManager::Terminate() { plugin_map.clear(); } +llvm::ArrayRef<PluginNamespace> PluginManager::GetPluginNamespaces() { + // Currently supported set of plugin namespaces. This will be expanded + // over time. + static PluginNamespace PluginNamespaces[] = { + {"system-runtime", PluginManager::GetSystemRuntimePluginInfo, + PluginManager::SetSystemRuntimePluginEnabled}, + {"instrumentation-runtime", + PluginManager::GetInstrumentationRuntimePluginInfo, + PluginManager::SetInstrumentationRuntimePluginEnabled}}; + + return PluginNamespaces; +} + +llvm::json::Object PluginManager::GetJSON(llvm::StringRef pattern) { + llvm::json::Object plugin_stats; + + for (const PluginNamespace &plugin_ns : GetPluginNamespaces()) { + llvm::json::Array namespace_stats; + + for (const RegisteredPluginInfo &plugin : plugin_ns.get_info()) { + if (MatchPluginName(pattern, plugin_ns, plugin)) { + llvm::json::Object plugin_json; + plugin_json.try_emplace("name", plugin.name); + plugin_json.try_emplace("enabled", plugin.enabled); + namespace_stats.emplace_back(std::move(plugin_json)); + } + } + if (!namespace_stats.empty()) + plugin_stats.try_emplace(plugin_ns.name, std::move(namespace_stats)); + } + + return plugin_stats; +} + +bool PluginManager::MatchPluginName(llvm::StringRef pattern, + const PluginNamespace &plugin_ns, + const RegisteredPluginInfo &plugin_info) { + // The empty pattern matches all plugins. + if (pattern.empty()) + return true; + + // Check if the pattern matches the namespace. + if (pattern == plugin_ns.name) + return true; + + // Check if the pattern matches the qualified name. + std::string qualified_name = (plugin_ns.name + "." + plugin_info.name).str(); + return pattern == qualified_name; +} + template <typename Callback> struct PluginInstance { typedef Callback CallbackType; @@ -1513,6 +1563,16 @@ PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex(uint32_t idx) { return GetInstrumentationRuntimeInstances().GetCallbackAtIndex(idx); } +std::vector<RegisteredPluginInfo> +PluginManager::GetInstrumentationRuntimePluginInfo() { + return GetInstrumentationRuntimeInstances().GetPluginInfoForAllInstances(); +} + +bool PluginManager::SetInstrumentationRuntimePluginEnabled(llvm::StringRef name, + bool enable) { + return GetInstrumentationRuntimeInstances().SetInstanceEnabled(name, enable); +} + #pragma mark TypeSystem struct TypeSystemInstance : public PluginInstance<TypeSystemCreateInstance> { diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp index 4cfd0629ea271..6ec8f8963baf9 100644 --- a/lldb/source/Target/Statistics.cpp +++ b/lldb/source/Target/Statistics.cpp @@ -10,6 +10,7 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Target/DynamicLoader.h" @@ -294,6 +295,7 @@ llvm::json::Value DebuggerStats::ReportStatistics( const bool include_targets = options.GetIncludeTargets(); const bool include_modules = options.GetIncludeModules(); const bool include_transcript = options.GetIncludeTranscript(); + const bool include_plugins = options.GetIncludePlugins(); json::Array json_targets; json::Array json_modules; @@ -489,6 +491,10 @@ llvm::json::Value DebuggerStats::ReportStatistics( } } + if (include_plugins) { + global_stats.try_emplace("plugins", PluginManager::GetJSON()); + } + return std::move(global_stats); } diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py index a9a7e933a3211..83132b40d85db 100644 --- a/lldb/test/API/commands/statistics/basic/TestStats.py +++ b/lldb/test/API/commands/statistics/basic/TestStats.py @@ -1068,3 +1068,89 @@ def test_multiple_targets(self): all_targets_stats = self.get_stats("--all-targets") self.assertIsNotNone(self.find_module_in_metrics(main_exe, all_targets_stats)) self.assertIsNotNone(self.find_module_in_metrics(second_exe, all_targets_stats)) + + # Return some level of the plugin stats hierarchy. + # Will return either the top-level node, the namespace node, or a specific + # plugin node based on requested values. + # + # If any of the requested keys are not found in the stats then return None. + # + # Plugin stats look like this: + # + # "plugins": { + # "system-runtime": [ + # { + # "enabled": true, + # "name": "systemruntime-macosx" + # } + # ] + # }, + def get_plugin_stats(self, debugger_stats, plugin_namespace=None, plugin_name=None): + # Get top level plugin stats. + if "plugins" not in debugger_stats: + return None + plugins = debugger_stats["plugins"] + if not plugin_namespace: + return plugins + + # Plugin namespace stats. + if plugin_namespace not in plugins: + return None + plugins_for_namespace = plugins[plugin_namespace] + if not plugin_name: + return plugins_for_namespace + + # Specific plugin stats. + for plugin in debugger_stats["plugins"][plugin_namespace]: + if plugin["name"] == plugin_name: + return plugin + return None + + def test_plugin_stats(self): + """ + Test "statistics dump" contains plugin info. + """ + self.build() + exe = self.getBuildArtifact("a.out") + target = self.createTestTarget(file_path=exe) + debugger_stats = self.get_stats() + + # Verify that the statistics dump contains the plugin information. + plugins = self.get_plugin_stats(debugger_stats) + self.assertIsNotNone(plugins) + + # Check for a known plugin namespace that should be in the stats. + system_runtime_plugins = self.get_plugin_stats(debugger_stats, "system-runtime") + self.assertIsNotNone(system_runtime_plugins) + + # Validate the keys exists for the bottom-level plugin stats. + plugin_keys_exist = [ + "name", + "enabled", + ] + for plugin in system_runtime_plugins: + self.verify_keys( + plugin, 'debugger_stats["plugins"]["system-runtime"]', plugin_keys_exist + ) + + # Check for a known plugin that is enabled by default. + system_runtime_macosx_plugin = self.get_plugin_stats( + debugger_stats, "system-runtime", "systemruntime-macosx" + ) + self.assertIsNotNone(system_runtime_macosx_plugin) + self.assertTrue(system_runtime_macosx_plugin["enabled"]) + + # Now disable the plugin and check the stats again. + # The stats should show the plugin is disabled. + self.runCmd("plugin disable system-runtime.systemruntime-macosx") + debugger_stats = self.get_stats() + system_runtime_macosx_plugin = self.get_plugin_stats( + debugger_stats, "system-runtime", "systemruntime-macosx" + ) + self.assertIsNotNone(system_runtime_macosx_plugin) + self.assertFalse(system_runtime_macosx_plugin["enabled"]) + + # Plugins should not show up in the stats when disabled with an option. + debugger_stats = self.get_stats("--plugins false") + plugins = self.get_plugin_stats(debugger_stats) + self.assertIsNone(plugins) diff --git a/lldb/test/Shell/Commands/command-plugin-enable+disable.test b/lldb/test/Shell/Commands/command-plugin-enable+disable.test new file mode 100644 index 0000000000000..af646e60abef0 --- /dev/null +++ b/lldb/test/Shell/Commands/command-plugin-enable+disable.test @@ -0,0 +1,87 @@ +# This test validates the plugin enable and disable commands. +# Currently it works only for system-runtime plugins and we only have one +# system runtime plugin so testing is a bit limited. +# +# Note that commands that return errors will stop running a script, so we +# have new RUN lines for any command that is expected to return an error. + +# RUN: %lldb -s %s -o exit 2>&1 | FileCheck %s + +# Test plugin list shows the default state which is expected to be enabled. +plugin list +# CHECK-LABEL: plugin list +# CHECK: system-runtime +# CHECK: [+] systemruntime-macosx System runtime plugin for Mac OS X native libraries + +# Test plugin disable disables a plugin. +plugin disable system-runtime.systemruntime-macosx +# CHECK-LABEL: plugin disable system-runtime.systemruntime-macosx +# CHECK: system-runtime +# CHECK: [-] systemruntime-macosx System runtime plugin for Mac OS X native libraries + +# Make sure plugin list shows it disabled as well. +plugin list +# CHECK: system-runtime +# CHECK: [-] systemruntime-macosx System runtime plugin for Mac OS X native libraries + +# Test plugin enable re-enables a plugin. +plugin enable system-runtime.systemruntime-macosx +# CHECK-LABEL: plugin enable system-runtime.systemruntime-macosx +# CHECK: system-runtime +# CHECK: [+] systemruntime-macosx System runtime plugin for Mac OS X native libraries + +# Make sure plugin list shows it enabled as well. +plugin list +# CHECK: system-runtime +# CHECK: [+] systemruntime-macosx System runtime plugin for Mac OS X native libraries + +# Test plugin disable with namespace works. +plugin disable system-runtime +# CHECK-LABEL: plugin disable system-runtime +# CHECK: system-runtime +# CHECK: [-] systemruntime-macosx System runtime plugin for Mac OS X native libraries + +# Test plugin enable with namespace works. +plugin enable system-runtime +# CHECK-LABEL: plugin enable system-runtime +# CHECK: system-runtime +# CHECK: [+] systemruntime-macosx System runtime plugin for Mac OS X native libraries + +# Test plugin enable/disable for instrumentation plugin works. +plugin enable instrumentation-runtime +# CHECK-LABEL: plugin enable instrumentation-runtime +# CHECK: instrumentation-runtime +# CHECK: [+] AddressSanitizer +plugin disable instrumentation-runtime +# CHECK-LABEL: plugin disable instrumentation-runtime +# CHECK: instrumentation-runtime +# CHECK: [-] AddressSanitizer + +# Test plugin enable with multiple arguments. +plugin enable system-runtime instrumentation-runtime +# CHECK-LABEL: plugin enable system-runtime instrumentation-runtime +# CHECK: system-runtime +# CHECK: [+] systemruntime-macosx System runtime plugin for Mac OS X native libraries. +# CHECK: instrumentation-runtime +# CHECK: [+] AddressSanitizer AddressSanitizer instrumentation runtime plugin. + +# Test plugin disable with multiple arguments. +plugin disable system-runtime instrumentation-runtime +# CHECK-LABEL: plugin disable system-runtime instrumentation-runtime +# CHECK: system-runtime +# CHECK: [-] systemruntime-macosx System runtime plugin for Mac OS X native libraries. +# CHECK: instrumentation-runtime +# CHECK: [-] AddressSanitizer AddressSanitizer instrumentation runtime plugin. + +# Test plugin enable/disable for unknown plugin returns an error. +# RUN: %lldb -o "plugin enable some-plugin-that-does-not-exist" 2>&1 | FileCheck %s --check-prefix=ERROR_PLUGIN_NOT_FOUND +# RUN: %lldb -o "plugin disable some-plugin-that-does-not-exist" 2>&1 | FileCheck %s --check-prefix=ERROR_PLUGIN_NOT_FOUND +# RUN: %lldb -o "plugin enable system-runtime some-plugin-that-does-not-exist" 2>&1 | FileCheck %s --check-prefix=ERROR_PLUGIN_NOT_FOUND +# ERROR_PLUGIN_NOT_FOUND: error: Found no matching plugins + +# Test plugin enable/disable requires a plugin name. +# RUN: %lldb -o "plugin enable" 2>&1 | FileCheck %s --check-prefix=ERROR_ARGUMENTS_ENABLE +# ERROR_ARGUMENTS_ENABLE: error: 'plugin enable' requires one or more arguments + +# RUN: %lldb -o "plugin disable" 2>&1 | FileCheck %s --check-prefix=ERROR_ARGUMENTS_DISABLE +# ERROR_ARGUMENTS_DISABLE: error: 'plugin disable' requires one or more arguments diff --git a/lldb/test/Shell/Commands/command-plugin-list.test b/lldb/test/Shell/Commands/command-plugin-list.test new file mode 100644 index 0000000000000..9d3680d48cdd0 --- /dev/null +++ b/lldb/test/Shell/Commands/command-plugin-list.test @@ -0,0 +1,82 @@ +# This test validates the plugin list command. +# Currently it works only for system-runtime plugins and we only have one +# system runtime plugin so testing is a bit limited. +# +# Note that commands that return errors will stop running a script, so we +# have new RUN lines for any command that is expected to return an error. + +# RUN: %lldb -s %s -o exit 2>&1 | FileCheck %s + +# Test plugin list without an argument will list all plugins. +plugin list +# CHECK-LABEL: plugin list +# CHECK: system-runtime +# CHECK: [+] systemruntime-macosx System runtime plugin for Mac OS X native libraries +# CHECK: instrumentation-runtime +# CHECK: [+] AddressSanitizer AddressSanitizer instrumentation runtime plugin. + +# Test plugin list works with fully qualified name. +plugin list system-runtime.systemruntime-macosx +# CHECK-LABEL: plugin list system-runtime.systemruntime-macosx +# CHECK: system-runtime +# CHECK: [+] systemruntime-macosx System runtime plugin for Mac OS X native libraries + +# Test plugin list on plugin namespace works. +plugin list system-runtime +# CHECK-LABEL: plugin list system-runtime +# CHECK: system-runtime +# CHECK: [+] systemruntime-macosx System runtime plugin for Mac OS X native libraries + +# Test plugin list on multiple args works. +plugin list system-runtime instrumentation-runtime.AddressSanitizer +# CHECK-LABEL: plugin list system-runtime instrumentation-runtime.AddressSanitizer +# CHECK: system-runtime +# CHECK: [+] systemruntime-macosx System runtime plugin for Mac OS X native libraries +# CHECK: instrumentation-runtime +# CHECK: [+] AddressSanitizer AddressSanitizer instrumentation runtime plugin. + +# Test json output for plugin list. +plugin list --json +# CHECK-LABEL plugin list --json +# CHECK: { +# CHECK-DAG: "instrumentation-runtime": +# CHECK-DAG: "system-runtime": +# CHECK: } + +# Test json output for plugin list with a namespace +plugin list system-runtime --json +# CHECK-LABEL plugin list --json +# CHECK: { +# CHECK: "system-runtime": [ +# CHECK: { +# CHECK-DAG: "enabled": true +# CHECK-DAG: "name": "systemruntime-macosx" +# CHECK: } +# CHECK: ] +# CHECK: } + +# Test json output for listing multiple plugins +plugin list --json system-runtime instrumentation-runtime.AddressSanitizer +# CHECK-LABEL plugin list --json system-runtime instrumentation-runtime.AddressSanitizer +# CHECK: { +# CHECK-DAG: "instrumentation-runtime": +# CHECK-DAG: "name": "AddressSanitizer" +# CHECK-DAG: "system-runtime": +# CHECK: } + + +# Test plugin list does not match a plugin name by substring. +# RUN: %lldb -o "plugin list macosx" 2>&1 | FileCheck %s --check-prefix=ERROR_PLUGIN_NOT_FOUND + +# Test plugin list does not match a plugin namespace by substring. +# RUN: %lldb -o "plugin list system-runtime." 2>&1 | FileCheck %s --check-prefix=ERROR_PLUGIN_NOT_FOUND + +# Test plugin list returns an error for unknown second argument +# RUN: %lldb -o "plugin list system-runtime foo" 2>&1 | FileCheck %s --check-prefix=ERROR_PLUGIN_NOT_FOUND + +# Test plugin list returns an error for unknown second argument +# RUN: %lldb -o "plugin list --json system-runtime foo" 2>&1 | FileCheck %s --check-prefix=ERROR_PLUGIN_NOT_FOUND + +# Test plugin list for unknown plugin returns an error. +# RUN: %lldb -o "plugin list some-plugin-that-does-not-exist" 2>&1 | FileCheck %s --check-prefix=ERROR_PLUGIN_NOT_FOUND +# ERROR_PLUGIN_NOT_FOUND: error: Found no matching plugins diff --git a/lldb/unittests/Core/PluginManagerTest.cpp b/lldb/unittests/Core/PluginManagerTest.cpp index 9b0ce2286d273..c7a56b28a42fc 100644 --- a/lldb/unittests/Core/PluginManagerTest.cpp +++ b/lldb/unittests/Core/PluginManagerTest.cpp @@ -379,3 +379,93 @@ TEST_F(PluginManagerTest, UnRegisterSystemRuntimePluginChangesOrder) { ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), CreateSystemRuntimePluginB); } + +TEST_F(PluginManagerTest, MatchPluginName) { + PluginNamespace Foo{"foo", nullptr, nullptr}; + RegisteredPluginInfo Bar{"bar", "bar plugin ", true}; + RegisteredPluginInfo Baz{"baz", "baz plugin ", true}; + + // Empty pattern matches everything. + ASSERT_TRUE(PluginManager::MatchPluginName("", Foo, Bar)); + + // Plugin namespace matches all plugins in that namespace. + ASSERT_TRUE(PluginManager::MatchPluginName("foo", Foo, Bar)); + ASSERT_TRUE(PluginManager::MatchPluginName("foo", Foo, Baz)); + + // Fully qualified plugin name matches only that plugin. + ASSERT_TRUE(PluginManager::MatchPluginName("foo.bar", Foo, Bar)); + ASSERT_FALSE(PluginManager::MatchPluginName("foo.baz", Foo, Bar)); + + // Prefix match should not match. + ASSERT_FALSE(PluginManager::MatchPluginName("f", Foo, Bar)); + ASSERT_FALSE(PluginManager::MatchPluginName("foo.", Foo, Bar)); + ASSERT_FALSE(PluginManager::MatchPluginName("foo.ba", Foo, Bar)); +} + +TEST_F(PluginManagerTest, JsonFormat) { + RegisterMockSystemRuntimePlugins(); + + // We expect the following JSON output: + // { + // "system-runtime": [ + // { + // "enabled": true, + // "name": "a" + // }, + // { + // "enabled": true, + // "name": "b" + // }, + // { + // "enabled": true, + // "name": "c" + // } + // ] + // } + llvm::json::Object obj = PluginManager::GetJSON(); + + // We should have a "system-runtime" array in the top-level object. + llvm::json::Array *maybe_array = obj.getArray("system-runtime"); + ASSERT_TRUE(maybe_array != nullptr); + auto &array = *maybe_array; + ASSERT_EQ(array.size(), 3u); + + // Check plugin "a" info. + ASSERT_TRUE(array[0].getAsObject() != nullptr); + ASSERT_TRUE(array[0].getAsObject()->getString("name") == "a"); + ASSERT_TRUE(array[0].getAsObject()->getBoolean("enabled") == true); + + // Check plugin "b" info. + ASSERT_TRUE(array[1].getAsObject() != nullptr); + ASSERT_TRUE(array[1].getAsObject()->getString("name") == "b"); + ASSERT_TRUE(array[1].getAsObject()->getBoolean("enabled") == true); + + // Check plugin "c" info. + ASSERT_TRUE(array[2].getAsObject() != nullptr); + ASSERT_TRUE(array[2].getAsObject()->getString("name") == "c"); + ASSERT_TRUE(array[2].getAsObject()->getBoolean("enabled") == true); + + // Disabling a plugin should be reflected in the JSON output. + ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false)); + array = *PluginManager::GetJSON().getArray("system-runtime"); + ASSERT_TRUE(array[0].getAsObject()->getBoolean("enabled") == true); + ASSERT_TRUE(array[1].getAsObject()->getBoolean("enabled") == false); + ASSERT_TRUE(array[2].getAsObject()->getBoolean("enabled") == true); + + // Un-registering a plugin should be reflected in the JSON output. + ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB)); + array = *PluginManager::GetJSON().getArray("system-runtime"); + ASSERT_EQ(array.size(), 2u); + ASSERT_TRUE(array[0].getAsObject()->getString("name") == "a"); + ASSERT_TRUE(array[1].getAsObject()->getString("name") == "c"); + + // Filtering the JSON output should only include the matching plugins. + array = + *PluginManager::GetJSON("system-runtime.c").getArray("system-runtime"); + ASSERT_EQ(array.size(), 1u); + ASSERT_TRUE(array[0].getAsObject()->getString("name") == "c"); + + // Empty JSON output is allowed if there are no matching plugins. + obj = PluginManager::GetJSON("non-existent-plugin"); + ASSERT_TRUE(obj.empty()); +} _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits