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 <pa...@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 { _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits