[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