Index: lldb/include/lldb/Target/Target.h =================================================================== --- lldb/include/lldb/Target/Target.h +++ lldb/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,36 @@ static void ImageSearchPathsChanged(const PathMappingList &path_list, void *baton); + //------------------------------------------------------------------ + // Utilities for `stats` command. + //------------------------------------------------------------------ +private: + std::map m_stats_map; + bool m_collecting_stats = false; + +public: + void SetCollectingStats(bool v) { m_collecting_stats = v; } + + bool GetCollectingStats() { return m_collecting_stats; } + + void RegisterStats(lldb_private::StatisticKind key) { + if (m_stats_map.find(key) != m_stats_map.end()) + return; + m_stats_map[key] = 0; + } + + void IncrementStats(lldb_private::StatisticKind key) { + if (!GetCollectingStats()) + return; + lldbassert(m_stats_map.find(key) != m_stats_map.end() && + "stat not registered"); + m_stats_map[key] += 1; + } + + std::map GetStatistics() { + return m_stats_map; + } + private: //------------------------------------------------------------------ /// Construct with optional file and arch. Index: lldb/include/lldb/lldb-private-enumerations.h =================================================================== --- lldb/include/lldb/lldb-private-enumerations.h +++ lldb/include/lldb/lldb-private-enumerations.h @@ -234,6 +234,17 @@ Typedef }; +//---------------------------------------------------------------------- +// Enumerations that can be used to specify the kind of metric we're +// looking at when collecting stats. +//---------------------------------------------------------------------- +enum class StatisticKind { + ExpressionSuccessful = 0, + ExpressionFailure, + FrameVarSuccess, + FrameVarFailure +}; + } // namespace lldb_private namespace llvm { Index: lldb/packages/Python/lldbsuite/test/functionalities/stats/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/functionalities/stats/Makefile @@ -0,0 +1,3 @@ +LEVEL = ../../make +C_SOURCES := main.c +include $(LEVEL)/Makefile.rules Index: lldb/packages/Python/lldbsuite/test/functionalities/stats/TestStats.py =================================================================== --- /dev/null +++ lldb/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/packages/Python/lldbsuite/test/functionalities/stats/main.c =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/functionalities/stats/main.c @@ -0,0 +1,16 @@ +// Test that the lldb command `stats` works. + +int main(void) { + int patatino = 27; + //%self.expect("stats disable", substrs=['need to enable stats before disabling'], error=True) + //%self.expect("stats enable") + //%self.expect("stats enable", substrs=['already enabled'], error=True) + //%self.expect("expr patatino", substrs=['27']) + //%self.expect("stats disable", substrs=['expr evaluation successes : 1', 'expr evaluation failures : 0']) + //%self.expect("frame var", substrs=['27']) + //%self.expect("stats enable") + //%self.expect("frame var", substrs=['27']) + //%self.expect("stats disable", substrs=['frame var successes : 1', 'frame var failures : 0']) + + return 0; +} Index: lldb/source/Commands/CommandObjectExpression.cpp =================================================================== --- lldb/source/Commands/CommandObjectExpression.cpp +++ lldb/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/source/Commands/CommandObjectFrame.cpp =================================================================== --- lldb/source/Commands/CommandObjectFrame.cpp +++ lldb/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/source/Commands/CommandObjectStats.h =================================================================== --- lldb/source/Commands/CommandObjectStats.h +++ lldb/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/source/Commands/CommandObjectStats.cpp =================================================================== --- lldb/source/Commands/CommandObjectStats.cpp +++ lldb/source/Commands/CommandObjectStats.cpp @@ -11,18 +11,107 @@ #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) {} + + ~CommandObjectStatsEnable() override = default; + + bool DoExecute(Args &command, CommandReturnObject &result) override { + auto exe_ctx = m_interpreter.GetExecutionContext(); + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) { + result.AppendError("stats command needs a target"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (target->GetCollectingStats() == true) { + result.AppendError("stats already enabled"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + target->RegisterStats(StatisticKind::ExpressionSuccessful); + target->RegisterStats(StatisticKind::ExpressionFailure); + target->RegisterStats(StatisticKind::FrameVarSuccess); + target->RegisterStats(StatisticKind::FrameVarFailure); + + target->SetCollectingStats(true); + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } +}; + +class CommandObjectStatsDisable : 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"; + } + llvm_unreachable("Statistic not registered!"); + } -bool CommandObjectStats::DoExecute(Args &command, CommandReturnObject &result) { - return true; +public: + CommandObjectStatsDisable(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "disable", + "Disable statistics collection and dump results", + nullptr) {} + + ~CommandObjectStatsDisable() override = default; + + bool DoExecute(Args &command, CommandReturnObject &result) override { + auto exe_ctx = m_interpreter.GetExecutionContext(); + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) { + result.AppendError("stats command needs a target"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (target->GetCollectingStats() == false) { + result.AppendError("need to enable stats before disabling them"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + target->SetCollectingStats(false); + + result.AppendMessage("### Start STATISTICS dump ###"); + for (auto &Stat : target->GetStatistics()) { + result.AppendMessageWithFormat( + "%s : %u\n", GetStatDescription(Stat.first).c_str(), Stat.second); + } + result.AppendMessage("### End STATISTICS dump ###\n"); + result.SetStatus(eReturnStatusSuccessFinishResult); + return true; + } +}; + +CommandObjectStats::CommandObjectStats(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "stats", + "Print statistics about a debugging session", + "stats []") { + LoadSubCommand("enable", + CommandObjectSP(new CommandObjectStatsEnable(interpreter))); + LoadSubCommand("disable", + CommandObjectSP(new CommandObjectStatsDisable(interpreter))); } -CommandObjectStats::~CommandObjectStats() {} +CommandObjectStats::~CommandObjectStats() = default;