[Lldb-commits] [lldb] a74e4ac - [LLDB][Telemetry] Collect telemetry from client when allowed. (#129728)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Apr 25 17:19:33 PDT 2025
Author: Vy Nguyen
Date: 2025-04-26T02:19:29+02:00
New Revision: a74e4ac9d52029f9c9fe80654acd9fdd34d2a4cd
URL: https://github.com/llvm/llvm-project/commit/a74e4ac9d52029f9c9fe80654acd9fdd34d2a4cd
DIFF: https://github.com/llvm/llvm-project/commit/a74e4ac9d52029f9c9fe80654acd9fdd34d2a4cd.diff
LOG: [LLDB][Telemetry] Collect telemetry from client when allowed. (#129728)
This patch is slightly different from other impl in that we dispatch
client-telemetry via a different helper method. This is to make it
easier for vendor to opt-out (simply by overriding the method to do
nothing). There is also a configuration option to disallow collecting
client telemetry.
---------
Co-authored-by: Pavel Labath <pavel at labath.sk>
Added:
Modified:
lldb/include/lldb/API/SBDebugger.h
lldb/include/lldb/Core/Debugger.h
lldb/include/lldb/Core/Telemetry.h
lldb/source/API/SBDebugger.cpp
lldb/source/Core/Debugger.cpp
lldb/source/Core/Telemetry.cpp
lldb/tools/lldb-dap/DAP.cpp
lldb/tools/lldb-dap/LLDBUtils.h
lldb/unittests/Core/TelemetryTest.cpp
Removed:
################################################################################
diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h
index 3ece2a2e3a9f2..192fbee9c0c6d 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -13,6 +13,7 @@
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBPlatform.h"
+#include "lldb/API/SBStructuredData.h"
namespace lldb_private {
class CommandPluginInterfaceImplementation;
@@ -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);
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 0595125b1813d..c9e5310cded1a 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -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"
@@ -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"
@@ -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);
diff --git a/lldb/include/lldb/Core/Telemetry.h b/lldb/include/lldb/Core/Telemetry.h
index fa01e2e4af90f..7889cda40e75f 100644
--- a/lldb/include/lldb/Core/Telemetry.h
+++ b/lldb/include/lldb/Core/Telemetry.h
@@ -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
@@ -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;
@@ -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.
@@ -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();
diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index 2cfcdc78c8111..603e306497841 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -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);
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 1a0723a2f3b3f..25bb42bad152c 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -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
diff --git a/lldb/source/Core/Telemetry.cpp b/lldb/source/Core/Telemetry.cpp
index 8db29889e0846..a819d5366cedc 100644
--- a/lldb/source/Core/Telemetry.cpp
+++ b/lldb/source/Core/Telemetry.cpp
@@ -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);
@@ -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 {
@@ -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();
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index e76430b75e80e..55d49667b6398 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -700,6 +700,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);
@@ -716,11 +718,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
@@ -744,6 +750,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) {
@@ -764,6 +772,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));
@@ -772,6 +781,7 @@ bool DAP::HandleObject(const Message &M) {
return true;
}
+ dispatcher.Set("error", "Unsupported protocol message");
DAP_LOG(log, "Unsupported protocol message");
return false;
diff --git a/lldb/tools/lldb-dap/LLDBUtils.h b/lldb/tools/lldb-dap/LLDBUtils.h
index 7ce242ec887ae..610ebce83566c 100644
--- a/lldb/tools/lldb-dap/LLDBUtils.h
+++ b/lldb/tools/lldb-dap/LLDBUtils.h
@@ -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 {
@@ -159,6 +161,43 @@ 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.
+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;
+};
+
/// Get the stop-disassembly-display settings
///
/// \param[in] debugger
@@ -168,6 +207,7 @@ GetEnvironmentFromArguments(const llvm::json::Object &arguments);
/// The value of the stop-disassembly-display setting
lldb::StopDisassemblyType GetStopDisassemblyDisplay(lldb::SBDebugger &debugger);
+
/// Take ownership of the stored error.
llvm::Error ToError(const lldb::SBError &error);
diff --git a/lldb/unittests/Core/TelemetryTest.cpp b/lldb/unittests/Core/TelemetryTest.cpp
index 910149d865c13..8d69b9a6cdc76 100644
--- a/lldb/unittests/Core/TelemetryTest.cpp
+++ b/lldb/unittests/Core/TelemetryTest.cpp
@@ -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 {
More information about the lldb-commits
mailing list