mirror of
https://github.com/intel/llvm.git
synced 2026-01-14 03:14:06 +08:00
[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@devlieghere.com> Co-authored-by: Pavel Labath <pavel@labath.sk>
This commit is contained in:
@@ -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 @@ protected:
|
||||
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 different 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
|
||||
|
||||
@@ -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 @@ public:
|
||||
|
||||
} // 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user