[Lldb-commits] [lldb] [llvm] [llvm]Added lib/Telemetry (PR #98528)

Vy Nguyen via lldb-commits lldb-commits at lists.llvm.org
Tue Aug 6 12:16:39 PDT 2024


https://github.com/oontvoo updated https://github.com/llvm/llvm-project/pull/98528

>From 2fa1fa227e6ff93f8904d0f9d56432401d673ed7 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Wed, 10 Jul 2024 15:27:38 -0400
Subject: [PATCH 1/3] [llvm]Added lib/Telemetry  - Provide a base API for llvm
 Telemetry  - Provide some concrete implementation for it in lldb/Telemetry

---
 lldb/include/lldb/API/SBDebugger.h            |   4 +
 lldb/include/lldb/Core/Debugger.h             |   8 +
 lldb/include/lldb/Core/Telemetry.h            | 153 +++++
 lldb/include/lldb/Target/Process.h            |   3 +
 lldb/source/API/SBDebugger.cpp                |   9 +
 lldb/source/Core/CMakeLists.txt               |   2 +
 lldb/source/Core/Debugger.cpp                 |  31 +-
 lldb/source/Core/Telemetry.cpp                | 606 ++++++++++++++++++
 .../source/Interpreter/CommandInterpreter.cpp |  44 +-
 lldb/source/Target/Process.cpp                |   7 +-
 lldb/source/Target/Target.cpp                 |  15 +-
 lldb/tools/lldb-dap/DAP.cpp                   |   6 +-
 llvm/include/llvm/Telemetry/Telemetry.h       |  99 +++
 llvm/lib/CMakeLists.txt                       |   1 +
 llvm/lib/Telemetry/CMakeLists.txt             |   6 +
 llvm/lib/Telemetry/Telemetry.cpp              |  32 +
 16 files changed, 1011 insertions(+), 15 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/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h
index 84ea9c0f772e1..de09995679ad9 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -9,10 +9,12 @@
 #ifndef LLDB_API_SBDEBUGGER_H
 #define LLDB_API_SBDEBUGGER_H
 
+#include <chrono>
 #include <cstdio>
 
 #include "lldb/API/SBDefines.h"
 #include "lldb/API/SBPlatform.h"
+#include "lldb/API/SBStructuredData.h"
 
 namespace lldb_private {
 class CommandPluginInterfaceImplementation;
@@ -245,6 +247,8 @@ class LLDB_API SBDebugger {
 
   lldb::SBTarget GetDummyTarget();
 
+  void SendTelemetry(SBStructuredData *entry);
+
   // Return true if target is deleted from the target list of the debugger.
   bool DeleteTarget(lldb::SBTarget &target);
 
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index a72c2596cc2c5..13a444e438fec 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -19,6 +19,7 @@
 #include "lldb/Core/FormatEntity.h"
 #include "lldb/Core/IOHandler.h"
 #include "lldb/Core/SourceManager.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/Core/UserSettingsController.h"
 #include "lldb/Host/HostThread.h"
 #include "lldb/Host/StreamFile.h"
@@ -31,6 +32,7 @@
 #include "lldb/Utility/Diagnostics.h"
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/Utility/Status.h"
+#include "lldb/Utility/StructuredData.h"
 #include "lldb/Utility/UserID.h"
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-enumerations.h"
@@ -137,6 +139,10 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
 
   lldb::StreamFileSP GetErrorStreamSP() { return m_error_stream_sp; }
 
+  std::shared_ptr<LldbTelemeter> GetTelemeter() { return m_telemeter; }
+
+  void SendClientTelemetry(lldb_private::StructuredData::Object *entry);
+
   File &GetInputFile() { return *m_input_file_sp; }
 
   File &GetOutputFile() { return m_output_stream_sp->GetFile(); }
@@ -754,6 +760,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
   uint32_t m_interrupt_requested = 0; ///< Tracks interrupt requests
   std::mutex m_interrupt_mutex;
 
+  std::shared_ptr<LldbTelemeter> m_telemeter;
   // Events for m_sync_broadcaster
   enum {
     eBroadcastBitEventThreadIsListening = (1 << 0),
@@ -766,6 +773,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
 
   Debugger(const Debugger &) = delete;
   const Debugger &operator=(const Debugger &) = delete;
+  TelemetryEventStats stats;
 };
 
 } // namespace lldb_private
diff --git a/lldb/include/lldb/Core/Telemetry.h b/lldb/include/lldb/Core/Telemetry.h
new file mode 100644
index 0000000000000..15cc3139764ad
--- /dev/null
+++ b/lldb/include/lldb/Core/Telemetry.h
@@ -0,0 +1,153 @@
+#ifndef LLDB_CORE_TELEMETRY_H
+#define LLDB_CORE_TELEMETRY_H
+
+#include <chrono>
+#include <ctime>
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_map>
+
+#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/Telemetry/Telemetry.h"
+
+using namespace llvm::telemetry;
+
+namespace lldb_private {
+
+struct DebuggerTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
+  std::string username;
+  std::string lldb_git_sha;
+  std::string lldb_path;
+  std::string cwd;
+
+  std::string ToString() const override;
+};
+
+struct TargetTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
+  // All entries emitted for the same SBTarget will have the same
+  // target_uuid.
+  std::string target_uuid;
+  std::string file_format;
+
+  std::string binary_path;
+  size_t binary_size;
+
+  std::string ToString() const override;
+};
+
+// Entry from client (eg., SB-API)
+struct ClientTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
+  std::string request_name;
+  std::string error_msg;
+  std::string ToString() const override;
+};
+
+struct CommandTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
+  // 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 TelemetryConfig struct)
+  std::string original_command;
+  std::string args;
+
+  std::string ToString() const override;
+};
+
+// The "catch-all" entry to store a set of custom/non-standard
+// data.
+struct MiscTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
+  // 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::unordered_map<std::string, std::string> meta_data;
+
+  std::string ToString() const override;
+};
+
+class LldbTelemeter : public llvm::telemetry::Telemeter {
+public:
+  static std::shared_ptr<LldbTelemeter> CreateInstance(Debugger *);
+
+  virtual ~LldbTelemeter() = default;
+
+  // void LogStartup(llvm::StringRef lldb_path,
+  //                 TelemetryInfo *entry) override;
+  // void LogExit(llvm::StringRef lldb_path, TelemetryInfo *entry)
+  // override;
+
+  // Invoked upon process exit
+  virtual void LogProcessExit(int status, llvm::StringRef exit_string,
+                              TelemetryEventStats stats,
+                              Target *target_ptr) = 0;
+
+  // 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(lldb::ModuleSP exec_mod,
+                                          TelemetryEventStats stats) = 0;
+  virtual void LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
+                                        TelemetryEventStats stats) = 0;
+
+  // 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(llvm::StringRef uuid,
+                               llvm::StringRef original_command,
+                               TelemetryEventStats stats,
+                               Target *target_ptr) = 0;
+  virtual void LogCommandEnd(llvm::StringRef uuid, llvm::StringRef command_name,
+                             llvm::StringRef command_args,
+                             TelemetryEventStats stats, Target *target_ptr,
+                             CommandReturnObject *result) = 0;
+
+  virtual std::string GetNextUUID() = 0;
+
+  // For client (eg., SB API) to send telemetry entries.
+  virtual void
+  LogClientTelemetry(lldb_private::StructuredData::Object *entry) = 0;
+};
+
+// Logger configs: LLDB users can also supply their own configs via:
+// $HOME/.lldb_telemetry_config
+//
+// We can propose simple syntax: <field_name><colon><value>
+// Eg.,
+// enable_telemetry:true
+// destination:stdout
+// destination:stderr
+// destination:/path/to/some/file
+//
+// The allowed field_name values are:
+//  * enable_telemetry
+//       If the fields are specified more than once, the last line will take
+//       precedence If enable_logging is set to false, no logging will occur.
+//  * destination.
+//       This is allowed to be specified multiple times - it will add to the
+//       default (ie, specified by vendor) list of destinations.
+//       The value can be either:
+//          + one of the two magic values "stdout" or "stderr".
+//          + a path to a local file
+// !!NOTE!!: We decided to use a separate file instead of the existing settings
+//         file because that file is parsed too late in the process and by the
+//         there might have been lots of telemetry-entries that need to be
+//         sent already.
+//         This approach avoid losing log entries if LLDB crashes during init.
+TelemetryConfig *GetTelemetryConfig();
+
+} // namespace lldb_private
+#endif // LLDB_CORE_TELEMETRY_H
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index ceaf547ebddaf..c35ff397a203f 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -28,6 +28,7 @@
 #include "lldb/Core/LoadedModuleInfoList.h"
 #include "lldb/Core/PluginInterface.h"
 #include "lldb/Core/SourceManager.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/Core/ThreadSafeValue.h"
 #include "lldb/Core/ThreadedCommunication.h"
 #include "lldb/Core/UserSettingsController.h"
@@ -3285,6 +3286,8 @@ void PruneThreadPlans();
 
   Process(const Process &) = delete;
   const Process &operator=(const Process &) = delete;
+
+  TelemetryEventStats m_event_stats;
 };
 
 /// RAII guard that should be acquired when an utility function is called within
diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index fb035a36e7d74..3629fcc2574af 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -34,6 +34,7 @@
 #include "lldb/API/SBTypeNameSpecifier.h"
 #include "lldb/API/SBTypeSummary.h"
 #include "lldb/API/SBTypeSynthetic.h"
+#include <iostream>
 
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/DebuggerEvents.h"
@@ -968,6 +969,14 @@ SBTarget SBDebugger::GetDummyTarget() {
   return sb_target;
 }
 
