[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