[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:47:06 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/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 <vyng at 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 <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/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 <vyng at 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



More information about the lldb-commits mailing list