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

via lldb-commits lldb-commits at lists.llvm.org
Mon Feb 5 13:17:33 PST 2024


Author: jeffreytan81
Date: 2024-02-05T13:17:29-08:00
New Revision: 76706090c2f672ae933798292bfa889f9e3dac3d

URL: https://github.com/llvm/llvm-project/commit/76706090c2f672ae933798292bfa889f9e3dac3d
DIFF: https://github.com/llvm/llvm-project/commit/76706090c2f672ae933798292bfa889f9e3dac3d.diff

LOG: Add commands frequency to statistics dump (#80375)

Adding command interpreter statistics into "statistics dump" command so
that we can track the command usage frequency for telemetry purpose.
This is useful to answer questions like what is the most frequently used
lldb commands across all our users.

---------

Co-authored-by: jeffreytan81 <jeffreytan at fb.com>

Added: 
    

Modified: 
    lldb/include/lldb/API/SBCommandInterpreter.h
    lldb/include/lldb/API/SBStructuredData.h
    lldb/include/lldb/Interpreter/CommandInterpreter.h
    lldb/source/API/SBCommandInterpreter.cpp
    lldb/source/Commands/CommandObjectCommands.cpp
    lldb/source/Interpreter/CommandInterpreter.cpp
    lldb/source/Interpreter/CommandObject.cpp
    lldb/source/Target/Statistics.cpp
    lldb/test/API/commands/statistics/basic/TestStats.py
    lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h
index b7f5b3bf3396e..ba2e049204b8e 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;
@@ -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..d190bcdcab449 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;
@@ -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()];
+  }
+
+  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 llvm::StringMap<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..7b87dc507e4be 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -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..a51e5ab1af30c 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);
@@ -1644,8 +1646,9 @@ 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", 
-                                     llvm::toString(std::move(llvm_error)).c_str());
+        result.AppendErrorWithFormat(
+            "cannot add command: %s",
+            llvm::toString(std::move(llvm_error)).c_str());
     }
   }
 
@@ -1788,12 +1791,13 @@ class CommandObjectCommandsScriptDelete : public CommandObjectParsed {
       return;
     }
     const char *leaf_cmd = command[num_args - 1].c_str();
-    llvm::Error llvm_error = container->RemoveUserSubcommand(leaf_cmd,
-                                            /* multiword not okay */ false);
+    llvm::Error llvm_error =
+        container->RemoveUserSubcommand(leaf_cmd,
+                                        /* multiword not okay */ false);
     if (llvm_error) {
-      result.AppendErrorWithFormat("could not delete command '%s': %s",
-                                   leaf_cmd, 
-                                   llvm::toString(std::move(llvm_error)).c_str());
+      result.AppendErrorWithFormat(
+          "could not delete command '%s': %s", 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..8c3972a2ba4ce 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -3055,8 +3055,8 @@ void CommandInterpreter::PrintCommandOutput(IOHandler &io_handler,
   }
 
   std::lock_guard<std::recursive_mutex> guard(io_handler.GetOutputMutex());
-  if (had_output && INTERRUPT_REQUESTED(GetDebugger(), 
-                                        "Interrupted dumping command output"))
+  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.getKey(), command_usage.getValue());
+  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..4699710035b2d 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},
@@ -307,7 +311,8 @@ llvm::json::Value DebuggerStats::ReportStatistics(Debugger &debugger,
       {"totalModuleCount", num_modules},
       {"totalModuleCountHasDebugInfo", num_modules_has_debug_info},
       {"totalModuleCountWithVariableErrors", num_modules_with_variable_errors},
-      {"totalModuleCountWithIncompleteTypes", num_modules_with_incomplete_types},
+      {"totalModuleCountWithIncompleteTypes",
+       num_modules_with_incomplete_types},
       {"totalDebugInfoEnabled", num_debug_info_enabled_modules},
       {"totalSymbolTableStripped", num_stripped_modules},
   };

diff  --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index 6fb0cc6c3be73..0172ac536e979 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..fe55922fe4c31 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