+void SBDebugger::SendTelemetry(SBStructuredData *entry) {
+  if (lldb_private::Debugger *debugger = this->get()) {
+    debugger->SendClientTelemetry(entry->m_impl_up->GetObjectSP().get());
+  } else {
+    std::cerr << " --- cannot send telemetry entry - debugger is null\n";
+  }
+}
+
 bool SBDebugger::DeleteTarget(lldb::SBTarget &target) {
   LLDB_INSTRUMENT_VA(this, target);
 
diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt
index dbc620b91b1ed..d7f8297fa555c 100644
--- a/lldb/source/Core/CMakeLists.txt
+++ b/lldb/source/Core/CMakeLists.txt
@@ -52,6 +52,7 @@ add_lldb_library(lldbCore
   SourceLocationSpec.cpp
   SourceManager.cpp
   StreamAsynchronousIO.cpp
+  Telemetry.cpp
   ThreadedCommunication.cpp
   UserSettingsController.cpp
   Value.cpp
@@ -94,6 +95,7 @@ add_lldb_library(lldbCore
     Support
     Demangle
     TargetParser
+    Telemetry
   )
 
 add_dependencies(lldbCore
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 309e01e456580..2947d2d537767 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/Progress.h"
 #include "lldb/Core/StreamAsynchronousIO.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/DataFormatters/DataVisualization.h"
 #include "lldb/Expression/REPL.h"
 #include "lldb/Host/File.h"
@@ -53,6 +54,8 @@
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/StreamString.h"
 #include "lldb/lldb-enumerations.h"
+#include "llvm/Telemetry/Telemetry.h"
+#include <chrono>
 
 #if defined(_WIN32)
 #include "lldb/Host/windows/PosixApi.h"
@@ -733,12 +736,20 @@ void Debugger::InstanceInitialize() {
 
 DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
                                     void *baton) {
+  SteadyTimePoint start_time = std::chrono::steady_clock::now();
   DebuggerSP debugger_sp(new Debugger(log_callback, baton));
+  debugger_sp->stats.m_start = start_time;
   if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
     std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
     g_debugger_list_ptr->push_back(debugger_sp);
   }
   debugger_sp->InstanceInitialize();
+  TelemetryEventStats init_stats(start_time, std::chrono::steady_clock::now());
+  llvm::telemetry::TelemetryInfo entry;
+  entry.stats = {start_time, std::chrono::steady_clock::now()};
+  debugger_sp->m_telemeter->LogStartup(
+      HostInfo::GetProgramFileSpec().GetPathAsConstString().GetCString(),
+      &entry);
   return debugger_sp;
 }
 
@@ -860,7 +871,8 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
       m_sync_broadcaster(nullptr, "lldb.debugger.sync"),
       m_broadcaster(m_broadcaster_manager_sp,
                     GetStaticBroadcasterClass().str()),
-      m_forward_listener_sp(), m_clear_once() {
+      m_forward_listener_sp(), m_clear_once(),
+      m_telemeter(LldbTelemeter::CreateInstance(this)) {
   // Initialize the debugger properties as early as possible as other parts of
   // LLDB will start querying them during construction.
   m_collection_sp->Initialize(g_debugger_properties);
@@ -952,6 +964,18 @@ void Debugger::Clear() {
   //     static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp);
   //     static void Debugger::Terminate();
   llvm::call_once(m_clear_once, [this]() {
+    // Log the "quit" event.
+    // Note: this session_stats include the time since LLDB starts till quit
+    // (now).
+    // TBD: we could also record stats for *just* the quit action, if needed?
+    //      (ie., how long it takes to run all these cleanup functions?)
+    llvm::telemetry::TelemetryInfo entry;
+    entry.stats = {/*start_session*/ stats.m_start,
+                   /*end_session*/ std::chrono::steady_clock::now()};
+    m_telemeter->LogExit(
+        HostInfo::GetProgramFileSpec().GetPathAsConstString().GetCString(),
+        &entry);
+
     ClearIOHandlers();
     StopIOHandlerThread();
     StopEventHandlerThread();
@@ -2231,6 +2255,11 @@ Status Debugger::RunREPL(LanguageType language, const char *repl_options) {
   return err;
 }
 
+void Debugger::SendClientTelemetry(
+    lldb_private::StructuredData::Object *entry) {
+  m_telemeter->LogClientTelemetry(entry);
+}
+
 llvm::ThreadPoolInterface &Debugger::GetThreadPool() {
   assert(g_thread_pool &&
          "Debugger::GetThreadPool called before Debugger::Initialize");
diff --git a/lldb/source/Core/Telemetry.cpp b/lldb/source/Core/Telemetry.cpp
new file mode 100644
index 0000000000000..6a6f67bd8d034
--- /dev/null
+++ b/lldb/source/Core/Telemetry.cpp
@@ -0,0 +1,606 @@
+
+//===-- 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 <stdbool.h>
+#include <sys/auxv.h>
+
+#include <memory>
+
+#include <chrono>
+#include <cstdlib>
+#include <ctime>
+#include <fstream>
+#include <iostream>
+#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/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/FileSystem.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Telemetry/Telemetry.h"
+
+// Vendor specific.
+void ApplyVendorSpecificConfigs(llvm::telemetry::TelemetryConfig *config)
+    __attribute__((weak));
+std::shared_ptr<llvm::telemetry::TelemetryInfo>
+SanitizeSensitiveFields(const llvm::telemetry::TelemetryInfo *entry)
+    __attribute__((weak));
+std::shared_ptr<lldb_private::LldbTelemeter>
+CreateVendorSpecificTelemeter(llvm::telemetry::TelemetryConfig *config)
+    __attribute__((weak));
+
+namespace lldb_private {
+
+static std::string GetDuration(const TelemetryEventStats &stats) {
+  if (stats.m_end.has_value())
+    return std::to_string((stats.m_end.value() - stats.m_start).count()) +
+           "(nanosec)";
+  return "<NONE>";
+}
+
+std::string DebuggerTelemetryInfo::ToString() const {
+  std::string duration_desc =
+      (exit_description.has_value() ? "  lldb session duration: "
+                                    : "  lldb startup duration: ") +
+      std::to_string((stats.m_end.value() - stats.m_start).count()) +
+      "(nanosec)\n";
+
+  return TelemetryInfo::ToString() + "\n" + ("[DebuggerTelemetryInfo]\n") +
+         ("  username: " + username + "\n") +
+         ("  lldb_git_sha: " + lldb_git_sha + "\n") +
+         ("  lldb_path: " + lldb_path + "\n") + ("  cwd: " + cwd + "\n") +
+         duration_desc + "\n";
+}
+
+std::string ClientTelemetryInfo::ToString() const {
+  return TelemetryInfo::ToString() + "\n" + ("[DapRequestInfoEntry]\n") +
+         ("  request_name: " + request_name + "\n") +
+         ("  request_duration: " + GetDuration(stats) + "(nanosec)\n") +
+         ("  error_msg: " + error_msg + "\n");
+}
+
+std::string TargetTelemetryInfo::ToString() const {
+  std::string exit_or_load_desc;
+  if (exit_description.has_value()) {
+    // If this entry was emitted for an exit
+    exit_or_load_desc = "  process_duration: " + GetDuration(stats) +
+                        "  exit: " + exit_description->ToString() + "\n";
+  } else {
+    // This was emitted for a load event.
+    // See if it was the start-load or end-load entry
+    if (stats.m_end.has_value()) {
+      exit_or_load_desc =
+          "  startup_init_duration: " + GetDuration(stats) + "\n";
+    } else {
+      exit_or_load_desc = " startup_init_start\n";
+    }
+  }
+  return TelemetryInfo::ToString() + "\n" + ("[TargetTelemetryInfo]\n") +
+         ("  target_uuid: " + target_uuid + "\n") +
+         ("  file_format: " + file_format + "\n") +
+         ("  binary_path: " + binary_path + "\n") +
+         ("  binary_size: " + std::to_string(binary_size) + "\n") +
+         exit_or_load_desc;
+}
+
+static std::string StatusToString(CommandReturnObject *result) {
+  std::string msg;
+  switch (result->GetStatus()) {
+  case lldb::eReturnStatusInvalid:
+    msg = "invalid";
+    break;
+  case lldb::eReturnStatusSuccessFinishNoResult:
+    msg = "success_finish_no_result";
+    break;
+  case lldb::eReturnStatusSuccessFinishResult:
+    msg = "success_finish_result";
+    break;
+  case lldb::eReturnStatusSuccessContinuingNoResult:
+    msg = "success_continuing_no_result";
+    break;
+  case lldb::eReturnStatusSuccessContinuingResult:
+    msg = "success_continuing_result";
+    break;
+  case lldb::eReturnStatusStarted:
+    msg = "started";
+    break;
+  case lldb::eReturnStatusFailed:
+    msg = "failed";
+    break;
+  case lldb::eReturnStatusQuit:
+    msg = "quit";
+    break;
+  }
+  if (llvm::StringRef error_data = result->GetErrorData();
+      !error_data.empty()) {
+    msg += " Error msg: " + error_data.str();
+  }
+  return msg;
+}
+
+std::string CommandTelemetryInfo::ToString() const {
+  // Whether this entry was emitted at the start or at the end of the
+  // command-execution.
+  if (stats.m_end.has_value()) {
+    return TelemetryInfo::ToString() + "\n" +
+           ("[CommandTelemetryInfo] - END\n") +
+           ("  target_uuid: " + target_uuid + "\n") +
+           ("  command_uuid: " + command_uuid + "\n") +
+           ("  command_name: " + command_name + "\n") +
+           ("  args: " + args + "\n") +
+           ("  command_runtime: " + GetDuration(stats) + "\n") +
+           (exit_description.has_value() ? exit_description->ToString()
+                                         : "no exit-description") +
+           "\n";
+  } else {
+    return TelemetryInfo::ToString() + "\n" +
+           ("[CommandTelemetryInfo] - START\n") +
+           ("  target_uuid: " + target_uuid + "\n") +
+           ("  command_uuid: " + command_uuid + "\n") +
+           ("  original_command: " + original_command + "\n");
+  }
+}
+
+std::string MiscTelemetryInfo::ToString() const {
+  std::string ret =
+      TelemetryInfo::ToString() + "\n" + ("[MiscTelemetryInfo]\n") +
+      ("  target_uuid: " + target_uuid + "\n") + ("  meta_data:\n");
+
+  for (const auto &kv : meta_data) {
+    ret += ("    " + kv.first + ": " + kv.second + "\n");
+  }
+  return ret;
+}
+
+class StreamTelemetryDestination : public TelemetryDestination {
+public:
+  StreamTelemetryDestination(std::ostream &os, std::string desc)
+      : os(os), desc(desc) {}
+  llvm::Error EmitEntry(const TelemetryInfo *entry) override {
+    if (SanitizeSensitiveFields)
+      os << SanitizeSensitiveFields(entry)->ToString() << "\n";
+    else
+      os << entry->ToString() << "\n";
+    os.flush();
+    return llvm::ErrorSuccess();
+  }
+
+  std::string name() const override { return desc; }
+
+private:
+  std::ostream &os;
+  const std::string desc;
+};
+
+// No-op logger to use when users disable telemetry
+class NoOpTelemeter : public LldbTelemeter {
+public:
+  static std::shared_ptr<LldbTelemeter> CreateInstance(Debugger *debugger) {
+    static std::shared_ptr<LldbTelemeter> ins(new NoOpTelemeter(debugger));
+    return ins;
+  }
+
+  NoOpTelemeter(Debugger *debugger) {}
+  void LogStartup(llvm::StringRef tool_path, TelemetryInfo *entry) override {}
+  void LogExit(llvm::StringRef tool_path, TelemetryInfo *entry) override {}
+  void LogProcessExit(int status, llvm::StringRef exit_string,
+                      TelemetryEventStats stats, Target *target_ptr) override {}
+  void LogMainExecutableLoadStart(lldb::ModuleSP exec_mod,
+                                  TelemetryEventStats stats) override {}
+  void LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
+                                TelemetryEventStats stats) override {}
+
+  void LogCommandStart(llvm::StringRef uuid, llvm::StringRef original_command,
+                       TelemetryEventStats stats, Target *target_ptr) override {
+  }
+  void LogCommandEnd(llvm::StringRef uuid, llvm::StringRef command_name,
+                     llvm::StringRef command_args, TelemetryEventStats stats,
+                     Target *target_ptr, CommandReturnObject *result) override {
+  }
+
+  void
+  LogClientTelemetry(lldb_private::StructuredData::Object *entry) override {}
+
+  void AddDestination(TelemetryDestination *destination) override {}
+  std::string GetNextUUID() override { return ""; }
+};
+
+class BasicTelemeter : public LldbTelemeter {
+public:
+  static std::shared_ptr<BasicTelemeter> CreateInstance(Debugger *);
+
+  virtual ~BasicTelemeter() = default;
+
+  void LogStartup(llvm::StringRef lldb_path, TelemetryInfo *entry) override;
+  void LogExit(llvm::StringRef lldb_path, TelemetryInfo *entry) override;
+
+  void LogProcessExit(int status, llvm::StringRef exit_string,
+                      TelemetryEventStats stats, Target *target_ptr) override;
+  void LogMainExecutableLoadStart(lldb::ModuleSP exec_mod,
+                                  TelemetryEventStats stats) override;
+  void LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
+                                TelemetryEventStats stats) override;
+
+  void LogCommandStart(llvm::StringRef uuid, llvm::StringRef original_command,
+                       TelemetryEventStats stats, Target *target_ptr) override;
+  void LogCommandEnd(llvm::StringRef uuid, llvm::StringRef command_name,
+                     llvm::StringRef command_args, TelemetryEventStats stats,
+                     Target *target_ptr, CommandReturnObject *result) override;
+
+  void LogClientTelemetry(lldb_private::StructuredData::Object *entry) override;
+
+  void AddDestination(TelemetryDestination *destination) override {
+    m_destinations.push_back(destination);
+  }
+
+  std::string GetNextUUID() override {
+    return std::to_string(uuid_seed.fetch_add(1));
+  }
+
+protected:
+  BasicTelemeter(Debugger *debugger);
+
+  void CollectMiscBuildInfo();
+
+private:
+  template <typename EntrySubType> EntrySubType MakeBaseEntry() {
+    EntrySubType entry;
+    entry.session_uuid = m_session_uuid;
+    entry.counter = counter.fetch_add(1);
+    return entry;
+  }
+
+  void EmitToDestinations(const TelemetryInfo *entry);
+
+  Debugger *m_debugger;
+  const std::string m_session_uuid;
+  std::string startup_lldb_path;
+
+  // counting number of entries.
+  std::atomic<size_t> counter = 0;
+
+  std::vector<TelemetryDestination *> m_destinations;
+
+  std::atomic<size_t> uuid_seed = 0;
+};
+
+static std::string MakeUUID(lldb_private::Debugger *debugger) {
+  std::string ret;
+  uint8_t random_bytes[16];
+  if (auto ec = llvm::getRandomBytes(random_bytes, 16)) {
+    std::cerr << "entropy source failure: " + 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;
+}
+
+BasicTelemeter::BasicTelemeter(lldb_private::Debugger *debugger)
+    : m_debugger(debugger), m_session_uuid(MakeUUID(debugger)) {}
+
+std::shared_ptr<BasicTelemeter>
+BasicTelemeter::CreateInstance(lldb_private::Debugger *debugger) {
+  auto *config = GetTelemetryConfig();
+
+  BasicTelemeter *ins = new BasicTelemeter(debugger);
+  for (const std ::string &dest : config->additional_destinations) {
+    if (dest == "stdout") {
+      ins->AddDestination(new StreamTelemetryDestination(std::cout, "stdout"));
+    } else if (dest == "stderr") {
+      ins->AddDestination(new StreamTelemetryDestination(std::cerr, "stderr"));
+    } else {
+      // TODO: handle file paths
+    }
+  }
+
+  return std::shared_ptr<BasicTelemeter>(ins);
+}
+
+void BasicTelemeter::EmitToDestinations(const TelemetryInfo *entry) {
+  // TODO: can do this in a separate thread (need to own the ptrs!).
+  for (auto destination : m_destinations) {
+    auto err = destination->EmitEntry(entry);
+    if (err) {
+      std::cerr << "error emitting to destination: " << destination->name()
+                << "\n";
+    }
+  }
+}
+
+void BasicTelemeter::LogStartup(llvm::StringRef lldb_path,
+                                TelemetryInfo *entry) {
+  startup_lldb_path = lldb_path.str();
+  lldb_private::DebuggerTelemetryInfo startup_info =
+      MakeBaseEntry<lldb_private::DebuggerTelemetryInfo>();
+
+  auto &resolver = lldb_private::HostInfo::GetUserIDResolver();
+  auto opt_username = resolver.GetUserName(lldb_private::HostInfo::GetUserID());
+  if (opt_username)
+    startup_info.username = *opt_username;
+
+  startup_info.lldb_git_sha = lldb_private::GetVersion(); // TODO: fix this
+  startup_info.lldb_path = startup_lldb_path;
+  startup_info.stats = entry->stats;
+
+  llvm::SmallString<64> cwd;
+  if (!llvm::sys::fs::current_path(cwd)) {
+    startup_info.cwd = cwd.c_str();
+  } else {
+    MiscTelemetryInfo misc_info = MakeBaseEntry<MiscTelemetryInfo>();
+    misc_info.meta_data["internal_errors"] = "Cannot determine CWD";
+    EmitToDestinations(&misc_info);
+  }
+
+  std::cout << "emitting startup info\n";
+  EmitToDestinations(&startup_info);
+
+  // Optional part
+  CollectMiscBuildInfo();
+}
+
+void BasicTelemeter::LogExit(llvm::StringRef lldb_path, TelemetryInfo *entry) {
+  std::cout << "debugger exiting at " << lldb_path.str() << "\n";
+  // we should be shutting down the same instance that we started?!
+  // llvm::Assert(startup_lldb_path == lldb_path.str());
+
+  lldb_private::DebuggerTelemetryInfo exit_info =
+      MakeBaseEntry<lldb_private::DebuggerTelemetryInfo>();
+  exit_info.stats = entry->stats;
+  exit_info.lldb_path = startup_lldb_path;
+  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.
+        exit_info.exit_description = {-1, "no process launched."};
+      } else {
+        exit_info.exit_description = {proc->GetExitStatus(), ""};
+        if (const char *description = proc->GetExitDescription())
+          exit_info.exit_description->description = std::string(description);
+      }
+    }
+  }
+  EmitToDestinations(&exit_info);
+}
+
+void BasicTelemeter::LogProcessExit(int status, llvm::StringRef exit_string,
+                                    TelemetryEventStats stats,
+                                    Target *target_ptr) {
+  lldb_private::TargetTelemetryInfo exit_info =
+      MakeBaseEntry<lldb_private::TargetTelemetryInfo>();
+  exit_info.stats = stats;
+  exit_info.target_uuid =
+      target_ptr && !target_ptr->IsDummyTarget()
+          ? target_ptr->GetExecutableModule()->GetUUID().GetAsString()
+          : "";
+  exit_info.exit_description = {status, exit_string.str()};
+
+  std::cout << "emitting process exit ...\n";
+  EmitToDestinations(&exit_info);
+}
+
+void BasicTelemeter::CollectMiscBuildInfo() {
+  // collecting use-case specific data
+}
+
+void BasicTelemeter::LogMainExecutableLoadStart(lldb::ModuleSP exec_mod,
+                                                TelemetryEventStats stats) {
+  TargetTelemetryInfo target_info = MakeBaseEntry<TargetTelemetryInfo>();
+  target_info.stats = std::move(stats);
+  target_info.binary_path =
+      exec_mod->GetFileSpec().GetPathAsConstString().GetCString();
+  target_info.file_format = exec_mod->GetArchitecture().GetArchitectureName();
+  target_info.target_uuid = exec_mod->GetUUID().GetAsString();
+  if (auto err = llvm::sys::fs::file_size(exec_mod->GetFileSpec().GetPath(),
+                                          target_info.binary_size)) {
+    // If there was error obtaining it, just reset the size to 0.
+    // Maybe log the error too?
+    target_info.binary_size = 0;
+  }
+  EmitToDestinations(&target_info);
+}
+
+void BasicTelemeter::LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
+                                              TelemetryEventStats stats) {
+  TargetTelemetryInfo target_info = MakeBaseEntry<TargetTelemetryInfo>();
+  target_info.stats = std::move(stats);
+  target_info.binary_path =
+      exec_mod->GetFileSpec().GetPathAsConstString().GetCString();
+  target_info.file_format = exec_mod->GetArchitecture().GetArchitectureName();
+  target_info.target_uuid = exec_mod->GetUUID().GetAsString();
+  if (auto err = llvm::sys::fs::file_size(exec_mod->GetFileSpec().GetPath(),
+                                          target_info.binary_size)) {
+    // If there was error obtaining it, just reset the size to 0.
+    // Maybe log the error too?
+    target_info.binary_size = 0;
+  }
+  EmitToDestinations(&target_info);
+
+  // Collect some more info,  might be useful?
+  MiscTelemetryInfo misc_info = MakeBaseEntry<MiscTelemetryInfo>();
+  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());
+  EmitToDestinations(&misc_info);
+}
+
+void BasicTelemeter::LogClientTelemetry(
+    lldb_private::StructuredData::Object *entry) {
+  ClientTelemetryInfo client_info = MakeBaseEntry<ClientTelemetryInfo>();
+  auto *dictionary = entry->GetAsDictionary();
+  llvm::StringRef request_name;
+  if (!dictionary->GetValueForKeyAsString("request_name", request_name, "")) {
+    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();
+
+  size_t start_time;
+  size_t end_time;
+  if (!dictionary->GetValueForKeyAsInteger("start_time", start_time) ||
+      !dictionary->GetValueForKeyAsInteger("end_time", end_time)) {
+    MiscTelemetryInfo misc_info = MakeBaseEntry<MiscTelemetryInfo>();
+    misc_info.meta_data["internal_errors"] =
+        "Cannot determine start/end time from client entry";
+    EmitToDestinations(&misc_info);
+    return;
+  }
+
+  EmitToDestinations(&client_info);
+}
+
+void BasicTelemeter::LogCommandStart(llvm::StringRef uuid,
+                                     llvm::StringRef original_command,
+                                     TelemetryEventStats stats,
+                                     Target *target_ptr) {
+
+  lldb_private::CommandTelemetryInfo command_info =
+      MakeBaseEntry<lldb_private::CommandTelemetryInfo>();
+
+  // If we have a target attached to this command, then get the UUID.
+  command_info.target_uuid = "";
+  if (target_ptr && target_ptr->GetExecutableModule() != nullptr) {
+    command_info.target_uuid =
+        target_ptr->GetExecutableModule()->GetUUID().GetAsString();
+  }
+  command_info.command_uuid = uuid.str();
+  command_info.original_command = original_command.str();
+  command_info.stats = std::move(stats);
+
+  EmitToDestinations(&command_info);
+}
+
+void BasicTelemeter::LogCommandEnd(llvm::StringRef uuid,
+                                   llvm::StringRef command_name,
+                                   llvm::StringRef command_args,
+                                   TelemetryEventStats stats,
+                                   Target *target_ptr,
+                                   CommandReturnObject *result) {
+
+  lldb_private::CommandTelemetryInfo command_info =
+      MakeBaseEntry<lldb_private::CommandTelemetryInfo>();
+
+  // If we have a target attached to this command, then get the UUID.
+  command_info.target_uuid = "";
+  if (target_ptr && target_ptr->GetExecutableModule() != nullptr) {
+    command_info.target_uuid =
+        target_ptr->GetExecutableModule()->GetUUID().GetAsString();
+  }
+  command_info.command_uuid = uuid.str();
+  command_info.command_name = command_name.str();
+  command_info.args = command_args.str();
+  command_info.stats = std::move(stats);
+  command_info.exit_description = {result->Succeeded() ? 0 : -1,
+                                   StatusToString(result)};
+  EmitToDestinations(&command_info);
+}
+
+llvm::StringRef parse_value(llvm::StringRef str, llvm::StringRef label) {
+  return str.substr(label.size()).trim();
+}
+
+bool parse_field(llvm::StringRef str, llvm::StringRef label) {
+  if (parse_value(str, label) == "true")
+    return true;
+  return false;
+}
+
+llvm::telemetry::TelemetryConfig *GetTelemetryConfig() {
+  static llvm::telemetry::TelemetryConfig *config = []() {
+    bool enable_telemetry = false;
+    std::vector<std::string> additional_destinations;
+
+    // Look in the $HOME/.lldb_telemetry_config file to populate the struct
+    llvm::SmallString<64> init_file;
+    FileSystem::Instance().GetHomeDirectory(init_file);
+    llvm::sys::path::append(init_file, ".lldb_telemetry_config");
+    FileSystem::Instance().Resolve(init_file);
+    if (llvm::sys::fs::exists(init_file)) {
+      auto contents = llvm::MemoryBuffer::getFile(init_file, /*IsText*/ true);
+      if (contents) {
+        llvm::line_iterator iter =
+            llvm::line_iterator(contents->get()->getMemBufferRef());
+        for (; !iter.is_at_eof(); ++iter) {
+          if (iter->starts_with("enable_telemetry:")) {
+            enable_telemetry = parse_field(*iter, "enable_telemetry:");
+          } else if (iter->starts_with("destination:")) {
+            llvm::StringRef dest = parse_value(*iter, "destination:");
+            if (dest == "stdout") {
+              additional_destinations.push_back("stdout");
+            } else if (dest == "stderr") {
+              additional_destinations.push_back("stderr");
+            } else {
+              additional_destinations.push_back(dest.str());
+            }
+          }
+        }
+      } else {
+        std::cerr << "Error reading config file at " << init_file.c_str()
+                  << "\n";
+      }
+    }
+
+    auto *ret = new llvm::telemetry::TelemetryConfig{enable_telemetry,
+                                                     additional_destinations};
+    if (ApplyVendorSpecificConfigs)
+      ApplyVendorSpecificConfigs(ret);
+
+    return ret;
+  }();
+  return config;
+}
+
+std::shared_ptr<LldbTelemeter>
+LldbTelemeter::CreateInstance(lldb_private::Debugger *debugger) {
+  auto *config = GetTelemetryConfig();
+  if (!config->enable_telemetry) {
+    return NoOpTelemeter::CreateInstance(debugger);
+  }
+
+  if (CreateVendorSpecificTelemeter)
+    return CreateVendorSpecificTelemeter(config);
+  return BasicTelemeter::CreateInstance(debugger);
+}
+} // namespace lldb_private
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index fc07168b6c0ac..e60f9e7d3838d 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -12,6 +12,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <typeinfo>
 #include <vector>
 
 #include "Commands/CommandObjectApropos.h"
@@ -56,7 +57,10 @@
 #include "lldb/Utility/StructuredData.h"
 #include "lldb/Utility/Timer.h"
 
+#include "lldb/Core/Telemetry.h"
 #include "lldb/Host/Config.h"
+#include "lldb/Interpreter/CommandObject.h"
+
 #if LLDB_ENABLE_LIBEDIT
 #include "lldb/Host/Editline.h"
 #endif
@@ -1848,8 +1852,28 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
                                        LazyBool lazy_add_to_history,
                                        CommandReturnObject &result,
                                        bool force_repeat_command) {
+  TelemetryEventStats command_stats(std::chrono::steady_clock::now());
+  auto telemeter = GetDebugger().GetTelemeter();
+  // Generate a UUID for this command so the logger can match
+  // the start/end entries correctly.
+  const std::string command_uuid = telemeter->GetNextUUID();
+
+  telemeter->LogCommandStart(command_uuid, command_line, command_stats,
+                             GetExecutionContext().GetTargetPtr());
+
   std::string command_string(command_line);
   std::string original_command_string(command_line);
+  std::string parsed_command_args;
+  CommandObject *cmd_obj = nullptr;
+
+  auto log_on_exit = llvm::make_scope_exit([&]() {
+    command_stats.m_end = std::chrono::steady_clock::now();
+    llvm::StringRef command_name =
+        cmd_obj ? cmd_obj->GetCommandName() : "<not found>";
+    telemeter->LogCommandEnd(command_uuid, command_name, parsed_command_args,
+                             command_stats,
+                             GetExecutionContext().GetTargetPtr(), &result);
+  });
 
   Log *log = GetLog(LLDBLog::Commands);
   llvm::PrettyStackTraceFormat stack_trace("HandleCommand(command = \"%s\")",
@@ -1887,11 +1911,10 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
 
   bool empty_command = false;
   bool comment_command = false;
-  if (command_string.empty())
+  if (command_string.empty()) {
     empty_command = true;
-  else {
+  } else {
     const char *k_space_characters = "\t\n\v\f\r ";
-
     size_t non_space = command_string.find_first_not_of(k_space_characters);
     // Check for empty line or comment line (lines whose first non-space
     // character is the comment character for this interpreter)
@@ -1954,7 +1977,7 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
   // From 1 above, we can determine whether the Execute function wants raw
   // input or not.
 
-  CommandObject *cmd_obj = ResolveCommandImpl(command_string, result);
+  cmd_obj = ResolveCommandImpl(command_string, result);
 
   // We have to preprocess the whole command string for Raw commands, since we
   // don't know the structure of the command.  For parsed commands, we only
@@ -2015,30 +2038,29 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
     if (add_to_history)
       m_command_history.AppendString(original_command_string);
 
-    std::string remainder;
     const std::size_t actual_cmd_name_len = cmd_obj->GetCommandName().size();
     if (actual_cmd_name_len < command_string.length())
-      remainder = command_string.substr(actual_cmd_name_len);
+      parsed_command_args = command_string.substr(actual_cmd_name_len);
 
     // Remove any initial spaces
-    size_t pos = remainder.find_first_not_of(k_white_space);
+    size_t pos = parsed_command_args.find_first_not_of(k_white_space);
     if (pos != 0 && pos != std::string::npos)
-      remainder.erase(0, pos);
+      parsed_command_args.erase(0, pos);
 
     LLDB_LOGF(
         log, "HandleCommand, command line after removing command name(s): '%s'",
-        remainder.c_str());
+        parsed_command_args.c_str());
 
     // To test whether or not transcript should be saved, `transcript_item` is
     // used instead of `GetSaveTrasncript()`. This is because the latter will
     // fail when the command is "settings set interpreter.save-transcript true".
     if (transcript_item) {
       transcript_item->AddStringItem("commandName", cmd_obj->GetCommandName());
-      transcript_item->AddStringItem("commandArguments", remainder);
+      transcript_item->AddStringItem("commandArguments", parsed_command_args);
     }
 
     ElapsedTime elapsed(execute_time);
-    cmd_obj->Execute(remainder.c_str(), result);
+    cmd_obj->Execute(parsed_command_args.c_str(), result);
   }
 
   LLDB_LOGF(log, "HandleCommand, command %s",
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index dc7f6c9e86a47..ededeb9e8b28f 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include <atomic>
+#include <chrono>
 #include <memory>
 #include <mutex>
 #include <optional>
@@ -477,7 +478,8 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp,
       m_clear_thread_plans_on_stop(false), m_force_next_event_delivery(false),
       m_last_broadcast_state(eStateInvalid), m_destroy_in_process(false),
       m_can_interpret_function_calls(false), m_run_thread_plan_lock(),
-      m_can_jit(eCanJITDontKnow) {
+      m_can_jit(eCanJITDontKnow),
+      m_event_stats(std::chrono::steady_clock::now()) {
   CheckInWithManager();
 
   Log *log = GetLog(LLDBLog::Object);
@@ -1085,6 +1087,7 @@ bool Process::SetExitStatus(int status, llvm::StringRef exit_string) {
   // Use a mutex to protect setting the exit status.
   std::lock_guard<std::mutex> guard(m_exit_status_mutex);
 
+  m_event_stats.m_end = std::chrono::steady_clock::now();
   Log *log(GetLog(LLDBLog::State | LLDBLog::Process));
   LLDB_LOG(log, "(plugin = {0} status = {1} ({1:x8}), description=\"{2}\")",
            GetPluginName(), status, exit_string);
@@ -1098,6 +1101,8 @@ bool Process::SetExitStatus(int status, llvm::StringRef exit_string) {
         GetPluginName());
     return false;
   }
+  GetTarget().GetDebugger().GetTelemeter()->LogProcessExit(
+      status, exit_string, m_event_stats, &GetTarget());
 
   m_exit_status = status;
   if (!exit_string.empty())
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index ec0da8a1378a8..60bd544bd2a92 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -24,6 +24,7 @@
 #include "lldb/Core/Section.h"
 #include "lldb/Core/SourceManager.h"
 #include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/Core/ValueObject.h"
 #include "lldb/Core/ValueObjectConstResult.h"
 #include "lldb/Expression/DiagnosticManager.h"
@@ -67,6 +68,7 @@
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SetVector.h"
 
+#include <chrono>
 #include <memory>
 #include <mutex>
 #include <optional>
@@ -1470,11 +1472,22 @@ void Target::DidExec() {
 
 void Target::SetExecutableModule(ModuleSP &executable_sp,
                                  LoadDependentFiles load_dependent_files) {
+
   Log *log = GetLog(LLDBLog::Target);
   ClearModules(false);
-
   if (executable_sp) {
+    TelemetryEventStats load_executable_stats(std::chrono::steady_clock::now());
+    m_debugger.GetTelemeter()->LogMainExecutableLoadStart(
+        executable_sp, load_executable_stats);
+
+    auto log_on_exit = llvm::make_scope_exit([&]() {
+      load_executable_stats.m_end = std::chrono::steady_clock::now();
+      m_debugger.GetTelemeter()->LogMainExecutableLoadEnd(
+          executable_sp, load_executable_stats);
+    });
+
     ElapsedTime elapsed(m_stats.GetCreateTime());
+
     LLDB_SCOPED_TIMERF("Target::SetExecutableModule (executable = '%s')",
                        executable_sp->GetFileSpec().GetPath().c_str());
 
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 0196aed819f2b..aa43d845ccfbe 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -6,6 +6,11 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "DAP.h"
+#include "LLDBUtils.h"
+#include "lldb/API/SBStructuredData.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FormatVariadic.h"
 #include <chrono>
 #include <cstdarg>
 #include <fstream>
@@ -14,7 +19,6 @@
 
 #include "DAP.h"
 #include "LLDBUtils.h"
-#include "lldb/API/SBCommandInterpreter.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/FormatVariadic.h"
 
diff --git a/llvm/include/llvm/Telemetry/Telemetry.h b/llvm/include/llvm/Telemetry/Telemetry.h
new file mode 100644
index 0000000000000..e34b228b219c1
--- /dev/null
+++ b/llvm/include/llvm/Telemetry/Telemetry.h
@@ -0,0 +1,99 @@
+#ifndef LVM_TELEMETRY_TELEMETRY_H
+#define LVM_TELEMETRY_TELEMETRY_H
+
+#include <chrono>
+#include <ctime>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+
+namespace llvm {
+namespace telemetry {
+
+using SteadyTimePoint = std::chrono::time_point<std::chrono::steady_clock>;
+
+struct TelemetryConfig {
+  // If true, telemetry will be enabled.
+  bool enable_telemetry;
+
+  // Additional destinations to send the logged entries.
+  // Could be stdout, stderr, or some local paths.
+  // Note: these are destinations are __in addition to__ whatever the default
+  // destination(s) are, as implemented by vendors.
+  std::vector<std::string> additional_destinations;
+};
+
+struct TelemetryEventStats {
+  // REQUIRED: Start time of event
+  SteadyTimePoint m_start;
+  // OPTIONAL: End time of event - may be empty if not meaningful.
+  std::optional<SteadyTimePoint> m_end;
+  // TBD: could add some memory stats here too?
+
+  TelemetryEventStats() = default;
+  TelemetryEventStats(SteadyTimePoint start) : m_start(start) {}
+  TelemetryEventStats(SteadyTimePoint start, SteadyTimePoint end)
+      : m_start(start), m_end(end) {}
+
+  std::string ToString() const;
+};
+
+struct ExitDescription {
+  int exit_code;
+  std::string description;
+
+  std::string ToString() const;
+};
+
+// The base class contains the basic set of data.
+// Downstream implementations can add more fields as needed.
+struct TelemetryInfo {
+  // A "session" corresponds to every time the tool starts.
+  // All entries emitted for the same session will have
+  // the same session_uuid
+  std::string session_uuid;
+
+  TelemetryEventStats stats;
+
+  std::optional<ExitDescription> exit_description;
+
+  // Counting number of entries.
+  // (For each set of entries with the same session_uuid, this value should
+  // be unique for each entry)
+  size_t counter;
+
+  TelemetryInfo() = default;
+  ~TelemetryInfo() = default;
+  virtual std::string ToString() const;
+};
+
+// Where/how to send the telemetry entries.
+class TelemetryDestination {
+public:
+  virtual ~TelemetryDestination() = default;
+  virtual Error EmitEntry(const TelemetryInfo *entry) = 0;
+  virtual std::string name() const = 0;
+};
+
+class Telemeter {
+public:
+  virtual ~Telemeter() = default;
+
+  // Invoked upon tool startup
+  virtual void LogStartup(llvm::StringRef tool_path, TelemetryInfo *entry) = 0;
+
+  // Invoked upon tool exit.
+  virtual void LogExit(llvm::StringRef tool_path, TelemetryInfo *entry) = 0;
+
+  virtual void AddDestination(TelemetryDestination *destination) = 0;
+};
+
+} // namespace telemetry
+} // namespace llvm
+
+#endif // LVM_TELEMETRY_TELEMETRY_H
diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt
index 638c3bd6f90f5..1d2fb32922648 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 0000000000000..8208bdadb05e9
--- /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 0000000000000..f7100685ee2d2
--- /dev/null
+++ b/llvm/lib/Telemetry/Telemetry.cpp
@@ -0,0 +1,32 @@
+#include "llvm/Telemetry/Telemetry.h"
+
+namespace llvm {
+namespace telemetry {
+
+std::string TelemetryEventStats::ToString() const {
+  std::string result;
+  llvm::raw_string_ostream os(result);
+  os << "start_timestamp: " << m_start.time_since_epoch().count()
+     << ", end_timestamp: "
+     << (m_end.has_value() ? std::to_string(m_end->time_since_epoch().count())
+                           : "<NONE>");
+  return result;
+}
+
+std::string ExitDescription::ToString() const {
+  return "exit_code: " + std::to_string(exit_code) +
+         ", description: " + description + "\n";
+}
+
+std::string TelemetryInfo::ToString() const {
+  return "[TelemetryInfo]\n" + ("  session_uuid:" + session_uuid + "\n") +
+         ("  stats: " + stats.ToString() + "\n") +
+         ("  exit_description: " +
+          (exit_description.has_value() ? exit_description->ToString()
+                                        : "<NONE>") +
+          "\n") +
+         ("  counter: " + std::to_string(counter) + "\n");
+}
+
+} // namespace telemetry
+} // namespace llvm
\ No newline at end of file

>From 09ac6e101e1988f650a72fcd5cce715defee1690 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Wed, 10 Jul 2024 15:27:38 -0400
Subject: [PATCH 2/3] [llvm]Added lib/Telemetry  - Provide a base API for llvm
 Telemetry  - Provide some concrete implementation for it in lldb/Telemetry

---
 lldb/include/lldb/API/SBDebugger.h            |   4 +
 lldb/include/lldb/Core/Debugger.h             |   8 +
 lldb/include/lldb/Core/Telemetry.h            | 153 +++++
 lldb/include/lldb/Target/Process.h            |   3 +
 lldb/source/API/SBDebugger.cpp                |   9 +
 lldb/source/Core/CMakeLists.txt               |   2 +
 lldb/source/Core/Debugger.cpp                 |  31 +-
 lldb/source/Core/Telemetry.cpp                | 618 ++++++++++++++++++
 .../source/Interpreter/CommandInterpreter.cpp |  44 +-
 lldb/source/Target/Process.cpp                |   7 +-
 lldb/source/Target/Target.cpp                 |  15 +-
 lldb/tools/lldb-dap/DAP.cpp                   |   6 +-
 llvm/include/llvm/Telemetry/Telemetry.h       |  99 +++
 llvm/lib/CMakeLists.txt                       |   1 +
 llvm/lib/Telemetry/CMakeLists.txt             |   6 +
 llvm/lib/Telemetry/Telemetry.cpp              |  32 +
 16 files changed, 1023 insertions(+), 15 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/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h
index 84ea9c0f772e1..de09995679ad9 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -9,10 +9,12 @@
 #ifndef LLDB_API_SBDEBUGGER_H
 #define LLDB_API_SBDEBUGGER_H
 
+#include <chrono>
 #include <cstdio>
 
 #include "lldb/API/SBDefines.h"
 #include "lldb/API/SBPlatform.h"
+#include "lldb/API/SBStructuredData.h"
 
 namespace lldb_private {
 class CommandPluginInterfaceImplementation;
@@ -245,6 +247,8 @@ class LLDB_API SBDebugger {
 
   lldb::SBTarget GetDummyTarget();
 
+  void SendTelemetry(SBStructuredData *entry);
+
   // Return true if target is deleted from the target list of the debugger.
   bool DeleteTarget(lldb::SBTarget &target);
 
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index a72c2596cc2c5..13a444e438fec 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -19,6 +19,7 @@
 #include "lldb/Core/FormatEntity.h"
 #include "lldb/Core/IOHandler.h"
 #include "lldb/Core/SourceManager.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/Core/UserSettingsController.h"
 #include "lldb/Host/HostThread.h"
 #include "lldb/Host/StreamFile.h"
@@ -31,6 +32,7 @@
 #include "lldb/Utility/Diagnostics.h"
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/Utility/Status.h"
+#include "lldb/Utility/StructuredData.h"
 #include "lldb/Utility/UserID.h"
 #include "lldb/lldb-defines.h"
 #include "lldb/lldb-enumerations.h"
@@ -137,6 +139,10 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
 
   lldb::StreamFileSP GetErrorStreamSP() { return m_error_stream_sp; }
 
+  std::shared_ptr<LldbTelemeter> GetTelemeter() { return m_telemeter; }
+
+  void SendClientTelemetry(lldb_private::StructuredData::Object *entry);
+
   File &GetInputFile() { return *m_input_file_sp; }
 
   File &GetOutputFile() { return m_output_stream_sp->GetFile(); }
@@ -754,6 +760,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
   uint32_t m_interrupt_requested = 0; ///< Tracks interrupt requests
   std::mutex m_interrupt_mutex;
 
+  std::shared_ptr<LldbTelemeter> m_telemeter;
   // Events for m_sync_broadcaster
   enum {
     eBroadcastBitEventThreadIsListening = (1 << 0),
@@ -766,6 +773,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
 
   Debugger(const Debugger &) = delete;
   const Debugger &operator=(const Debugger &) = delete;
+  TelemetryEventStats stats;
 };
 
 } // namespace lldb_private
diff --git a/lldb/include/lldb/Core/Telemetry.h b/lldb/include/lldb/Core/Telemetry.h
new file mode 100644
index 0000000000000..15cc3139764ad
--- /dev/null
+++ b/lldb/include/lldb/Core/Telemetry.h
@@ -0,0 +1,153 @@
+#ifndef LLDB_CORE_TELEMETRY_H
+#define LLDB_CORE_TELEMETRY_H
+
+#include <chrono>
+#include <ctime>
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_map>
+
+#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/Telemetry/Telemetry.h"
+
+using namespace llvm::telemetry;
+
+namespace lldb_private {
+
+struct DebuggerTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
+  std::string username;
+  std::string lldb_git_sha;
+  std::string lldb_path;
+  std::string cwd;
+
+  std::string ToString() const override;
+};
+
+struct TargetTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
+  // All entries emitted for the same SBTarget will have the same
+  // target_uuid.
+  std::string target_uuid;
+  std::string file_format;
+
+  std::string binary_path;
+  size_t binary_size;
+
+  std::string ToString() const override;
+};
+
+// Entry from client (eg., SB-API)
+struct ClientTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
+  std::string request_name;
+  std::string error_msg;
+  std::string ToString() const override;
+};
+
+struct CommandTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
+  // 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 TelemetryConfig struct)
+  std::string original_command;
+  std::string args;
+
+  std::string ToString() const override;
+};
+
+// The "catch-all" entry to store a set of custom/non-standard
+// data.
+struct MiscTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
+  // 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::unordered_map<std::string, std::string> meta_data;
+
+  std::string ToString() const override;
+};
+
+class LldbTelemeter : public llvm::telemetry::Telemeter {
+public:
+  static std::shared_ptr<LldbTelemeter> CreateInstance(Debugger *);
+
+  virtual ~LldbTelemeter() = default;
+
+  // void LogStartup(llvm::StringRef lldb_path,
+  //                 TelemetryInfo *entry) override;
+  // void LogExit(llvm::StringRef lldb_path, TelemetryInfo *entry)
+  // override;
+
+  // Invoked upon process exit
+  virtual void LogProcessExit(int status, llvm::StringRef exit_string,
+                              TelemetryEventStats stats,
+                              Target *target_ptr) = 0;
+
+  // 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(lldb::ModuleSP exec_mod,
+                                          TelemetryEventStats stats) = 0;
+  virtual void LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
+                                        TelemetryEventStats stats) = 0;
+
+  // 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(llvm::StringRef uuid,
+                               llvm::StringRef original_command,
+                               TelemetryEventStats stats,
+                               Target *target_ptr) = 0;
+  virtual void LogCommandEnd(llvm::StringRef uuid, llvm::StringRef command_name,
+                             llvm::StringRef command_args,
+                             TelemetryEventStats stats, Target *target_ptr,
+                             CommandReturnObject *result) = 0;
+
+  virtual std::string GetNextUUID() = 0;
+
+  // For client (eg., SB API) to send telemetry entries.
+  virtual void
+  LogClientTelemetry(lldb_private::StructuredData::Object *entry) = 0;
+};
+
+// Logger configs: LLDB users can also supply their own configs via:
+// $HOME/.lldb_telemetry_config
+//
+// We can propose simple syntax: <field_name><colon><value>
+// Eg.,
+// enable_telemetry:true
+// destination:stdout
+// destination:stderr
+// destination:/path/to/some/file
+//
+// The allowed field_name values are:
+//  * enable_telemetry
+//       If the fields are specified more than once, the last line will take
+//       precedence If enable_logging is set to false, no logging will occur.
+//  * destination.
+//       This is allowed to be specified multiple times - it will add to the
+//       default (ie, specified by vendor) list of destinations.
+//       The value can be either:
+//          + one of the two magic values "stdout" or "stderr".
+//          + a path to a local file
+// !!NOTE!!: We decided to use a separate file instead of the existing settings
+//         file because that file is parsed too late in the process and by the
+//         there might have been lots of telemetry-entries that need to be
+//         sent already.
+//         This approach avoid losing log entries if LLDB crashes during init.
+TelemetryConfig *GetTelemetryConfig();
+
+} // namespace lldb_private
+#endif // LLDB_CORE_TELEMETRY_H
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index ceaf547ebddaf..c35ff397a203f 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -28,6 +28,7 @@
 #include "lldb/Core/LoadedModuleInfoList.h"
 #include "lldb/Core/PluginInterface.h"
 #include "lldb/Core/SourceManager.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/Core/ThreadSafeValue.h"
 #include "lldb/Core/ThreadedCommunication.h"
 #include "lldb/Core/UserSettingsController.h"
@@ -3285,6 +3286,8 @@ void PruneThreadPlans();
 
   Process(const Process &) = delete;
   const Process &operator=(const Process &) = delete;
+
+  TelemetryEventStats m_event_stats;
 };
 
 /// RAII guard that should be acquired when an utility function is called within
diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index fb035a36e7d74..3629fcc2574af 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -34,6 +34,7 @@
 #include "lldb/API/SBTypeNameSpecifier.h"
 #include "lldb/API/SBTypeSummary.h"
 #include "lldb/API/SBTypeSynthetic.h"
+#include <iostream>
 
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/DebuggerEvents.h"
@@ -968,6 +969,14 @@ SBTarget SBDebugger::GetDummyTarget() {
   return sb_target;
 }
 
+void SBDebugger::SendTelemetry(SBStructuredData *entry) {
+  if (lldb_private::Debugger *debugger = this->get()) {
+    debugger->SendClientTelemetry(entry->m_impl_up->GetObjectSP().get());
+  } else {
+    std::cerr << " --- cannot send telemetry entry - debugger is null\n";
+  }
+}
+
 bool SBDebugger::DeleteTarget(lldb::SBTarget &target) {
   LLDB_INSTRUMENT_VA(this, target);
 
diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt
index dbc620b91b1ed..d7f8297fa555c 100644
--- a/lldb/source/Core/CMakeLists.txt
+++ b/lldb/source/Core/CMakeLists.txt
@@ -52,6 +52,7 @@ add_lldb_library(lldbCore
   SourceLocationSpec.cpp
   SourceManager.cpp
   StreamAsynchronousIO.cpp
+  Telemetry.cpp
   ThreadedCommunication.cpp
   UserSettingsController.cpp
   Value.cpp
@@ -94,6 +95,7 @@ add_lldb_library(lldbCore
     Support
     Demangle
     TargetParser
+    Telemetry
   )
 
 add_dependencies(lldbCore
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 309e01e456580..2947d2d537767 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/Progress.h"
 #include "lldb/Core/StreamAsynchronousIO.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/DataFormatters/DataVisualization.h"
 #include "lldb/Expression/REPL.h"
 #include "lldb/Host/File.h"
@@ -53,6 +54,8 @@
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/StreamString.h"
 #include "lldb/lldb-enumerations.h"
+#include "llvm/Telemetry/Telemetry.h"
+#include <chrono>
 
 #if defined(_WIN32)
 #include "lldb/Host/windows/PosixApi.h"
@@ -733,12 +736,20 @@ void Debugger::InstanceInitialize() {
 
 DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
                                     void *baton) {
+  SteadyTimePoint start_time = std::chrono::steady_clock::now();
   DebuggerSP debugger_sp(new Debugger(log_callback, baton));
+  debugger_sp->stats.m_start = start_time;
   if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
     std::lock_guard<std::recursive_mutex> guard(*g_debugger_list_mutex_ptr);
     g_debugger_list_ptr->push_back(debugger_sp);
   }
   debugger_sp->InstanceInitialize();
+  TelemetryEventStats init_stats(start_time, std::chrono::steady_clock::now());
+  llvm::telemetry::TelemetryInfo entry;
+  entry.stats = {start_time, std::chrono::steady_clock::now()};
+  debugger_sp->m_telemeter->LogStartup(
+      HostInfo::GetProgramFileSpec().GetPathAsConstString().GetCString(),
+      &entry);
   return debugger_sp;
 }
 
@@ -860,7 +871,8 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
       m_sync_broadcaster(nullptr, "lldb.debugger.sync"),
       m_broadcaster(m_broadcaster_manager_sp,
                     GetStaticBroadcasterClass().str()),
-      m_forward_listener_sp(), m_clear_once() {
+      m_forward_listener_sp(), m_clear_once(),
+      m_telemeter(LldbTelemeter::CreateInstance(this)) {
   // Initialize the debugger properties as early as possible as other parts of
   // LLDB will start querying them during construction.
   m_collection_sp->Initialize(g_debugger_properties);
@@ -952,6 +964,18 @@ void Debugger::Clear() {
   //     static void Debugger::Destroy(lldb::DebuggerSP &debugger_sp);
   //     static void Debugger::Terminate();
   llvm::call_once(m_clear_once, [this]() {
+    // Log the "quit" event.
+    // Note: this session_stats include the time since LLDB starts till quit
+    // (now).
+    // TBD: we could also record stats for *just* the quit action, if needed?
+    //      (ie., how long it takes to run all these cleanup functions?)
+    llvm::telemetry::TelemetryInfo entry;
+    entry.stats = {/*start_session*/ stats.m_start,
+                   /*end_session*/ std::chrono::steady_clock::now()};
+    m_telemeter->LogExit(
+        HostInfo::GetProgramFileSpec().GetPathAsConstString().GetCString(),
+        &entry);
+
     ClearIOHandlers();
     StopIOHandlerThread();
     StopEventHandlerThread();
@@ -2231,6 +2255,11 @@ Status Debugger::RunREPL(LanguageType language, const char *repl_options) {
   return err;
 }
 
+void Debugger::SendClientTelemetry(
+    lldb_private::StructuredData::Object *entry) {
+  m_telemeter->LogClientTelemetry(entry);
+}
+
 llvm::ThreadPoolInterface &Debugger::GetThreadPool() {
   assert(g_thread_pool &&
          "Debugger::GetThreadPool called before Debugger::Initialize");
diff --git a/lldb/source/Core/Telemetry.cpp b/lldb/source/Core/Telemetry.cpp
new file mode 100644
index 0000000000000..b8ea75906a919
--- /dev/null
+++ b/lldb/source/Core/Telemetry.cpp
@@ -0,0 +1,618 @@
+
+//===-- 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 <stdbool.h>
+#include <sys/auxv.h>
+
+#include <memory>
+
+#include <chrono>
+#include <cstdlib>
+#include <ctime>
+#include <fstream>
+#include <iostream>
+#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/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/FileSystem.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Telemetry/Telemetry.h"
+
+#ifdef HAS_VENDOR_TELEMETRY_PLUGINS
+// TODO: could make this path a build-variable rather than hard-coded
+#include "lldb/Core/VendorTelemetryPlugin.h"
+
+// This must include definitions for these functions
+extern void ApplyVendorSpecificConfigs(
+    llvm::telemetry::TelemetryConfig *config) /* __attribute__((weak))*/;
+extern std::shared_ptr<llvm::telemetry::TelemetryInfo> SanitizeSensitiveFields(
+    const llvm::telemetry::TelemetryInfo *entry) /*__attribute__((weak))*/;
+extern std::shared_ptr<lldb_private::LldbTelemeter>
+CreateVendorSpecificTelemeter(
+    llvm::telemetry::TelemetryConfig *config) /*__attribute__((weak))*/;
+#endif
+
+namespace lldb_private {
+
+static std::string GetDuration(const TelemetryEventStats &stats) {
+  if (stats.m_end.has_value())
+    return std::to_string((stats.m_end.value() - stats.m_start).count()) +
+           "(nanosec)";
+  return "<NONE>";
+}
+
+std::string DebuggerTelemetryInfo::ToString() const {
+  std::string duration_desc =
+      (exit_description.has_value() ? "  lldb session duration: "
+                                    : "  lldb startup duration: ") +
+      std::to_string((stats.m_end.value() - stats.m_start).count()) +
+      "(nanosec)\n";
+
+  return TelemetryInfo::ToString() + "\n" + ("[DebuggerTelemetryInfo]\n") +
+         ("  username: " + username + "\n") +
+         ("  lldb_git_sha: " + lldb_git_sha + "\n") +
+         ("  lldb_path: " + lldb_path + "\n") + ("  cwd: " + cwd + "\n") +
+         duration_desc + "\n";
+}
+
+std::string ClientTelemetryInfo::ToString() const {
+  return TelemetryInfo::ToString() + "\n" + ("[DapRequestInfoEntry]\n") +
+         ("  request_name: " + request_name + "\n") +
+         ("  request_duration: " + GetDuration(stats) + "(nanosec)\n") +
+         ("  error_msg: " + error_msg + "\n");
+}
+
+std::string TargetTelemetryInfo::ToString() const {
+  std::string exit_or_load_desc;
+  if (exit_description.has_value()) {
+    // If this entry was emitted for an exit
+    exit_or_load_desc = "  process_duration: " + GetDuration(stats) +
+                        "  exit: " + exit_description->ToString() + "\n";
+  } else {
+    // This was emitted for a load event.
+    // See if it was the start-load or end-load entry
+    if (stats.m_end.has_value()) {
+      exit_or_load_desc =
+          "  startup_init_duration: " + GetDuration(stats) + "\n";
+    } else {
+      exit_or_load_desc = " startup_init_start\n";
+    }
+  }
+  return TelemetryInfo::ToString() + "\n" + ("[TargetTelemetryInfo]\n") +
+         ("  target_uuid: " + target_uuid + "\n") +
+         ("  file_format: " + file_format + "\n") +
+         ("  binary_path: " + binary_path + "\n") +
+         ("  binary_size: " + std::to_string(binary_size) + "\n") +
+         exit_or_load_desc;
+}
+
+static std::string StatusToString(CommandReturnObject *result) {
+  std::string msg;
+  switch (result->GetStatus()) {
+  case lldb::eReturnStatusInvalid:
+    msg = "invalid";
+    break;
+  case lldb::eReturnStatusSuccessFinishNoResult:
+    msg = "success_finish_no_result";
+    break;
+  case lldb::eReturnStatusSuccessFinishResult:
+    msg = "success_finish_result";
+    break;
+  case lldb::eReturnStatusSuccessContinuingNoResult:
+    msg = "success_continuing_no_result";
+    break;
+  case lldb::eReturnStatusSuccessContinuingResult:
+    msg = "success_continuing_result";
+    break;
+  case lldb::eReturnStatusStarted:
+    msg = "started";
+    break;
+  case lldb::eReturnStatusFailed:
+    msg = "failed";
+    break;
+  case lldb::eReturnStatusQuit:
+    msg = "quit";
+    break;
+  }
+  if (llvm::StringRef error_data = result->GetErrorData();
+      !error_data.empty()) {
+    msg += " Error msg: " + error_data.str();
+  }
+  return msg;
+}
+
+std::string CommandTelemetryInfo::ToString() const {
+  // Whether this entry was emitted at the start or at the end of the
+  // command-execution.
+  if (stats.m_end.has_value()) {
+    return TelemetryInfo::ToString() + "\n" +
+           ("[CommandTelemetryInfo] - END\n") +
+           ("  target_uuid: " + target_uuid + "\n") +
+           ("  command_uuid: " + command_uuid + "\n") +
+           ("  command_name: " + command_name + "\n") +
+           ("  args: " + args + "\n") +
+           ("  command_runtime: " + GetDuration(stats) + "\n") +
+           (exit_description.has_value() ? exit_description->ToString()
+                                         : "no exit-description") +
+           "\n";
+  } else {
+    return TelemetryInfo::ToString() + "\n" +
+           ("[CommandTelemetryInfo] - START\n") +
+           ("  target_uuid: " + target_uuid + "\n") +
+           ("  command_uuid: " + command_uuid + "\n") +
+           ("  original_command: " + original_command + "\n");
+  }
+}
+
+std::string MiscTelemetryInfo::ToString() const {
+  std::string ret =
+      TelemetryInfo::ToString() + "\n" + ("[MiscTelemetryInfo]\n") +
+      ("  target_uuid: " + target_uuid + "\n") + ("  meta_data:\n");
+
+  for (const auto &kv : meta_data) {
+    ret += ("    " + kv.first + ": " + kv.second + "\n");
+  }
+  return ret;
+}
+
+class StreamTelemetryDestination : public TelemetryDestination {
+public:
+  StreamTelemetryDestination(std::ostream &os, std::string desc)
+      : os(os), desc(desc) {}
+  llvm::Error EmitEntry(const TelemetryInfo *entry) override {
+    // Unless there exists a custom (vendor-defined) data-cleanup
+    // for printing, upstream Telemetry should not leak anything other than the
+    // basic.
+#ifdef HAS_TELEMETRY_FIELDS_PRINTER
+    os << SanitizeSensitiveFields(entry)->ToString() << "\n";
+#else
+    os << "session_uuid: " << entry->session_uuid
+       << "<the rest is omitted due to PII risk>\n";
+#endif
+    os.flush();
+    return llvm::ErrorSuccess();
+  }
+
+  std::string name() const override { return desc; }
+
+private:
+  std::ostream &os;
+  const std::string desc;
+};
+
+// No-op logger to use when users disable telemetry
+class NoOpTelemeter : public LldbTelemeter {
+public:
+  static std::shared_ptr<LldbTelemeter> CreateInstance(Debugger *debugger) {
+    static std::shared_ptr<LldbTelemeter> ins(new NoOpTelemeter(debugger));
+    return ins;
+  }
+
+  NoOpTelemeter(Debugger *debugger) {}
+  void LogStartup(llvm::StringRef tool_path, TelemetryInfo *entry) override {}
+  void LogExit(llvm::StringRef tool_path, TelemetryInfo *entry) override {}
+  void LogProcessExit(int status, llvm::StringRef exit_string,
+                      TelemetryEventStats stats, Target *target_ptr) override {}
+  void LogMainExecutableLoadStart(lldb::ModuleSP exec_mod,
+                                  TelemetryEventStats stats) override {}
+  void LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
+                                TelemetryEventStats stats) override {}
+
+  void LogCommandStart(llvm::StringRef uuid, llvm::StringRef original_command,
+                       TelemetryEventStats stats, Target *target_ptr) override {
+  }
+  void LogCommandEnd(llvm::StringRef uuid, llvm::StringRef command_name,
+                     llvm::StringRef command_args, TelemetryEventStats stats,
+                     Target *target_ptr, CommandReturnObject *result) override {
+  }
+
+  void
+  LogClientTelemetry(lldb_private::StructuredData::Object *entry) override {}
+
+  void AddDestination(TelemetryDestination *destination) override {}
+  std::string GetNextUUID() override { return ""; }
+};
+
+class BasicTelemeter : public LldbTelemeter {
+public:
+  static std::shared_ptr<BasicTelemeter> CreateInstance(Debugger *);
+
+  virtual ~BasicTelemeter() = default;
+
+  void LogStartup(llvm::StringRef lldb_path, TelemetryInfo *entry) override;
+  void LogExit(llvm::StringRef lldb_path, TelemetryInfo *entry) override;
+
+  void LogProcessExit(int status, llvm::StringRef exit_string,
+                      TelemetryEventStats stats, Target *target_ptr) override;
+  void LogMainExecutableLoadStart(lldb::ModuleSP exec_mod,
+                                  TelemetryEventStats stats) override;
+  void LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
+                                TelemetryEventStats stats) override;
+
+  void LogCommandStart(llvm::StringRef uuid, llvm::StringRef original_command,
+                       TelemetryEventStats stats, Target *target_ptr) override;
+  void LogCommandEnd(llvm::StringRef uuid, llvm::StringRef command_name,
+                     llvm::StringRef command_args, TelemetryEventStats stats,
+                     Target *target_ptr, CommandReturnObject *result) override;
+
+  void LogClientTelemetry(lldb_private::StructuredData::Object *entry) override;
+
+  void AddDestination(TelemetryDestination *destination) override {
+    m_destinations.push_back(destination);
+  }
+
+  std::string GetNextUUID() override {
+    return std::to_string(uuid_seed.fetch_add(1));
+  }
+
+protected:
+  BasicTelemeter(Debugger *debugger);
+
+  void CollectMiscBuildInfo();
+
+private:
+  template <typename EntrySubType> EntrySubType MakeBaseEntry() {
+    EntrySubType entry;
+    entry.session_uuid = m_session_uuid;
+    entry.counter = counter.fetch_add(1);
+    return entry;
+  }
+
+  void EmitToDestinations(const TelemetryInfo *entry);
+
+  Debugger *m_debugger;
+  const std::string m_session_uuid;
+  std::string startup_lldb_path;
+
+  // counting number of entries.
+  std::atomic<size_t> counter = 0;
+
+  std::vector<TelemetryDestination *> m_destinations;
+
+  std::atomic<size_t> uuid_seed = 0;
+};
+
+static std::string MakeUUID(lldb_private::Debugger *debugger) {
+  std::string ret;
+  uint8_t random_bytes[16];
+  if (auto ec = llvm::getRandomBytes(random_bytes, 16)) {
+    std::cerr << "entropy source failure: " + 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;
+}
+
+BasicTelemeter::BasicTelemeter(lldb_private::Debugger *debugger)
+    : m_debugger(debugger), m_session_uuid(MakeUUID(debugger)) {}
+
+std::shared_ptr<BasicTelemeter>
+BasicTelemeter::CreateInstance(lldb_private::Debugger *debugger) {
+  auto *config = GetTelemetryConfig();
+
+  BasicTelemeter *ins = new BasicTelemeter(debugger);
+  for (const std ::string &dest : config->additional_destinations) {
+    if (dest == "stdout") {
+      ins->AddDestination(new StreamTelemetryDestination(std::cout, "stdout"));
+    } else if (dest == "stderr") {
+      ins->AddDestination(new StreamTelemetryDestination(std::cerr, "stderr"));
+    } else {
+      // TODO: handle file paths
+    }
+  }
+
+  return std::shared_ptr<BasicTelemeter>(ins);
+}
+
+void BasicTelemeter::EmitToDestinations(const TelemetryInfo *entry) {
+  // TODO: can do this in a separate thread (need to own the ptrs!).
+  for (auto destination : m_destinations) {
+    auto err = destination->EmitEntry(entry);
+    if (err) {
+      std::cerr << "error emitting to destination: " << destination->name()
+                << "\n";
+    }
+  }
+}
+
+void BasicTelemeter::LogStartup(llvm::StringRef lldb_path,
+                                TelemetryInfo *entry) {
+  startup_lldb_path = lldb_path.str();
+  lldb_private::DebuggerTelemetryInfo startup_info =
+      MakeBaseEntry<lldb_private::DebuggerTelemetryInfo>();
+
+  auto &resolver = lldb_private::HostInfo::GetUserIDResolver();
+  auto opt_username = resolver.GetUserName(lldb_private::HostInfo::GetUserID());
+  if (opt_username)
+    startup_info.username = *opt_username;
+
+  startup_info.lldb_git_sha = lldb_private::GetVersion(); // TODO: fix this
+  startup_info.lldb_path = startup_lldb_path;
+  startup_info.stats = entry->stats;
+
+  llvm::SmallString<64> cwd;
+  if (!llvm::sys::fs::current_path(cwd)) {
+    startup_info.cwd = cwd.c_str();
+  } else {
+    MiscTelemetryInfo misc_info = MakeBaseEntry<MiscTelemetryInfo>();
+    misc_info.meta_data["internal_errors"] = "Cannot determine CWD";
+    EmitToDestinations(&misc_info);
+  }
+
+  std::cout << "emitting startup info\n";
+  EmitToDestinations(&startup_info);
+
+  // Optional part
+  CollectMiscBuildInfo();
+}
+
+void BasicTelemeter::LogExit(llvm::StringRef lldb_path, TelemetryInfo *entry) {
+  std::cout << "debugger exiting at " << lldb_path.str() << "\n";
+  // we should be shutting down the same instance that we started?!
+  // llvm::Assert(startup_lldb_path == lldb_path.str());
+
+  lldb_private::DebuggerTelemetryInfo exit_info =
+      MakeBaseEntry<lldb_private::DebuggerTelemetryInfo>();
+  exit_info.stats = entry->stats;
+  exit_info.lldb_path = startup_lldb_path;
+  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.
+        exit_info.exit_description = {-1, "no process launched."};
+      } else {
+        exit_info.exit_description = {proc->GetExitStatus(), ""};
+        if (const char *description = proc->GetExitDescription())
+          exit_info.exit_description->description = std::string(description);
+      }
+    }
+  }
+  EmitToDestinations(&exit_info);
+}
+
+void BasicTelemeter::LogProcessExit(int status, llvm::StringRef exit_string,
+                                    TelemetryEventStats stats,
+                                    Target *target_ptr) {
+  lldb_private::TargetTelemetryInfo exit_info =
+      MakeBaseEntry<lldb_private::TargetTelemetryInfo>();
+  exit_info.stats = stats;
+  exit_info.target_uuid =
+      target_ptr && !target_ptr->IsDummyTarget()
+          ? target_ptr->GetExecutableModule()->GetUUID().GetAsString()
+          : "";
+  exit_info.exit_description = {status, exit_string.str()};
+
+  std::cout << "emitting process exit ...\n";
+  EmitToDestinations(&exit_info);
+}
+
+void BasicTelemeter::CollectMiscBuildInfo() {
+  // collecting use-case specific data
+}
+
+void BasicTelemeter::LogMainExecutableLoadStart(lldb::ModuleSP exec_mod,
+                                                TelemetryEventStats stats) {
+  TargetTelemetryInfo target_info = MakeBaseEntry<TargetTelemetryInfo>();
+  target_info.stats = std::move(stats);
+  target_info.binary_path =
+      exec_mod->GetFileSpec().GetPathAsConstString().GetCString();
+  target_info.file_format = exec_mod->GetArchitecture().GetArchitectureName();
+  target_info.target_uuid = exec_mod->GetUUID().GetAsString();
+  if (auto err = llvm::sys::fs::file_size(exec_mod->GetFileSpec().GetPath(),
+                                          target_info.binary_size)) {
+    // If there was error obtaining it, just reset the size to 0.
+    // Maybe log the error too?
+    target_info.binary_size = 0;
+  }
+  EmitToDestinations(&target_info);
+}
+
+void BasicTelemeter::LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
+                                              TelemetryEventStats stats) {
+  TargetTelemetryInfo target_info = MakeBaseEntry<TargetTelemetryInfo>();
+  target_info.stats = std::move(stats);
+  target_info.binary_path =
+      exec_mod->GetFileSpec().GetPathAsConstString().GetCString();
+  target_info.file_format = exec_mod->GetArchitecture().GetArchitectureName();
+  target_info.target_uuid = exec_mod->GetUUID().GetAsString();
+  if (auto err = llvm::sys::fs::file_size(exec_mod->GetFileSpec().GetPath(),
+                                          target_info.binary_size)) {
+    // If there was error obtaining it, just reset the size to 0.
+    // Maybe log the error too?
+    target_info.binary_size = 0;
+  }
+  EmitToDestinations(&target_info);
+
+  // Collect some more info,  might be useful?
+  MiscTelemetryInfo misc_info = MakeBaseEntry<MiscTelemetryInfo>();
+  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());
+  EmitToDestinations(&misc_info);
+}
+
+void BasicTelemeter::LogClientTelemetry(
+    lldb_private::StructuredData::Object *entry) {
+  ClientTelemetryInfo client_info = MakeBaseEntry<ClientTelemetryInfo>();
+  auto *dictionary = entry->GetAsDictionary();
+  llvm::StringRef request_name;
+  if (!dictionary->GetValueForKeyAsString("request_name", request_name, "")) {
+    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();
+
+  size_t start_time;
+  size_t end_time;
+  if (!dictionary->GetValueForKeyAsInteger("start_time", start_time) ||
+      !dictionary->GetValueForKeyAsInteger("end_time", end_time)) {
+    MiscTelemetryInfo misc_info = MakeBaseEntry<MiscTelemetryInfo>();
+    misc_info.meta_data["internal_errors"] =
+        "Cannot determine start/end time from client entry";
+    EmitToDestinations(&misc_info);
+    return;
+  }
+
+  EmitToDestinations(&client_info);
+}
+
+void BasicTelemeter::LogCommandStart(llvm::StringRef uuid,
+                                     llvm::StringRef original_command,
+                                     TelemetryEventStats stats,
+                                     Target *target_ptr) {
+
+  lldb_private::CommandTelemetryInfo command_info =
+      MakeBaseEntry<lldb_private::CommandTelemetryInfo>();
+
+  // If we have a target attached to this command, then get the UUID.
+  command_info.target_uuid = "";
+  if (target_ptr && target_ptr->GetExecutableModule() != nullptr) {
+    command_info.target_uuid =
+        target_ptr->GetExecutableModule()->GetUUID().GetAsString();
+  }
+  command_info.command_uuid = uuid.str();
+  command_info.original_command = original_command.str();
+  command_info.stats = std::move(stats);
+
+  EmitToDestinations(&command_info);
+}
+
+void BasicTelemeter::LogCommandEnd(llvm::StringRef uuid,
+                                   llvm::StringRef command_name,
+                                   llvm::StringRef command_args,
+                                   TelemetryEventStats stats,
+                                   Target *target_ptr,
+                                   CommandReturnObject *result) {
+
+  lldb_private::CommandTelemetryInfo command_info =
+      MakeBaseEntry<lldb_private::CommandTelemetryInfo>();
+
+  // If we have a target attached to this command, then get the UUID.
+  command_info.target_uuid = "";
+  if (target_ptr && target_ptr->GetExecutableModule() != nullptr) {
+    command_info.target_uuid =
+        target_ptr->GetExecutableModule()->GetUUID().GetAsString();
+  }
+  command_info.command_uuid = uuid.str();
+  command_info.command_name = command_name.str();
+  command_info.args = command_args.str();
+  command_info.stats = std::move(stats);
+  command_info.exit_description = {result->Succeeded() ? 0 : -1,
+                                   StatusToString(result)};
+  EmitToDestinations(&command_info);
+}
+
+llvm::StringRef parse_value(llvm::StringRef str, llvm::StringRef label) {
+  return str.substr(label.size()).trim();
+}
+
+bool parse_field(llvm::StringRef str, llvm::StringRef label) {
+  if (parse_value(str, label) == "true")
+    return true;
+  return false;
+}
+
+llvm::telemetry::TelemetryConfig *MakeTelemetryConfig() {
+  bool enable_telemetry = false;
+  std::vector<std::string> additional_destinations;
+
+  // Look in the $HOME/.lldb_telemetry_config file to populate the struct
+  llvm::SmallString<64> init_file;
+  FileSystem::Instance().GetHomeDirectory(init_file);
+  llvm::sys::path::append(init_file, ".lldb_telemetry_config");
+  FileSystem::Instance().Resolve(init_file);
+  if (llvm::sys::fs::exists(init_file)) {
+    auto contents = llvm::MemoryBuffer::getFile(init_file, /*IsText*/ true);
+    if (contents) {
+      llvm::line_iterator iter =
+          llvm::line_iterator(contents->get()->getMemBufferRef());
+      for (; !iter.is_at_eof(); ++iter) {
+        if (iter->starts_with("enable_telemetry:")) {
+          enable_telemetry = parse_field(*iter, "enable_telemetry:");
+        } else if (iter->starts_with("destination:")) {
+          llvm::StringRef dest = parse_value(*iter, "destination:");
+          if (dest == "stdout") {
+            additional_destinations.push_back("stdout");
+          } else if (dest == "stderr") {
+            additional_destinations.push_back("stderr");
+          } else {
+            additional_destinations.push_back(dest.str());
+          }
+        }
+      }
+    } else {
+      std::cerr << "Error reading config file at " << init_file.c_str() << "\n";
+    }
+  }
+
+  auto *ret = new llvm::telemetry::TelemetryConfig{enable_telemetry,
+                                                   additional_destinations};
+#ifdef HAS_VENDOR_TELEMETRY_CONFIG
+  ApplyVendorSpecificConfigs(ret);
+#endif
+  return ret;
+}
+
+llvm::telemetry::TelemetryConfig *GetTelemetryConfig() {
+  static llvm::telemetry::TelemetryConfig *config = MakeTelemetryConfig();
+  return config;
+}
+
+std::shared_ptr<LldbTelemeter>
+LldbTelemeter::CreateInstance(lldb_private::Debugger *debugger) {
+  auto *config = GetTelemetryConfig();
+  if (!config->enable_telemetry) {
+    return NoOpTelemeter::CreateInstance(debugger);
+  }
+
+#ifdef HAS_VENDOR_TELEMETER
+  return CreateVendorSpecificTelemeter(config);
+#else
+  return BasicTelemeter::CreateInstance(debugger);
+#endif
+}
+} // namespace lldb_private
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index fc07168b6c0ac..e60f9e7d3838d 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -12,6 +12,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <typeinfo>
 #include <vector>
 
 #include "Commands/CommandObjectApropos.h"
@@ -56,7 +57,10 @@
 #include "lldb/Utility/StructuredData.h"
 #include "lldb/Utility/Timer.h"
 
+#include "lldb/Core/Telemetry.h"
 #include "lldb/Host/Config.h"
+#include "lldb/Interpreter/CommandObject.h"
+
 #if LLDB_ENABLE_LIBEDIT
 #include "lldb/Host/Editline.h"
 #endif
@@ -1848,8 +1852,28 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
                                        LazyBool lazy_add_to_history,
                                        CommandReturnObject &result,
                                        bool force_repeat_command) {
+  TelemetryEventStats command_stats(std::chrono::steady_clock::now());
+  auto telemeter = GetDebugger().GetTelemeter();
+  // Generate a UUID for this command so the logger can match
+  // the start/end entries correctly.
+  const std::string command_uuid = telemeter->GetNextUUID();
+
+  telemeter->LogCommandStart(command_uuid, command_line, command_stats,
+                             GetExecutionContext().GetTargetPtr());
+
   std::string command_string(command_line);
   std::string original_command_string(command_line);
+  std::string parsed_command_args;
+  CommandObject *cmd_obj = nullptr;
+
+  auto log_on_exit = llvm::make_scope_exit([&]() {
+    command_stats.m_end = std::chrono::steady_clock::now();
+    llvm::StringRef command_name =
+        cmd_obj ? cmd_obj->GetCommandName() : "<not found>";
+    telemeter->LogCommandEnd(command_uuid, command_name, parsed_command_args,
+                             command_stats,
+                             GetExecutionContext().GetTargetPtr(), &result);
+  });
 
   Log *log = GetLog(LLDBLog::Commands);
   llvm::PrettyStackTraceFormat stack_trace("HandleCommand(command = \"%s\")",
@@ -1887,11 +1911,10 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
 
   bool empty_command = false;
   bool comment_command = false;
-  if (command_string.empty())
+  if (command_string.empty()) {
     empty_command = true;
-  else {
+  } else {
     const char *k_space_characters = "\t\n\v\f\r ";
-
     size_t non_space = command_string.find_first_not_of(k_space_characters);
     // Check for empty line or comment line (lines whose first non-space
     // character is the comment character for this interpreter)
@@ -1954,7 +1977,7 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
   // From 1 above, we can determine whether the Execute function wants raw
   // input or not.
 
-  CommandObject *cmd_obj = ResolveCommandImpl(command_string, result);
+  cmd_obj = ResolveCommandImpl(command_string, result);
 
   // We have to preprocess the whole command string for Raw commands, since we
   // don't know the structure of the command.  For parsed commands, we only
@@ -2015,30 +2038,29 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
     if (add_to_history)
       m_command_history.AppendString(original_command_string);
 
-    std::string remainder;
     const std::size_t actual_cmd_name_len = cmd_obj->GetCommandName().size();
     if (actual_cmd_name_len < command_string.length())
-      remainder = command_string.substr(actual_cmd_name_len);
+      parsed_command_args = command_string.substr(actual_cmd_name_len);
 
     // Remove any initial spaces
-    size_t pos = remainder.find_first_not_of(k_white_space);
+    size_t pos = parsed_command_args.find_first_not_of(k_white_space);
     if (pos != 0 && pos != std::string::npos)
-      remainder.erase(0, pos);
+      parsed_command_args.erase(0, pos);
 
     LLDB_LOGF(
         log, "HandleCommand, command line after removing command name(s): '%s'",
-        remainder.c_str());
+        parsed_command_args.c_str());
 
     // To test whether or not transcript should be saved, `transcript_item` is
     // used instead of `GetSaveTrasncript()`. This is because the latter will
     // fail when the command is "settings set interpreter.save-transcript true".
     if (transcript_item) {
       transcript_item->AddStringItem("commandName", cmd_obj->GetCommandName());
-      transcript_item->AddStringItem("commandArguments", remainder);
+      transcript_item->AddStringItem("commandArguments", parsed_command_args);
     }
 
     ElapsedTime elapsed(execute_time);
-    cmd_obj->Execute(remainder.c_str(), result);
+    cmd_obj->Execute(parsed_command_args.c_str(), result);
   }
 
   LLDB_LOGF(log, "HandleCommand, command %s",
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index dc7f6c9e86a47..ededeb9e8b28f 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include <atomic>
+#include <chrono>
 #include <memory>
 #include <mutex>
 #include <optional>
@@ -477,7 +478,8 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp,
       m_clear_thread_plans_on_stop(false), m_force_next_event_delivery(false),
       m_last_broadcast_state(eStateInvalid), m_destroy_in_process(false),
       m_can_interpret_function_calls(false), m_run_thread_plan_lock(),
-      m_can_jit(eCanJITDontKnow) {
+      m_can_jit(eCanJITDontKnow),
+      m_event_stats(std::chrono::steady_clock::now()) {
   CheckInWithManager();
 
   Log *log = GetLog(LLDBLog::Object);
@@ -1085,6 +1087,7 @@ bool Process::SetExitStatus(int status, llvm::StringRef exit_string) {
   // Use a mutex to protect setting the exit status.
   std::lock_guard<std::mutex> guard(m_exit_status_mutex);
 
+  m_event_stats.m_end = std::chrono::steady_clock::now();
   Log *log(GetLog(LLDBLog::State | LLDBLog::Process));
   LLDB_LOG(log, "(plugin = {0} status = {1} ({1:x8}), description=\"{2}\")",
            GetPluginName(), status, exit_string);
@@ -1098,6 +1101,8 @@ bool Process::SetExitStatus(int status, llvm::StringRef exit_string) {
         GetPluginName());
     return false;
   }
+  GetTarget().GetDebugger().GetTelemeter()->LogProcessExit(
+      status, exit_string, m_event_stats, &GetTarget());
 
   m_exit_status = status;
   if (!exit_string.empty())
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index ec0da8a1378a8..60bd544bd2a92 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -24,6 +24,7 @@
 #include "lldb/Core/Section.h"
 #include "lldb/Core/SourceManager.h"
 #include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Core/Telemetry.h"
 #include "lldb/Core/ValueObject.h"
 #include "lldb/Core/ValueObjectConstResult.h"
 #include "lldb/Expression/DiagnosticManager.h"
@@ -67,6 +68,7 @@
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SetVector.h"
 
+#include <chrono>
 #include <memory>
 #include <mutex>
 #include <optional>
@@ -1470,11 +1472,22 @@ void Target::DidExec() {
 
 void Target::SetExecutableModule(ModuleSP &executable_sp,
                                  LoadDependentFiles load_dependent_files) {
+
   Log *log = GetLog(LLDBLog::Target);
   ClearModules(false);
-
   if (executable_sp) {
+    TelemetryEventStats load_executable_stats(std::chrono::steady_clock::now());
+    m_debugger.GetTelemeter()->LogMainExecutableLoadStart(
+        executable_sp, load_executable_stats);
+
+    auto log_on_exit = llvm::make_scope_exit([&]() {
+      load_executable_stats.m_end = std::chrono::steady_clock::now();
+      m_debugger.GetTelemeter()->LogMainExecutableLoadEnd(
+          executable_sp, load_executable_stats);
+    });
+
     ElapsedTime elapsed(m_stats.GetCreateTime());
+
     LLDB_SCOPED_TIMERF("Target::SetExecutableModule (executable = '%s')",
                        executable_sp->GetFileSpec().GetPath().c_str());
 
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 0196aed819f2b..aa43d845ccfbe 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -6,6 +6,11 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "DAP.h"
+#include "LLDBUtils.h"
+#include "lldb/API/SBStructuredData.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FormatVariadic.h"
 #include <chrono>
 #include <cstdarg>
 #include <fstream>
@@ -14,7 +19,6 @@
 
 #include "DAP.h"
 #include "LLDBUtils.h"
-#include "lldb/API/SBCommandInterpreter.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/FormatVariadic.h"
 
diff --git a/llvm/include/llvm/Telemetry/Telemetry.h b/llvm/include/llvm/Telemetry/Telemetry.h
new file mode 100644
index 0000000000000..e34b228b219c1
--- /dev/null
+++ b/llvm/include/llvm/Telemetry/Telemetry.h
@@ -0,0 +1,99 @@
+#ifndef LVM_TELEMETRY_TELEMETRY_H
+#define LVM_TELEMETRY_TELEMETRY_H
+
+#include <chrono>
+#include <ctime>
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+
+namespace llvm {
+namespace telemetry {
+
+using SteadyTimePoint = std::chrono::time_point<std::chrono::steady_clock>;
+
+struct TelemetryConfig {
+  // If true, telemetry will be enabled.
+  bool enable_telemetry;
+
+  // Additional destinations to send the logged entries.
+  // Could be stdout, stderr, or some local paths.
+  // Note: these are destinations are __in addition to__ whatever the default
+  // destination(s) are, as implemented by vendors.
+  std::vector<std::string> additional_destinations;
+};
+
+struct TelemetryEventStats {
+  // REQUIRED: Start time of event
+  SteadyTimePoint m_start;
+  // OPTIONAL: End time of event - may be empty if not meaningful.
+  std::optional<SteadyTimePoint> m_end;
+  // TBD: could add some memory stats here too?
+
+  TelemetryEventStats() = default;
+  TelemetryEventStats(SteadyTimePoint start) : m_start(start) {}
+  TelemetryEventStats(SteadyTimePoint start, SteadyTimePoint end)
+      : m_start(start), m_end(end) {}
+
+  std::string ToString() const;
+};
+
+struct ExitDescription {
+  int exit_code;
+  std::string description;
+
+  std::string ToString() const;
+};
+
+// The base class contains the basic set of data.
+// Downstream implementations can add more fields as needed.
+struct TelemetryInfo {
+  // A "session" corresponds to every time the tool starts.
+  // All entries emitted for the same session will have
+  // the same session_uuid
+  std::string session_uuid;
+
+  TelemetryEventStats stats;
+
+  std::optional<ExitDescription> exit_description;
+
+  // Counting number of entries.
+  // (For each set of entries with the same session_uuid, this value should
+  // be unique for each entry)
+  size_t counter;
+
+  TelemetryInfo() = default;
+  ~TelemetryInfo() = default;
+  virtual std::string ToString() const;
+};
+
+// Where/how to send the telemetry entries.
+class TelemetryDestination {
+public:
+  virtual ~TelemetryDestination() = default;
+  virtual Error EmitEntry(const TelemetryInfo *entry) = 0;
+  virtual std::string name() const = 0;
+};
+
+class Telemeter {
+public:
+  virtual ~Telemeter() = default;
+
+  // Invoked upon tool startup
+  virtual void LogStartup(llvm::StringRef tool_path, TelemetryInfo *entry) = 0;
+
+  // Invoked upon tool exit.
+  virtual void LogExit(llvm::StringRef tool_path, TelemetryInfo *entry) = 0;
+
+  virtual void AddDestination(TelemetryDestination *destination) = 0;
+};
+
+} // namespace telemetry
+} // namespace llvm
+
+#endif // LVM_TELEMETRY_TELEMETRY_H
diff --git a/llvm/lib/CMakeLists.txt b/llvm/lib/CMakeLists.txt
index 638c3bd6f90f5..1d2fb32922648 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 0000000000000..8208bdadb05e9
--- /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 0000000000000..f7100685ee2d2
--- /dev/null
+++ b/llvm/lib/Telemetry/Telemetry.cpp
@@ -0,0 +1,32 @@
+#include "llvm/Telemetry/Telemetry.h"
+
+namespace llvm {
+namespace telemetry {
+
+std::string TelemetryEventStats::ToString() const {
+  std::string result;
+  llvm::raw_string_ostream os(result);
+  os << "start_timestamp: " << m_start.time_since_epoch().count()
+     << ", end_timestamp: "
+     << (m_end.has_value() ? std::to_string(m_end->time_since_epoch().count())
+                           : "<NONE>");
+  return result;
+}
+
+std::string ExitDescription::ToString() const {
+  return "exit_code: " + std::to_string(exit_code) +
+         ", description: " + description + "\n";
+}
+
+std::string TelemetryInfo::ToString() const {
+  return "[TelemetryInfo]\n" + ("  session_uuid:" + session_uuid + "\n") +
+         ("  stats: " + stats.ToString() + "\n") +
+         ("  exit_description: " +
+          (exit_description.has_value() ? exit_description->ToString()
+                                        : "<NONE>") +
+          "\n") +
+         ("  counter: " + std::to_string(counter) + "\n");
+}
+
+} // namespace telemetry
+} // namespace llvm
\ No newline at end of file

>From d29089596b366560f11b41105951fb808f361212 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Tue, 6 Aug 2024 15:16:23 -0400
Subject: [PATCH 3/3] Addressed review comments

---
 lldb/include/lldb/Core/Debugger.h             |  3 +-
 lldb/include/lldb/Core/Telemetry.h            | 39 +++++++------
 lldb/include/lldb/Target/Process.h            |  3 +-
 lldb/source/Core/Debugger.cpp                 |  6 +-
 lldb/source/Core/Telemetry.cpp                | 58 ++++++++++---------
 .../source/Interpreter/CommandInterpreter.cpp |  5 +-
 lldb/source/Target/Target.cpp                 |  4 +-
 lldb/tools/lldb-dap/DAP.cpp                   |  5 --
 8 files changed, 66 insertions(+), 57 deletions(-)

diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 13a444e438fec..0a21bdc95a6ee 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -40,6 +40,7 @@
 #include "lldb/lldb-private-enumerations.h"
 #include "lldb/lldb-private-types.h"
 #include "lldb/lldb-types.h"
+#include "llvm/Telemetry/Telemetry.h"
 
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallVector.h"
@@ -773,7 +774,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
 
   Debugger(const Debugger &) = delete;
   const Debugger &operator=(const Debugger &) = delete;
-  TelemetryEventStats stats;
+  llvm::telemetry::TelemetryEventStats stats;
 };
 
 } // namespace lldb_private
diff --git a/lldb/include/lldb/Core/Telemetry.h b/lldb/include/lldb/Core/Telemetry.h
index 15cc3139764ad..f43636e9f8ea0 100644
--- a/lldb/include/lldb/Core/Telemetry.h
+++ b/lldb/include/lldb/Core/Telemetry.h
@@ -15,8 +15,6 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Telemetry/Telemetry.h"
 
-using namespace llvm::telemetry;
-
 namespace lldb_private {
 
 struct DebuggerTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
@@ -29,8 +27,7 @@ struct DebuggerTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
 };
 
 struct TargetTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
-  // All entries emitted for the same SBTarget will have the same
-  // target_uuid.
+  // The same as the executable-module's UUID.
   std::string target_uuid;
   std::string file_format;
 
@@ -47,6 +44,16 @@ struct ClientTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
   std::string ToString() const override;
 };
 
+struct CommandExitDescription : public ::llvm::telemetry::ExitDescription {
+  lldb::ReturnStatus ret_status;
+  CommandExitDescription(int ret_code, std::string ret_desc,
+                         lldb::ReturnStatus status) {
+    exit_code = ret_code;
+    description = std::move(ret_desc);
+    ret_status = status;
+  }
+};
+
 struct CommandTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
   // If the command is/can be associated with a target entry,
   // this field contains that target's UUID.
@@ -85,34 +92,32 @@ class LldbTelemeter : public llvm::telemetry::Telemeter {
 
   virtual ~LldbTelemeter() = default;
 
-  // void LogStartup(llvm::StringRef lldb_path,
-  //                 TelemetryInfo *entry) override;
-  // void LogExit(llvm::StringRef lldb_path, TelemetryInfo *entry)
-  // override;
-
   // Invoked upon process exit
   virtual void LogProcessExit(int status, llvm::StringRef exit_string,
-                              TelemetryEventStats stats,
+                              llvm::telemetry::TelemetryEventStats stats,
                               Target *target_ptr) = 0;
 
   // 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(lldb::ModuleSP exec_mod,
-                                          TelemetryEventStats stats) = 0;
-  virtual void LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
-                                        TelemetryEventStats stats) = 0;
+  virtual void
+  LogMainExecutableLoadStart(lldb::ModuleSP exec_mod,
+                             llvm::telemetry::TelemetryEventStats stats) = 0;
+  virtual void
+  LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
+                           llvm::telemetry::TelemetryEventStats stats) = 0;
 
   // 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(llvm::StringRef uuid,
                                llvm::StringRef original_command,
-                               TelemetryEventStats stats,
+                               llvm::telemetry::TelemetryEventStats stats,
                                Target *target_ptr) = 0;
   virtual void LogCommandEnd(llvm::StringRef uuid, llvm::StringRef command_name,
                              llvm::StringRef command_args,
-                             TelemetryEventStats stats, Target *target_ptr,
+                             llvm::telemetry::TelemetryEventStats stats,
+                             Target *target_ptr,
                              CommandReturnObject *result) = 0;
 
   virtual std::string GetNextUUID() = 0;
@@ -147,7 +152,7 @@ class LldbTelemeter : public llvm::telemetry::Telemeter {
 //         there might have been lots of telemetry-entries that need to be
 //         sent already.
 //         This approach avoid losing log entries if LLDB crashes during init.
-TelemetryConfig *GetTelemetryConfig();
+llvm::telemetry::TelemetryConfig *GetTelemetryConfig();
 
 } // namespace lldb_private
 #endif // LLDB_CORE_TELEMETRY_H
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index c35ff397a203f..f607c8a84f259 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -57,6 +57,7 @@
 #include "lldb/Utility/UnimplementedError.h"
 #include "lldb/Utility/UserIDResolver.h"
 #include "lldb/lldb-private.h"
+#include "llvm/Telemetry/Telemetry.h"
 
 #include "llvm/ADT/AddressRanges.h"
 #include "llvm/ADT/ArrayRef.h"
@@ -3287,7 +3288,7 @@ void PruneThreadPlans();
   Process(const Process &) = delete;
   const Process &operator=(const Process &) = delete;
 
-  TelemetryEventStats m_event_stats;
+  llvm::telemetry::TelemetryEventStats m_event_stats;
 };
 
 /// RAII guard that should be acquired when an utility function is called within
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 2947d2d537767..b7c72b4378182 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -736,7 +736,8 @@ void Debugger::InstanceInitialize() {
 
 DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
                                     void *baton) {
-  SteadyTimePoint start_time = std::chrono::steady_clock::now();
+  llvm::telemetry::SteadyTimePoint start_time =
+      std::chrono::steady_clock::now();
   DebuggerSP debugger_sp(new Debugger(log_callback, baton));
   debugger_sp->stats.m_start = start_time;
   if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) {
@@ -744,7 +745,8 @@ DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
     g_debugger_list_ptr->push_back(debugger_sp);
   }
   debugger_sp->InstanceInitialize();
-  TelemetryEventStats init_stats(start_time, std::chrono::steady_clock::now());
+  llvm::telemetry::TelemetryEventStats init_stats(
+      start_time, std::chrono::steady_clock::now());
   llvm::telemetry::TelemetryInfo entry;
   entry.stats = {start_time, std::chrono::steady_clock::now()};
   debugger_sp->m_telemeter->LogStartup(
diff --git a/lldb/source/Core/Telemetry.cpp b/lldb/source/Core/Telemetry.cpp
index 846fec54b31d0..d07d82e3ba874 100644
--- a/lldb/source/Core/Telemetry.cpp
+++ b/lldb/source/Core/Telemetry.cpp
@@ -17,7 +17,6 @@
 #include <cstdlib>
 #include <ctime>
 #include <fstream>
-#include <iostream>
 #include <string>
 #include <typeinfo>
 #include <utility>
@@ -34,6 +33,7 @@
 #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"
@@ -47,6 +47,7 @@
 #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"
 
 #ifdef HAS_VENDOR_TELEMETRY_PLUGINS
@@ -63,6 +64,8 @@ CreateVendorSpecificTelemeter(
     llvm::telemetry::TelemetryConfig *config) /*__attribute__((weak))*/;
 #endif
 
+using namespace llvm::telemetry;
+
 namespace lldb_private {
 
 static std::string GetDuration(const TelemetryEventStats &stats) {
@@ -176,19 +179,20 @@ std::string CommandTelemetryInfo::ToString() const {
 }
 
 std::string MiscTelemetryInfo::ToString() const {
-  std::string ret =
-      TelemetryInfo::ToString() + "\n" + ("[MiscTelemetryInfo]\n") +
-      ("  target_uuid: " + target_uuid + "\n") + ("  meta_data:\n");
-
+  std::string ret;
+  llvm::raw_string_ostream ret_strm(ret);
+  ret_strm << TelemetryInfo::ToString() << "\n[MiscTelemetryInfo]\n"
+           << "  target_uuid: " << target_uuid + "\n"
+           << "  meta_data:\n";
   for (const auto &kv : meta_data) {
-    ret += ("    " + kv.first + ": " + kv.second + "\n");
+    ret_strm << "    " << kv.first << ": " << kv.second << "\n";
   }
   return ret;
 }
 
 class StreamTelemetryDestination : public TelemetryDestination {
 public:
-  StreamTelemetryDestination(std::ostream &os, std::string desc)
+  StreamTelemetryDestination(llvm::raw_ostream &os, std::string desc)
       : os(os), desc(desc) {}
   llvm::Error EmitEntry(const TelemetryInfo *entry) override {
     // Unless there exists a custom (vendor-defined) data-cleanup
@@ -207,7 +211,7 @@ class StreamTelemetryDestination : public TelemetryDestination {
   std::string name() const override { return desc; }
 
 private:
-  std::ostream &os;
+  llvm::raw_ostream &os;
   const std::string desc;
 };
 
@@ -307,7 +311,8 @@ static std::string MakeUUID(lldb_private::Debugger *debugger) {
   std::string ret;
   uint8_t random_bytes[16];
   if (auto ec = llvm::getRandomBytes(random_bytes, 16)) {
-    std::cerr << "entropy source failure: " + ec.message();
+    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()) +
@@ -329,9 +334,11 @@ BasicTelemeter::CreateInstance(lldb_private::Debugger *debugger) {
   BasicTelemeter *ins = new BasicTelemeter(debugger);
   for (const std ::string &dest : config->additional_destinations) {
     if (dest == "stdout") {
-      ins->AddDestination(new StreamTelemetryDestination(std::cout, "stdout"));
+      ins->AddDestination(
+          new StreamTelemetryDestination(llvm::outs(), "stdout"));
     } else if (dest == "stderr") {
-      ins->AddDestination(new StreamTelemetryDestination(std::cerr, "stderr"));
+      ins->AddDestination(
+          new StreamTelemetryDestination(llvm::errs(), "stderr"));
     } else {
       // TODO: handle file paths
     }
@@ -342,11 +349,12 @@ BasicTelemeter::CreateInstance(lldb_private::Debugger *debugger) {
 
 void BasicTelemeter::EmitToDestinations(const TelemetryInfo *entry) {
   // TODO: can do this in a separate thread (need to own the ptrs!).
-  for (auto destination : m_destinations) {
-    auto err = destination->EmitEntry(entry);
+  for (TelemetryDestination *destination : m_destinations) {
+    llvm::Error err = destination->EmitEntry(entry);
     if (err) {
-      std::cerr << "error emitting to destination: " << destination->name()
-                << "\n";
+      LLDB_LOG(GetLog(LLDBLog::Object),
+               "Error emitting to destination(name = {0})",
+               destination->name());
     }
   }
 }
@@ -357,8 +365,9 @@ void BasicTelemeter::LogStartup(llvm::StringRef lldb_path,
   lldb_private::DebuggerTelemetryInfo startup_info =
       MakeBaseEntry<lldb_private::DebuggerTelemetryInfo>();
 
-  auto &resolver = lldb_private::HostInfo::GetUserIDResolver();
-  auto opt_username = resolver.GetUserName(lldb_private::HostInfo::GetUserID());
+  UserIDResolver &resolver = lldb_private::HostInfo::GetUserIDResolver();
+  std::optional<llvm::StringRef> opt_username =
+      resolver.GetUserName(lldb_private::HostInfo::GetUserID());
   if (opt_username)
     startup_info.username = *opt_username;
 
@@ -375,7 +384,6 @@ void BasicTelemeter::LogStartup(llvm::StringRef lldb_path,
     EmitToDestinations(&misc_info);
   }
 
-  std::cout << "emitting startup info\n";
   EmitToDestinations(&startup_info);
 
   // Optional part
@@ -383,7 +391,6 @@ void BasicTelemeter::LogStartup(llvm::StringRef lldb_path,
 }
 
 void BasicTelemeter::LogExit(llvm::StringRef lldb_path, TelemetryInfo *entry) {
-  std::cout << "debugger exiting at " << lldb_path.str() << "\n";
   // we should be shutting down the same instance that we started?!
   // llvm::Assert(startup_lldb_path == lldb_path.str());
 
@@ -420,7 +427,6 @@ void BasicTelemeter::LogProcessExit(int status, llvm::StringRef exit_string,
           : "";
   exit_info.exit_description = {status, exit_string.str()};
 
-  std::cout << "emitting process exit ...\n";
   EmitToDestinations(&exit_info);
 }
 
@@ -453,12 +459,8 @@ void BasicTelemeter::LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
       exec_mod->GetFileSpec().GetPathAsConstString().GetCString();
   target_info.file_format = exec_mod->GetArchitecture().GetArchitectureName();
   target_info.target_uuid = exec_mod->GetUUID().GetAsString();
-  if (auto err = llvm::sys::fs::file_size(exec_mod->GetFileSpec().GetPath(),
-                                          target_info.binary_size)) {
-    // If there was error obtaining it, just reset the size to 0.
-    // Maybe log the error too?
-    target_info.binary_size = 0;
-  }
+  target_info.binary_size = exec_mod->GetObjectFile()->GetByteSize();
+
   EmitToDestinations(&target_info);
 
   // Collect some more info,  might be useful?
@@ -556,7 +558,6 @@ bool parse_field(llvm::StringRef str, llvm::StringRef label) {
   return false;
 }
 
-<<<<<<< HEAD
 llvm::telemetry::TelemetryConfig *MakeTelemetryConfig() {
   bool enable_telemetry = false;
   std::vector<std::string> additional_destinations;
@@ -586,7 +587,8 @@ llvm::telemetry::TelemetryConfig *MakeTelemetryConfig() {
         }
       }
     } else {
-      std::cerr << "Error reading config file at " << init_file.c_str() << "\n";
+      LLDB_LOG(GetLog(LLDBLog::Object), "Error reading config file at {0}",
+               init_file.c_str());
     }
   }
 
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index e60f9e7d3838d..34c5cbb2c2f99 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -12,7 +12,6 @@
 #include <memory>
 #include <optional>
 #include <string>
-#include <typeinfo>
 #include <vector>
 
 #include "Commands/CommandObjectApropos.h"
@@ -60,6 +59,7 @@
 #include "lldb/Core/Telemetry.h"
 #include "lldb/Host/Config.h"
 #include "lldb/Interpreter/CommandObject.h"
+#include "llvm/Telemetry/Telemetry.h"
 
 #if LLDB_ENABLE_LIBEDIT
 #include "lldb/Host/Editline.h"
@@ -1852,7 +1852,8 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
                                        LazyBool lazy_add_to_history,
                                        CommandReturnObject &result,
                                        bool force_repeat_command) {
-  TelemetryEventStats command_stats(std::chrono::steady_clock::now());
+  llvm::telemetry::TelemetryEventStats command_stats(
+      std::chrono::steady_clock::now());
   auto telemeter = GetDebugger().GetTelemeter();
   // Generate a UUID for this command so the logger can match
   // the start/end entries correctly.
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 60bd544bd2a92..3d34ec4b7059c 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -64,6 +64,7 @@
 #include "lldb/Utility/State.h"
 #include "lldb/Utility/StreamString.h"
 #include "lldb/Utility/Timer.h"
+#include "llvm/Telemetry/Telemetry.h"
 
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SetVector.h"
@@ -1476,7 +1477,8 @@ void Target::SetExecutableModule(ModuleSP &executable_sp,
   Log *log = GetLog(LLDBLog::Target);
   ClearModules(false);
   if (executable_sp) {
-    TelemetryEventStats load_executable_stats(std::chrono::steady_clock::now());
+    llvm::telemetry::TelemetryEventStats load_executable_stats(
+        std::chrono::steady_clock::now());
     m_debugger.GetTelemeter()->LogMainExecutableLoadStart(
         executable_sp, load_executable_stats);
 
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index aa43d845ccfbe..dd5d58340b078 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -6,11 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "DAP.h"
-#include "LLDBUtils.h"
-#include "lldb/API/SBStructuredData.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/FormatVariadic.h"
 #include <chrono>
 #include <cstdarg>
 #include <fstream>



More information about the lldb-commits mailing list