[Lldb-commits] [lldb] [lldb] Track execution time of commands in statistics (PR #200817)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Jun 1 06:36:11 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: Raphael Isemann (Teemperor)
<details>
<summary>Changes</summary>
LLDB tracks how often certain commands are invoked. This patch adds to this functionality how long it takes in total for all these commands to be invoked.
---
Full diff: https://github.com/llvm/llvm-project/pull/200817.diff
6 Files Affected:
- (modified) lldb/include/lldb/Interpreter/CommandInterpreter.h (+17-4)
- (modified) lldb/source/Commands/CommandObjectCommands.cpp (+17-13)
- (modified) lldb/source/Interpreter/CommandInterpreter.cpp (+6-2)
- (modified) lldb/source/Interpreter/CommandObject.cpp (+6-2)
- (modified) lldb/test/API/commands/statistics/basic/TestStats.py (+12-1)
- (modified) lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py (+2-1)
``````````diff
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 6fd5b5285c27d..7e77d7adb223a 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -683,8 +683,9 @@ class CommandInterpreter : public Broadcaster,
Status PreprocessCommand(std::string &command);
Status PreprocessToken(std::string &token);
- void IncreaseCommandUsage(const CommandObject &cmd_obj) {
- ++m_command_usages[cmd_obj.GetCommandName()];
+ void IncreaseCommandUsage(const CommandObject &cmd_obj,
+ const StatsDuration &duration) {
+ m_command_stats[cmd_obj.GetCommandName()].addInvocation(duration);
}
void SetPrintCallback(CommandReturnObjectCallback callback);
@@ -815,8 +816,20 @@ class CommandInterpreter : public Broadcaster,
bool m_allow_exit_code = false;
/// Command usage statistics.
- typedef llvm::StringMap<uint64_t> CommandUsageMap;
- CommandUsageMap m_command_usages;
+ struct CommandStats {
+ /// How often this command was invoked.
+ uint64_t invocations = 0;
+ /// Total wall time for all invocations.
+ StatsDuration totalDuration;
+
+ void addInvocation(const StatsDuration &duration) {
+ invocations += 1;
+ totalDuration += duration.get();
+ }
+ };
+
+ typedef llvm::StringMap<CommandStats> CommandStatsMap;
+ CommandStatsMap m_command_stats;
/// Turn on settings `interpreter.save-transcript` for LLDB to populate
/// this stream. Otherwise this stream is empty.
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index 84e661ec01f53..5f80167edeb58 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -1089,25 +1089,29 @@ class CommandObjectPythonFunction : public CommandObjectRaw {
CommandReturnObject &result) override {
ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
- m_interpreter.IncreaseCommandUsage(*this);
-
Status error;
result.SetStatus(eReturnStatusInvalid);
- if (!scripter || !scripter->RunScriptBasedCommand(
- m_function_name.c_str(), raw_command_line, m_synchro,
- result, error, m_exe_ctx)) {
- result.AppendError(error.AsCString());
- } else {
- // Don't change the status if the command already set it...
- if (result.GetStatus() == eReturnStatusInvalid) {
- if (result.GetOutputString().empty())
- result.SetStatus(eReturnStatusSuccessFinishNoResult);
- else
- result.SetStatus(eReturnStatusSuccessFinishResult);
+ StatsDuration duration;
+ {
+ ElapsedTime measure(duration);
+ if (!scripter || !scripter->RunScriptBasedCommand(
+ m_function_name.c_str(), raw_command_line, m_synchro,
+ result, error, m_exe_ctx)) {
+ result.AppendError(error.AsCString());
+ } else {
+ // Don't change the status if the command already set it...
+ if (result.GetStatus() == eReturnStatusInvalid) {
+ if (result.GetOutputString().empty())
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ else
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ }
}
}
+
+ m_interpreter.IncreaseCommandUsage(*this, duration);
}
private:
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 0c5456c2c3b57..20e29eee45162 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -3911,8 +3911,12 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line,
llvm::json::Value CommandInterpreter::GetStatistics() {
llvm::json::Object stats;
- for (const auto &command_usage : m_command_usages)
- stats.try_emplace(command_usage.getKey(), command_usage.getValue());
+ for (const auto &command_usage : m_command_stats) {
+ const auto &cmd = command_usage.second;
+ llvm::json::Object cmdObj{{"invocations", cmd.invocations},
+ {"duration", cmd.totalDuration.get().count()}};
+ stats.try_emplace(command_usage.first(), std::move(cmdObj));
+ }
return stats;
}
diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp
index 75abf49e77207..702c2e8da43eb 100644
--- a/lldb/source/Interpreter/CommandObject.cpp
+++ b/lldb/source/Interpreter/CommandObject.cpp
@@ -856,9 +856,13 @@ void CommandObjectParsed::Execute(const char *args_string,
Cleanup();
return;
}
- m_interpreter.IncreaseCommandUsage(*this);
DoExecuteStatusCheck check(result);
- DoExecute(cmd_args, result);
+ StatsDuration duration;
+ {
+ ElapsedTime measure(duration);
+ DoExecute(cmd_args, result);
+ }
+ m_interpreter.IncreaseCommandUsage(*this, duration);
}
}
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index a32b8feecc5cf..67a484e7fabb3 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -437,7 +437,18 @@ def test_commands(self):
command_stats = self.get_command_stats(debug_stats)
self.assertNotEqual(command_stats, None)
- self.assertEqual(command_stats["target list"], 2)
+ self.assertEqual(command_stats["target list"]["invocations"], 2)
+
+ # Duration should always be positive.
+ old_duration = command_stats["target list"]["duration"]
+ self.assertGreater(command_stats["target list"]["duration"], 0)
+
+ # Check that duration increases with each command.
+ interp.HandleCommand("target list", result)
+ debug_stats = self.get_stats()
+ command_stats = self.get_command_stats(debug_stats)
+ self.assertGreater(command_stats["target list"]["duration"], old_duration)
+
def test_breakpoints(self):
"""Test "statistics dump"
diff --git a/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py b/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py
index 5f09e8fe07c0b..ff98bd94ddadd 100644
--- a/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py
+++ b/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py
@@ -120,7 +120,8 @@ def test_command_stats_api(self):
command_stats = json.loads(stream.GetData())
# Verify bt command is correctly parsed into final form.
- self.assertEqual(command_stats["thread backtrace"], 1)
+ self.assertEqual(command_stats["thread backtrace"]["invocations"], 1)
+ self.assertGreater(command_stats["thread backtrace"]["duration"], 0)
# Verify original raw command is not duplicatedly captured.
self.assertNotIn("bt", command_stats)
# Verify bt's regex command is not duplicatedly captured.
``````````
</details>
https://github.com/llvm/llvm-project/pull/200817
More information about the lldb-commits
mailing list