[Lldb-commits] [lldb] [llvm] [lldb][telemetry] Implement LLDB Telemetry (part 1) (PR #119716)
Vy Nguyen via lldb-commits
lldb-commits at lists.llvm.org
Wed Dec 18 11:32:53 PST 2024
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 <vyng at google.com>
Date: Thu, 12 Dec 2024 10:47:09 -0500
Subject: [PATCH 1/3] [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 <vyng at google.com>
Date: Tue, 17 Dec 2024 13:44:45 -0500
Subject: [PATCH 2/3] Update lldb/source/Core/Telemetry.cpp
Co-authored-by: Pavel Labath <pavel at 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 <vyng at google.com>
Date: Wed, 18 Dec 2024 14:32:39 -0500
Subject: [PATCH 3/3] 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(
More information about the lldb-commits
mailing list