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 28 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
8 changes: 8 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 @@ -250,6 +251,13 @@ class LLDB_API SBDebugger {

lldb::SBTarget GetDummyTarget();

#ifndef SWIG
// Dispatch telemery from client to server if client-telemetry is enabled
// (by vendor), otherwise the data is ignored.
// Invoking this from python client (with SWIG) is not supported.
void DispatchClientTelemetry(const lldb::SBStructuredData &data);
#endif

// 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 @@ -20,6 +20,8 @@
#include "lldb/Core/IOHandler.h"
#include "lldb/Core/SourceManager.h"
#include "lldb/Core/Statusline.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 @@ -32,6 +34,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 @@ -124,6 +127,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
23 changes: 21 additions & 2 deletions lldb/include/lldb/Core/Telemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,16 @@ struct LLDBConfig : public ::llvm::telemetry::Config {
// the vendor while creating the Manager.
const bool detailed_command_telemetry;

explicit LLDBConfig(bool enable_telemetry, bool detailed_command_telemetry)
// 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 enable_client_telemetry;

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

// We expect each (direct) subclass of LLDBTelemetryInfo to
Expand All @@ -56,6 +63,7 @@ struct LLDBConfig : public ::llvm::telemetry::Config {
struct LLDBEntryKind : public ::llvm::telemetry::EntryKind {
// clang-format off
static const llvm::telemetry::KindType BaseInfo = 0b11000000;
static const llvm::telemetry::KindType ClientInfo = 0b11100000;
static const llvm::telemetry::KindType CommandInfo = 0b11010000;
static const llvm::telemetry::KindType DebuggerInfo = 0b11001000;
static const llvm::telemetry::KindType ExecModuleInfo = 0b11000100;
Expand Down Expand Up @@ -89,6 +97,14 @@ struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo {
void serialize(llvm::telemetry::Serializer &serializer) const override;
};

struct ClientInfo : public LLDBBaseTelemetryInfo {
std::string client_name;
std::string client_data;
std::optional<std::string> error_msg;

void serialize(llvm::telemetry::Serializer &serializer) const override;
};

struct CommandInfo : public LLDBBaseTelemetryInfo {
/// If the command is/can be associated with a target entry this field
/// contains that target's UUID. <EMPTY> otherwise.
Expand Down Expand Up @@ -217,6 +233,9 @@ class TelemetryManager : public llvm::telemetry::Manager {

const LLDBConfig *GetConfig() { return m_config.get(); }

virtual void
DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry,
Debugger *debugger);
virtual llvm::StringRef GetInstanceName() const = 0;

static TelemetryManager *GetInstance();
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 @@ -926,6 +926,17 @@ SBTarget SBDebugger::GetDummyTarget() {
return sb_target;
}

void SBDebugger::DispatchClientTelemetry(const lldb::SBStructuredData &entry) {
LLDB_INSTRUMENT_VA(this);
if (m_opaque_sp) {
m_opaque_sp->DispatchClientTelemetry(*entry.m_impl_up);
} 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 @@ -841,6 +841,12 @@ DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
return debugger_sp;
}

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

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
73 changes: 72 additions & 1 deletion lldb/source/Core/Telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
serializer.write("end_time", ToNanosec(end_time.value()));
}

void ClientInfo::serialize(Serializer &serializer) const {
LLDBBaseTelemetryInfo::serialize(serializer);
serializer.write("client_data", client_data);
serializer.write("client_name", client_name);
if (error_msg.has_value())
serializer.write("error_msg", error_msg.value());
}

void CommandInfo::serialize(Serializer &serializer) const {
LLDBBaseTelemetryInfo::serialize(serializer);

Expand Down Expand Up @@ -112,6 +120,63 @@ llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {
return llvm::Error::success();
}

void TelemetryManager::DispatchClientTelemetry(
const lldb_private::StructuredDataImpl &entry, Debugger *debugger) {
if (!m_config->enable_client_telemetry)
return;

ClientInfo client_info;
client_info.debugger = debugger;
if (entry.GetObjectSP()->GetType() != lldb::eStructuredDataTypeDictionary) {
LLDB_LOG(GetLog(LLDBLog::Object), "Expected Dictionary type but got {0}.",
entry.GetObjectSP()->GetType());
return;
}

auto *dict = entry.GetObjectSP()->GetAsDictionary();

llvm::StringRef client_name;
if (dict->GetValueForKeyAsString("client_name", client_name))
client_info.client_name = client_name.str();
else
LLDB_LOG(GetLog(LLDBLog::Object),
"Cannot determine client_name from client-telemetry entry");

llvm::StringRef client_data;
if (dict->GetValueForKeyAsString("client_data", client_data))
client_info.client_data = client_data.str();
else
LLDB_LOG(GetLog(LLDBLog::Object),
"Cannot determine client_data from client-telemetry entry");

int64_t start_time;
if (dict->GetValueForKeyAsInteger("start_time", start_time)) {
client_info.start_time +=
std::chrono::nanoseconds(static_cast<size_t>(start_time));
} else {
LLDB_LOG(GetLog(LLDBLog::Object),
"Cannot determine start-time from client-telemetry entry");
}

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

llvm::StringRef error_msg;
if (dict->GetValueForKeyAsString("error", error_msg))
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");
}

class NoOpTelemetryManager : public TelemetryManager {
public:
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override {
Expand All @@ -121,12 +186,18 @@ class NoOpTelemetryManager : public TelemetryManager {

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

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

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

llvm::Error dispatch(llvm::telemetry::TelemetryInfo *entry) override {
// Does nothing.
return llvm::Error::success();
Expand Down
10 changes: 10 additions & 0 deletions lldb/tools/lldb-dap/DAP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,8 @@ void DAP::SetTarget(const lldb::SBTarget target) {
}

bool DAP::HandleObject(const Message &M) {
TelemetryDispatcher dispatcher(&debugger);
dispatcher.Set("client_name", transport.GetClientName().str());
if (const auto *req = std::get_if<Request>(&M)) {
{
std::lock_guard<std::mutex> guard(m_active_request_mutex);
Expand All @@ -723,11 +725,15 @@ bool DAP::HandleObject(const Message &M) {
});

auto handler_pos = request_handlers.find(req->command);
dispatcher.Set("client_data",
llvm::Twine("request_command:", req->command).str());
if (handler_pos != request_handlers.end()) {
handler_pos->second->Run(*req);
return true; // Success
}

dispatcher.Set("error",
llvm::Twine("unhandled-command:" + req->command).str());
DAP_LOG(log, "({0}) error: unhandled command '{1}'",
transport.GetClientName(), req->command);
return false; // Fail
Expand All @@ -751,6 +757,8 @@ bool DAP::HandleObject(const Message &M) {
// Result should be given, use null if not.
if (resp->success) {
(*response_handler)(resp->body);
dispatcher.Set("client_data",
llvm::Twine("response_command:", resp->command).str());
} else {
llvm::StringRef message = "Unknown error, response failed";
if (resp->message) {
Expand All @@ -771,6 +779,7 @@ bool DAP::HandleObject(const Message &M) {
}),
*resp->message);
}
dispatcher.Set("error", message.str());

(*response_handler)(llvm::createStringError(
std::error_code(-1, std::generic_category()), message));
Expand All @@ -779,6 +788,7 @@ bool DAP::HandleObject(const Message &M) {
return true;
}

dispatcher.Set("error", "Unsupported protocol message");
DAP_LOG(log, "Unsupported protocol message");

return false;
Expand Down
48 changes: 48 additions & 0 deletions lldb/tools/lldb-dap/LLDBUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include <chrono>
#include <string>

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

/// Helper for sending telemetry to lldb server, if client-telemetry is enabled.
#ifndef SWIG
class TelemetryDispatcher {
public:
TelemetryDispatcher(lldb::SBDebugger *debugger) {
m_telemetry_json = llvm::json::Object();
m_telemetry_json.try_emplace(
"start_time",
std::chrono::steady_clock::now().time_since_epoch().count());
this->debugger = debugger;
}

void Set(std::string key, std::string value) {
m_telemetry_json.try_emplace(key, value);
}

void Set(std::string key, int64_t value) {
m_telemetry_json.try_emplace(key, value);
}

~TelemetryDispatcher() {
m_telemetry_json.try_emplace(
"end_time",
std::chrono::steady_clock::now().time_since_epoch().count());

lldb::SBStructuredData telemetry_entry;
llvm::json::Value val(std::move(m_telemetry_json));

std::string string_rep = llvm::to_string(val);
telemetry_entry.SetFromJSON(string_rep.c_str());
debugger->DispatchClientTelemetry(telemetry_entry);
}

private:
llvm::json::Object m_telemetry_json;
lldb::SBDebugger *debugger;
};
#else
// Dummy class that does nothing.
class TelemetryDispatcher {
public:
TelemetryDispatcher(lldb::SBDebugger * /*debugger*/) {}
void Set(std::string /*key*/, std::string /*value*/) {}
void Set(std::string /*key*/, int64_t /*value*/) {}
};
#endif
/// Take ownership of the stored error.
llvm::Error ToError(const lldb::SBError &error);

Expand Down
3 changes: 2 additions & 1 deletion lldb/unittests/Core/TelemetryTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ class FakePlugin : public telemetry::TelemetryManager {
public:
FakePlugin()
: telemetry::TelemetryManager(std::make_unique<telemetry::LLDBConfig>(
/*enable_telemetry=*/true, /*detailed_command_telemetry=*/true)) {}
/*enable_telemetry=*/true, /*detailed_command_telemetry=*/true,
/*enable_client_telemetry=*/true)) {}

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