[Lldb-commits] [lldb] [RFC][LLDB] Telemetry in LLDB (PR #87815)

Alex Langford via lldb-commits lldb-commits at lists.llvm.org
Fri Apr 5 16:06:24 PDT 2024


================
@@ -0,0 +1,620 @@
+
+//===-- 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 <memory>
+#include <stdbool.h>
+#include <sys/auxv.h>
+
+#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 "third_party/llvm/llvm-project/llvm/include/llvm/ADT/StringRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Chrono.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 "clang/Basic/Version.h"
+
+namespace lldb_private {
+
+std::string BaseTelemetryEntry::ToString() const {
+  return "[BaseTelemetryEntry]\n" + ("  session_uuid:" + session_uuid + "\n") +
+         ("  start timestamp: " +
+          std::to_string(stats.m_start.time_since_epoch().count()) + "\n") +
+         (" counter: " + std::to_string(counter));
+}
+
+std::string DebuggerInfoEntry::ToString() const {
+  auto duration = stats.Duration();
+  return BaseTelemetryEntry::ToString() + "\n" + ("[DebuggerInfoEntry]\n") +
+         ("  username: " + username + "\n") +
+         ("  lldb_git_sha: " + lldb_git_sha + "\n") +
+         ("  lldb_path: " + lldb_path + "\n") + ("  cwd: " + cwd + "\n") +
+         ("  lldb startup/session duration: " +
+          (duration.has_value() ? std::to_string(duration->count())
+                                : "<empty>") +
+          "(nanosec)\n") +
+         (lldb_exit.has_value()
+              ? ("lldb_exit_code: " + std::to_string(lldb_exit->exit_code) +
+                 ", lldb_exit_description: " + lldb_exit->description + "\n  ")
+              : (""));
+}
+
+std::string ClientTelemetryEntry::ToString() const {
+  return BaseTelemetryEntry::ToString() + "\n" + ("[DapRequestInfoEntry]\n") +
+         ("  request_name: " + request_name + "\n") +
+         ("  request_duration: " + std::to_string(stats.Duration()->count()) +
+          "(nanosec)\n") +
+         ("  error_msg: " + error_msg + "\n");
+}
+
+std::string TargetInfoEntry::ToString() const {
+  std::string exit_or_load_desc;
+  if (process_exit.has_value()) {
+    // If this entry was emitted for an exit
+    exit_or_load_desc =
+        "  process_duration: " + std::to_string(stats.Duration()->count()) +
+        "(nanosec)" +
+        "\n"
+        "  exit_code: " +
+        std::to_string(process_exit->exit_code) +
+        ", exit description: " + process_exit->description + "\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: " +
+                          std::to_string(stats.Duration()->count()) +
+                          "(nanosec)" + "\n";
+    } else {
+      exit_or_load_desc = " startup_init_start\n";
+    }
+  }
+  return BaseTelemetryEntry::ToString() + "\n" + ("[TargetInfoEntry]\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) {
+  // TODO:  surely there's a better way to translate status to text???
+  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 CommandInfoEntry::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 BaseTelemetryEntry::ToString() + "\n" +
+           ("[CommandInfoEntry] - END\n") +
+           ("  target_uuid: " + target_uuid + "\n") +
+           ("  command_uuid: " + command_uuid + "\n") +
+           ("  command_name: " + command_name + "\n") +
+           ("  args: " + args + "\n") +
+           ("  command_runtime: " + std::to_string(stats.Duration()->count()) +
+            "(nanosec)\n") +
+           ("  exit_code: " + std::to_string(exit_description.exit_code) +
+            ", exit description: " + exit_description.description + "\n");
+  } else {
+    // NOt going to have much info at the beginning.
+    return BaseTelemetryEntry::ToString() + "\n" +
+           ("[CommandInfoEntry] - START\n") +
+           ("  target_uuid: " + target_uuid + "\n") +
+           ("  command_uuid: " + command_uuid + "\n") +
+           ("  original_command: " + original_command + "\n");
+  }
+}
+
+std::string MiscInfoEntry::ToString() const {
+  std::string ret =
+      BaseTelemetryEntry::ToString() + "\n" + ("[MiscInfoEntry]\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,
+                             bool omit_sensitive_fields)
+      : os(os), desc(desc), omit_sensitive_fields(omit_sensitive_fields) {}
+  Status EmitEntry(const lldb_private::BaseTelemetryEntry *entry) override {
+    Status ret_status;
+    if (omit_sensitive_fields) {
+      // clean up the data before logging
+      // TODO: clean up the data before logging
+      os << entry->ToString() << "\n";
+    } else {
+      os << entry->ToString() << "\n";
+    }
+    os.flush();
+    return ret_status;
+  }
+
+  std::string name() const override { return desc; }
+
+private:
+  std::ostream &os;
+  const std::string desc;
+  const bool omit_sensitive_fields;
+};
+
+// No-op logger to use when users disable logging.
+class NoOpTelemetryLogger : public TelemetryLogger {
+public:
+  static std::shared_ptr<TelemetryLogger> CreateInstance(Debugger *debugger) {
+    static std::shared_ptr<TelemetryLogger> ins(
+        new NoOpTelemetryLogger(debugger));
+    return ins;
+  }
+
+  NoOpTelemetryLogger(Debugger *debugger) {}
+  void LogStartup(llvm::StringRef lldb_path,
+                  TelemetryEventStats stats) override {}
+  void LogExit(llvm::StringRef lldb_path, TelemetryEventStats stats) 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 BasicTelemetryLogger : public TelemetryLogger {
+public:
+  static std::shared_ptr<TelemetryLogger> CreateInstance(Debugger *);
+
+  virtual ~BasicTelemetryLogger() = default;
+
+  void LogStartup(llvm::StringRef lldb_path,
+                  TelemetryEventStats stats) override;
+  void LogExit(llvm::StringRef lldb_path, TelemetryEventStats stats) 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:
+  BasicTelemetryLogger(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 BaseTelemetryEntry *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;
+}
+BasicTelemetryLogger::BasicTelemetryLogger(lldb_private::Debugger *debugger)
+    : m_debugger(debugger), m_session_uuid(MakeUUID(debugger)) {}
+
+std::shared_ptr<TelemetryLogger>
+BasicTelemetryLogger::CreateInstance(lldb_private::Debugger *debugger) {
+  auto *config = GetLoggerConfig();
+  assert(config->enable_logging);
+
+  BasicTelemetryLogger *ins = new BasicTelemetryLogger(debugger);
+
+  // TODO: configure which destination(s) to use here.
+  for (const std ::string &dest : config->additional_destinations) {
+    if (dest == "stdout") {
+      ins->AddDestination(
+          new StreamTelemetryDestination(std::cout, "stdout", true));
+    } else if (dest == "stderr") {
+      ins->AddDestination(
+          new StreamTelemetryDestination(std::cerr, "stderr", true));
+    } else {
+      // TODO: handle file paths
+    }
+  }
+
+  return std::shared_ptr<BasicTelemetryLogger>(ins);
+}
+
+void BasicTelemetryLogger::EmitToDestinations(
+    const lldb_private::BaseTelemetryEntry *entry) {
+  // TODO: can do this in a separate thread (need to own the ptrs!).
+  for (auto destination : m_destinations) {
+    destination->EmitEntry(entry);
+  }
+}
+
+void BasicTelemetryLogger::LogStartup(llvm::StringRef lldb_path,
+                                      TelemetryEventStats stats) {
+  std::cout << "debugger starting up\n";
+
+  startup_lldb_path = lldb_path.str();
+  lldb_private::DebuggerInfoEntry startup_info =
+      MakeBaseEntry<lldb_private::DebuggerInfoEntry>();
+
+  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
----------------
bulbazord wrote:

What does it mean to fix this?

https://github.com/llvm/llvm-project/pull/87815


More information about the lldb-commits mailing list