[Lldb-commits] [lldb] [LLDB][Telemetry]Defind telemetry::CommandInfo (PR #129354)

Vy Nguyen via lldb-commits lldb-commits at lists.llvm.org
Fri Feb 28 20:05:47 PST 2025


https://github.com/oontvoo created https://github.com/llvm/llvm-project/pull/129354

and collect telemetry about a command's execution.

*NOTE: Please consider this PR a DRAFT ( Waiting on PR/127696 to be submitted. )

>From 5a992aff351a93ff820d15ace3ebc2bea59dd5fc Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Fri, 28 Feb 2025 23:03:35 -0500
Subject: [PATCH] [LLDB][Telemetry]Defind telemetry::CommandInfo and collect
 telemetry about a command's execution.

---
 lldb/include/lldb/Core/Telemetry.h            | 127 +++++++++++++++++-
 lldb/source/Core/Telemetry.cpp                |  14 ++
 .../source/Interpreter/CommandInterpreter.cpp |  20 +++
 3 files changed, 158 insertions(+), 3 deletions(-)

diff --git a/lldb/include/lldb/Core/Telemetry.h b/lldb/include/lldb/Core/Telemetry.h
index b72556ecaf3c9..30b8474156124 100644
--- a/lldb/include/lldb/Core/Telemetry.h
+++ b/lldb/include/lldb/Core/Telemetry.h
@@ -12,11 +12,14 @@
 #include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Interpreter/CommandReturnObject.h"
 #include "lldb/Utility/StructuredData.h"
+#include "lldb/Utility/LLDBLog.h"
 #include "lldb/lldb-forward.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/FunctionExtras.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Telemetry/Telemetry.h"
+#include <atomic>
 #include <chrono>
 #include <ctime>
 #include <memory>
@@ -27,8 +30,16 @@
 namespace lldb_private {
 namespace telemetry {
 
+struct LLDBConfig : public ::llvm::telemetry::Config {
+  const bool m_collect_original_command;
+
+  explicit LLDBConfig(bool enable_telemetry, bool collect_original_command)
+      : ::llvm::telemetry::Config(enable_telemetry), m_collect_original_command(collect_original_command) {}
+};
+
 struct LLDBEntryKind : public ::llvm::telemetry::EntryKind {
-  static const llvm::telemetry::KindType BaseInfo = 0b11000;
+  static const llvm::telemetry::KindType BaseInfo = 0b11000000;
+  static const llvm::telemetry::KindType CommandInfo = 0b11010000;
 };
 
 /// Defines a convenient type for timestamp of various events.
@@ -41,6 +52,7 @@ struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo {
   std::optional<SteadyTimePoint> end_time;
   // TBD: could add some memory stats here too?
 
+  lldb::user_id_t debugger_id = LLDB_INVALID_UID;
   Debugger *debugger;
 
   // For dyn_cast, isa, etc operations.
@@ -56,6 +68,42 @@ struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo {
   void serialize(llvm::telemetry::Serializer &serializer) const override;
 };
 
+
+struct CommandInfo : public LLDBBaseTelemetryInfo {
+
+  // If the command is/can be associated with a target entry this field contains
+  // that target's UUID. <EMPTY> otherwise.
+  std::string target_uuid;
+  // A unique ID for a command so the manager can match the start entry with
+  // its end entry. These values only need to be unique within the same session.
+  // Necessary because we'd send off an entry right before a command's execution
+  // and another right after. This is to avoid losing telemetry if the command
+  // does not execute successfully.
+  int command_id;
+
+  // Eg., "breakpoint set"
+  std::string command_name;
+
+  // !!NOTE!! These two fields are not collected (upstream) due to PII risks.
+  // (Downstream impl may add them if needed).
+  // std::string original_command;
+  // std::string args;
+
+  lldb::ReturnStatus ret_status;
+  std::string error_data;
+
+
+  CommandInfo() = default;
+
+  llvm::telemetry::KindType getKind() const override { return LLDBEntryKind::CommandInfo; }
+
+  static bool classof(const llvm::telemetry::TelemetryInfo *T) {
+    return (T->getKind() & LLDBEntryKind::CommandInfo) == LLDBEntryKind::CommandInfo;
+  }
+
+  void serialize(Serializer &serializer) const override;
+};
+
 /// The base Telemetry manager instance in LLDB.
 /// This class declares additional instrumentation points
 /// applicable to LLDB.
@@ -63,19 +111,92 @@ class TelemetryManager : public llvm::telemetry::Manager {
 public:
   llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override;
 
+  int MakeNextCommandId();
+
+  LLDBConfig* GetConfig() { return m_config.get(); }
+
   virtual llvm::StringRef GetInstanceName() const = 0;
   static TelemetryManager *getInstance();
 
 protected:
-  TelemetryManager(std::unique_ptr<llvm::telemetry::Config> config);
+  TelemetryManager(std::unique_ptr<LLDBConfig> config);
 
   static void setInstance(std::unique_ptr<TelemetryManager> manger);
 
 private:
-  std::unique_ptr<llvm::telemetry::Config> m_config;
+  std::unique_ptr<LLDBConfig> m_config;
+  const std::string m_id;
+  // We assign each command (in the same session) a unique id so that their
+  // "start" and "end" entries can be matched up.
+  // These values don't need to be unique across runs (because they are
+  // secondary-key), hence a simple counter is sufficent.
+  std::atomic<int> command_id_seed = 0;
   static std::unique_ptr<TelemetryManager> g_instance;
 };
 
+/// Helper RAII class for collecting telemetry.
+template <typename Info> struct ScopedDispatcher {
+  // The debugger pointer is optional because we may not have a debugger yet.
+  // In that case, caller must set the debugger later.
+  ScopedDispatcher(Debugger *debugger = nullptr) {
+    // Start the timer.
+    m_start_time = std::chrono::steady_clock::now();
+    debugger = debugger;
+  }
+  ScopedDispatcher(llvm::unique_function<void(Info *info)> final_callback,
+                   Debugger *debugger = nullptr)
+      : m_final_callback(std::move(final_callback)) {
+    // Start the timer.
+    m_start_time = std::chrono::steady_clock::now();
+    debugger = debugger;
+  }
+
+
+  template typename<T>
+      T GetIfEnable(llvm::unique_function<T(TelemetryManager*)> callable,
+                                   T default_value) {
+    TelemetryManager *manager = TelemetryManager::GetInstanceIfEnabled();
+    if (!manager)
+      return default_value;
+    return callable(manager);
+  }
+
+  void SetDebugger(Debugger *debugger) { debugger = debugger; }
+
+  void SetFinalCallback(llvm::unique_function<void(Info *info)> final_callback) {
+    m_final_callback(std::move(final_callback));
+  }
+
+  void DispatchIfEnable(llvm::unique_function<void(Info *info)> populate_fields_cb) {
+    TelemetryManager *manager = TelemetryManager::GetInstanceIfEnabled();
+    if (!manager)
+      return;
+    Info info;
+    // Populate the common fields we know aboutl
+    info.start_time = m_start_time;
+    info.end_time = std::chrono::steady_clock::now();
+    info.debugger = debugger;
+    // The callback will set the rest.
+    populate_fields_cb(&info);
+    // And then we dispatch.
+    if (llvm::Error er = manager->dispatch(&info)) {
+      LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(er),
+                     "Failed to dispatch entry of type: {0}", m_info.getKind());
+    }
+
+  }
+
+  ~ScopedDispatcher() {
+    // TODO: check if there's a cb to call?
+    DispatchIfEnable(std::move(m_final_callback));
+  }
+
+private:
+  SteadyTimePoint m_start_time;
+  llvm::unique_function<void(Info *info)> m_final_callback;
+  Debugger * debugger;
+};
+
 } // namespace telemetry
 } // namespace lldb_private
 #endif // LLDB_CORE_TELEMETRY_H
diff --git a/lldb/source/Core/Telemetry.cpp b/lldb/source/Core/Telemetry.cpp
index 5222f76704f91..7fb32f75f474e 100644
--- a/lldb/source/Core/Telemetry.cpp
+++ b/lldb/source/Core/Telemetry.cpp
@@ -43,6 +43,16 @@ void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
     serializer.write("end_time", ToNanosec(end_time.value()));
 }
 
+void CommandInfo::serialize(Serializer &serializer) const {
+  LLDBBaseTelemetryInfo::serializer(serializer);
+
+  serializer.write("target_uuid", target_uuid);
+  serializer.write("command_id", command_id);
+  serializer.write("command_name", command_name);
+  serializer.write("ret_status", ret_status);
+  serializer.write("error_data", error_data);
+}
+
 [[maybe_unused]] static std::string MakeUUID(Debugger *debugger) {
   uint8_t random_bytes[16];
   if (auto ec = llvm::getRandomBytes(random_bytes, 16)) {
@@ -66,6 +76,10 @@ llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {
   return llvm::Error::success();
 }
 
+int TelemetryManager::MakeNextCommandId() {
+
+}
+
 std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;
 TelemetryManager *TelemetryManager::getInstance() { return g_instance.get(); }
 
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index c363f20081f9e..aab85145b4c3b 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -47,6 +47,7 @@
 
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/Host/StreamFile.h"
 #include "lldb/Utility/ErrorMessages.h"
 #include "lldb/Utility/LLDBLog.h"
@@ -88,6 +89,7 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Telemetry/Telemetry.h"
 
 #if defined(__APPLE__)
 #include <TargetConditionals.h>
@@ -1883,10 +1885,28 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
                                        LazyBool lazy_add_to_history,
                                        CommandReturnObject &result,
                                        bool force_repeat_command) {
+  lldb_private::telemetry::ScopedDispatcher<
+      lldb_private::telemetry:CommandInfo> helper;
+  const int command_id = helper.GetIfEnable<int>([](lldb_private::telemetry::TelemetryManager* ins){
+    return ins->MakeNextCommandId(); }, 0);
+
   std::string command_string(command_line);
   std::string original_command_string(command_string);
   std::string real_original_command_string(command_string);
 
+  helper.DispatchIfEnable([&](lldb_private::telemetry:CommandInfo* info,
+                              lldb_private::telemetry::TelemetryManager* ins){
+    info.command_id = command_id;
+    if (Target* target = GetExecutionContext().GetTargetPtr()) {
+      // If we have a target attached to this command, then get the UUID.
+      info.target_uuid = target->GetExecutableModule() != nullptr
+                         ? GetExecutableModule()->GetUUID().GetAsString()
+                         : "";
+    }
+    if (ins->GetConfig()->m_collect_original_command)
+      info.original_command = original_command_string;
+  });
+
   Log *log = GetLog(LLDBLog::Commands);
   LLDB_LOGF(log, "Processing command: %s", command_line);
   LLDB_SCOPED_TIMERF("Processing command: %s.", command_line);



More information about the lldb-commits mailing list