[Lldb-commits] [lldb] Add commands frequency to statistics dump (PR #80375)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Feb 1 20:25:09 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: None (jeffreytan81)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/80375.diff
10 Files Affected:
- (modified) lldb/include/lldb/API/SBCommandInterpreter.h (+6-3)
- (modified) lldb/include/lldb/API/SBStructuredData.h (+1)
- (modified) lldb/include/lldb/Interpreter/CommandInterpreter.h (+13-2)
- (modified) lldb/source/API/SBCommandInterpreter.cpp (+14-1)
- (modified) lldb/source/Commands/CommandObjectCommands.cpp (+15-10)
- (modified) lldb/source/Interpreter/CommandInterpreter.cpp (+9-2)
- (modified) lldb/source/Interpreter/CommandObject.cpp (+1)
- (modified) lldb/source/Target/Statistics.cpp (+6-1)
- (modified) lldb/test/API/commands/statistics/basic/TestStats.py (+24)
- (modified) lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py (+26)
``````````diff
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..eb1f0f48a9d3c 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);
@@ -1623,7 +1625,8 @@ class CommandObjectCommandsScriptAdd : public CommandObjectParsed,
m_options.m_class_name.c_str());
if (!cmd_obj_sp) {
result.AppendErrorWithFormatv("cannot create helper object for: "
- "'{0}'", m_options.m_class_name);
+ "'{0}'",
+ m_options.m_class_name);
return;
}
@@ -1631,7 +1634,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,8 +1647,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 +1792,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..2ff8ae7b73cc2 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.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..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)
``````````
</details>
https://github.com/llvm/llvm-project/pull/80375
More information about the lldb-commits
mailing list