Index: lldb/trunk/include/lldb/Target/Target.h =================================================================== --- lldb/trunk/include/lldb/Target/Target.h +++ lldb/trunk/include/lldb/Target/Target.h @@ -35,6 +35,7 @@ #include "lldb/Target/ProcessLaunchInfo.h" #include "lldb/Target/SectionLoadHistory.h" #include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/Timeout.h" #include "lldb/lldb-public.h" @@ -1284,6 +1285,28 @@ static void ImageSearchPathsChanged(const PathMappingList &path_list, void *baton); + //------------------------------------------------------------------ + // Utilities for `statistics` command. + //------------------------------------------------------------------ +private: + std::vector m_stats_storage; + bool m_collecting_stats = false; + +public: + void SetCollectingStats(bool v) { m_collecting_stats = v; } + + bool GetCollectingStats() { return m_collecting_stats; } + + void IncrementStats(lldb_private::StatisticKind key) { + if (!GetCollectingStats()) + return; + lldbassert(key < lldb_private::StatisticKind::StatisticMax && + "invalid statistics!"); + m_stats_storage[key] += 1; + } + + std::vector GetStatistics() { return m_stats_storage; } + private: //------------------------------------------------------------------ /// Construct with optional file and arch. Index: lldb/trunk/include/lldb/lldb-private-enumerations.h =================================================================== --- lldb/trunk/include/lldb/lldb-private-enumerations.h +++ lldb/trunk/include/lldb/lldb-private-enumerations.h @@ -234,6 +234,18 @@ Typedef }; +//---------------------------------------------------------------------- +// Enumerations that can be used to specify the kind of metric we're +// looking at when collecting stats. +//---------------------------------------------------------------------- +enum StatisticKind { + ExpressionSuccessful = 0, + ExpressionFailure = 1, + FrameVarSuccess = 2, + FrameVarFailure = 3, + StatisticMax = 4 +}; + } // namespace lldb_private namespace llvm { Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/stats/Makefile =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/stats/Makefile +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/stats/Makefile @@ -0,0 +1,3 @@ +LEVEL = ../../make +C_SOURCES := main.c +include $(LEVEL)/Makefile.rules Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/stats/TestStats.py =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/stats/TestStats.py +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/stats/TestStats.py @@ -0,0 +1,5 @@ +from lldbsuite.test import lldbinline +from lldbsuite.test import decorators + +lldbinline.MakeInlineTest( + __file__, globals(), []) Index: lldb/trunk/packages/Python/lldbsuite/test/functionalities/stats/main.c =================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/functionalities/stats/main.c +++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/stats/main.c @@ -0,0 +1,18 @@ +// Test that the lldb command `statistics` works. + +int main(void) { + int patatino = 27; + //%self.expect("statistics disable", substrs=['need to enable statistics before disabling'], error=True) + //%self.expect("statistics enable") + //%self.expect("statistics enable", substrs=['already enabled'], error=True) + //%self.expect("expr patatino", substrs=['27']) + //%self.expect("statistics disable") + //%self.expect("statistics dump", substrs=['expr evaluation successes : 1', 'expr evaluation failures : 0']) + //%self.expect("frame var", substrs=['27']) + //%self.expect("statistics enable") + //%self.expect("frame var", substrs=['27']) + //%self.expect("statistics disable") + //%self.expect("statistics dump", substrs=['frame var successes : 1', 'frame var failures : 0']) + + return 0; +} Index: lldb/trunk/source/Commands/CommandObjectExpression.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectExpression.cpp +++ lldb/trunk/source/Commands/CommandObjectExpression.cpp @@ -623,11 +623,12 @@ if (expr == nullptr) expr = command; + Target *target = m_interpreter.GetExecutionContext().GetTargetPtr(); + if (!target) + target = GetDummyTarget(); + if (EvaluateExpression(expr, &(result.GetOutputStream()), &(result.GetErrorStream()), &result)) { - Target *target = m_interpreter.GetExecutionContext().GetTargetPtr(); - if (!target) - target = GetDummyTarget(); if (!m_fixed_expression.empty() && target->GetEnableNotifyAboutFixIts()) { CommandHistory &history = m_interpreter.GetCommandHistory(); @@ -644,9 +645,15 @@ } history.AppendString(fixed_command); } + // Increment statistics to record this expression evaluation + // success. + target->IncrementStats(StatisticKind::ExpressionSuccessful); return true; } + // Increment statistics to record this expression evaluation + // failure. + target->IncrementStats(StatisticKind::ExpressionFailure); result.SetStatus(eReturnStatusFailed); return false; } Index: lldb/trunk/source/Commands/CommandObjectFrame.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectFrame.cpp +++ lldb/trunk/source/Commands/CommandObjectFrame.cpp @@ -720,7 +720,16 @@ m_interpreter.TruncationWarningGiven(); } - return result.Succeeded(); + // Increment statistics. + bool res = result.Succeeded(); + Target *target = m_exe_ctx.GetTargetPtr(); + if (!target) + target = GetDummyTarget(); + if (res) + target->IncrementStats(StatisticKind::FrameVarSuccess); + else + target->IncrementStats(StatisticKind::FrameVarFailure); + return res; } protected: Index: lldb/trunk/source/Commands/CommandObjectStats.h =================================================================== --- lldb/trunk/source/Commands/CommandObjectStats.h +++ lldb/trunk/source/Commands/CommandObjectStats.h @@ -11,16 +11,14 @@ #define liblldb_CommandObjectStats_h_ #include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" namespace lldb_private { -class CommandObjectStats : public CommandObjectParsed { +class CommandObjectStats : public CommandObjectMultiword { public: CommandObjectStats(CommandInterpreter &interpreter); ~CommandObjectStats() override; - -protected: - bool DoExecute(Args &command, CommandReturnObject &result) override; }; } // namespace lldb_private Index: lldb/trunk/source/Commands/CommandObjectStats.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectStats.cpp +++ lldb/trunk/source/Commands/CommandObjectStats.cpp @@ -11,18 +11,114 @@ #include "lldb/Host/Host.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" using namespace lldb; using namespace lldb_private; -CommandObjectStats::CommandObjectStats(CommandInterpreter &interpreter) - : CommandObjectParsed( - interpreter, "stats", "Print statistics about a debugging session", - nullptr) { -} +class CommandObjectStatsEnable : public CommandObjectParsed { +public: + CommandObjectStatsEnable(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "enable", + "Enable statistics collection", nullptr, + eCommandProcessMustBePaused) {} + + ~CommandObjectStatsEnable() override = default; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + Target *target = GetSelectedOrDummyTarget(); + + if (target->GetCollectingStats()) { + result.AppendError("statistics already enabled"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + target->SetCollectingStats(true); + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } +}; + +class CommandObjectStatsDisable : public CommandObjectParsed { +public: + CommandObjectStatsDisable(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "disable", + "Disable statistics collection", nullptr, + eCommandProcessMustBePaused) {} + + ~CommandObjectStatsDisable() override = default; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + Target *target = GetSelectedOrDummyTarget(); -bool CommandObjectStats::DoExecute(Args &command, CommandReturnObject &result) { - return true; + if (!target->GetCollectingStats()) { + result.AppendError("need to enable statistics before disabling them"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + target->SetCollectingStats(false); + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } +}; + +class CommandObjectStatsDump : public CommandObjectParsed { +private: + std::string GetStatDescription(lldb_private::StatisticKind K) { + switch (K) { + case StatisticKind::ExpressionSuccessful: + return "Number of expr evaluation successes"; + case StatisticKind::ExpressionFailure: + return "Number of expr evaluation failures"; + case StatisticKind::FrameVarSuccess: + return "Number of frame var successes"; + case StatisticKind::FrameVarFailure: + return "Number of frame var failures"; + case StatisticKind::StatisticMax: + return ""; + } + llvm_unreachable("Statistic not registered!"); + } + +public: + CommandObjectStatsDump(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "dump", "Dump statistics results", + nullptr, eCommandProcessMustBePaused) {} + + ~CommandObjectStatsDump() override = default; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + Target *target = GetSelectedOrDummyTarget(); + + uint32_t i = 0; + for (auto &stat : target->GetStatistics()) { + result.AppendMessageWithFormat( + "%s : %u\n", + GetStatDescription(static_cast(i)) + .c_str(), + stat); + i += 1; + } + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } +}; + +CommandObjectStats::CommandObjectStats(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "statistics", + "Print statistics about a debugging session", + "statistics []") { + LoadSubCommand("enable", + CommandObjectSP(new CommandObjectStatsEnable(interpreter))); + LoadSubCommand("disable", + CommandObjectSP(new CommandObjectStatsDisable(interpreter))); + LoadSubCommand("dump", + CommandObjectSP(new CommandObjectStatsDump(interpreter))); } -CommandObjectStats::~CommandObjectStats() {} +CommandObjectStats::~CommandObjectStats() = default; Index: lldb/trunk/source/Interpreter/CommandInterpreter.cpp =================================================================== --- lldb/trunk/source/Interpreter/CommandInterpreter.cpp +++ lldb/trunk/source/Interpreter/CommandInterpreter.cpp @@ -425,7 +425,7 @@ CommandObjectSP(new CommandObjectMultiwordSettings(*this)); m_command_dict["source"] = CommandObjectSP(new CommandObjectMultiwordSource(*this)); - m_command_dict["stats"] = CommandObjectSP(new CommandObjectStats(*this)); + m_command_dict["statistics"] = CommandObjectSP(new CommandObjectStats(*this)); m_command_dict["target"] = CommandObjectSP(new CommandObjectMultiwordTarget(*this)); m_command_dict["thread"] = Index: lldb/trunk/source/Target/Target.cpp =================================================================== --- lldb/trunk/source/Target/Target.cpp +++ lldb/trunk/source/Target/Target.cpp @@ -87,13 +87,14 @@ Broadcaster(debugger.GetBroadcasterManager(), Target::GetStaticBroadcasterClass().AsCString()), ExecutionContextScope(), m_debugger(debugger), m_platform_sp(platform_sp), - m_mutex(), m_arch(target_arch), - m_images(this), m_section_load_history(), m_breakpoint_list(false), - m_internal_breakpoint_list(true), m_watchpoint_list(), m_process_sp(), - m_search_filter_sp(), m_image_search_paths(ImageSearchPathsChanged, this), - m_ast_importer_sp(), m_source_manager_ap(), m_stop_hooks(), - m_stop_hook_next_id(0), m_valid(true), m_suppress_stop_hooks(false), - m_is_dummy_target(is_dummy_target) + m_mutex(), m_arch(target_arch), m_images(this), m_section_load_history(), + m_breakpoint_list(false), m_internal_breakpoint_list(true), + m_watchpoint_list(), m_process_sp(), m_search_filter_sp(), + m_image_search_paths(ImageSearchPathsChanged, this), m_ast_importer_sp(), + m_source_manager_ap(), m_stop_hooks(), m_stop_hook_next_id(0), + m_valid(true), m_suppress_stop_hooks(false), + m_is_dummy_target(is_dummy_target), + m_stats_storage(static_cast(StatisticKind::StatisticMax)) { SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed");