Author: Vy Nguyen
Date: 2025-03-03T10:07:54-05:00
New Revision: a088b0ec7653f12e60d01959bc71ea4f7fd206f0
URL: 
https://github.com/llvm/llvm-project/commit/a088b0ec7653f12e60d01959bc71ea4f7fd206f0
DIFF: 
https://github.com/llvm/llvm-project/commit/a088b0ec7653f12e60d01959bc71ea4f7fd206f0.diff

LOG: [LLDB][Telemetry]Define DebuggerTelemetryInfo and related methods (#127696)

This type of entry is used to collect data about the debugger
startup/exit

Also introduced a helper ScopedDispatcher
---------

Co-authored-by: Jonas Devlieghere <jo...@devlieghere.com>
Co-authored-by: Pavel Labath <pa...@labath.sk>

Added: 
    

Modified: 
    lldb/include/lldb/Core/Telemetry.h
    lldb/source/Core/Debugger.cpp
    lldb/source/Core/Telemetry.cpp
    lldb/unittests/Core/TelemetryTest.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Core/Telemetry.h 
b/lldb/include/lldb/Core/Telemetry.h
index 3bf38dac04c3d..7d8716f1659b5 100644
--- a/lldb/include/lldb/Core/Telemetry.h
+++ b/lldb/include/lldb/Core/Telemetry.h
@@ -11,8 +11,10 @@
 
 #include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/StructuredData.h"
 #include "lldb/lldb-forward.h"
+#include "llvm/ADT/FunctionExtras.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/JSON.h"
@@ -22,13 +24,20 @@
 #include <memory>
 #include <optional>
 #include <string>
-#include <unordered_map>
 
 namespace lldb_private {
 namespace telemetry {
 
+// We expect each (direct) subclass of LLDBTelemetryInfo to
+// have an LLDBEntryKind in the form 0b11xxxxxxxx
+// Specifically:
+//  - Length: 8 bits
+//  - First two bits (MSB) must be 11 - the common prefix
+// If any of the subclass has descendents, those descendents
+// must have their LLDBEntryKind in the similar form (ie., share common prefix)
 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 DebuggerInfo = 0b11000100;
 };
 
 /// Defines a convenient type for timestamp of various events.
@@ -41,6 +50,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 +66,26 @@ struct LLDBBaseTelemetryInfo : public 
llvm::telemetry::TelemetryInfo {
   void serialize(llvm::telemetry::Serializer &serializer) const override;
 };
 
+struct DebuggerInfo : public LLDBBaseTelemetryInfo {
+  std::string lldb_version;
+
+  bool is_exit_entry = false;
+
+  DebuggerInfo() = default;
+
+  llvm::telemetry::KindType getKind() const override {
+    return LLDBEntryKind::DebuggerInfo;
+  }
+
+  static bool classof(const llvm::telemetry::TelemetryInfo *T) {
+    // Subclasses of this is also acceptable
+    return (T->getKind() & LLDBEntryKind::DebuggerInfo) ==
+           LLDBEntryKind::DebuggerInfo;
+  }
+
+  void serialize(llvm::telemetry::Serializer &serializer) const override;
+};
+
 /// The base Telemetry manager instance in LLDB.
 /// This class declares additional instrumentation points
 /// applicable to LLDB.
@@ -63,9 +93,14 @@ class TelemetryManager : public llvm::telemetry::Manager {
 public:
   llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override;
 
+  const llvm::telemetry::Config *GetConfig();
+
   virtual llvm::StringRef GetInstanceName() const = 0;
+
   static TelemetryManager *GetInstance();
 
+  static TelemetryManager *GetInstanceIfEnabled();
+
 protected:
   TelemetryManager(std::unique_ptr<llvm::telemetry::Config> config);
 
@@ -73,9 +108,51 @@ class TelemetryManager : public llvm::telemetry::Manager {
 
 private:
   std::unique_ptr<llvm::telemetry::Config> m_config;
+  // Each instance of a TelemetryManager is assigned a unique ID.
+  const std::string m_id;
+
   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(llvm::unique_function<void(Info *info)> callback,
+                   Debugger *debugger = nullptr)
+      : m_callback(std::move(callback)) {
+    // Start the timer.
+    m_start_time = std::chrono::steady_clock::now();
+    m_info.debugger = debugger;
+  }
+
+  void SetDebugger(Debugger *debugger) { m_info.debugger = debugger; }
+
+  ~ScopedDispatcher() {
+    // If Telemetry is disabled (either at buildtime or runtime),
+    // then don't do anything.
+    TelemetryManager *manager = TelemetryManager::GetInstanceIfEnabled();
+    if (!manager)
+      return;
+
+    m_info.start_time = m_start_time;
+    // Populate common fields that we can only set now.
+    m_info.end_time = std::chrono::steady_clock::now();
+    // The callback will set the remaining fields.
+    m_callback(&m_info);
+    // And then we dispatch.
+    if (llvm::Error er = manager->dispatch(&m_info)) {
+      LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(er),
+                     "Failed to dispatch entry of type: {0}", 
m_info.getKind());
+    }
+  }
+
+private:
+  SteadyTimePoint m_start_time;
+  llvm::unique_function<void(Info *info)> m_callback;
+  Info m_info;
+};
+
 } // namespace telemetry
 } // namespace lldb_private
 #endif // LLDB_CORE_TELEMETRY_H

diff  --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 585138535203d..68d1bd30cc5b0 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/Progress.h"
 #include "lldb/Core/StreamAsynchronousIO.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/DataFormatters/DataVisualization.h"
 #include "lldb/Expression/REPL.h"
 #include "lldb/Host/File.h"
@@ -52,6 +53,7 @@
 #include "lldb/Utility/State.h"
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/StreamString.h"
+#include "lldb/Version/Version.h"
 #include "lldb/lldb-enumerations.h"
 
 #if defined(_WIN32)
@@ -62,6 +64,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/iterator.h"
+#include "llvm/Config/llvm-config.h"
 #include "llvm/Support/DynamicLibrary.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Process.h"
@@ -69,6 +72,7 @@
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/raw_ostream.h"
 
+#include <chrono>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
@@ -761,7 +765,14 @@ void Debugger::InstanceInitialize() {
 
 DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
                                     void *baton) {
+  lldb_private::telemetry::ScopedDispatcher<
+      lldb_private::telemetry::DebuggerInfo>
+      helper([](lldb_private::telemetry::DebuggerInfo *entry) {
+        entry->lldb_version = lldb_private::GetVersion();
+      });
   DebuggerSP debugger_sp(new Debugger(log_callback, baton));
+  helper.SetDebugger(debugger_sp.get());
+
   if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
     std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
     g_debugger_list_ptr->push_back(debugger_sp);
@@ -987,6 +998,12 @@ void Debugger::Clear() {
   //     static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp);
   //     static void Debugger::Terminate();
   llvm::call_once(m_clear_once, [this]() {
+    telemetry::ScopedDispatcher<telemetry::DebuggerInfo> helper(
+        [this](lldb_private::telemetry::DebuggerInfo *info) {
+          assert(this == info->debugger);
+          info->is_exit_entry = true;
+        },
+        this);
     ClearIOHandlers();
     StopIOHandlerThread();
     StopEventHandlerThread();

diff  --git a/lldb/source/Core/Telemetry.cpp b/lldb/source/Core/Telemetry.cpp
index fef63e3696e70..367e94d6566de 100644
--- a/lldb/source/Core/Telemetry.cpp
+++ b/lldb/source/Core/Telemetry.cpp
@@ -7,8 +7,10 @@
 
//===----------------------------------------------------------------------===//
 #include "lldb/Core/Telemetry.h"
 #include "lldb/Core/Debugger.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/UUID.h"
+#include "lldb/Version/Version.h"
 #include "lldb/lldb-enumerations.h"
 #include "lldb/lldb-forward.h"
 #include "llvm/ADT/StringRef.h"
@@ -30,6 +32,26 @@ static uint64_t ToNanosec(const SteadyTimePoint Point) {
   return std::chrono::nanoseconds(Point.time_since_epoch()).count();
 }
 
+// Generate a unique string. This should be unique across 
diff erent runs.
+// We build such string by combining three parts:
+// <16 random bytes>_<timestamp>
+// This reduces the chances of getting the same UUID, even when the same
+// user runs the two copies of binary at the same time.
+static std::string MakeUUID() {
+  uint8_t random_bytes[16];
+  std::string randomString = "_";
+  if (auto ec = llvm::getRandomBytes(random_bytes, 16)) {
+    LLDB_LOG(GetLog(LLDBLog::Object),
+             "Failed to generate random bytes for UUID: {0}", ec.message());
+  } else {
+    randomString = UUID(random_bytes).GetAsString();
+  }
+
+  return llvm::formatv(
+      "{0}_{1}", randomString,
+      std::chrono::steady_clock::now().time_since_epoch().count());
+}
+
 void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
   serializer.write("entry_kind", getKind());
   serializer.write("session_id", SessionId);
@@ -38,29 +60,27 @@ void LLDBBaseTelemetryInfo::serialize(Serializer 
&serializer) const {
     serializer.write("end_time", ToNanosec(end_time.value()));
 }
 
-[[maybe_unused]] static std::string MakeUUID(Debugger *debugger) {
-  uint8_t random_bytes[16];
-  if (auto ec = llvm::getRandomBytes(random_bytes, 16)) {
-    LLDB_LOG(GetLog(LLDBLog::Object),
-             "Failed to generate random bytes for UUID: {0}", ec.message());
-    // Fallback to using timestamp + debugger ID.
-    return llvm::formatv(
-        "{0}_{1}", std::chrono::steady_clock::now().time_since_epoch().count(),
-        debugger->GetID());
-  }
-  return UUID(random_bytes).GetAsString();
+void DebuggerInfo::serialize(Serializer &serializer) const {
+  LLDBBaseTelemetryInfo::serialize(serializer);
+
+  serializer.write("lldb_version", lldb_version);
+  serializer.write("is_exit_entry", is_exit_entry);
 }
 
 TelemetryManager::TelemetryManager(std::unique_ptr<Config> config)
-    : m_config(std::move(config)) {}
+    : m_config(std::move(config)), m_id(MakeUUID()) {}
 
 llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {
-  // Do nothing for now.
-  // In up-coming patch, this would be where the manager
-  // attach the session_uuid to the entry.
+  // Assign the manager_id, and debugger_id, if available, to this entry.
+  LLDBBaseTelemetryInfo *lldb_entry = llvm::cast<LLDBBaseTelemetryInfo>(entry);
+  lldb_entry->SessionId = m_id;
+  if (Debugger *debugger = lldb_entry->debugger)
+    lldb_entry->debugger_id = debugger->GetID();
   return llvm::Error::success();
 }
 
+const Config *TelemetryManager::GetConfig() { return m_config.get(); }
+
 std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;
 TelemetryManager *TelemetryManager::GetInstance() {
   if (!Config::BuildTimeEnableTelemetry)
@@ -68,8 +88,19 @@ TelemetryManager *TelemetryManager::GetInstance() {
   return g_instance.get();
 }
 
+TelemetryManager *TelemetryManager::GetInstanceIfEnabled() {
+  // Telemetry may be enabled at build time but disabled at runtime.
+  if (TelemetryManager *ins = TelemetryManager::GetInstance()) {
+    if (ins->GetConfig()->EnableTelemetry)
+      return ins;
+  }
+
+  return nullptr;
+}
+
 void TelemetryManager::SetInstance(std::unique_ptr<TelemetryManager> manager) {
-  g_instance = std::move(manager);
+  if (Config::BuildTimeEnableTelemetry)
+    g_instance = std::move(manager);
 }
 
 } // namespace telemetry

diff  --git a/lldb/unittests/Core/TelemetryTest.cpp 
b/lldb/unittests/Core/TelemetryTest.cpp
index 22fade58660b5..0e9f329110872 100644
--- a/lldb/unittests/Core/TelemetryTest.cpp
+++ b/lldb/unittests/Core/TelemetryTest.cpp
@@ -18,25 +18,34 @@
 
 namespace lldb_private {
 
-struct FakeTelemetryInfo : public llvm::telemetry::TelemetryInfo {
+struct FakeTelemetryInfo : public telemetry::LLDBBaseTelemetryInfo {
   std::string msg;
+  int num;
+
+  ::llvm::telemetry::KindType getKind() const override { return 0b11111111; }
 };
 
 class TestDestination : public llvm::telemetry::Destination {
 public:
-  TestDestination(std::vector<const llvm::telemetry::TelemetryInfo *> *entries)
+  TestDestination(
+      std::vector<std::unique_ptr<llvm::telemetry::TelemetryInfo>> *entries)
       : received_entries(entries) {}
 
   llvm::Error
   receiveEntry(const llvm::telemetry::TelemetryInfo *entry) override {
-    received_entries->push_back(entry);
+    // Save a copy of the entry for later verification (because the original
+    // entry might have gone out of scope by the time verification is done.
+    if (auto *fake_entry = llvm::dyn_cast<FakeTelemetryInfo>(entry))
+      received_entries->push_back(
+          std::make_unique<FakeTelemetryInfo>(*fake_entry));
     return llvm::Error::success();
   }
 
   llvm::StringLiteral name() const override { return "TestDestination"; }
 
 private:
-  std::vector<const llvm::telemetry::TelemetryInfo *> *received_entries;
+  std::vector<std::unique_ptr<llvm::telemetry::TelemetryInfo>>
+      *received_entries;
 };
 
 class FakePlugin : public telemetry::TelemetryManager {
@@ -66,6 +75,8 @@ class FakePlugin : public telemetry::TelemetryManager {
 
 } // namespace lldb_private
 
+using namespace lldb_private::telemetry;
+
 #if LLVM_ENABLE_TELEMETRY
 #define TELEMETRY_TEST(suite, test) TEST(suite, test)
 #else
@@ -80,18 +91,51 @@ TELEMETRY_TEST(TelemetryTest, PluginTest) {
   auto *ins = lldb_private::telemetry::TelemetryManager::GetInstance();
   ASSERT_NE(ins, nullptr);
 
-  std::vector<const ::llvm::telemetry::TelemetryInfo *> expected_entries;
+  std::vector<std::unique_ptr<::llvm::telemetry::TelemetryInfo>>
+      received_entries;
   ins->addDestination(
-      std::make_unique<lldb_private::TestDestination>(&expected_entries));
+      std::make_unique<lldb_private::TestDestination>(&received_entries));
 
   lldb_private::FakeTelemetryInfo entry;
   entry.msg = "";
 
   ASSERT_THAT_ERROR(ins->dispatch(&entry), ::llvm::Succeeded());
-  ASSERT_EQ(1U, expected_entries.size());
+  ASSERT_EQ(1U, received_entries.size());
   EXPECT_EQ("In FakePlugin",
-            
llvm::dyn_cast<lldb_private::FakeTelemetryInfo>(expected_entries[0])
+            
llvm::dyn_cast<lldb_private::FakeTelemetryInfo>(received_entries[0])
                 ->msg);
 
   ASSERT_EQ("FakeTelemetryPlugin", ins->GetInstanceName());
 }
+
+TELEMETRY_TEST(TelemetryTest, ScopedDispatcherTest) {
+  lldb_private::FakePlugin::Initialize();
+  auto *ins = TelemetryManager::GetInstance();
+  ASSERT_NE(ins, nullptr);
+  std::vector<std::unique_ptr<::llvm::telemetry::TelemetryInfo>>
+      received_entries;
+  ins->addDestination(
+      std::make_unique<lldb_private::TestDestination>(&received_entries));
+
+  {
+    ScopedDispatcher<lldb_private::FakeTelemetryInfo> helper(
+        [](lldb_private::FakeTelemetryInfo *info) { info->num = 0; });
+  }
+
+  {
+    ScopedDispatcher<lldb_private::FakeTelemetryInfo> helper(
+        [](lldb_private::FakeTelemetryInfo *info) { info->num = 1; });
+  }
+
+  {
+    ScopedDispatcher<lldb_private::FakeTelemetryInfo> helper(
+        [](lldb_private::FakeTelemetryInfo *info) { info->num = 2; });
+  }
+
+  EXPECT_EQ(3U, received_entries.size());
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_EQ(
+        i, llvm::dyn_cast<lldb_private::FakeTelemetryInfo>(received_entries[i])
+               ->num);
+  }
+}


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to