https://github.com/oontvoo created https://github.com/llvm/llvm-project/pull/119716
Details: - This is a subset of PR/98528.( Pavel's suggestion was to split up the patch to make reviewing easier) - 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. >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] [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 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits