aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjeffreytan81 <jeffreytan@meta.com>2024-02-05 13:17:29 -0800
committerGitHub <noreply@github.com>2024-02-05 13:17:29 -0800
commit76706090c2f672ae933798292bfa889f9e3dac3d (patch)
treed3466a91127635d5c46bbb7868fb17693784669b
parent4b6062619acf1cdc7b426520dd908d9fab70ed49 (diff)
downloadllvm-76706090c2f672ae933798292bfa889f9e3dac3d.zip
llvm-76706090c2f672ae933798292bfa889f9e3dac3d.tar.gz
llvm-76706090c2f672ae933798292bfa889f9e3dac3d.tar.bz2
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@fb.com>
-rw-r--r--lldb/include/lldb/API/SBCommandInterpreter.h3
-rw-r--r--lldb/include/lldb/API/SBStructuredData.h1
-rw-r--r--lldb/include/lldb/Interpreter/CommandInterpreter.h11
-rw-r--r--lldb/source/API/SBCommandInterpreter.cpp13
-rw-r--r--lldb/source/Commands/CommandObjectCommands.cpp18
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp11
-rw-r--r--lldb/source/Interpreter/CommandObject.cpp1
-rw-r--r--lldb/source/Target/Statistics.cpp7
-rw-r--r--lldb/test/API/commands/statistics/basic/TestStats.py24
-rw-r--r--lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py26
10 files changed, 105 insertions, 10 deletions
diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h
index b7f5b3b..ba2e049 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 @@ public:
/// 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 35d321e..fc6e1ec 100644
--- a/lldb/include/lldb/API/SBStructuredData.h
+++ b/lldb/include/lldb/API/SBStructuredData.h
@@ -122,6 +122,7 @@ protected:
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 747188a..d190bcd 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 @@ public:
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 @@ private:
// 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 c3cbb00..7b87dc5 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 5b9af4a..a51e5ab 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -1123,6 +1123,8 @@ protected:
CommandReturnObject &result) override {
ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+ m_interpreter.IncreaseCommandUsage(*this);
+
Status error;
result.SetStatus(eReturnStatusInvalid);
@@ -1644,8 +1646,9 @@ protected:
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 @@ protected:
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 00651df..8c3972a 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 1ff9774..6324c7e 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 c739ac7..4699710 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 6fb0cc6..0172ac5 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -76,6 +76,11 @@ class TestCase(TestBase):
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 @@ class TestCase(TestBase):
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 33490c9..fe55922 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 @@ class TestStatsAPI(TestBase):
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)