[Lldb-commits] [lldb] [RFC][LLDB] Telemetry in LLDB (PR #87815)
Vy Nguyen via lldb-commits
lldb-commits at lists.llvm.org
Fri Apr 5 11:31:34 PDT 2024
https://github.com/oontvoo created https://github.com/llvm/llvm-project/pull/87815
Implement telemetry in LLDB.
(See previous discussions at https://discourse.llvm.org/t/rfc-lldb-telemetry-metrics/64588)
>From cdee622a6646ba5c16a3c8156a5a50a938a14b57 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Fri, 5 Apr 2024 14:14:30 -0400
Subject: [PATCH 1/2] [lldb]POC implementation for telemetry in LLDB
---
lldb/include/lldb/API/SBDebugger.h | 4 +
lldb/include/lldb/Core/Debugger.h | 10 +
lldb/include/lldb/Core/Telemetry.h | 206 ++++++
lldb/include/lldb/Target/Process.h | 3 +
lldb/source/API/SBDebugger.cpp | 9 +
lldb/source/Core/CMakeLists.txt | 1 +
lldb/source/Core/Debugger.cpp | 32 +-
lldb/source/Core/Telemetry.cpp | 620 ++++++++++++++++++
.../source/Interpreter/CommandInterpreter.cpp | 44 +-
lldb/source/Target/Process.cpp | 7 +-
lldb/source/Target/Target.cpp | 17 +-
lldb/tools/lldb-dap/DAP.cpp | 18 +-
12 files changed, 953 insertions(+), 18 deletions(-)
create mode 100644 lldb/include/lldb/Core/Telemetry.h
create mode 100644 lldb/source/Core/Telemetry.cpp
diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h
index 62b2f91f5076d5..c4b51fc925f094 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 "third_party/llvm/llvm-project/lldb/include/lldb/API/SBStructuredData.h"
namespace lldb_private {
class CommandPluginInterfaceImplementation;
@@ -243,6 +245,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 418c2403d020f4..a3ecb7e4724374 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"
@@ -38,6 +39,7 @@
#include "lldb/lldb-private-enumerations.h"
#include "lldb/lldb-private-types.h"
#include "lldb/lldb-types.h"
+#include "third_party/llvm/llvm-project/lldb/include/lldb/Utility/StructuredData.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringMap.h"
@@ -145,6 +147,12 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
lldb::StreamFileSP GetErrorStreamSP() { return m_error_stream_sp; }
+ std::shared_ptr<TelemetryLogger> GetTelemetryLogger() {
+ return m_telemetry_logger;
+ }
+
+ void SendClientTelemetry(lldb_private::StructuredData::Object *entry);
+
File &GetInputFile() { return *m_input_file_sp; }
File &GetOutputFile() { return m_output_stream_sp->GetFile(); }
@@ -737,6 +745,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<TelemetryLogger> m_telemetry_logger;
// Events for m_sync_broadcaster
enum {
eBroadcastBitEventThreadIsListening = (1 << 0),
@@ -749,6 +758,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 00000000000000..1c7b030bb2a49c
--- /dev/null
+++ b/lldb/include/lldb/Core/Telemetry.h
@@ -0,0 +1,206 @@
+#ifndef LLDB_CORE_TELEMETRY_H
+#define LLDB_CORE_TELEMETRY_H
+
+#include <chrono>
+#include <ctime>
+#include <memory>
+#include <optional>
+#include <string>
+
+#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"
+
+namespace lldb_private {
+
+using SteadyTimePoint = std::chrono::time_point<std::chrono::steady_clock>;
+
+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::optional<std::chrono::nanoseconds> Duration() const {
+ if (m_end.has_value())
+ return *m_end - m_start;
+ else
+ return std::nullopt;
+ }
+};
+
+struct LoggerConfig {
+ // If true, loggings will be enabled.
+ bool enable_logging;
+
+ // 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;
+};
+
+// The base class contains the basic set of data.
+// Downstream implementations can add more fields as needed.
+struct BaseTelemetryEntry {
+ // A "session" corresponds to every time lldb starts.
+ // All entries emitted for the same session will have
+ // the same session_uuid
+ std::string session_uuid;
+
+ TelemetryEventStats stats;
+
+ // 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;
+
+ virtual ~BaseTelemetryEntry() = default;
+ virtual std::string ToString() const;
+};
+
+struct ExitDescription {
+ int exit_code;
+ std::string description;
+};
+
+struct DebuggerInfoEntry : public BaseTelemetryEntry {
+ std::string username;
+ std::string lldb_git_sha;
+ std::string lldb_path;
+ std::string cwd;
+
+ // The debugger exit info. Not populated if this entry was emitted for startup
+ // event.
+ std::optional<ExitDescription> lldb_exit;
+
+ std::string ToString() const override;
+};
+
+struct TargetInfoEntry : public BaseTelemetryEntry {
+ // 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;
+
+ // The process(target) exit info. Not populated of this entry was emitted for
+ // startup event.
+ std::optional<ExitDescription> process_exit;
+
+ std::string ToString() const override;
+};
+
+// Entry from client (eg., SB-API)
+struct ClientTelemetryEntry : public BaseTelemetryEntry {
+ std::string request_name;
+ std::string error_msg;
+ std::string ToString() const override;
+};
+
+struct CommandInfoEntry : public BaseTelemetryEntry {
+ // 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 LoggerConfig struct)
+ std::string original_command;
+ std::string args;
+
+ ExitDescription exit_description;
+
+ std::string ToString() const override;
+};
+
+// The "catch-all" entry to store a set of custom/non-standard
+// data.
+struct MiscInfoEntry : public BaseTelemetryEntry {
+ // 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;
+};
+
+// Where/how to send the logged entries.
+class TelemetryDestination {
+public:
+ virtual ~TelemetryDestination() = default;
+ virtual Status EmitEntry(const BaseTelemetryEntry *entry) = 0;
+ virtual std::string name() const = 0;
+};
+
+// The logger itself!
+class TelemetryLogger {
+public:
+ static std::shared_ptr<TelemetryLogger> CreateInstance(Debugger *);
+
+ virtual ~TelemetryLogger() = default;
+
+ // Invoked upon lldb startup
+ virtual void LogStartup(llvm::StringRef lldb_path,
+ TelemetryEventStats stats) = 0;
+
+ // Invoked upon lldb exit.
+ virtual void LogExit(llvm::StringRef lldb_path,
+ TelemetryEventStats stats) = 0;
+
+ // 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;
+
+ virtual void AddDestination(TelemetryDestination *destination) = 0;
+};
+
+LoggerConfig *GetLoggerConfig();
+
+} // 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 2f3a3c22422efe..c9a7f3db2eb0bb 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"
@@ -3249,6 +3250,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 fbcf30e67fc1cd..6078ab59a20afc 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"
@@ -965,6 +966,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 log dap request - 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 10525ac39e6ef5..4ce9e5d81679f5 100644
--- a/lldb/source/Core/CMakeLists.txt
+++ b/lldb/source/Core/CMakeLists.txt
@@ -54,6 +54,7 @@ add_lldb_library(lldbCore
SourceLocationSpec.cpp
SourceManager.cpp
StreamAsynchronousIO.cpp
+ Telemetry.cpp
ThreadedCommunication.cpp
UserSettingsController.cpp
Value.cpp
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index ebd112110e5f2d..929b22e1df311d 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"
@@ -733,12 +734,23 @@ 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());
+
+ // TODO: we could split up the logging:
+ // - LogStartup_start: called at the beginning
+ // - LogStartup_end: called at the end
+ // This way if the init crashes, we still have some logging.
+ debugger_sp->m_telemetry_logger->LogStartup(
+ HostInfo::GetProgramFileSpec().GetPathAsConstString().GetCString(),
+ std::move(init_stats));
return debugger_sp;
}
@@ -847,7 +859,8 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
m_sync_broadcaster(nullptr, "lldb.debugger.sync"),
m_broadcaster(m_broadcaster_manager_sp,
GetStaticBroadcasterClass().AsCString()),
- m_forward_listener_sp(), m_clear_once() {
+ m_forward_listener_sp(), m_clear_once(),
+ m_telemetry_logger(TelemetryLogger::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);
@@ -939,6 +952,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?)
+ TelemetryEventStats session_stats{
+ /*start_session*/ stats.m_start,
+ /*end_session*/ std::chrono::steady_clock::now()};
+ m_telemetry_logger->LogExit(
+ HostInfo::GetProgramFileSpec().GetPathAsConstString().GetCString(),
+ session_stats);
+
ClearIOHandlers();
StopIOHandlerThread();
StopEventHandlerThread();
@@ -2198,6 +2223,11 @@ Status Debugger::RunREPL(LanguageType language, const char *repl_options) {
return err;
}
+void Debugger::SendClientTelemetry(
+ lldb_private::StructuredData::Object *entry) {
+ m_telemetry_logger->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 00000000000000..2f8bca1da582b8
--- /dev/null
+++ b/lldb/source/Core/Telemetry.cpp
@@ -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
+ startup_info.lldb_path = startup_lldb_path;
+ startup_info.stats = stats;
+
+ llvm::SmallString<64> cwd;
+ if (!llvm::sys::fs::current_path(cwd)) {
+ startup_info.cwd = cwd.c_str();
+ } else {
+ MiscInfoEntry misc_info = MakeBaseEntry<MiscInfoEntry>();
+ 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 BasicTelemetryLogger::LogProcessExit(int status,
+ llvm::StringRef exit_string,
+ TelemetryEventStats stats,
+ Target *target_ptr) {
+ lldb_private::TargetInfoEntry exit_info =
+ MakeBaseEntry<lldb_private::TargetInfoEntry>();
+ exit_info.stats = stats;
+ exit_info.target_uuid =
+ target_ptr && !target_ptr->IsDummyTarget()
+ ? target_ptr->GetExecutableModule()->GetUUID().GetAsString()
+ : "";
+ exit_info.process_exit = {status, exit_string.str()};
+
+ std::cout << "emitting process exit ...\n";
+ EmitToDestinations(&exit_info);
+}
+
+void BasicTelemetryLogger::LogExit(llvm::StringRef lldb_path,
+ TelemetryEventStats stats) {
+ std::cout << "debugger exiting at " << lldb_path.str() << "\n";
+ // we should be shutting down the same instance that we started?!
+ assert(startup_lldb_path == lldb_path.str());
+
+ lldb_private::DebuggerInfoEntry exit_info =
+ MakeBaseEntry<lldb_private::DebuggerInfoEntry>();
+ exit_info.stats = 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.lldb_exit = {-1, "no process launched."};
+ } else {
+ exit_info.lldb_exit = {proc->GetExitStatus(), ""};
+ if (const char *description = proc->GetExitDescription())
+ exit_info.lldb_exit->description = std::string(description);
+ }
+ }
+ }
+ EmitToDestinations(&exit_info);
+}
+
+void BasicTelemetryLogger::CollectMiscBuildInfo() {
+ // collecting use-case specific data
+}
+
+void BasicTelemetryLogger::LogMainExecutableLoadStart(
+ lldb::ModuleSP exec_mod, TelemetryEventStats stats) {
+ TargetInfoEntry target_info = MakeBaseEntry<TargetInfoEntry>();
+ 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 BasicTelemetryLogger::LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
+ TelemetryEventStats stats) {
+ TargetInfoEntry target_info = MakeBaseEntry<TargetInfoEntry>();
+ 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?
+ MiscInfoEntry misc_info = MakeBaseEntry<MiscInfoEntry>();
+ 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 BasicTelemetryLogger::LogClientTelemetry(
+ lldb_private::StructuredData::Object *entry) {
+ ClientTelemetryEntry data_entry = MakeBaseEntry<ClientTelemetryEntry>();
+ auto *dictionary = entry->GetAsDictionary();
+ llvm::StringRef request_name;
+ if (!dictionary->GetValueForKeyAsString("request_name", request_name, "")) {
+ MiscInfoEntry misc_info = MakeBaseEntry<MiscInfoEntry>();
+ // TODO: log the entry too?
+ misc_info.meta_data["internal_errors"] =
+ "Cannot determine request name from client entry";
+ EmitToDestinations(&misc_info);
+ return;
+ }
+ data_entry.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)) {
+ MiscInfoEntry misc_info = MakeBaseEntry<MiscInfoEntry>();
+ misc_info.meta_data["internal_errors"] =
+ "Cannot determine start/end time from client entry";
+ EmitToDestinations(&misc_info);
+ return;
+ }
+
+ std::chrono::nanoseconds start_time_nanos =
+ std::chrono::nanoseconds(start_time);
+ std::chrono::nanoseconds end_time_nanos = std::chrono::nanoseconds(end_time);
+ data_entry.stats.m_start = SteadyTimePoint(start_time_nanos);
+ data_entry.stats.m_end = SteadyTimePoint(end_time_nanos);
+
+ EmitToDestinations(&data_entry);
+}
+void BasicTelemetryLogger::LogCommandStart(llvm::StringRef uuid,
+ llvm::StringRef original_command,
+ TelemetryEventStats stats,
+ Target *target_ptr) {
+
+ lldb_private::CommandInfoEntry command_info =
+ MakeBaseEntry<lldb_private::CommandInfoEntry>();
+
+ // 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 BasicTelemetryLogger::LogCommandEnd(llvm::StringRef uuid,
+ llvm::StringRef command_name,
+ llvm::StringRef command_args,
+ TelemetryEventStats stats,
+ Target *target_ptr,
+ CommandReturnObject *result) {
+
+ lldb_private::CommandInfoEntry command_info =
+ MakeBaseEntry<lldb_private::CommandInfoEntry>();
+
+ // 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.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;
+}
+
+LoggerConfig *GetLoggerConfig() {
+ static lldb_private::LoggerConfig *config = []() {
+ bool enable_logging = true;
+ std::vector<std::string> additional_destinations;
+
+ // TODO parse 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_logging:")) {
+ enable_logging = parse_field(*iter, "enable_logging:");
+ } 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());
+ }
+ }
+ }
+ }
+ // TODO: maybe log that there's an issue reading the init-file?
+ }
+
+ if (additional_destinations.empty())
+ additional_destinations.push_back("stdout");
+ auto *ret =
+ new lldb_private::LoggerConfig{enable_logging, additional_destinations};
+ return ret;
+ }();
+ return config;
+}
+
+std::shared_ptr<TelemetryLogger>
+TelemetryLogger::CreateInstance(lldb_private::Debugger *debugger) {
+ auto *config = GetLoggerConfig();
+ if (!config->enable_logging) {
+ return NoOpTelemetryLogger::CreateInstance(debugger);
+ }
+ return BasicTelemetryLogger::CreateInstance(debugger);
+}
+
+} // namespace lldb_private
\ No newline at end of file
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 8c3972a2ba4ce2..c88fc703e82c60 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -6,11 +6,14 @@
//
//===----------------------------------------------------------------------===//
+#include <chrono>
#include <cstdlib>
+#include <iostream>
#include <limits>
#include <memory>
#include <optional>
#include <string>
+#include <typeinfo>
#include <vector>
#include "Commands/CommandObjectApropos.h"
@@ -53,7 +56,10 @@
#include "lldb/Utility/Stream.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
@@ -1868,8 +1874,28 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
LazyBool lazy_add_to_history,
CommandReturnObject &result,
bool force_repeat_command) {
+ std::cout << "HandleCommand: " << command_line << "\n";
+ TelemetryEventStats command_stats(std::chrono::steady_clock::now());
+ auto logger = GetDebugger().GetTelemetryLogger();
+ // Generate a UUID for this command so the logger can match
+ // the start/end entries correctly.
+ const std::string command_uuid = logger->GetNextUUID();
+
+ logger->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() : "";
+ logger->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\")",
@@ -1893,11 +1919,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)
@@ -1960,7 +1985,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
@@ -2021,21 +2046,20 @@ 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());
- 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 f02ec37cb0f08f..c3035eb9362043 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>
@@ -450,7 +451,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);
@@ -1058,6 +1060,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);
@@ -1071,6 +1074,8 @@ bool Process::SetExitStatus(int status, llvm::StringRef exit_string) {
GetPluginName());
return false;
}
+ GetTarget().GetDebugger().GetTelemetryLogger()->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 09b0ac42631d1a..a07c7a85ac8184 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.GetTelemetryLogger()->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.GetTelemetryLogger()->LogMainExecutableLoadEnd(
+ executable_sp, load_executable_stats);
+ });
+
ElapsedTime elapsed(m_stats.GetCreateTime());
+
LLDB_SCOPED_TIMERF("Target::SetExecutableModule (executable = '%s')",
executable_sp->GetFileSpec().GetPath().c_str());
@@ -1486,6 +1499,8 @@ void Target::SetExecutableModule(ModuleSP &executable_sp,
// what we found in the executable module.
if (!m_arch.GetSpec().IsValid()) {
m_arch = executable_sp->GetArchitecture();
+ // executable_sp->GetArchitecture().GetSpec().GetArchitectureName();
+ // m_arch.
LLDB_LOG(log,
"Target::SetExecutableModule setting architecture to {0} ({1}) "
"based on executable file",
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index b254ddfef0d5ff..da7f3bc7cb8ff9 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -6,17 +6,17 @@
//
//===----------------------------------------------------------------------===//
+#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>
#include <mutex>
#include <sstream>
-#include "DAP.h"
-#include "LLDBUtils.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/FormatVariadic.h"
-
#if defined(_WIN32)
#define NOMINMAX
#include <fcntl.h>
@@ -570,17 +570,25 @@ PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
}
bool DAP::HandleObject(const llvm::json::Object &object) {
+ // auto start_time = std::chrono::steady_clock::now();
const auto packet_type = GetString(object, "type");
if (packet_type == "request") {
const auto command = GetString(object, "command");
auto handler_pos = request_handlers.find(std::string(command));
if (handler_pos != request_handlers.end()) {
handler_pos->second(object);
+ // auto end_time = std::chrono::steady_clock::now();
+ // TODO: fill in the SBStructuredData and send it.
+ // debugger.SendTelemetry(...);
return true; // Success
} else {
+ // auto end_time = std::chrono::steady_clock::now();
if (log)
*log << "error: unhandled command \"" << command.data() << "\""
<< std::endl;
+ // auto end_time = std::chrono::steady_clock::now();
+ // TODO: fill in the SBStructuredData and send it.
+ // debugger.SendTelemetry(...);
return false; // Fail
}
}
>From 7321a9073f1ec326f53ada9f40e258c038037af2 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Fri, 5 Apr 2024 14:28:40 -0400
Subject: [PATCH 2/2] more docs on config
---
lldb/include/lldb/Core/Telemetry.h | 31 ++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/lldb/include/lldb/Core/Telemetry.h b/lldb/include/lldb/Core/Telemetry.h
index 1c7b030bb2a49c..f021437772aa09 100644
--- a/lldb/include/lldb/Core/Telemetry.h
+++ b/lldb/include/lldb/Core/Telemetry.h
@@ -200,6 +200,37 @@ class TelemetryLogger {
virtual void AddDestination(TelemetryDestination *destination) = 0;
};
+/*
+
+Logger configs: LLDB users can also supply their own configs via:
+$XDG_CONFIG_HOME/.lldb_telemetry_config
+
+
+We can propose simple syntax: <field_name><colon><value>
+Eg.,
+enable_logging:true
+destination:stdout
+destination:stderr
+destination:/path/to/some/file
+
+The allowed field_name values are:
+ * enable_logging
+ 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 of
+ + 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.
+
+*/
LoggerConfig *GetLoggerConfig();
} // namespace lldb_private
More information about the lldb-commits
mailing list