https://github.com/oontvoo updated https://github.com/llvm/llvm-project/pull/119716
>From 44fa888e6fce3788342011e445b97b293cba1c5e Mon Sep 17 00:00:00 2001 From: Vy Nguyen <v...@google.com> Date: Thu, 12 Dec 2024 10:47:09 -0500 Subject: [PATCH 1/4] [lldb][telemetry] Implement LLDB Telemetry (part 1) Details: - This is a subset of PR/98528. - This contains only the concrete implementation of the framework to be used but no usages yet. - I plan to send two follow-up patches: + part2 : includes changes in the plugin-manager to set up the plugin stuff. + part3 : includes changes in LLDB/LLDB-DAP to use the framework Note: Please ignore all changes under llvm/.... These will be reverted after the pending LLVM patch is submitted. --- lldb/include/lldb/Core/Telemetry.h | 309 ++++++++++++++++++++++ lldb/include/lldb/lldb-enumerations.h | 4 +- lldb/source/Core/CMakeLists.txt | 2 + lldb/source/Core/Telemetry.cpp | 338 ++++++++++++++++++++++++ lldb/test/CMakeLists.txt | 20 +- llvm/include/llvm/Telemetry/Telemetry.h | 133 ++++++++++ llvm/lib/CMakeLists.txt | 1 + llvm/lib/Telemetry/CMakeLists.txt | 6 + llvm/lib/Telemetry/Telemetry.cpp | 11 + 9 files changed, 812 insertions(+), 12 deletions(-) create mode 100644 lldb/include/lldb/Core/Telemetry.h create mode 100644 lldb/source/Core/Telemetry.cpp create mode 100644 llvm/include/llvm/Telemetry/Telemetry.h create mode 100644 llvm/lib/Telemetry/CMakeLists.txt create mode 100644 llvm/lib/Telemetry/Telemetry.cpp diff --git a/lldb/include/lldb/Core/Telemetry.h b/lldb/include/lldb/Core/Telemetry.h new file mode 100644 index 00000000000000..241d957672b6ca --- /dev/null +++ b/lldb/include/lldb/Core/Telemetry.h @@ -0,0 +1,309 @@ +//===-- Telemetry.h ----------------------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_CORE_TELEMETRY_H +#define LLDB_CORE_TELEMETRY_H + +#include <atomic> +#include <chrono> +#include <ctime> +#include <memory> +#include <optional> +#include <string> +#include <unordered_map> + +#include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-forward.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" +#include "llvm/Telemetry/Telemetry.h" + +namespace lldb_private { + +using llvm::telemetry::Destination; +using llvm::telemetry::KindType; +using llvm::telemetry::Serializer; +using llvm::telemetry::TelemetryInfo; + +struct LldbEntryKind : public ::llvm::telemetry::EntryKind { + static const KindType BaseInfo = 0b11000; + static const KindType DebuggerInfo = 0b11001; + static const KindType TargetInfo = 0b11010; + static const KindType ClientInfo = 0b11100; + static const KindType CommandInfo = 0b11101; + static const KindType MiscInfo = 0b11110; +}; + +/// Defines a convenient type for timestamp of various events. +/// This is used by the EventStats below. +using SteadyTimePoint = std::chrono::time_point<std::chrono::steady_clock>; + +/// Various time (and possibly memory) statistics of an event. +struct EventStats { + // REQUIRED: Start time of an event + SteadyTimePoint start; + // OPTIONAL: End time of an event - may be empty if not meaningful. + std::optional<SteadyTimePoint> end; + // TBD: could add some memory stats here too? + + EventStats() = default; + EventStats(SteadyTimePoint start) : start(start) {} + EventStats(SteadyTimePoint start, SteadyTimePoint end) + : start(start), end(end) {} +}; + +/// Describes the exit signal of an event. +struct ExitDescription { + int exit_code; + std::string description; +}; + +struct LldbBaseTelemetryInfo : public TelemetryInfo { + EventStats stats; + + // For dyn_cast, isa, etc operations. + KindType getKind() const override { return LldbEntryKind::BaseInfo; } + + static bool classof(const TelemetryInfo *t) { + if (t == nullptr) + return false; + // Subclasses of this is also acceptable. + return (t->getKind() & LldbEntryKind::BaseInfo) == LldbEntryKind::BaseInfo; + } + + void serialize(Serializer &serializer) const override; +}; + +struct DebuggerTelemetryInfo : public LldbBaseTelemetryInfo { + std::string username; + std::string lldb_git_sha; + std::string lldb_path; + std::string cwd; + + std::optional<ExitDescription> exit_desc; + DebuggerTelemetryInfo() = default; + + // Provide a copy ctor because we may need to make a copy before + // sanitizing the data. + // (The sanitization might differ between different Destination classes). + DebuggerTelemetryInfo(const DebuggerTelemetryInfo &other) { + username = other.username; + lldb_git_sha = other.lldb_git_sha; + lldb_path = other.lldb_path; + cwd = other.cwd; + }; + + KindType getKind() const override { return LldbEntryKind::DebuggerInfo; } + + static bool classof(const TelemetryInfo *T) { + if (T == nullptr) + return false; + return T->getKind() == LldbEntryKind::DebuggerInfo; + } + + void serialize(Serializer &serializer) const override; +}; + +struct TargetTelemetryInfo : public LldbBaseTelemetryInfo { + lldb::ModuleSP exec_mod; + Target *target_ptr; + + // The same as the executable-module's UUID. + std::string target_uuid; + std::string file_format; + + std::string binary_path; + size_t binary_size; + + std::optional<ExitDescription> exit_desc; + TargetTelemetryInfo() = default; + + TargetTelemetryInfo(const TargetTelemetryInfo &other) { + exec_mod = other.exec_mod; + target_uuid = other.target_uuid; + file_format = other.file_format; + binary_path = other.binary_path; + binary_size = other.binary_size; + exit_desc = other.exit_desc; + } + + KindType getKind() const override { return LldbEntryKind::TargetInfo; } + + static bool classof(const TelemetryInfo *T) { + if (T == nullptr) + return false; + return T->getKind() == LldbEntryKind::TargetInfo; + } + + void serialize(Serializer &serializer) const override; +}; + +// Entry from client (eg., SB-API) +struct ClientTelemetryInfo : public LldbBaseTelemetryInfo { + std::string request_name; + std::string error_msg; + + ClientTelemetryInfo() = default; + + ClientTelemetryInfo(const ClientTelemetryInfo &other) { + request_name = other.request_name; + error_msg = other.error_msg; + } + + KindType getKind() const override { return LldbEntryKind::ClientInfo; } + + static bool classof(const TelemetryInfo *T) { + if (T == nullptr) + return false; + return T->getKind() == LldbEntryKind::ClientInfo; + } + + void serialize(Serializer &serializer) const override; +}; + +struct CommandTelemetryInfo : public LldbBaseTelemetryInfo { + Target *target_ptr; + CommandReturnObject *result; + + // If the command is/can be associated with a target entry, + // this field contains that target's UUID. + // <EMPTY> otherwise. + std::string target_uuid; + std::string command_uuid; + + // Eg., "breakpoint set" + std::string command_name; + + // !!NOTE!!: The following fields may be omitted due to PII risk. + // (Configurable via the telemery::Config struct) + std::string original_command; + std::string args; + + std::optional<ExitDescription> exit_desc; + lldb::ReturnStatus ret_status; + + CommandTelemetryInfo() = default; + + CommandTelemetryInfo(const CommandTelemetryInfo &other) { + target_uuid = other.target_uuid; + command_uuid = other.command_uuid; + command_name = other.command_name; + original_command = other.original_command; + args = other.args; + exit_desc = other.exit_desc; + ret_status = other.ret_status; + } + + KindType getKind() const override { return LldbEntryKind::CommandInfo; } + + static bool classof(const TelemetryInfo *T) { + if (T == nullptr) + return false; + return T->getKind() == LldbEntryKind::CommandInfo; + } + + void serialize(Serializer &serializer) const override; +}; + +/// The "catch-all" entry to store a set of custom/non-standard +/// data. +struct MiscTelemetryInfo : public LldbBaseTelemetryInfo { + /// If the event is/can be associated with a target entry, + /// this field contains that target's UUID. + /// <EMPTY> otherwise. + std::string target_uuid; + + /// Set of key-value pairs for any optional (or impl-specific) data + std::map<std::string, std::string> meta_data; + + MiscTelemetryInfo() = default; + + MiscTelemetryInfo(const MiscTelemetryInfo &other) { + target_uuid = other.target_uuid; + meta_data = other.meta_data; + } + + KindType getKind() const override { return LldbEntryKind::MiscInfo; } + + static bool classof(const TelemetryInfo *T) { + if (T == nullptr) + return false; + return T->getKind() == LldbEntryKind::MiscInfo; + } + + void serialize(Serializer &serializer) const override; +}; + +/// The base Telemetry manager instance in LLDB +/// This class declares additional instrumentation points +/// applicable to LLDB. +class TelemetryManager : public llvm::telemetry::Manager { +public: + /// Creates an instance of TelemetryManager. + /// This uses the plugin registry to find an instance: + /// - If a vendor supplies a implementation, it will use it. + /// - If not, it will either return a no-op instance or a basic + /// implementation for testing. + /// + /// See also lldb_private::TelemetryVendor. + static std::unique_ptr<TelemetryManager> + CreateInstance(std::unique_ptr<llvm::telemetry::Config> config, + Debugger *debugger); + + /// To be invoked upon LLDB startup. + virtual void LogStartup(DebuggerTelemetryInfo *entry); + + /// To be invoked upon LLDB exit. + virtual void LogExit(DebuggerTelemetryInfo *entry); + + /// To be invoked upon loading the main executable module. + /// We log in a fire-n-forget fashion so that if the load + /// crashes, we don't lose the entry. + virtual void LogMainExecutableLoadStart(TargetTelemetryInfo *entry); + virtual void LogMainExecutableLoadEnd(TargetTelemetryInfo *entry); + + /// To be invoked upon process exit. + virtual void LogProcessExit(TargetTelemetryInfo *entry); + + /// Invoked for each command + /// We log in a fire-n-forget fashion so that if the command execution + /// crashes, we don't lose the entry. + virtual void LogCommandStart(CommandTelemetryInfo *entry); + virtual void LogCommandEnd(CommandTelemetryInfo *entry); + + /// For client (eg., SB API) to send telemetry entries. + virtual void + LogClientTelemetry(const lldb_private::StructuredDataImpl &entry); + + virtual std::string GetNextUUID() { + return std::to_string(uuid_seed.fetch_add(1)); + } + + llvm::Error dispatch(TelemetryInfo *entry) override; + void addDestination(std::unique_ptr<Destination> destination) override; + +protected: + TelemetryManager(std::unique_ptr<llvm::telemetry::Config> config, + Debugger *debugger); + TelemetryManager() = default; + virtual void CollectMiscBuildInfo(); + +private: + std::atomic<size_t> uuid_seed = 0; + std::unique_ptr<llvm::telemetry::Config> m_config; + Debugger *m_debugger; + const std::string m_session_uuid; + std::vector<std::unique_ptr<Destination>> m_destinations; +}; + +} // namespace lldb_private +#endif // LLDB_CORE_TELEMETRY_H diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 938f6e3abe8f2a..8015f42c5ffc8c 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -257,8 +257,8 @@ enum StopReason { }; /// Command Return Status Types. -enum ReturnStatus { - eReturnStatusInvalid, +enum ReturnStatus : int { + eReturnStatusInvalid = 0, eReturnStatusSuccessFinishNoResult, eReturnStatusSuccessFinishResult, eReturnStatusSuccessContinuingNoResult, diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt index dbc620b91b1ed1..4a02f7f1fc85e5 100644 --- a/lldb/source/Core/CMakeLists.txt +++ b/lldb/source/Core/CMakeLists.txt @@ -51,6 +51,7 @@ add_lldb_library(lldbCore Section.cpp SourceLocationSpec.cpp SourceManager.cpp + Telemetry.cpp StreamAsynchronousIO.cpp ThreadedCommunication.cpp UserSettingsController.cpp @@ -94,6 +95,7 @@ add_lldb_library(lldbCore Support Demangle TargetParser + Telemetry ) add_dependencies(lldbCore diff --git a/lldb/source/Core/Telemetry.cpp b/lldb/source/Core/Telemetry.cpp new file mode 100644 index 00000000000000..5ddad030ef962e --- /dev/null +++ b/lldb/source/Core/Telemetry.cpp @@ -0,0 +1,338 @@ + +//===-- Telemetry.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "lldb/Core/Telemetry.h" + +#include <chrono> +#include <cstdlib> +#include <ctime> +#include <fstream> +#include <memory> +#include <string> +#include <typeinfo> +#include <utility> +#include <vector> + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBProcess.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Statistics.h" +#include "lldb/Utility/ConstString.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/UUID.h" +#include "lldb/Version/Version.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Telemetry/Telemetry.h" + +namespace lldb_private { + +using ::llvm::Error; +using ::llvm::telemetry::Destination; +using ::llvm::telemetry::TelemetryInfo; + +static size_t ToNanosecOrZero(const std::optional<SteadyTimePoint> &Point) { + if (!Point.has_value()) + return 0; + + return Point.value().time_since_epoch().count(); +} + +void LldbBaseTelemetryInfo::serialize(Serializer &serializer) const { + serializer.writeInt32("EntryKind", getKind()); + serializer.writeString("SessionId", SessionId); +} + +void DebuggerTelemetryInfo::serialize(Serializer &serializer) const { + LldbBaseTelemetryInfo::serialize(serializer); + serializer.writeString("username", username); + serializer.writeString("lldb_path", lldb_path); + serializer.writeString("cwd", cwd); + serializer.writeSizeT("start", stats.start.time_since_epoch().count()); + serializer.writeSizeT("end", ToNanosecOrZero(stats.end)); +} + +void ClientTelemetryInfo::serialize(Serializer &serializer) const { + LldbBaseTelemetryInfo::serialize(serializer); + serializer.writeString("request_name", request_name); + serializer.writeString("error_msg", error_msg); + serializer.writeSizeT("start", stats.start.time_since_epoch().count()); + serializer.writeSizeT("end", ToNanosecOrZero(stats.end)); +} + +void TargetTelemetryInfo::serialize(Serializer &serializer) const { + LldbBaseTelemetryInfo::serialize(serializer); + serializer.writeString("target_uuid", target_uuid); + serializer.writeString("binary_path", binary_path); + serializer.writeSizeT("binary_size", binary_size); +} + +void CommandTelemetryInfo::serialize(Serializer &serializer) const { + LldbBaseTelemetryInfo::serialize(serializer); + serializer.writeString("target_uuid", target_uuid); + serializer.writeString("command_uuid", command_uuid); + serializer.writeString("args", args); + serializer.writeString("original_command", original_command); + serializer.writeSizeT("start", stats.start.time_since_epoch().count()); + serializer.writeSizeT("end", ToNanosecOrZero(stats.end)); + + // If this entry was emitted at the end of the command-execution, + // then calculate the runtime too. + if (stats.end.has_value()) { + serializer.writeSizeT("command_runtime", + (stats.end.value() - stats.start).count()); + if (exit_desc.has_value()) { + serializer.writeInt32("exit_code", exit_desc->exit_code); + serializer.writeString("exit_msg", exit_desc->description); + serializer.writeInt32("return_status", static_cast<int>(ret_status)); + } + } +} + +void MiscTelemetryInfo::serialize(Serializer &serializer) const { + LldbBaseTelemetryInfo::serialize(serializer); + serializer.writeString("target_uuid", target_uuid); + serializer.writeKeyValueMap("meta_data", meta_data); +} + +static std::string MakeUUID(lldb_private::Debugger *debugger) { + std::string ret; + uint8_t random_bytes[16]; + if (auto ec = llvm::getRandomBytes(random_bytes, 16)) { + LLDB_LOG(GetLog(LLDBLog::Object), + "Failed to generate random bytes for UUID: {0}", ec.message()); + // fallback to using timestamp + debugger ID. + ret = std::to_string( + std::chrono::steady_clock::now().time_since_epoch().count()) + + "_" + std::to_string(debugger->GetID()); + } else { + ret = lldb_private::UUID(random_bytes).GetAsString(); + } + + return ret; +} + +TelemetryManager::TelemetryManager( + std::unique_ptr<llvm::telemetry::Config> config, + lldb_private::Debugger *debugger) + : m_config(std::move(config)), m_debugger(debugger), + m_session_uuid(MakeUUID(debugger)) {} + +std::unique_ptr<TelemetryManager> TelemetryManager::CreateInstance( + std::unique_ptr<llvm::telemetry::Config> config, + lldb_private::Debugger *debugger) { + + TelemetryManager *ins = new TelemetryManager(std::move(config), debugger); + + return std::unique_ptr<TelemetryManager>(ins); +} + +llvm::Error TelemetryManager::dispatch(TelemetryInfo *entry) { + entry->SessionId = m_session_uuid; + + for (auto &destination : m_destinations) { + llvm::Error err = destination->receiveEntry(entry); + if (err) { + return std::move(err); + } + } + return Error::success(); +} + +void TelemetryManager::addDestination( + std::unique_ptr<Destination> destination) { + m_destinations.push_back(std::move(destination)); +} + +void TelemetryManager::LogStartup(DebuggerTelemetryInfo *entry) { + UserIDResolver &resolver = lldb_private::HostInfo::GetUserIDResolver(); + std::optional<llvm::StringRef> opt_username = + resolver.GetUserName(lldb_private::HostInfo::GetUserID()); + if (opt_username) + entry->username = *opt_username; + + entry->lldb_git_sha = + lldb_private::GetVersion(); // TODO: find the real git sha? + + llvm::SmallString<64> cwd; + if (!llvm::sys::fs::current_path(cwd)) { + entry->cwd = cwd.c_str(); + } else { + MiscTelemetryInfo misc_info; + misc_info.meta_data["internal_errors"] = "Cannot determine CWD"; + if (auto er = dispatch(&misc_info)) { + LLDB_LOG(GetLog(LLDBLog::Object), + "Failed to dispatch misc-info from startup"); + } + } + + if (auto er = dispatch(entry)) { + LLDB_LOG(GetLog(LLDBLog::Object), "Failed to dispatch entry from startup"); + } + + // Optional part + CollectMiscBuildInfo(); +} + +void TelemetryManager::LogExit(DebuggerTelemetryInfo *entry) { + if (auto *selected_target = + m_debugger->GetSelectedExecutionContext().GetTargetPtr()) { + if (!selected_target->IsDummyTarget()) { + const lldb::ProcessSP proc = selected_target->GetProcessSP(); + if (proc == nullptr) { + // no process has been launched yet. + entry->exit_desc = {-1, "no process launched."}; + } else { + entry->exit_desc = {proc->GetExitStatus(), ""}; + if (const char *description = proc->GetExitDescription()) + entry->exit_desc->description = std::string(description); + } + } + } + dispatch(entry); +} + +void TelemetryManager::LogProcessExit(TargetTelemetryInfo *entry) { + entry->target_uuid = + entry->target_ptr && !entry->target_ptr->IsDummyTarget() + ? entry->target_ptr->GetExecutableModule()->GetUUID().GetAsString() + : ""; + + dispatch(entry); +} + +void TelemetryManager::CollectMiscBuildInfo() { + // collecting use-case specific data +} + +void TelemetryManager::LogMainExecutableLoadStart(TargetTelemetryInfo *entry) { + entry->binary_path = + entry->exec_mod->GetFileSpec().GetPathAsConstString().GetCString(); + entry->file_format = entry->exec_mod->GetArchitecture().GetArchitectureName(); + entry->target_uuid = entry->exec_mod->GetUUID().GetAsString(); + if (auto err = llvm::sys::fs::file_size( + entry->exec_mod->GetFileSpec().GetPath(), entry->binary_size)) { + // If there was error obtaining it, just reset the size to 0. + // Maybe log the error too? + entry->binary_size = 0; + } + dispatch(entry); +} + +void TelemetryManager::LogMainExecutableLoadEnd(TargetTelemetryInfo *entry) { + lldb::ModuleSP exec_mod = entry->exec_mod; + entry->binary_path = + exec_mod->GetFileSpec().GetPathAsConstString().GetCString(); + entry->file_format = exec_mod->GetArchitecture().GetArchitectureName(); + entry->target_uuid = exec_mod->GetUUID().GetAsString(); + entry->binary_size = exec_mod->GetObjectFile()->GetByteSize(); + + dispatch(entry); + + // Collect some more info, might be useful? + MiscTelemetryInfo misc_info; + misc_info.target_uuid = exec_mod->GetUUID().GetAsString(); + misc_info.meta_data["symtab_index_time"] = + std::to_string(exec_mod->GetSymtabIndexTime().get().count()); + misc_info.meta_data["symtab_parse_time"] = + std::to_string(exec_mod->GetSymtabParseTime().get().count()); + dispatch(&misc_info); +} + +void TelemetryManager::LogClientTelemetry( + const lldb_private::StructuredDataImpl &entry) { + // TODO: pull the dictionary out of entry + ClientTelemetryInfo client_info; + /* + std::optional<llvm::StringRef> request_name = entry.getString("request_name"); + if (!request_name.has_value()) { + MiscTelemetryInfo misc_info = MakeBaseEntry<MiscTelemetryInfo>(); + misc_info.meta_data["internal_errors"] = + "Cannot determine request name from client entry"; + // TODO: Dump the errornous entry to stderr too? + EmitToDestinations(&misc_info); + return; + } + 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"); + + if (!start_time.has_value() || !end_time.has_value()) { + MiscTelemetryInfo misc_info = MakeBaseEntry<MiscTelemetryInfo>(); + misc_info.meta_data["internal_errors"] = + "Cannot determine start/end time from client entry"; + EmitToDestinations(&misc_info); + return; + } + + SteadyTimePoint epoch; + client_info.Stats.Start = + epoch + std::chrono::nanoseconds(static_cast<size_t>(*start_time)); + client_info.Stats.End = + 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(); + */ + + dispatch(&client_info); +} + +void TelemetryManager::LogCommandStart(CommandTelemetryInfo *entry) { + // If we have a target attached to this command, then get the UUID. + if (entry->target_ptr && + entry->target_ptr->GetExecutableModule() != nullptr) { + entry->target_uuid = + entry->target_ptr->GetExecutableModule()->GetUUID().GetAsString(); + } else { + entry->target_uuid = ""; + } + + dispatch(entry); +} + +void TelemetryManager::LogCommandEnd(CommandTelemetryInfo *entry) { + // If we have a target attached to this command, then get the UUID. + if (entry->target_ptr && + entry->target_ptr->GetExecutableModule() != nullptr) { + entry->target_uuid = + entry->target_ptr->GetExecutableModule()->GetUUID().GetAsString(); + } else { + entry->target_uuid = ""; + } + + entry->exit_desc = {entry->result->Succeeded() ? 0 : -1, ""}; + if (llvm::StringRef error_data = entry->result->GetErrorData(); + !error_data.empty()) { + entry->exit_desc->description = error_data.str(); + } + entry->ret_status = entry->result->GetStatus(); + dispatch(entry); +} + +} // namespace lldb_private diff --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt index 6449ac5a9247f6..a5a342da7cfaa4 100644 --- a/lldb/test/CMakeLists.txt +++ b/lldb/test/CMakeLists.txt @@ -3,7 +3,7 @@ # Lit requires a Python3 interpreter, let's be careful and fail early if it's # not present. if (NOT DEFINED Python3_EXECUTABLE) - message(SEND_ERROR + message(FATAL_ERROR "LLDB test suite requires a Python3 interpreter but none " "was found. Please install Python3 or disable tests with " "`LLDB_INCLUDE_TESTS=OFF`.") @@ -12,7 +12,7 @@ endif() if(LLDB_ENFORCE_STRICT_TEST_REQUIREMENTS) message(STATUS "Enforcing strict test requirements for LLDB") # Lit uses psutil to do per-test timeouts. - set(useful_python_modules psutil packaging) + set(useful_python_modules psutil) if(NOT WIN32) # We no longer vendor pexpect and it is not used on Windows. @@ -22,7 +22,7 @@ if(LLDB_ENFORCE_STRICT_TEST_REQUIREMENTS) foreach(module ${useful_python_modules}) lldb_find_python_module(${module}) if (NOT PY_${module}_FOUND) - message(SEND_ERROR + message(FATAL_ERROR "Python module '${module}' not found. Please install it via pip or via " "your operating system's package manager. Alternatively, disable " "strict testing requirements with " @@ -66,10 +66,10 @@ if (LLDB_TEST_OBJC_GNUSTEP) find_package(GNUstepObjC) if (NOT GNUstepObjC_FOUND) if (LLDB_TEST_OBJC_GNUSTEP_DIR) - message(SEND_ERROR "Failed to find GNUstep libobjc2 in ${LLDB_TEST_OBJC_GNUSTEP_DIR}. " + message(FATAL_ERROR "Failed to find GNUstep libobjc2 in ${LLDB_TEST_OBJC_GNUSTEP_DIR}. " "Please check LLDB_TEST_OBJC_GNUSTEP_DIR or turn off LLDB_TEST_OBJC_GNUSTEP.") else() - message(SEND_ERROR "Failed to find GNUstep libobjc2. " + message(FATAL_ERROR "Failed to find GNUstep libobjc2. " "Please set LLDB_TEST_OBJC_GNUSTEP_DIR or turn off LLDB_TEST_OBJC_GNUSTEP.") endif() endif() @@ -108,6 +108,9 @@ endfunction(add_lldb_test_dependency) add_lldb_test_dependency(lldb) add_lldb_test_dependency(lldb-test) +# Enable Telemetry for testing. +target_compile_definitions(lldb PRIVATE -DTEST_TELEMETRY) + # On Darwin, darwin-debug is an hard dependency for the testsuites. if (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_lldb_test_dependency(darwin-debug) @@ -185,7 +188,7 @@ if(TARGET clang) set(LIBCXX_LIBRARY_DIR "${LLDB_TEST_LIBCXX_ROOT_DIR}/lib${LIBCXX_LIBDIR_SUFFIX}") set(LIBCXX_GENERATED_INCLUDE_DIR "${LLDB_TEST_LIBCXX_ROOT_DIR}/include/c++/v1") else() - message(SEND_ERROR + message(FATAL_ERROR "Couldn't find libcxx build in '${LLDB_TEST_LIBCXX_ROOT_DIR}'. To run the " "test-suite for a standalone LLDB build please build libcxx and point " "LLDB_TEST_LIBCXX_ROOT_DIR to it.") @@ -194,7 +197,7 @@ if(TARGET clang) # We require libcxx for the test suite, so if we aren't building it, # provide a helpful error about how to resolve the situation. if(NOT LLDB_HAS_LIBCXX) - message(SEND_ERROR + message(FATAL_ERROR "LLDB test suite requires libc++, but it is currently disabled. " "Please add `libcxx` to `LLVM_ENABLE_RUNTIMES` or disable tests via " "`LLDB_INCLUDE_TESTS=OFF`.") @@ -235,8 +238,6 @@ if (CMAKE_SIZEOF_VOID_P EQUAL 8) set(LLDB_IS_64_BITS 1) endif() -set(LLDB_TEST_SHELL_DISABLE_REMOTE OFF CACHE BOOL "Disable remote Shell tests execution") - # These values are not canonicalized within LLVM. llvm_canonicalize_cmake_booleans( LLDB_BUILD_INTEL_PT @@ -246,7 +247,6 @@ llvm_canonicalize_cmake_booleans( LLVM_ENABLE_ZLIB LLVM_ENABLE_SHARED_LIBS LLDB_HAS_LIBCXX - LLDB_TEST_SHELL_DISABLE_REMOTE LLDB_TOOL_LLDB_SERVER_BUILD LLDB_USE_SYSTEM_DEBUGSERVER LLDB_IS_64_BITS) diff --git a/llvm/include/llvm/Telemetry/Telemetry.h b/llvm/include/llvm/Telemetry/Telemetry.h new file mode 100644 index 00000000000000..f6198bd4d34010 --- /dev/null +++ b/llvm/include/llvm/Telemetry/Telemetry.h @@ -0,0 +1,133 @@ +//===- llvm/Telemetry/Telemetry.h - Telemetry -------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file provides the basic framework for Telemetry +/// Refer to its documentation at llvm/docs/Telemetry.rst for more details. +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TELEMETRY_TELEMETRY_H +#define LLVM_TELEMETRY_TELEMETRY_H + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/JSON.h" +#include <memory> +#include <optional> +#include <string> + +namespace llvm { +namespace telemetry { + +class Serializer { +public: + virtual llvm::Error start() = 0; + virtual void writeBool(StringRef KeyName, bool Value) = 0; + virtual void writeInt32(StringRef KeyName, int Value) = 0; + virtual void writeSizeT(StringRef KeyName, size_t Value) = 0; + virtual void writeString(StringRef KeyName, StringRef Value) = 0; + virtual void + writeKeyValueMap(StringRef KeyName, + const std::map<std::string, std::string> &Value) = 0; + virtual llvm::Error finish() = 0; +}; + +/// Configuration for the Telemeter class. +/// This stores configurations from both users and vendors and is passed +/// to the Telemeter upon construction. (Any changes to the config after +/// the Telemeter's construction will not have any effect on it). +/// +/// This struct can be extended as needed to add additional configuration +/// points specific to a vendor's implementation. +struct Config { + // If true, telemetry will be enabled. + const bool EnableTelemetry; + Config(bool E) : EnableTelemetry(E) {} + + virtual std::string makeSessionId() { return "0"; } +}; + +/// For isa, dyn_cast, etc operations on TelemetryInfo. +typedef unsigned KindType; +/// This struct is used by TelemetryInfo to support isa<>, dyn_cast<> +/// operations. +/// It is defined as a struct (rather than an enum) because it is +/// expected to be extended by subclasses which may have +/// additional TelemetryInfo types defined to describe different events. +struct EntryKind { + static const KindType Base = 0; +}; + +/// TelemetryInfo is the data courier, used to move instrumented data +/// from the tool being monitored to the Telemetry framework. +/// +/// This base class contains only the basic set of telemetry data. +/// Downstream implementations can define more subclasses with +/// additional fields to describe different events and concepts. +/// +/// For example, The LLDB debugger can define a DebugCommandInfo subclass +/// which has additional fields about the debug-command being instrumented, +/// such as `CommandArguments` or `CommandName`. +struct TelemetryInfo { + // This represents a unique-id, conventionally corresponding to + // a tool's session - i.e., every time the tool starts until it exits. + // + // Note: a tool could have multiple sessions running at once, in which + // case, these shall be multiple sets of TelemetryInfo with multiple unique + // ids. + // + // Different usages can assign different types of IDs to this field. + std::string SessionId; + + TelemetryInfo() = default; + virtual ~TelemetryInfo() = default; + + virtual void serialize(Serializer &serializer) const; + + // For isa, dyn_cast, etc, operations. + virtual KindType getKind() const { return EntryKind::Base; } + static bool classof(const TelemetryInfo *T) { + if (T == nullptr) + return false; + return T->getKind() == EntryKind::Base; + } +}; + +/// This class presents a data sink to which the Telemetry framework +/// sends data. +/// +/// Its implementation is transparent to the framework. +/// It is up to the vendor to decide which pieces of data to forward +/// and where to forward them. +class Destination { +public: + virtual ~Destination() = default; + virtual Error receiveEntry(const TelemetryInfo *Entry) = 0; + virtual llvm::StringLiteral name() const = 0; +}; + +/// This class is the main interaction point between any LLVM tool +/// and this framework. +/// It is responsible for collecting telemetry data from the tool being +/// monitored and transmitting the data elsewhere. +class Manager { +public: + // Dispatch Telemetry data to the Destination(s). + // The argument is non-const because the Manager may add or remove + // data from the entry. + virtual Error dispatch(TelemetryInfo *Entry) = 0; + + // Register a Destination. + virtual void addDestination(std::unique_ptr<Destination> Destination) = 0; +}; + +} // namespace telemetry +} // namespace llvm + +#endif // LLVM_TELEMETRY_TELEMETRY_H diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt index 503c77cb13bd07..f6465612d30c0b 100644 --- a/llvm/lib/CMakeLists.txt +++ b/llvm/lib/CMakeLists.txt @@ -41,6 +41,7 @@ add_subdirectory(ProfileData) add_subdirectory(Passes) add_subdirectory(TargetParser) add_subdirectory(TextAPI) +add_subdirectory(Telemetry) add_subdirectory(ToolDrivers) add_subdirectory(XRay) if (LLVM_INCLUDE_TESTS) diff --git a/llvm/lib/Telemetry/CMakeLists.txt b/llvm/lib/Telemetry/CMakeLists.txt new file mode 100644 index 00000000000000..8208bdadb05e94 --- /dev/null +++ b/llvm/lib/Telemetry/CMakeLists.txt @@ -0,0 +1,6 @@ +add_llvm_component_library(LLVMTelemetry + Telemetry.cpp + + ADDITIONAL_HEADER_DIRS + "${LLVM_MAIN_INCLUDE_DIR}/llvm/Telemetry" +) diff --git a/llvm/lib/Telemetry/Telemetry.cpp b/llvm/lib/Telemetry/Telemetry.cpp new file mode 100644 index 00000000000000..b7ee3c2bb1778b --- /dev/null +++ b/llvm/lib/Telemetry/Telemetry.cpp @@ -0,0 +1,11 @@ +#include "llvm/Telemetry/Telemetry.h" + +namespace llvm { +namespace telemetry { + +void TelemetryInfo::serialize(Serializer &serializer) const { + serializer.writeString("SessionId", SessionId); +} + +} // namespace telemetry +} // namespace llvm >From 23dd58e37826a1ba300b7eb887104c46ab92c0f9 Mon Sep 17 00:00:00 2001 From: Vy Nguyen <v...@google.com> Date: Tue, 17 Dec 2024 13:44:45 -0500 Subject: [PATCH 2/4] Update lldb/source/Core/Telemetry.cpp Co-authored-by: Pavel Labath <pa...@labath.sk> --- lldb/source/Core/Telemetry.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lldb/source/Core/Telemetry.cpp b/lldb/source/Core/Telemetry.cpp index 5ddad030ef962e..17d677f538784c 100644 --- a/lldb/source/Core/Telemetry.cpp +++ b/lldb/source/Core/Telemetry.cpp @@ -145,9 +145,7 @@ std::unique_ptr<TelemetryManager> TelemetryManager::CreateInstance( std::unique_ptr<llvm::telemetry::Config> config, lldb_private::Debugger *debugger) { - TelemetryManager *ins = new TelemetryManager(std::move(config), debugger); - - return std::unique_ptr<TelemetryManager>(ins); + return std::unique_ptr<TelemetryManager>(new TelemetryManager(std::move(config), debugger)); } llvm::Error TelemetryManager::dispatch(TelemetryInfo *entry) { >From a2cbce63f67c4e530d2cccb2e046ad8f9c577dc9 Mon Sep 17 00:00:00 2001 From: Vy Nguyen <v...@google.com> Date: Wed, 18 Dec 2024 14:32:39 -0500 Subject: [PATCH 3/4] address review comments --- lldb/include/lldb/Core/Telemetry.h | 5 +- lldb/source/Core/Telemetry.cpp | 87 +++++++++++++++--------------- 2 files changed, 44 insertions(+), 48 deletions(-) diff --git a/lldb/include/lldb/Core/Telemetry.h b/lldb/include/lldb/Core/Telemetry.h index 241d957672b6ca..4d7dfe517ce39e 100644 --- a/lldb/include/lldb/Core/Telemetry.h +++ b/lldb/include/lldb/Core/Telemetry.h @@ -45,7 +45,8 @@ struct LldbEntryKind : public ::llvm::telemetry::EntryKind { /// Defines a convenient type for timestamp of various events. /// This is used by the EventStats below. -using SteadyTimePoint = std::chrono::time_point<std::chrono::steady_clock>; +using SteadyTimePoint = std::chrono::time_point<std::chrono::steady_clock, + std::chrono::nanoseconds>; /// Various time (and possibly memory) statistics of an event. struct EventStats { @@ -74,8 +75,6 @@ struct LldbBaseTelemetryInfo : public TelemetryInfo { KindType getKind() const override { return LldbEntryKind::BaseInfo; } static bool classof(const TelemetryInfo *t) { - if (t == nullptr) - return false; // Subclasses of this is also acceptable. return (t->getKind() & LldbEntryKind::BaseInfo) == LldbEntryKind::BaseInfo; } diff --git a/lldb/source/Core/Telemetry.cpp b/lldb/source/Core/Telemetry.cpp index 17d677f538784c..e37890aecb8678 100644 --- a/lldb/source/Core/Telemetry.cpp +++ b/lldb/source/Core/Telemetry.cpp @@ -22,6 +22,7 @@ #include "lldb/API/SBProcess.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" +#include "lldb/Core/TelemetryVendor.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Interpreter/CommandInterpreter.h" @@ -54,68 +55,65 @@ using ::llvm::Error; using ::llvm::telemetry::Destination; using ::llvm::telemetry::TelemetryInfo; -static size_t ToNanosecOrZero(const std::optional<SteadyTimePoint> &Point) { - if (!Point.has_value()) - return 0; - - return Point.value().time_since_epoch().count(); +static unsigned long long ToNanosec(const SteadyTimePoint Point) { + return nanoseconds(Point.value().time_since_epoch()).count(); } void LldbBaseTelemetryInfo::serialize(Serializer &serializer) const { - serializer.writeInt32("EntryKind", getKind()); - serializer.writeString("SessionId", SessionId); + serializer.write("EntryKind", getKind()); + serializer.write("SessionId", SessionId); } void DebuggerTelemetryInfo::serialize(Serializer &serializer) const { LldbBaseTelemetryInfo::serialize(serializer); - serializer.writeString("username", username); - serializer.writeString("lldb_path", lldb_path); - serializer.writeString("cwd", cwd); - serializer.writeSizeT("start", stats.start.time_since_epoch().count()); - serializer.writeSizeT("end", ToNanosecOrZero(stats.end)); + serializer.write("username", username); + serializer.write("lldb_path", lldb_path); + serializer.write("cwd", cwd); + serializer.write("start", ToNanosec(stats.start)); + if (stats.end.has_value()) + serializer.write("end", ToNanosec(stats.end.value())); } void ClientTelemetryInfo::serialize(Serializer &serializer) const { LldbBaseTelemetryInfo::serialize(serializer); - serializer.writeString("request_name", request_name); - serializer.writeString("error_msg", error_msg); - serializer.writeSizeT("start", stats.start.time_since_epoch().count()); - serializer.writeSizeT("end", ToNanosecOrZero(stats.end)); + serializer.write("request_name", request_name); + serializer.write("error_msg", error_msg); + serializer.write("start", ToNanosec(stats.start)); + if (stats.end.has_value()) + serializer.write("end", ToNanosec(stats.end.value())); } void TargetTelemetryInfo::serialize(Serializer &serializer) const { LldbBaseTelemetryInfo::serialize(serializer); - serializer.writeString("target_uuid", target_uuid); - serializer.writeString("binary_path", binary_path); - serializer.writeSizeT("binary_size", binary_size); + serializer.write("target_uuid", target_uuid); + serializer.write("binary_path", binary_path); + serializer.write("binary_size", binary_size); } void CommandTelemetryInfo::serialize(Serializer &serializer) const { LldbBaseTelemetryInfo::serialize(serializer); - serializer.writeString("target_uuid", target_uuid); - serializer.writeString("command_uuid", command_uuid); - serializer.writeString("args", args); - serializer.writeString("original_command", original_command); - serializer.writeSizeT("start", stats.start.time_since_epoch().count()); - serializer.writeSizeT("end", ToNanosecOrZero(stats.end)); - - // If this entry was emitted at the end of the command-execution, - // then calculate the runtime too. - if (stats.end.has_value()) { - serializer.writeSizeT("command_runtime", - (stats.end.value() - stats.start).count()); - if (exit_desc.has_value()) { - serializer.writeInt32("exit_code", exit_desc->exit_code); - serializer.writeString("exit_msg", exit_desc->description); - serializer.writeInt32("return_status", static_cast<int>(ret_status)); - } + serializer.write("target_uuid", target_uuid); + serializer.write("command_uuid", command_uuid); + serializer.write("args", args); + serializer.write("original_command", original_command); + serializer.write("start", ToNanosec(stats.start)); + if (stats.end.has_value()) + serializer.write("end", ToNanosec(stats.end.value())); + + if (exit_desc.has_value()) { + serializer.write("exit_code", exit_desc->exit_code); + serializer.write("exit_msg", exit_desc->description); + serializer.write("return_status", static_cast<int>(ret_status)); } } void MiscTelemetryInfo::serialize(Serializer &serializer) const { LldbBaseTelemetryInfo::serialize(serializer); - serializer.writeString("target_uuid", target_uuid); - serializer.writeKeyValueMap("meta_data", meta_data); + serializer.write("target_uuid", target_uuid); + write.beginObject("meta_data"); + for (const auto &kv : meta_data) + serializer.write(kv.first, kv.second); + serializer.endObject(); } static std::string MakeUUID(lldb_private::Debugger *debugger) { @@ -144,20 +142,19 @@ TelemetryManager::TelemetryManager( std::unique_ptr<TelemetryManager> TelemetryManager::CreateInstance( std::unique_ptr<llvm::telemetry::Config> config, lldb_private::Debugger *debugger) { - - return std::unique_ptr<TelemetryManager>(new TelemetryManager(std::move(config), debugger)); + return std::unique_ptr<TelemetryManager>( + new TelemetryManager(std::move(config), debugger)); } llvm::Error TelemetryManager::dispatch(TelemetryInfo *entry) { entry->SessionId = m_session_uuid; + llvm::Error defferedErrs = llvm::Error::success(); for (auto &destination : m_destinations) { - llvm::Error err = destination->receiveEntry(entry); - if (err) { - return std::move(err); - } + if (auto err = destination->receiveEntry(entry)) + deferredErrs = llvm::joinErrors(std::move(deferredErrs), std::move(err)); } - return Error::success(); + return std::move(deferredErrs); } void TelemetryManager::addDestination( >From cead66c963276876a68cf14958252b9ea8869dde Mon Sep 17 00:00:00 2001 From: Vy Nguyen <v...@google.com> Date: Wed, 18 Dec 2024 14:45:57 -0500 Subject: [PATCH 4/4] remove most of the impl from lldb/Telemetry, and keep only the basic: - The telemetry-manager with no new methods defined yet - The LldbBaseTelemetryInfo --- lldb/include/lldb/Core/Telemetry.h | 217 +------------------------ lldb/source/Core/Telemetry.cpp | 246 +---------------------------- 2 files changed, 9 insertions(+), 454 deletions(-) diff --git a/lldb/include/lldb/Core/Telemetry.h b/lldb/include/lldb/Core/Telemetry.h index 4d7dfe517ce39e..8bb3c28ac4b5b0 100644 --- a/lldb/include/lldb/Core/Telemetry.h +++ b/lldb/include/lldb/Core/Telemetry.h @@ -10,7 +10,6 @@ #ifndef LLDB_CORE_TELEMETRY_H #define LLDB_CORE_TELEMETRY_H -#include <atomic> #include <chrono> #include <ctime> #include <memory> @@ -36,11 +35,6 @@ using llvm::telemetry::TelemetryInfo; struct LldbEntryKind : public ::llvm::telemetry::EntryKind { static const KindType BaseInfo = 0b11000; - static const KindType DebuggerInfo = 0b11001; - static const KindType TargetInfo = 0b11010; - static const KindType ClientInfo = 0b11100; - static const KindType CommandInfo = 0b11101; - static const KindType MiscInfo = 0b11110; }; /// Defines a convenient type for timestamp of various events. @@ -71,6 +65,8 @@ struct ExitDescription { struct LldbBaseTelemetryInfo : public TelemetryInfo { EventStats stats; + std::optional<ExitDescription> exit_desc; + // For dyn_cast, isa, etc operations. KindType getKind() const override { return LldbEntryKind::BaseInfo; } @@ -82,222 +78,19 @@ struct LldbBaseTelemetryInfo : public TelemetryInfo { void serialize(Serializer &serializer) const override; }; -struct DebuggerTelemetryInfo : public LldbBaseTelemetryInfo { - std::string username; - std::string lldb_git_sha; - std::string lldb_path; - std::string cwd; - - std::optional<ExitDescription> exit_desc; - DebuggerTelemetryInfo() = default; - - // Provide a copy ctor because we may need to make a copy before - // sanitizing the data. - // (The sanitization might differ between different Destination classes). - DebuggerTelemetryInfo(const DebuggerTelemetryInfo &other) { - username = other.username; - lldb_git_sha = other.lldb_git_sha; - lldb_path = other.lldb_path; - cwd = other.cwd; - }; - - KindType getKind() const override { return LldbEntryKind::DebuggerInfo; } - - static bool classof(const TelemetryInfo *T) { - if (T == nullptr) - return false; - return T->getKind() == LldbEntryKind::DebuggerInfo; - } - - void serialize(Serializer &serializer) const override; -}; - -struct TargetTelemetryInfo : public LldbBaseTelemetryInfo { - lldb::ModuleSP exec_mod; - Target *target_ptr; - - // The same as the executable-module's UUID. - std::string target_uuid; - std::string file_format; - - std::string binary_path; - size_t binary_size; - - std::optional<ExitDescription> exit_desc; - TargetTelemetryInfo() = default; - - TargetTelemetryInfo(const TargetTelemetryInfo &other) { - exec_mod = other.exec_mod; - target_uuid = other.target_uuid; - file_format = other.file_format; - binary_path = other.binary_path; - binary_size = other.binary_size; - exit_desc = other.exit_desc; - } - - KindType getKind() const override { return LldbEntryKind::TargetInfo; } - - static bool classof(const TelemetryInfo *T) { - if (T == nullptr) - return false; - return T->getKind() == LldbEntryKind::TargetInfo; - } - - void serialize(Serializer &serializer) const override; -}; - -// Entry from client (eg., SB-API) -struct ClientTelemetryInfo : public LldbBaseTelemetryInfo { - std::string request_name; - std::string error_msg; - - ClientTelemetryInfo() = default; - - ClientTelemetryInfo(const ClientTelemetryInfo &other) { - request_name = other.request_name; - error_msg = other.error_msg; - } - - KindType getKind() const override { return LldbEntryKind::ClientInfo; } - - static bool classof(const TelemetryInfo *T) { - if (T == nullptr) - return false; - return T->getKind() == LldbEntryKind::ClientInfo; - } - - void serialize(Serializer &serializer) const override; -}; - -struct CommandTelemetryInfo : public LldbBaseTelemetryInfo { - Target *target_ptr; - CommandReturnObject *result; - - // If the command is/can be associated with a target entry, - // this field contains that target's UUID. - // <EMPTY> otherwise. - std::string target_uuid; - std::string command_uuid; - - // Eg., "breakpoint set" - std::string command_name; - - // !!NOTE!!: The following fields may be omitted due to PII risk. - // (Configurable via the telemery::Config struct) - std::string original_command; - std::string args; - - std::optional<ExitDescription> exit_desc; - lldb::ReturnStatus ret_status; - - CommandTelemetryInfo() = default; - - CommandTelemetryInfo(const CommandTelemetryInfo &other) { - target_uuid = other.target_uuid; - command_uuid = other.command_uuid; - command_name = other.command_name; - original_command = other.original_command; - args = other.args; - exit_desc = other.exit_desc; - ret_status = other.ret_status; - } - - KindType getKind() const override { return LldbEntryKind::CommandInfo; } - - static bool classof(const TelemetryInfo *T) { - if (T == nullptr) - return false; - return T->getKind() == LldbEntryKind::CommandInfo; - } - - void serialize(Serializer &serializer) const override; -}; - -/// The "catch-all" entry to store a set of custom/non-standard -/// data. -struct MiscTelemetryInfo : public LldbBaseTelemetryInfo { - /// If the event is/can be associated with a target entry, - /// this field contains that target's UUID. - /// <EMPTY> otherwise. - std::string target_uuid; - - /// Set of key-value pairs for any optional (or impl-specific) data - std::map<std::string, std::string> meta_data; - - MiscTelemetryInfo() = default; - - MiscTelemetryInfo(const MiscTelemetryInfo &other) { - target_uuid = other.target_uuid; - meta_data = other.meta_data; - } - - KindType getKind() const override { return LldbEntryKind::MiscInfo; } - - static bool classof(const TelemetryInfo *T) { - if (T == nullptr) - return false; - return T->getKind() == LldbEntryKind::MiscInfo; - } - - void serialize(Serializer &serializer) const override; -}; - /// The base Telemetry manager instance in LLDB /// This class declares additional instrumentation points /// applicable to LLDB. class TelemetryManager : public llvm::telemetry::Manager { public: - /// Creates an instance of TelemetryManager. - /// This uses the plugin registry to find an instance: - /// - If a vendor supplies a implementation, it will use it. - /// - If not, it will either return a no-op instance or a basic - /// implementation for testing. - /// - /// See also lldb_private::TelemetryVendor. - static std::unique_ptr<TelemetryManager> - CreateInstance(std::unique_ptr<llvm::telemetry::Config> config, - Debugger *debugger); - - /// To be invoked upon LLDB startup. - virtual void LogStartup(DebuggerTelemetryInfo *entry); - - /// To be invoked upon LLDB exit. - virtual void LogExit(DebuggerTelemetryInfo *entry); - - /// To be invoked upon loading the main executable module. - /// We log in a fire-n-forget fashion so that if the load - /// crashes, we don't lose the entry. - virtual void LogMainExecutableLoadStart(TargetTelemetryInfo *entry); - virtual void LogMainExecutableLoadEnd(TargetTelemetryInfo *entry); - - /// To be invoked upon process exit. - virtual void LogProcessExit(TargetTelemetryInfo *entry); - - /// Invoked for each command - /// We log in a fire-n-forget fashion so that if the command execution - /// crashes, we don't lose the entry. - virtual void LogCommandStart(CommandTelemetryInfo *entry); - virtual void LogCommandEnd(CommandTelemetryInfo *entry); - - /// For client (eg., SB API) to send telemetry entries. - virtual void - LogClientTelemetry(const lldb_private::StructuredDataImpl &entry); - - virtual std::string GetNextUUID() { - return std::to_string(uuid_seed.fetch_add(1)); - } + TelemetryManager(std::unique_ptr<llvm::telemetry::Config> config, + Debugger *debugger); llvm::Error dispatch(TelemetryInfo *entry) override; - void addDestination(std::unique_ptr<Destination> destination) override; -protected: - TelemetryManager(std::unique_ptr<llvm::telemetry::Config> config, - Debugger *debugger); - TelemetryManager() = default; - virtual void CollectMiscBuildInfo(); + void addDestination(std::unique_ptr<Destination> destination) override; private: - std::atomic<size_t> uuid_seed = 0; std::unique_ptr<llvm::telemetry::Config> m_config; Debugger *m_debugger; const std::string m_session_uuid; diff --git a/lldb/source/Core/Telemetry.cpp b/lldb/source/Core/Telemetry.cpp index e37890aecb8678..bd13e7457b488b 100644 --- a/lldb/source/Core/Telemetry.cpp +++ b/lldb/source/Core/Telemetry.cpp @@ -11,42 +11,24 @@ #include <chrono> #include <cstdlib> #include <ctime> -#include <fstream> #include <memory> #include <string> -#include <typeinfo> #include <utility> #include <vector> -#include "lldb/API/SBDebugger.h" -#include "lldb/API/SBProcess.h" #include "lldb/Core/Debugger.h" -#include "lldb/Core/Module.h" -#include "lldb/Core/TelemetryVendor.h" -#include "lldb/Host/FileSystem.h" -#include "lldb/Host/HostInfo.h" -#include "lldb/Interpreter/CommandInterpreter.h" -#include "lldb/Target/Process.h" #include "lldb/Target/Statistics.h" #include "lldb/Utility/ConstString.h" -#include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/UUID.h" #include "lldb/Version/Version.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Error.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/JSON.h" -#include "llvm/Support/LineIterator.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Path.h" #include "llvm/Support/RandomNumberGenerator.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Telemetry/Telemetry.h" namespace lldb_private { @@ -60,62 +42,17 @@ static unsigned long long ToNanosec(const SteadyTimePoint Point) { } void LldbBaseTelemetryInfo::serialize(Serializer &serializer) const { - serializer.write("EntryKind", getKind()); - serializer.write("SessionId", SessionId); -} - -void DebuggerTelemetryInfo::serialize(Serializer &serializer) const { - LldbBaseTelemetryInfo::serialize(serializer); - serializer.write("username", username); - serializer.write("lldb_path", lldb_path); - serializer.write("cwd", cwd); - serializer.write("start", ToNanosec(stats.start)); - if (stats.end.has_value()) - serializer.write("end", ToNanosec(stats.end.value())); -} - -void ClientTelemetryInfo::serialize(Serializer &serializer) const { - LldbBaseTelemetryInfo::serialize(serializer); - serializer.write("request_name", request_name); - serializer.write("error_msg", error_msg); - serializer.write("start", ToNanosec(stats.start)); + serializer.write("entry_kind", getKind()); + serializer.write("session_id", SessionId); + serializer.write("start_time", ToNanosec(stats.start)); if (stats.end.has_value()) - serializer.write("end", ToNanosec(stats.end.value())); -} - -void TargetTelemetryInfo::serialize(Serializer &serializer) const { - LldbBaseTelemetryInfo::serialize(serializer); - serializer.write("target_uuid", target_uuid); - serializer.write("binary_path", binary_path); - serializer.write("binary_size", binary_size); -} - -void CommandTelemetryInfo::serialize(Serializer &serializer) const { - LldbBaseTelemetryInfo::serialize(serializer); - serializer.write("target_uuid", target_uuid); - serializer.write("command_uuid", command_uuid); - serializer.write("args", args); - serializer.write("original_command", original_command); - serializer.write("start", ToNanosec(stats.start)); - if (stats.end.has_value()) - serializer.write("end", ToNanosec(stats.end.value())); - + serializer.write("end_time", ToNanosec(stats.end.value())); if (exit_desc.has_value()) { serializer.write("exit_code", exit_desc->exit_code); serializer.write("exit_msg", exit_desc->description); - serializer.write("return_status", static_cast<int>(ret_status)); } } -void MiscTelemetryInfo::serialize(Serializer &serializer) const { - LldbBaseTelemetryInfo::serialize(serializer); - serializer.write("target_uuid", target_uuid); - write.beginObject("meta_data"); - for (const auto &kv : meta_data) - serializer.write(kv.first, kv.second); - serializer.endObject(); -} - static std::string MakeUUID(lldb_private::Debugger *debugger) { std::string ret; uint8_t random_bytes[16]; @@ -139,13 +76,6 @@ TelemetryManager::TelemetryManager( : m_config(std::move(config)), m_debugger(debugger), m_session_uuid(MakeUUID(debugger)) {} -std::unique_ptr<TelemetryManager> TelemetryManager::CreateInstance( - std::unique_ptr<llvm::telemetry::Config> config, - lldb_private::Debugger *debugger) { - return std::unique_ptr<TelemetryManager>( - new TelemetryManager(std::move(config), debugger)); -} - llvm::Error TelemetryManager::dispatch(TelemetryInfo *entry) { entry->SessionId = m_session_uuid; @@ -162,172 +92,4 @@ void TelemetryManager::addDestination( m_destinations.push_back(std::move(destination)); } -void TelemetryManager::LogStartup(DebuggerTelemetryInfo *entry) { - UserIDResolver &resolver = lldb_private::HostInfo::GetUserIDResolver(); - std::optional<llvm::StringRef> opt_username = - resolver.GetUserName(lldb_private::HostInfo::GetUserID()); - if (opt_username) - entry->username = *opt_username; - - entry->lldb_git_sha = - lldb_private::GetVersion(); // TODO: find the real git sha? - - llvm::SmallString<64> cwd; - if (!llvm::sys::fs::current_path(cwd)) { - entry->cwd = cwd.c_str(); - } else { - MiscTelemetryInfo misc_info; - misc_info.meta_data["internal_errors"] = "Cannot determine CWD"; - if (auto er = dispatch(&misc_info)) { - LLDB_LOG(GetLog(LLDBLog::Object), - "Failed to dispatch misc-info from startup"); - } - } - - if (auto er = dispatch(entry)) { - LLDB_LOG(GetLog(LLDBLog::Object), "Failed to dispatch entry from startup"); - } - - // Optional part - CollectMiscBuildInfo(); -} - -void TelemetryManager::LogExit(DebuggerTelemetryInfo *entry) { - if (auto *selected_target = - m_debugger->GetSelectedExecutionContext().GetTargetPtr()) { - if (!selected_target->IsDummyTarget()) { - const lldb::ProcessSP proc = selected_target->GetProcessSP(); - if (proc == nullptr) { - // no process has been launched yet. - entry->exit_desc = {-1, "no process launched."}; - } else { - entry->exit_desc = {proc->GetExitStatus(), ""}; - if (const char *description = proc->GetExitDescription()) - entry->exit_desc->description = std::string(description); - } - } - } - dispatch(entry); -} - -void TelemetryManager::LogProcessExit(TargetTelemetryInfo *entry) { - entry->target_uuid = - entry->target_ptr && !entry->target_ptr->IsDummyTarget() - ? entry->target_ptr->GetExecutableModule()->GetUUID().GetAsString() - : ""; - - dispatch(entry); -} - -void TelemetryManager::CollectMiscBuildInfo() { - // collecting use-case specific data -} - -void TelemetryManager::LogMainExecutableLoadStart(TargetTelemetryInfo *entry) { - entry->binary_path = - entry->exec_mod->GetFileSpec().GetPathAsConstString().GetCString(); - entry->file_format = entry->exec_mod->GetArchitecture().GetArchitectureName(); - entry->target_uuid = entry->exec_mod->GetUUID().GetAsString(); - if (auto err = llvm::sys::fs::file_size( - entry->exec_mod->GetFileSpec().GetPath(), entry->binary_size)) { - // If there was error obtaining it, just reset the size to 0. - // Maybe log the error too? - entry->binary_size = 0; - } - dispatch(entry); -} - -void TelemetryManager::LogMainExecutableLoadEnd(TargetTelemetryInfo *entry) { - lldb::ModuleSP exec_mod = entry->exec_mod; - entry->binary_path = - exec_mod->GetFileSpec().GetPathAsConstString().GetCString(); - entry->file_format = exec_mod->GetArchitecture().GetArchitectureName(); - entry->target_uuid = exec_mod->GetUUID().GetAsString(); - entry->binary_size = exec_mod->GetObjectFile()->GetByteSize(); - - dispatch(entry); - - // Collect some more info, might be useful? - MiscTelemetryInfo misc_info; - misc_info.target_uuid = exec_mod->GetUUID().GetAsString(); - misc_info.meta_data["symtab_index_time"] = - std::to_string(exec_mod->GetSymtabIndexTime().get().count()); - misc_info.meta_data["symtab_parse_time"] = - std::to_string(exec_mod->GetSymtabParseTime().get().count()); - dispatch(&misc_info); -} - -void TelemetryManager::LogClientTelemetry( - const lldb_private::StructuredDataImpl &entry) { - // TODO: pull the dictionary out of entry - ClientTelemetryInfo client_info; - /* - std::optional<llvm::StringRef> request_name = entry.getString("request_name"); - if (!request_name.has_value()) { - MiscTelemetryInfo misc_info = MakeBaseEntry<MiscTelemetryInfo>(); - misc_info.meta_data["internal_errors"] = - "Cannot determine request name from client entry"; - // TODO: Dump the errornous entry to stderr too? - EmitToDestinations(&misc_info); - return; - } - 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"); - - if (!start_time.has_value() || !end_time.has_value()) { - MiscTelemetryInfo misc_info = MakeBaseEntry<MiscTelemetryInfo>(); - misc_info.meta_data["internal_errors"] = - "Cannot determine start/end time from client entry"; - EmitToDestinations(&misc_info); - return; - } - - SteadyTimePoint epoch; - client_info.Stats.Start = - epoch + std::chrono::nanoseconds(static_cast<size_t>(*start_time)); - client_info.Stats.End = - 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(); - */ - - dispatch(&client_info); -} - -void TelemetryManager::LogCommandStart(CommandTelemetryInfo *entry) { - // If we have a target attached to this command, then get the UUID. - if (entry->target_ptr && - entry->target_ptr->GetExecutableModule() != nullptr) { - entry->target_uuid = - entry->target_ptr->GetExecutableModule()->GetUUID().GetAsString(); - } else { - entry->target_uuid = ""; - } - - dispatch(entry); -} - -void TelemetryManager::LogCommandEnd(CommandTelemetryInfo *entry) { - // If we have a target attached to this command, then get the UUID. - if (entry->target_ptr && - entry->target_ptr->GetExecutableModule() != nullptr) { - entry->target_uuid = - entry->target_ptr->GetExecutableModule()->GetUUID().GetAsString(); - } else { - entry->target_uuid = ""; - } - - entry->exit_desc = {entry->result->Succeeded() ? 0 : -1, ""}; - if (llvm::StringRef error_data = entry->result->GetErrorData(); - !error_data.empty()) { - entry->exit_desc->description = error_data.str(); - } - entry->ret_status = entry->result->GetStatus(); - dispatch(entry); -} - } // namespace lldb_private _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits