[Lldb-commits] [lldb] a088b0e - [LLDB][Telemetry]Define DebuggerTelemetryInfo and related methods (#127696)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Mar 3 07:07:58 PST 2025
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 <jonas at devlieghere.com>
Co-authored-by: Pavel Labath <pavel at 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);
+ }
+}
More information about the lldb-commits
mailing list