Skip to content

Commit a088b0e

Browse files
oontvooJDevliegherelabath
authored
[LLDB][Telemetry]Define DebuggerTelemetryInfo and related methods (llvm#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 <[email protected]> Co-authored-by: Pavel Labath <[email protected]>
1 parent 17857d9 commit a088b0e

File tree

4 files changed

+195
-26
lines changed

4 files changed

+195
-26
lines changed

lldb/include/lldb/Core/Telemetry.h

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
#include "lldb/Core/StructuredDataImpl.h"
1313
#include "lldb/Interpreter/CommandReturnObject.h"
14+
#include "lldb/Utility/LLDBLog.h"
1415
#include "lldb/Utility/StructuredData.h"
1516
#include "lldb/lldb-forward.h"
17+
#include "llvm/ADT/FunctionExtras.h"
1618
#include "llvm/ADT/StringExtras.h"
1719
#include "llvm/ADT/StringRef.h"
1820
#include "llvm/Support/JSON.h"
@@ -22,13 +24,20 @@
2224
#include <memory>
2325
#include <optional>
2426
#include <string>
25-
#include <unordered_map>
2627

2728
namespace lldb_private {
2829
namespace telemetry {
2930

31+
// We expect each (direct) subclass of LLDBTelemetryInfo to
32+
// have an LLDBEntryKind in the form 0b11xxxxxxxx
33+
// Specifically:
34+
// - Length: 8 bits
35+
// - First two bits (MSB) must be 11 - the common prefix
36+
// If any of the subclass has descendents, those descendents
37+
// must have their LLDBEntryKind in the similar form (ie., share common prefix)
3038
struct LLDBEntryKind : public ::llvm::telemetry::EntryKind {
31-
static const llvm::telemetry::KindType BaseInfo = 0b11000;
39+
static const llvm::telemetry::KindType BaseInfo = 0b11000000;
40+
static const llvm::telemetry::KindType DebuggerInfo = 0b11000100;
3241
};
3342

3443
/// Defines a convenient type for timestamp of various events.
@@ -41,6 +50,7 @@ struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo {
4150
std::optional<SteadyTimePoint> end_time;
4251
// TBD: could add some memory stats here too?
4352

53+
lldb::user_id_t debugger_id = LLDB_INVALID_UID;
4454
Debugger *debugger;
4555

4656
// For dyn_cast, isa, etc operations.
@@ -56,26 +66,93 @@ struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo {
5666
void serialize(llvm::telemetry::Serializer &serializer) const override;
5767
};
5868

69+
struct DebuggerInfo : public LLDBBaseTelemetryInfo {
70+
std::string lldb_version;
71+
72+
bool is_exit_entry = false;
73+
74+
DebuggerInfo() = default;
75+
76+
llvm::telemetry::KindType getKind() const override {
77+
return LLDBEntryKind::DebuggerInfo;
78+
}
79+
80+
static bool classof(const llvm::telemetry::TelemetryInfo *T) {
81+
// Subclasses of this is also acceptable
82+
return (T->getKind() & LLDBEntryKind::DebuggerInfo) ==
83+
LLDBEntryKind::DebuggerInfo;
84+
}
85+
86+
void serialize(llvm::telemetry::Serializer &serializer) const override;
87+
};
88+
5989
/// The base Telemetry manager instance in LLDB.
6090
/// This class declares additional instrumentation points
6191
/// applicable to LLDB.
6292
class TelemetryManager : public llvm::telemetry::Manager {
6393
public:
6494
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override;
6595

96+
const llvm::telemetry::Config *GetConfig();
97+
6698
virtual llvm::StringRef GetInstanceName() const = 0;
99+
67100
static TelemetryManager *GetInstance();
68101

102+
static TelemetryManager *GetInstanceIfEnabled();
103+
69104
protected:
70105
TelemetryManager(std::unique_ptr<llvm::telemetry::Config> config);
71106

72107
static void SetInstance(std::unique_ptr<TelemetryManager> manger);
73108

74109
private:
75110
std::unique_ptr<llvm::telemetry::Config> m_config;
111+
// Each instance of a TelemetryManager is assigned a unique ID.
112+
const std::string m_id;
113+
76114
static std::unique_ptr<TelemetryManager> g_instance;
77115
};
78116

117+
/// Helper RAII class for collecting telemetry.
118+
template <typename Info> struct ScopedDispatcher {
119+
// The debugger pointer is optional because we may not have a debugger yet.
120+
// In that case, caller must set the debugger later.
121+
ScopedDispatcher(llvm::unique_function<void(Info *info)> callback,
122+
Debugger *debugger = nullptr)
123+
: m_callback(std::move(callback)) {
124+
// Start the timer.
125+
m_start_time = std::chrono::steady_clock::now();
126+
m_info.debugger = debugger;
127+
}
128+
129+
void SetDebugger(Debugger *debugger) { m_info.debugger = debugger; }
130+
131+
~ScopedDispatcher() {
132+
// If Telemetry is disabled (either at buildtime or runtime),
133+
// then don't do anything.
134+
TelemetryManager *manager = TelemetryManager::GetInstanceIfEnabled();
135+
if (!manager)
136+
return;
137+
138+
m_info.start_time = m_start_time;
139+
// Populate common fields that we can only set now.
140+
m_info.end_time = std::chrono::steady_clock::now();
141+
// The callback will set the remaining fields.
142+
m_callback(&m_info);
143+
// And then we dispatch.
144+
if (llvm::Error er = manager->dispatch(&m_info)) {
145+
LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(er),
146+
"Failed to dispatch entry of type: {0}", m_info.getKind());
147+
}
148+
}
149+
150+
private:
151+
SteadyTimePoint m_start_time;
152+
llvm::unique_function<void(Info *info)> m_callback;
153+
Info m_info;
154+
};
155+
79156
} // namespace telemetry
80157
} // namespace lldb_private
81158
#endif // LLDB_CORE_TELEMETRY_H

lldb/source/Core/Debugger.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "lldb/Core/PluginManager.h"
1818
#include "lldb/Core/Progress.h"
1919
#include "lldb/Core/StreamAsynchronousIO.h"
20+
#include "lldb/Core/Telemetry.h"
2021
#include "lldb/DataFormatters/DataVisualization.h"
2122
#include "lldb/Expression/REPL.h"
2223
#include "lldb/Host/File.h"
@@ -52,6 +53,7 @@
5253
#include "lldb/Utility/State.h"
5354
#include "lldb/Utility/Stream.h"
5455
#include "lldb/Utility/StreamString.h"
56+
#include "lldb/Version/Version.h"
5557
#include "lldb/lldb-enumerations.h"
5658

5759
#if defined(_WIN32)
@@ -62,13 +64,15 @@
6264
#include "llvm/ADT/STLExtras.h"
6365
#include "llvm/ADT/StringRef.h"
6466
#include "llvm/ADT/iterator.h"
67+
#include "llvm/Config/llvm-config.h"
6568
#include "llvm/Support/DynamicLibrary.h"
6669
#include "llvm/Support/FileSystem.h"
6770
#include "llvm/Support/Process.h"
6871
#include "llvm/Support/ThreadPool.h"
6972
#include "llvm/Support/Threading.h"
7073
#include "llvm/Support/raw_ostream.h"
7174

75+
#include <chrono>
7276
#include <cstdio>
7377
#include <cstdlib>
7478
#include <cstring>
@@ -761,7 +765,14 @@ void Debugger::InstanceInitialize() {
761765

762766
DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
763767
void *baton) {
768+
lldb_private::telemetry::ScopedDispatcher<
769+
lldb_private::telemetry::DebuggerInfo>
770+
helper([](lldb_private::telemetry::DebuggerInfo *entry) {
771+
entry->lldb_version = lldb_private::GetVersion();
772+
});
764773
DebuggerSP debugger_sp(new Debugger(log_callback, baton));
774+
helper.SetDebugger(debugger_sp.get());
775+
765776
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
766777
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
767778
g_debugger_list_ptr->push_back(debugger_sp);
@@ -987,6 +998,12 @@ void Debugger::Clear() {
987998
// static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp);
988999
// static void Debugger::Terminate();
9891000
llvm::call_once(m_clear_once, [this]() {
1001+
telemetry::ScopedDispatcher<telemetry::DebuggerInfo> helper(
1002+
[this](lldb_private::telemetry::DebuggerInfo *info) {
1003+
assert(this == info->debugger);
1004+
info->is_exit_entry = true;
1005+
},
1006+
this);
9901007
ClearIOHandlers();
9911008
StopIOHandlerThread();
9921009
StopEventHandlerThread();

lldb/source/Core/Telemetry.cpp

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
//===----------------------------------------------------------------------===//
88
#include "lldb/Core/Telemetry.h"
99
#include "lldb/Core/Debugger.h"
10+
#include "lldb/Core/Telemetry.h"
1011
#include "lldb/Utility/LLDBLog.h"
1112
#include "lldb/Utility/UUID.h"
13+
#include "lldb/Version/Version.h"
1214
#include "lldb/lldb-enumerations.h"
1315
#include "lldb/lldb-forward.h"
1416
#include "llvm/ADT/StringRef.h"
@@ -30,6 +32,26 @@ static uint64_t ToNanosec(const SteadyTimePoint Point) {
3032
return std::chrono::nanoseconds(Point.time_since_epoch()).count();
3133
}
3234

35+
// Generate a unique string. This should be unique across different runs.
36+
// We build such string by combining three parts:
37+
// <16 random bytes>_<timestamp>
38+
// This reduces the chances of getting the same UUID, even when the same
39+
// user runs the two copies of binary at the same time.
40+
static std::string MakeUUID() {
41+
uint8_t random_bytes[16];
42+
std::string randomString = "_";
43+
if (auto ec = llvm::getRandomBytes(random_bytes, 16)) {
44+
LLDB_LOG(GetLog(LLDBLog::Object),
45+
"Failed to generate random bytes for UUID: {0}", ec.message());
46+
} else {
47+
randomString = UUID(random_bytes).GetAsString();
48+
}
49+
50+
return llvm::formatv(
51+
"{0}_{1}", randomString,
52+
std::chrono::steady_clock::now().time_since_epoch().count());
53+
}
54+
3355
void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
3456
serializer.write("entry_kind", getKind());
3557
serializer.write("session_id", SessionId);
@@ -38,38 +60,47 @@ void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
3860
serializer.write("end_time", ToNanosec(end_time.value()));
3961
}
4062

41-
[[maybe_unused]] static std::string MakeUUID(Debugger *debugger) {
42-
uint8_t random_bytes[16];
43-
if (auto ec = llvm::getRandomBytes(random_bytes, 16)) {
44-
LLDB_LOG(GetLog(LLDBLog::Object),
45-
"Failed to generate random bytes for UUID: {0}", ec.message());
46-
// Fallback to using timestamp + debugger ID.
47-
return llvm::formatv(
48-
"{0}_{1}", std::chrono::steady_clock::now().time_since_epoch().count(),
49-
debugger->GetID());
50-
}
51-
return UUID(random_bytes).GetAsString();
63+
void DebuggerInfo::serialize(Serializer &serializer) const {
64+
LLDBBaseTelemetryInfo::serialize(serializer);
65+
66+
serializer.write("lldb_version", lldb_version);
67+
serializer.write("is_exit_entry", is_exit_entry);
5268
}
5369

5470
TelemetryManager::TelemetryManager(std::unique_ptr<Config> config)
55-
: m_config(std::move(config)) {}
71+
: m_config(std::move(config)), m_id(MakeUUID()) {}
5672

5773
llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {
58-
// Do nothing for now.
59-
// In up-coming patch, this would be where the manager
60-
// attach the session_uuid to the entry.
74+
// Assign the manager_id, and debugger_id, if available, to this entry.
75+
LLDBBaseTelemetryInfo *lldb_entry = llvm::cast<LLDBBaseTelemetryInfo>(entry);
76+
lldb_entry->SessionId = m_id;
77+
if (Debugger *debugger = lldb_entry->debugger)
78+
lldb_entry->debugger_id = debugger->GetID();
6179
return llvm::Error::success();
6280
}
6381

82+
const Config *TelemetryManager::GetConfig() { return m_config.get(); }
83+
6484
std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;
6585
TelemetryManager *TelemetryManager::GetInstance() {
6686
if (!Config::BuildTimeEnableTelemetry)
6787
return nullptr;
6888
return g_instance.get();
6989
}
7090

91+
TelemetryManager *TelemetryManager::GetInstanceIfEnabled() {
92+
// Telemetry may be enabled at build time but disabled at runtime.
93+
if (TelemetryManager *ins = TelemetryManager::GetInstance()) {
94+
if (ins->GetConfig()->EnableTelemetry)
95+
return ins;
96+
}
97+
98+
return nullptr;
99+
}
100+
71101
void TelemetryManager::SetInstance(std::unique_ptr<TelemetryManager> manager) {
72-
g_instance = std::move(manager);
102+
if (Config::BuildTimeEnableTelemetry)
103+
g_instance = std::move(manager);
73104
}
74105

75106
} // namespace telemetry

0 commit comments

Comments
 (0)