Skip to content

Commit 24e9f78

Browse files
committed
[LLDB][Telemetry]Define DebuggerTelemetryInfo and related methods
- This type of entry is used to collect data about the debugger startup/exit - Tests will be added (They may need to be shell test with a "test-only" TelemetryManager plugin defined. I'm trying to figure out how to get that linked only when tests are running and not to the LLDB binary all the time.
1 parent 5ecce45 commit 24e9f78

File tree

3 files changed

+220
-13
lines changed

3 files changed

+220
-13
lines changed

lldb/include/lldb/Core/Telemetry.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "lldb/Interpreter/CommandReturnObject.h"
1414
#include "lldb/Utility/StructuredData.h"
1515
#include "lldb/lldb-forward.h"
16+
#include "llvm/ADT/DenseMap.h"
1617
#include "llvm/ADT/StringExtras.h"
1718
#include "llvm/ADT/StringRef.h"
1819
#include "llvm/Support/JSON.h"
@@ -29,6 +30,9 @@ namespace telemetry {
2930

3031
struct LLDBEntryKind : public ::llvm::telemetry::EntryKind {
3132
static const llvm::telemetry::KindType BaseInfo = 0b11000;
33+
static const llvm::telemetry::KindType DebuggerInfo = 0b11001;
34+
// There are other entries in between (added in separate PRs)
35+
static const llvm::telemetry::KindType MiscInfo = 0b11110;
3236
};
3337

3438
/// Defines a convenient type for timestamp of various events.
@@ -56,13 +60,83 @@ struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo {
5660
void serialize(llvm::telemetry::Serializer &serializer) const override;
5761
};
5862

63+
/// Describes the exit status of a debugger.
64+
struct ExitDescription {
65+
int exit_code;
66+
std::string description;
67+
};
68+
69+
struct DebuggerTelemetryInfo : public LLDBBaseTelemetryInfo {
70+
std::string username;
71+
std::string lldb_git_sha;
72+
std::string lldb_path;
73+
std::string cwd;
74+
std::optional<ExitDescription> exit_desc;
75+
76+
DebuggerTelemetryInfo() = default;
77+
78+
// Provide a copy ctor because we may need to make a copy before
79+
// sanitizing the data.
80+
// (The sanitization might differ between different Destination classes).
81+
DebuggerTelemetryInfo(const DebuggerTelemetryInfo &other) {
82+
username = other.username;
83+
lldb_git_sha = other.lldb_git_sha;
84+
lldb_path = other.lldb_path;
85+
cwd = other.cwd;
86+
};
87+
88+
llvm::telemetry::KindType getKind() const override {
89+
return LLDBEntryKind::DebuggerInfo;
90+
}
91+
92+
static bool classof(const llvm::telemetry::TelemetryInfo *T) {
93+
return T->getKind() == LLDBEntryKind::DebuggerInfo;
94+
}
95+
96+
void serialize(llvm::telemetry::Serializer &serializer) const override;
97+
};
98+
99+
/// The "catch-all" entry to store a set of non-standard data, such as
100+
/// error-messages, etc.
101+
struct MiscTelemetryInfo : public LLDBBaseTelemetryInfo {
102+
/// If the event is/can be associated with a target entry,
103+
/// this field contains that target's UUID.
104+
/// <EMPTY> otherwise.
105+
std::string target_uuid;
106+
107+
/// Set of key-value pairs for any optional (or impl-specific) data
108+
std::map<std::string, std::string> meta_data;
109+
110+
MiscTelemetryInfo() = default;
111+
112+
MiscTelemetryInfo(const MiscTelemetryInfo &other) {
113+
target_uuid = other.target_uuid;
114+
meta_data = other.meta_data;
115+
}
116+
117+
llvm::telemetry::KindType getKind() const override {
118+
return LLDBEntryKind::MiscInfo;
119+
}
120+
121+
static bool classof(const llvm::telemetry::TelemetryInfo *T) {
122+
return T->getKind() == LLDBEntryKind::MiscInfo;
123+
}
124+
125+
void serialize(llvm::telemetry::Serializer &serializer) const override;
126+
};
127+
59128
/// The base Telemetry manager instance in LLDB.
60129
/// This class declares additional instrumentation points
61130
/// applicable to LLDB.
62131
class TelemetryManager : public llvm::telemetry::Manager {
63132
public:
64133
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override;
65134

135+
const llvm::telemetry::Config *getConfig();
136+
137+
void atDebuggerStartup(DebuggerTelemetryInfo *entry);
138+
void atDebuggerExit(DebuggerTelemetryInfo *entry);
139+
66140
virtual llvm::StringRef GetInstanceName() const = 0;
67141
static TelemetryManager *getInstance();
68142

@@ -73,6 +147,10 @@ class TelemetryManager : public llvm::telemetry::Manager {
73147

74148
private:
75149
std::unique_ptr<llvm::telemetry::Config> m_config;
150+
// Each debugger is assigned a unique ID (session_id).
151+
// All TelemetryInfo entries emitted for the same debugger instance
152+
// will get the same session_id.
153+
llvm::DenseMap<Debugger *, std::string> session_ids;
76154
static std::unique_ptr<TelemetryManager> g_instance;
77155
};
78156

lldb/source/Core/Debugger.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,19 @@
6262
#include "llvm/ADT/STLExtras.h"
6363
#include "llvm/ADT/StringRef.h"
6464
#include "llvm/ADT/iterator.h"
65+
#include "llvm/Config/llvm-config.h"
6566
#include "llvm/Support/DynamicLibrary.h"
6667
#include "llvm/Support/FileSystem.h"
6768
#include "llvm/Support/Process.h"
6869
#include "llvm/Support/ThreadPool.h"
6970
#include "llvm/Support/Threading.h"
7071
#include "llvm/Support/raw_ostream.h"
7172

73+
#ifdef LLVM_BUILD_TELEMETRY
74+
#include "lldb/Core/Telemetry.h"
75+
#include <chrono>
76+
#endif
77+
7278
#include <cstdio>
7379
#include <cstdlib>
7480
#include <cstring>
@@ -761,12 +767,29 @@ void Debugger::InstanceInitialize() {
761767

762768
DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
763769
void *baton) {
770+
#ifdef LLVM_BUILD_TELEMETRY
771+
lldb_private::telemetry::SteadyTimePoint start_time =
772+
std::chrono::steady_clock::now();
773+
#endif
764774
DebuggerSP debugger_sp(new Debugger(log_callback, baton));
765775
if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
766776
std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
767777
g_debugger_list_ptr->push_back(debugger_sp);
768778
}
769779
debugger_sp->InstanceInitialize();
780+
781+
#ifdef LLVM_BUILD_TELEMETRY
782+
if (auto *telemetry_manager = telemetry::TelemetryManager::getInstance()) {
783+
if (telemetry_manager->getConfig()->EnableTelemetry) {
784+
lldb_private::telemetry::DebuggerTelemetryInfo entry;
785+
entry.start_time = start_time;
786+
entry.end_time = std::chrono::steady_clock::now();
787+
entry.debugger = debugger_sp.get();
788+
telemetry_manager->atDebuggerStartup(&entry);
789+
}
790+
}
791+
#endif
792+
770793
return debugger_sp;
771794
}
772795

@@ -985,6 +1008,10 @@ void Debugger::Clear() {
9851008
// static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp);
9861009
// static void Debugger::Terminate();
9871010
llvm::call_once(m_clear_once, [this]() {
1011+
#ifdef LLVM_BUILD_TELEMETRY
1012+
lldb_private::telemetry::SteadyTimePoint quit_start_time =
1013+
std::chrono::steady_clock::now();
1014+
#endif
9881015
ClearIOHandlers();
9891016
StopIOHandlerThread();
9901017
StopEventHandlerThread();
@@ -1007,6 +1034,19 @@ void Debugger::Clear() {
10071034

10081035
if (Diagnostics::Enabled())
10091036
Diagnostics::Instance().RemoveCallback(m_diagnostics_callback_id);
1037+
1038+
#ifdef LLVM_BUILD_TELEMETRY
1039+
if (auto telemetry_manager = telemetry::TelemetryManager::getInstance()) {
1040+
if (telemetry_manager->getConfig()->EnableTelemetry) {
1041+
// TBD: We *may* have to send off the log BEFORE the ClearIOHanders()?
1042+
lldb_private::telemetry::DebuggerTelemetryInfo entry;
1043+
entry.start_time = quit_start_time;
1044+
entry.end_time = std::chrono::steady_clock::now();
1045+
entry.debugger = this;
1046+
telemetry_manager->atDebuggerExit(&entry);
1047+
}
1048+
}
1049+
#endif
10101050
});
10111051
}
10121052

lldb/source/Core/Telemetry.cpp

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,20 @@
1010

1111
#ifdef LLVM_BUILD_TELEMETRY
1212

13-
#include "lldb/Core/Telemetry.h"
1413
#include "lldb/Core/Debugger.h"
14+
#include "lldb/Core/Telemetry.h"
15+
#include "lldb/Host/FileSystem.h"
16+
#include "lldb/Host/HostInfo.h"
17+
#include "lldb/Target/Process.h"
18+
#include "lldb/Target/Statistics.h"
1519
#include "lldb/Utility/LLDBLog.h"
1620
#include "lldb/Utility/UUID.h"
21+
#include "lldb/Version/Version.h"
1722
#include "lldb/lldb-enumerations.h"
1823
#include "lldb/lldb-forward.h"
1924
#include "llvm/ADT/StringRef.h"
2025
#include "llvm/Support/Error.h"
26+
#include "llvm/Support/Path.h"
2127
#include "llvm/Support/RandomNumberGenerator.h"
2228
#include "llvm/Telemetry/Telemetry.h"
2329
#include <chrono>
@@ -35,15 +41,7 @@ static uint64_t ToNanosec(const SteadyTimePoint Point) {
3541
return std::chrono::nanoseconds(Point.time_since_epoch()).count();
3642
}
3743

38-
void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
39-
serializer.write("entry_kind", getKind());
40-
serializer.write("session_id", SessionId);
41-
serializer.write("start_time", ToNanosec(start_time));
42-
if (end_time.has_value())
43-
serializer.write("end_time", ToNanosec(end_time.value()));
44-
}
45-
46-
[[maybe_unused]] static std::string MakeUUID(Debugger *debugger) {
44+
static std::string MakeUUID(Debugger *debugger) {
4745
uint8_t random_bytes[16];
4846
if (auto ec = llvm::getRandomBytes(random_bytes, 16)) {
4947
LLDB_LOG(GetLog(LLDBLog::Object),
@@ -56,16 +54,107 @@ void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
5654
return UUID(random_bytes).GetAsString();
5755
}
5856

57+
void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
58+
serializer.write("entry_kind", getKind());
59+
serializer.write("session_id", SessionId);
60+
serializer.write("start_time", ToNanosec(start_time));
61+
if (end_time.has_value())
62+
serializer.write("end_time", ToNanosec(end_time.value()));
63+
}
64+
65+
void DebuggerTelemetryInfo::serialize(Serializer &serializer) const {
66+
LLDBBaseTelemetryInfo::serialize(serializer);
67+
68+
serializer.write("username", username);
69+
serializer.write("lldb_git_sha", lldb_git_sha);
70+
serializer.write("lldb_path", lldb_path);
71+
serializer.write("cwd", cwd);
72+
if (exit_desc.has_value()) {
73+
serializer.write("exit_code", exit_desc->exit_code);
74+
serializer.write("exit_desc", exit_desc->description);
75+
}
76+
}
77+
78+
void MiscTelemetryInfo::serialize(Serializer &serializer) const {
79+
LLDBBaseTelemetryInfo::serialize(serializer);
80+
serializer.write("target_uuid", target_uuid);
81+
serializer.beginObject("meta_data");
82+
for (const auto &kv : meta_data)
83+
serializer.write(kv.first, kv.second);
84+
serializer.endObject();
85+
}
86+
5987
TelemetryManager::TelemetryManager(std::unique_ptr<Config> config)
6088
: m_config(std::move(config)) {}
6189

6290
llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {
63-
// Do nothing for now.
64-
// In up-coming patch, this would be where the manager
65-
// attach the session_uuid to the entry.
91+
LLDBBaseTelemetryInfo *lldb_entry =
92+
llvm::dyn_cast<LLDBBaseTelemetryInfo>(entry);
93+
std::string session_id = "";
94+
if (Debugger *debugger = lldb_entry->debugger) {
95+
auto session_id_pos = session_ids.find(debugger);
96+
if (session_id_pos != session_ids.end())
97+
session_id = session_id_pos->second;
98+
else
99+
session_id_pos->second = session_id = MakeUUID(debugger);
100+
}
101+
lldb_entry->SessionId = session_id;
102+
66103
return llvm::Error::success();
67104
}
68105

106+
const Config *getConfig() { return m_config.get(); }
107+
108+
void TelemetryManager::atDebuggerStartup(DebuggerTelemetryInfo *entry) {
109+
UserIDResolver &resolver = lldb_private::HostInfo::GetUserIDResolver();
110+
std::optional<llvm::StringRef> opt_username =
111+
resolver.GetUserName(lldb_private::HostInfo::GetUserID());
112+
if (opt_username)
113+
entry->username = *opt_username;
114+
115+
entry->lldb_git_sha =
116+
lldb_private::GetVersion(); // TODO: find the real git sha?
117+
118+
entry->lldb_path = HostInfo::GetProgramFileSpec().GetPath();
119+
120+
llvm::SmallString<64> cwd;
121+
if (!llvm::sys::fs::current_path(cwd)) {
122+
entry->cwd = cwd.c_str();
123+
} else {
124+
MiscTelemetryInfo misc_info;
125+
misc_info.meta_data["internal_errors"] = "Cannot determine CWD";
126+
if (auto er = dispatch(&misc_info)) {
127+
LLDB_LOG(GetLog(LLDBLog::Object),
128+
"Failed to dispatch misc-info at startup");
129+
}
130+
}
131+
132+
if (auto er = dispatch(entry)) {
133+
LLDB_LOG(GetLog(LLDBLog::Object), "Failed to dispatch entry at startup");
134+
}
135+
}
136+
137+
void TelemetryManager::atDebuggerExit(DebuggerTelemetryInfo *entry) {
138+
// There must be a reference to the debugger at this point.
139+
assert(entry->debugger != nullptr);
140+
141+
if (auto *selected_target =
142+
entry->debugger->GetSelectedExecutionContext().GetTargetPtr()) {
143+
if (!selected_target->IsDummyTarget()) {
144+
const lldb::ProcessSP proc = selected_target->GetProcessSP();
145+
if (proc == nullptr) {
146+
// no process has been launched yet.
147+
entry->exit_desc = {-1, "no process launched."};
148+
} else {
149+
entry->exit_desc = {proc->GetExitStatus(), ""};
150+
if (const char *description = proc->GetExitDescription())
151+
entry->exit_desc->description = std::string(description);
152+
}
153+
}
154+
}
155+
dispatch(entry);
156+
}
157+
69158
std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;
70159
TelemetryManager *TelemetryManager::getInstance() { return g_instance.get(); }
71160

0 commit comments

Comments
 (0)