davide created this revision. davide added reviewers: jingham, friss, JDevlieghere, aprantl, labath, clayborg.
This allows us to collect useful metrics about lldb debugging sessions. I thought that an example would be better than a thousand words: Process 19705 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = step in frame #0: 0x0000000100000fb4 blah`main at blah.c:3 1 int main(void) { 2 int a = 6; -> 3 return 0; 4 } (lldb) stats enable (lldb) frame var a (int) a = 6 (lldb) expr a (int) $1 = 6 (lldb) stats disable ### Start STATISTICS dump ### Number of expr evaluation successes : 1 Number of expr evaluation failures : 0 Number of frame var successes : 1 Number of frame var failures : 0 ### End STATISTICS dump ### Future improvements might include: 1. Passing a file, or implementing categories. The way this patch has been implements is generic enough to allow this to be extended easily without breaking the grammar 2. Adding an SBAPI and Python API for use in scripts. Thanks to Jim Ingham for discussing the design with me. https://reviews.llvm.org/D45547 Files: lldb/include/lldb/Target/Target.h lldb/include/lldb/lldb-private-enumerations.h lldb/packages/Python/lldbsuite/test/functionalities/stats/Makefile lldb/packages/Python/lldbsuite/test/functionalities/stats/TestStats.py lldb/packages/Python/lldbsuite/test/functionalities/stats/main.c lldb/source/Commands/CommandObjectExpression.cpp lldb/source/Commands/CommandObjectFrame.cpp lldb/source/Commands/CommandObjectStats.cpp lldb/source/Commands/CommandObjectStats.h
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 <subcommand> [<subcommand-options>]") { + LoadSubCommand("enable", + CommandObjectSP(new CommandObjectStatsEnable(interpreter))); + LoadSubCommand("disable", + CommandObjectSP(new CommandObjectStatsDisable(interpreter))); } -CommandObjectStats::~CommandObjectStats() {} +CommandObjectStats::~CommandObjectStats() = default; 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/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/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/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/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/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/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<lldb_private::StatisticKind, uint32_t> 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<lldb_private::StatisticKind, uint32_t> GetStatistics() { + return m_stats_map; + } + private: //------------------------------------------------------------------ /// Construct with optional file and arch.
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits