[Lldb-commits] [lldb] Add commands frequency to statistics dump (PR #80375)

via lldb-commits lldb-commits at lists.llvm.org
Thu Feb 1 18:08:30 PST 2024


https://github.com/jeffreytan81 created https://github.com/llvm/llvm-project/pull/80375

None

>From 59e1499ec0afebb533c4952f079278341b957241 Mon Sep 17 00:00:00 2001
From: jeffreytan81 <jeffreytan at fb.com>
Date: Thu, 1 Feb 2024 18:07:51 -0800
Subject: [PATCH] Add commands frequency to statistics dump

---
 lldb/include/lldb/API/SBCommandInterpreter.h  |  9 ++++---
 lldb/include/lldb/API/SBStructuredData.h      |  1 +
 .../lldb/Interpreter/CommandInterpreter.h     | 15 +++++++++--
 lldb/source/API/SBCommandInterpreter.cpp      | 15 ++++++++++-
 .../source/Commands/CommandObjectCommands.cpp | 10 ++++---
 .../source/Interpreter/CommandInterpreter.cpp |  9 ++++++-
 lldb/source/Interpreter/CommandObject.cpp     |  1 +
 lldb/source/Target/Statistics.cpp             |  4 +++
 .../commands/statistics/basic/TestStats.py    | 24 +++++++++++++++++
 .../stats_api/TestStatisticsAPI.py            | 26 +++++++++++++++++++
 10 files changed, 103 insertions(+), 11 deletions(-)

diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h
index b7f5b3bf3396e..b4629a4c2f5a8 100644
--- a/lldb/include/lldb/API/SBCommandInterpreter.h
+++ b/lldb/include/lldb/API/SBCommandInterpreter.h
@@ -13,6 +13,7 @@
 
 #include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBDefines.h"
+#include "lldb/API/SBStructuredData.h"
 
 namespace lldb_private {
 class CommandPluginInterfaceImplementation;
@@ -246,13 +247,13 @@ class SBCommandInterpreter {
                                        lldb::SBStringList &matches,
                                        lldb::SBStringList &descriptions);
 
-  /// Returns whether an interrupt flag was raised either by the SBDebugger - 
+  /// Returns whether an interrupt flag was raised either by the SBDebugger -
   /// when the function is not running on the RunCommandInterpreter thread, or
   /// by SBCommandInterpreter::InterruptCommand if it is.  If your code is doing
-  /// interruptible work, check this API periodically, and interrupt if it 
+  /// interruptible work, check this API periodically, and interrupt if it
   /// returns true.
   bool WasInterrupted() const;
-  
+
   /// Interrupts the command currently executing in the RunCommandInterpreter
   /// thread.
   ///
@@ -315,6 +316,8 @@ class SBCommandInterpreter {
   /// and aliases.  If successful, result->GetOutput has the full expansion.
   void ResolveCommand(const char *command_line, SBCommandReturnObject &result);
 
+  SBStructuredData GetStatistics();
+
 protected:
   friend class lldb_private::CommandPluginInterfaceImplementation;
 
diff --git a/lldb/include/lldb/API/SBStructuredData.h b/lldb/include/lldb/API/SBStructuredData.h
index 35d321eaa7b89..fc6e1ec95c7b8 100644
--- a/lldb/include/lldb/API/SBStructuredData.h
+++ b/lldb/include/lldb/API/SBStructuredData.h
@@ -122,6 +122,7 @@ class SBStructuredData {
   friend class SBTrace;
   friend class lldb_private::python::SWIGBridge;
   friend class lldb_private::lua::SWIGBridge;
+  friend class SBCommandInterpreter;
 
   SBStructuredData(const lldb_private::StructuredDataImpl &impl);
 
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 747188a15312f..c46cf0409bab6 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -28,6 +28,7 @@
 #include <mutex>
 #include <optional>
 #include <stack>
+#include <unordered_map>
 
 namespace lldb_private {
 class CommandInterpreter;
@@ -240,7 +241,7 @@ class CommandInterpreter : public Broadcaster,
     eCommandTypesAllThem = 0xFFFF  //< all commands
   };
 
-  // The CommandAlias and CommandInterpreter both have a hand in 
+  // The CommandAlias and CommandInterpreter both have a hand in
   // substituting for alias commands.  They work by writing special tokens
   // in the template form of the Alias command, and then detecting them when the
   // command is executed.  These are the special tokens:
@@ -575,7 +576,7 @@ class CommandInterpreter : public Broadcaster,
   void SetEchoCommentCommands(bool enable);
 
   bool GetRepeatPreviousCommand() const;
-  
+
   bool GetRequireCommandOverwrite() const;
 
   const CommandObject::CommandMap &GetUserCommands() const {
@@ -641,6 +642,12 @@ 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().str()];
+  }
+
+  llvm::json::Value GetStatistics();
+
 protected:
   friend class Debugger;
 
@@ -754,6 +761,10 @@ class CommandInterpreter : public Broadcaster,
   // If the driver is accepts custom exit codes for the 'quit' command.
   bool m_allow_exit_code = false;
 
+  /// Command usage statistics.
+  typedef std::unordered_map<std::string, uint64_t> CommandUsageMap;
+  CommandUsageMap m_command_usages;
+
   StreamString m_transcript_stream;
 };
 
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index c3cbb00145ed3..a8906686d1a11 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -150,7 +150,7 @@ bool SBCommandInterpreter::WasInterrupted() const {
 
 bool SBCommandInterpreter::InterruptCommand() {
   LLDB_INSTRUMENT_VA(this);
-  
+
   return (IsValid() ? m_opaque_ptr->InterruptCommand() : false);
 }
 
@@ -557,6 +557,19 @@ bool SBCommandInterpreter::SetCommandOverrideCallback(
   return false;
 }
 
+SBStructuredData SBCommandInterpreter::GetStatistics() {
+  LLDB_INSTRUMENT_VA(this);
+
+  SBStructuredData data;
+  if (!IsValid())
+    return data;
+
+  std::string json_str =
+      llvm::formatv("{0:2}", m_opaque_ptr->GetStatistics()).str();
+  data.m_impl_up->SetObjectSP(StructuredData::ParseJSON(json_str));
+  return data;
+}
+
 lldb::SBCommand SBCommandInterpreter::AddMultiwordCommand(const char *name,
                                                           const char *help) {
   LLDB_INSTRUMENT_VA(this, name, help);
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index 5b9af4a3e1b88..c81f56b6f91bc 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -1123,6 +1123,8 @@ class CommandObjectPythonFunction : public CommandObjectRaw {
                  CommandReturnObject &result) override {
     ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
 
+    m_interpreter.IncreaseCommandUsage(*this);
+
     Status error;
 
     result.SetStatus(eReturnStatusInvalid);
@@ -1577,7 +1579,7 @@ class CommandObjectCommandsScriptAdd : public CommandObjectParsed,
       case eLazyBoolNo:
         m_overwrite = false;
     }
-    
+
     Status path_error;
     m_container = GetCommandInterpreter().VerifyUserMultiwordCmdPath(
         command, true, path_error);
@@ -1631,7 +1633,7 @@ class CommandObjectCommandsScriptAdd : public CommandObjectParsed,
           m_interpreter, m_cmd_name, cmd_obj_sp, m_synchronicity,
           m_completion_type));
     }
-    
+
     // Assume we're going to succeed...
     result.SetStatus(eReturnStatusSuccessFinishNoResult);
     if (!m_container) {
@@ -1644,7 +1646,7 @@ class CommandObjectCommandsScriptAdd : public CommandObjectParsed,
       llvm::Error llvm_error =
           m_container->LoadUserSubcommand(m_cmd_name, new_cmd_sp, m_overwrite);
       if (llvm_error)
-        result.AppendErrorWithFormat("cannot add command: %s", 
+        result.AppendErrorWithFormat("cannot add command: %s",
                                      llvm::toString(std::move(llvm_error)).c_str());
     }
   }
@@ -1792,7 +1794,7 @@ class CommandObjectCommandsScriptDelete : public CommandObjectParsed {
                                             /* multiword not okay */ false);
     if (llvm_error) {
       result.AppendErrorWithFormat("could not delete command '%s': %s",
-                                   leaf_cmd, 
+                                   leaf_cmd,
                                    llvm::toString(std::move(llvm_error)).c_str());
       return;
     }
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 00651df48b622..86fbd48888a24 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -3055,7 +3055,7 @@ void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler,
   }
 
   std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
-  if (had_output && INTERRUPT_REQUESTED(GetDebugger(), 
+  if (had_output && INTERRUPT_REQUESTED(GetDebugger(),
                                         "Interrupted dumping command output"))
     stream->Printf("\n... Interrupted.\n");
   stream->Flush();
@@ -3547,3 +3547,10 @@ CommandInterpreter::ResolveCommandImpl(std::string &command_line,
 
   return cmd_obj;
 }
+
+llvm::json::Value CommandInterpreter::GetStatistics() {
+  llvm::json::Object stats;
+  for (const auto& command_usage : m_command_usages)
+    stats.try_emplace(command_usage.first, command_usage.second);
+  return stats;
+}
diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp
index 1ff9774a0da49..6324c7e701ed5 100644
--- a/lldb/source/Interpreter/CommandObject.cpp
+++ b/lldb/source/Interpreter/CommandObject.cpp
@@ -748,6 +748,7 @@ void CommandObjectParsed::Execute(const char *args_string,
           Cleanup();
           return;
         }
+        m_interpreter.IncreaseCommandUsage(*this);
         DoExecute(cmd_args, result);
       }
     }
diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index c739ac7058cae..7cdee3414d8a3 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -10,6 +10,7 @@
 
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/Module.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
 #include "lldb/Symbol/SymbolFile.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/Target.h"
@@ -291,10 +292,13 @@ llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger,
       {"strings", const_string_stats.ToJSON()},
   };
 
+  json::Value cmd_stats = debugger.GetCommandInterpreter().GetStatistics();
+
   json::Object global_stats{
       {"targets", std::move(json_targets)},
       {"modules", std::move(json_modules)},
       {"memory", std::move(json_memory)},
+      {"commands", std::move(cmd_stats)},
       {"totalSymbolTableParseTime", symtab_parse_time},
       {"totalSymbolTableIndexTime", symtab_index_time},
       {"totalSymbolTablesLoadedFromCache", symtabs_loaded},
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index 6fb0cc6c3be73..4ea5c6a74b317 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -76,6 +76,11 @@ def get_target_stats(self, debug_stats):
             return debug_stats["targets"][0]
         return None
 
+    def get_command_stats(self, debug_stats):
+        if "commands" in debug_stats:
+            return debug_stats["commands"]
+        return None
+
     def test_expressions_frame_var_counts(self):
         self.build()
         lldbutil.run_to_source_breakpoint(
@@ -355,6 +360,25 @@ def test_modules(self):
         self.assertNotEqual(exe_module, None)
         self.verify_keys(exe_module, 'module dict for "%s"' % (exe), module_keys)
 
+    def test_commands(self):
+        """
+            Test "statistics dump" and the command information.
+        """
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+        target = self.createTestTarget(file_path=exe)
+
+        interp = self.dbg.GetCommandInterpreter()
+        result = lldb.SBCommandReturnObject()
+        interp.HandleCommand('target list', result)
+        interp.HandleCommand('target list', result)
+
+        debug_stats = self.get_stats()
+
+        command_stats = self.get_command_stats(debug_stats)
+        self.assertNotEqual(command_stats, None)
+        self.assertEqual(command_stats["target list"], 2)
+
     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 33490c9c8f501..b6c972cbcf989 100644
--- a/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py
+++ b/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py
@@ -11,6 +11,9 @@ class TestStatsAPI(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
     def test_stats_api(self):
+        """
+            Test SBTarget::GetStatistics() API.
+        """
         self.build()
         exe = self.getBuildArtifact("a.out")
         target = self.dbg.CreateTarget(exe)
@@ -70,3 +73,26 @@ def test_stats_api(self):
             True,
             'Make sure the "failures" key in in "frameVariable" dictionary"',
         )
+
+    def test_command_stats_api(self):
+            """
+                Test GetCommandInterpreter::GetStatistics() API.
+            """
+            self.build()
+            exe = self.getBuildArtifact("a.out")
+            lldbutil.run_to_name_breakpoint(self, 'main')
+
+            interp = self.dbg.GetCommandInterpreter()
+            result = lldb.SBCommandReturnObject()
+            interp.HandleCommand('bt', result)
+
+            stream = lldb.SBStream()
+            res = interp.GetStatistics().GetAsJSON(stream)
+            command_stats = json.loads(stream.GetData())
+
+            # Verify bt command is correctly parsed into final form.
+            self.assertEqual(command_stats["thread backtrace"], 1)
+            # Verify original raw command is not duplicatedly captured.
+            self.assertNotIn('bt', command_stats)
+            # Verify bt's regex command is not duplicatedly captured.
+            self.assertNotIn('_regexp-bt', command_stats)



More information about the lldb-commits mailing list