Skip to content

[LLDB][Telemetry] Collect telemetry from client when allowed. #129728

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
Apr 26, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
21103ad
[LLDB][Telemetry] Collect telemetry from client when allowed.
oontvoo Mar 4, 2025
c0cf1c0
formatting
oontvoo Mar 4, 2025
4b13494
Merge branch 'main' into client
oontvoo Mar 18, 2025
11fd330
reconcile with new api change
oontvoo Mar 19, 2025
1aff2b1
Merge branch 'main' into client
oontvoo Mar 24, 2025
4718da4
formatting
oontvoo Mar 24, 2025
8b15611
Update SBDebugger.h
oontvoo Mar 24, 2025
a5a8ac7
Update lldb/source/API/SBDebugger.cpp
oontvoo Mar 25, 2025
4990441
Update lldb/source/Core/Telemetry.cpp
oontvoo Mar 25, 2025
63b80b2
Update lldb/source/Core/Telemetry.cpp
oontvoo Mar 25, 2025
56a2018
Update lldb/unittests/Core/TelemetryTest.cpp
oontvoo Mar 25, 2025
a31d160
address review comments
oontvoo Mar 25, 2025
60eb43a
rename
oontvoo Mar 25, 2025
f6f3852
rename again
oontvoo Mar 25, 2025
2d2d63d
update Telemetry
oontvoo Mar 25, 2025
eb4b0f1
update
oontvoo Mar 25, 2025
b875599
add client_name field
oontvoo Mar 26, 2025
c35e38e
disable client telemetry for SWIG
oontvoo Mar 26, 2025
31a0ed2
formatting
oontvoo Mar 26, 2025
ef5257e
Update SBDebugger.h
oontvoo Mar 26, 2025
fa90be0
Merge branch 'main' into client
oontvoo Mar 27, 2025
60de76d
formatting
oontvoo Mar 27, 2025
654f8ca
use GetClientName
oontvoo Mar 27, 2025
1c56b80
define macro to disable client-telemetry
oontvoo Mar 28, 2025
4c161d8
typo
oontvoo Mar 28, 2025
b408b88
update
oontvoo Mar 28, 2025
ce679cd
Merge branch 'main' into client
oontvoo Apr 24, 2025
af0504c
put condition behind ifndef SWIG
oontvoo Apr 24, 2025
253291f
remove unnecessary ifdef branch
oontvoo Apr 25, 2025
0a5da4a
Merge branch 'main' into client
oontvoo Apr 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lldb/include/lldb/API/SBDebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "lldb/API/SBDefines.h"
#include "lldb/API/SBPlatform.h"
#include "lldb/API/SBStructuredData.h"

namespace lldb_private {
class CommandPluginInterfaceImplementation;
Expand Down Expand Up @@ -249,6 +250,8 @@ class LLDB_API SBDebugger {

lldb::SBTarget GetDummyTarget();

void DispatchClientTelemetry(const lldb::SBStructuredData &data);

// Return true if target is deleted from the target list of the debugger.
bool DeleteTarget(lldb::SBTarget &target);

Expand Down
5 changes: 5 additions & 0 deletions lldb/include/lldb/Core/Debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "lldb/Core/FormatEntity.h"
#include "lldb/Core/IOHandler.h"
#include "lldb/Core/SourceManager.h"
#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Core/Telemetry.h"
#include "lldb/Core/UserSettingsController.h"
#include "lldb/Host/HostThread.h"
#include "lldb/Host/StreamFile.h"
Expand All @@ -31,6 +33,7 @@
#include "lldb/Utility/Diagnostics.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/UserID.h"
#include "lldb/lldb-defines.h"
#include "lldb/lldb-enumerations.h"
Expand Down Expand Up @@ -127,6 +130,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,

void Clear();

void DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry);

bool GetAsyncExecution();

void SetAsyncExecution(bool async);
Expand Down
29 changes: 24 additions & 5 deletions lldb/include/lldb/Core/Telemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@
namespace lldb_private {
namespace telemetry {

struct LLDBConfig : public ::llvm::telemetry::Config {
// If true, we will collect telemetry from LLDB's clients (eg., lldb-dap) via
// the SB interface. Must also be enabled by the vendor while creating the
// manager.
const bool m_enable_client_telemetry;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to chime in but I'm just new to this project and I'm wonder why you effectively need a flag. If the client needs to send manually a telemetry event, isn't this redudant?


explicit LLDBConfig(bool enable_telemetry, bool enable_client_telemetry)
: ::llvm::telemetry::Config(enable_telemetry),
m_enable_client_telemetry(enable_client_telemetry) {}
};

// We expect each (direct) subclass of LLDBTelemetryInfo to
// have an LLDBEntryKind in the form 0b11xxxxxxxx
// Specifically:
Expand All @@ -37,6 +48,7 @@ namespace telemetry {
// must have their LLDBEntryKind in the similar form (ie., share common prefix)
struct LLDBEntryKind : public ::llvm::telemetry::EntryKind {
static const llvm::telemetry::KindType BaseInfo = 0b11000000;
static const llvm::telemetry::KindType ClientInfo = 0b11100000;
static const llvm::telemetry::KindType DebuggerInfo = 0b11000100;
};

Expand Down Expand Up @@ -66,6 +78,11 @@ struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo {
void serialize(llvm::telemetry::Serializer &serializer) const override;
};

struct ClientInfo : public LLDBBaseTelemetryInfo {
std::string request_name;
std::optional<std::string> error_msg;
};

struct DebuggerInfo : public LLDBBaseTelemetryInfo {
std::string lldb_version;

Expand Down Expand Up @@ -93,21 +110,23 @@ class TelemetryManager : public llvm::telemetry::Manager {
public:
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override;

const llvm::telemetry::Config *GetConfig();
const LLDBConfig *GetConfig() { return m_config.get(); }

virtual void
DispatchClientTelemery(const lldb_private::StructuredDataImpl &entry,
Debugger *debugger);

virtual llvm::StringRef GetInstanceName() const = 0;

static TelemetryManager *GetInstance();

static TelemetryManager *GetInstanceIfEnabled();

protected:
TelemetryManager(std::unique_ptr<llvm::telemetry::Config> config);
TelemetryManager(std::unique_ptr<LLDBConfig> config);

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

private:
std::unique_ptr<llvm::telemetry::Config> m_config;
std::unique_ptr<LLDBConfig> m_config;
// Each instance of a TelemetryManager is assigned a unique ID.
const std::string m_id;

Expand Down
11 changes: 11 additions & 0 deletions lldb/source/API/SBDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,17 @@ SBTarget SBDebugger::GetDummyTarget() {
return sb_target;
}

void SBDebugger::DispatchClientTelemetry(const lldb::SBStructuredData &entry) {
LLDB_INSTRUMENT_VA(this);
if (lldb_private::Debugger *debugger = this->get()) {
debugger->DispatchClientTelemetry(*(entry.m_impl_up.get()));
} else {
Log *log = GetLog(LLDBLog::API);
LLDB_LOGF(log,
"Could not send telemetry from SBDebugger - debugger was null.");
}
}

bool SBDebugger::DeleteTarget(lldb::SBTarget &target) {
LLDB_INSTRUMENT_VA(this, target);

Expand Down
6 changes: 6 additions & 0 deletions lldb/source/Core/Debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,12 @@ DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
return debugger_sp;
}

void Debugger::DispatchClientTelemetry(
const lldb_private::StructuredDataImpl &entry) {
lldb_private::telemetry::TelemeryManager::GetInstance()
->DispatchClientTelemetry(entry);
}

void Debugger::HandleDestroyCallback() {
const lldb::user_id_t user_id = GetID();
// Invoke and remove all the callbacks in an FIFO order. Callbacks which are
Expand Down
99 changes: 86 additions & 13 deletions lldb/source/Core/Telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
if (end_time.has_value())
serializer.write("end_time", ToNanosec(end_time.value()));
}
void ClientInfo::serialize(Serializer &serializer) const {
LLDBBaseTelemetryInfo::serialize(serializer);
serializer.write("request_name", request_name);
if (error_msg.has_value())
serializer.write("error_msg", error_msg.value());
}

void DebuggerInfo::serialize(Serializer &serializer) const {
LLDBBaseTelemetryInfo::serialize(serializer);
Expand All @@ -67,7 +73,7 @@ void DebuggerInfo::serialize(Serializer &serializer) const {
serializer.write("is_exit_entry", is_exit_entry);
}

TelemetryManager::TelemetryManager(std::unique_ptr<Config> config)
TelemetryManager::TelemetryManager(std::unique_ptr<LLDBConfig> config)
: m_config(std::move(config)), m_id(MakeUUID()) {}

llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {
Expand All @@ -79,23 +85,90 @@ llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {
return llvm::Error::success();
}

const Config *TelemetryManager::GetConfig() { return m_config.get(); }
void TelemetryManager::DispatchClientTelemetry(
const lldb_private::StructuredDataImpl &entry, Debugger *debugger) {
if (!m_config->m_enable_client_telemetry)
return;

std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;
TelemetryManager *TelemetryManager::GetInstance() {
if (!Config::BuildTimeEnableTelemetry)
return nullptr;
return g_instance.get();
ClientInfo client_info;
client_info.debugger = debugger;

std::optional<llvm::StringRef> request_name = entry.getString("request_name");
if (!request_name.has_value())
LLDB_LOG(GetLog(LLDBLog::Object),
"Cannot determine request name from client-telemetry entry");
else
client_info.request_name = request_name->str();

std::optional<int64_t> start_time = entry.getInteger("start_time");
std::optional<int64_t> end_time = entry.getInteger("end_time");
SteadyTimePoint epoch;
if (!start_time.has_value()) {
LLDB_LOG(GetLog(LLDBLog::Object),
"Cannot determine start-time from client-telemetry entry");
client_info.start_time = 0;
} else {
client_info.start_time =
epoch + std::chrono::nanoseconds(static_cast<size_t>(*start_time));
}

if (!end_time.has_value()) {
LLDB_LOG(GetLog(LLDBLog::Object),
"Cannot determine end-time from client-telemetry entry");
} else {
client_info.end_time =
epoch + std::chrono::nanoseconds(static_cast<size_t>(*end_time));
}

std::optional<llvm::StringRef> error_msg = entry.getString("error");
if (error_msg.has_value())
client_info.error_msg = error_msg->str();

if (llvm::Error er = dispatch(&client_info))
LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(er),
"Failed to dispatch client telemetry");
}

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;
class NoOpTelemetryManager : public TelemetryManager {
public:
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override {
// Does nothing.
return llvm::Error::success();
}

explicit NoOpTelemetryManager()
: TelemetryManager(std::make_unique<LLDBConfig>(
/*EnableTelemetry*/ false, /*DetailedCommand*/ false)) {}

llvm::StringRef GetInstanceName() const override {
return "NoOpTelemetryManager";
}

llvm::Error dispatch(llvm::telemetry::TelemetryInfo *entry) override {
// Does nothing.
return llvm::Error::success();
}

return nullptr;
void DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry,
Debugger *debugger) override {
// Does nothing.
}

static NoOpTelemetryManager *GetInstance() {
static std::unique_ptr<NoOpTelemetryManager> g_ins =
std::make_unique<NoOpTelemetryManager>();
return g_ins.get();
}
};

std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;
TelemetryManager *TelemetryManager::GetInstance() {
// If Telemetry is disabled or if there is no default instance, then use the
// NoOp manager. We use a dummy instance to avoid having to do nullchecks in
// various places.
if (!Config::BuildTimeEnableTelemetry || !g_instance)
return NoOpTelemetryManager::GetInstance();
return g_instance.get();
}

void TelemetryManager::SetInstance(std::unique_ptr<TelemetryManager> manager) {
Expand Down
5 changes: 4 additions & 1 deletion lldb/tools/lldb-dap/DAP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -754,16 +754,19 @@ PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
}

bool DAP::HandleObject(const llvm::json::Object &object) {
TelemetryDispatcher dispatcher;

const auto packet_type = GetString(object, "type");
if (packet_type == "request") {
const auto command = GetString(object, "command");

dispatcher.set("request_name", command);
auto new_handler_pos = request_handlers.find(command);
if (new_handler_pos != request_handlers.end()) {
(*new_handler_pos->second)(object);
return true; // Success
}

dispatcher.set("error", llvm::Twine("unhandled-command:" + command).str());
if (log)
*log << "error: unhandled command \"" << command.data() << "\""
<< std::endl;
Expand Down
34 changes: 34 additions & 0 deletions lldb/tools/lldb-dap/LLDBUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
#include <chrono>
#include <string>

namespace lldb_dap {
Expand Down Expand Up @@ -154,6 +155,39 @@ uint32_t GetLLDBFrameID(uint64_t dap_frame_id);
lldb::SBEnvironment
GetEnvironmentFromArguments(const llvm::json::Object &arguments);

class TelemetryDispatcher {
public:
TelemetryDispatcher(SBDebugger *debugger) {
m_telemetry_array =
({"start_time",
std::chrono::steady_clock::now().time_since_epoch().count()});
this->debugger = debugger;
}

void Set(std::string key, std::string value) {
m_telemetry_array.push_back(llvm::json::Value{key, value})
}

void Set(std::string key, int64_t value) {
m_telemetry_array.push_back(llvm::json::Value{key, value})
}

~TelemetryDispatcher() {
m_telemetry_array.push_back(
{"end_time",
std::chrono::steady_clock::now().time_since_epoch().count()});
lldb::SBStructuredData telemetry_entry;
llvm::json::Value val(std::move(telemetry_array));
std::string string_rep = lldb_dap::JSONToString(val);
telemetry_entry.SetFromJSON(string_rep.c_str());
debugger->DispatchClientTelemetry(telemetry_entry);
}

private:
llvm::json::Array m_telemetry_array;
SBDebugger *debugger;
};

} // namespace lldb_dap

#endif
2 changes: 1 addition & 1 deletion lldb/unittests/Core/TelemetryTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class FakePlugin : public telemetry::TelemetryManager {
public:
FakePlugin()
: telemetry::TelemetryManager(
std::make_unique<llvm::telemetry::Config>(true)) {}
std::make_unique<telemetry::LLDBConfig>(true, true)) {}

// TelemetryManager interface
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override {
Expand Down
Loading