Skip to content

[lldb]Implement LLDB Telemetry #98528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
dbb8b15
[llvm][lib]Propose a simple Telemetry framework.
oontvoo Aug 7, 2024
6e43f67
add header
oontvoo Aug 7, 2024
e25f5fc
fixed typo
oontvoo Aug 7, 2024
ad3906c
Merge branch 'llvm:main' into llvm_telemetry
oontvoo Aug 9, 2024
24d07d6
Merge branch 'llvm:main' into llvm_telemetry
oontvoo Aug 22, 2024
0057bcf
add tests and addressed review comments
oontvoo Aug 29, 2024
750e4ac
formatting changes
oontvoo Aug 29, 2024
1378ed4
more formatting
oontvoo Aug 29, 2024
02e750e
more formatting changes
oontvoo Aug 29, 2024
63e99fc
Added header comment to describe the package
oontvoo Aug 29, 2024
fa88512
reformat header
oontvoo Aug 29, 2024
0866f64
addressed review comments and added separate docs in llvm/docs/
oontvoo Sep 3, 2024
a8523f7
updated field names in tests
oontvoo Sep 4, 2024
47e8b06
reformated doc and added additional details
oontvoo Sep 4, 2024
690c6ab
fix formatting
oontvoo Sep 4, 2024
db668f4
details
oontvoo Sep 4, 2024
0290a14
formatted TelemetryTest.cpp
oontvoo Sep 4, 2024
14b5234
fix formatting in docs
oontvoo Sep 4, 2024
6ca87e5
convert comments to doxygen style
oontvoo Sep 4, 2024
4155add
fix doc toc
oontvoo Sep 4, 2024
11ead4a
group all the testing-params into a Context struct
oontvoo Sep 5, 2024
48228ee
fix conversion warnings
oontvoo Sep 5, 2024
ed8a3f1
Merge branch 'llvm:main' into llvm_telemetry
oontvoo Sep 5, 2024
990e1ca
finish up todos
oontvoo Sep 5, 2024
42781f7
[lldb]Implement LLDB Telemetry :
oontvoo Sep 12, 2024
04b7da8
change the data type from json to structureddata
oontvoo Sep 17, 2024
0e49b93
Rework the default impl of Telemetry in LLDB as "Plugin" to allow for…
oontvoo Sep 20, 2024
10b5f46
Merge branch 'llvm:main' into telemetry_shared
oontvoo Sep 24, 2024
f85900d
UPdate LLDB implementation to match changes in LLVM's telemetry
oontvoo Dec 11, 2024
66b77aa
fix vtable issue
oontvoo Dec 11, 2024
67a0ed0
more renaming for consistency
oontvoo Dec 11, 2024
f7f7004
remove unused
oontvoo Dec 11, 2024
949b994
addressed review comments
oontvoo Dec 17, 2024
1eeb6f9
formatting
oontvoo Dec 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lldb/include/lldb/API/SBDebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "lldb/API/SBDefines.h"
#include "lldb/API/SBPlatform.h"
#include "lldb/API/SBStructuredData.h"

namespace lldb_private {
class CommandPluginInterfaceImplementation;
Expand Down Expand Up @@ -245,6 +246,8 @@ class LLDB_API SBDebugger {

lldb::SBTarget GetDummyTarget();

void SendTelemetry(const lldb::SBStructuredData &entry);

// Return true if target is deleted from the target list of the debugger.
bool DeleteTarget(lldb::SBTarget &target);

Expand Down
10 changes: 10 additions & 0 deletions lldb/include/lldb/Core/Debugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "lldb/Core/FormatEntity.h"
#include "lldb/Core/IOHandler.h"
#include "lldb/Core/SourceManager.h"
#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Core/Telemetry.h"
#include "lldb/Core/UserSettingsController.h"
#include "lldb/Host/HostThread.h"
#include "lldb/Host/StreamFile.h"
Expand All @@ -31,6 +33,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"
Expand All @@ -46,6 +49,7 @@
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Threading.h"
#include "llvm/Telemetry/Telemetry.h"

#include <cassert>
#include <cstddef>
Expand Down Expand Up @@ -149,6 +153,10 @@ class Debugger : public std::enable_shared_from_this<Debugger>,

repro::DataRecorder *GetInputRecorder();

LldbTelemeter *GetTelemeter() { return m_telemeter.get(); }

void SendClientTelemetry(const lldb_private::StructuredDataImpl &entry);

Status SetInputString(const char *data);

void SetInputFile(lldb::FileSP file);
Expand Down Expand Up @@ -759,6 +767,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
eBroadcastBitEventThreadIsListening = (1 << 0),
};

std::unique_ptr<LldbTelemeter> m_telemeter;

private:
// Use Debugger::CreateInstance() to get a shared pointer to a new debugger
// object
Expand Down
294 changes: 294 additions & 0 deletions lldb/include/lldb/Core/Telemetry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
//===-- Telemetry.h ----------------------------------------------*- C++
//-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_CORE_TELEMETRY_H
#define LLDB_CORE_TELEMETRY_H

#include <chrono>
#include <ctime>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>

#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/lldb-forward.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/JSON.h"
#include "llvm/Telemetry/Telemetry.h"

namespace lldb_private {

using llvm::telemetry::KindType;

struct LldbEntryKind : public ::llvm::telemetry::EntryKind {
static const KindType BaseInfo = 0b11000;
static const KindType DebuggerInfo = 0b11001;
static const KindType TargetInfo = 0b11010;
static const KindType ClientInfo = 0b11100;
static const KindType CommandInfo = 0b11101;
static const KindType MiscInfo = 0b11110;
};

struct LldbBaseTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
// For dyn_cast, isa, etc operations.
KindType getKind() const override { return LldbEntryKind::BaseInfo; }

static bool classof(const TelemetryInfo *T) {
if (T == nullptr)
return false;
// Subclasses of this is also acceptable.
return (T->getKind() & LldbEntryKind::BaseInfo) == LldbEntryKind::BaseInfo;
}

// Returns a human-readable string description of the struct.
// This is for debugging purposes only.
// It is NOT meant as a data-serialisation method.
virtual std::string ToString() const;
};

struct DebuggerTelemetryInfo : public LldbBaseTelemetryInfo {
std::string username;
std::string lldb_git_sha;
std::string lldb_path;
std::string cwd;

DebuggerTelemetryInfo() = default;

// Provide a copy ctor because we may need to make a copy before
// sanitizing the data.
// (The sanitization might differ between different Destination classes).
DebuggerTelemetryInfo(const DebuggerTelemetryInfo &other) {
username = other.username;
lldb_git_sha = other.lldb_git_sha;
lldb_path = other.lldb_path;
cwd = other.cwd;
};

KindType getKind() const override { return LldbEntryKind::DebuggerInfo; }

static bool classof(const TelemetryInfo *T) {
if (T == nullptr)
return false;
return T->getKind() == LldbEntryKind::DebuggerInfo;
}

llvm::json::Object serializeToJson() const override;

std::string ToString() const override;
};

struct TargetTelemetryInfo : public LldbBaseTelemetryInfo {
// The same as the executable-module's UUID.
std::string target_uuid;
std::string file_format;

std::string binary_path;
size_t binary_size;

TargetTelemetryInfo() = default;

TargetTelemetryInfo(const TargetTelemetryInfo &other) {
target_uuid = other.target_uuid;
file_format = other.file_format;
binary_path = other.binary_path;
binary_size = other.binary_size;
}

KindType getKind() const override { return LldbEntryKind::TargetInfo; }

static bool classof(const TelemetryInfo *T) {
if (T == nullptr)
return false;
return T->getKind() == LldbEntryKind::TargetInfo;
}

llvm::json::Object serializeToJson() const override;

std::string ToString() const override;
};

// Entry from client (eg., SB-API)
struct ClientTelemetryInfo : public LldbBaseTelemetryInfo {
std::string request_name;
std::string error_msg;

ClientTelemetryInfo() = default;

ClientTelemetryInfo(const ClientTelemetryInfo &other) {
request_name = other.request_name;
error_msg = other.error_msg;
}

KindType getKind() const override { return LldbEntryKind::ClientInfo; }

static bool classof(const TelemetryInfo *T) {
if (T == nullptr)
return false;
return T->getKind() == LldbEntryKind::ClientInfo;
}

llvm::json::Object serializeToJson() const override;

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) {
ExitCode = ret_code;
Description = std::move(ret_desc);
ret_status = status;
}
};

struct CommandTelemetryInfo : public LldbBaseTelemetryInfo {
// If the command is/can be associated with a target entry,
// this field contains that target's UUID.
// <EMPTY> otherwise.
std::string target_uuid;
std::string command_uuid;

// Eg., "breakpoint set"
std::string command_name;

// !!NOTE!!: The following fields may be omitted due to PII risk.
// (Configurable via the telemery::Config struct)
std::string original_command;
std::string args;

lldb::ReturnStatus ret_status;

CommandTelemetryInfo() = default;

CommandTelemetryInfo(const CommandTelemetryInfo &other) {
target_uuid = other.target_uuid;
command_uuid = other.command_uuid;
command_name = other.command_name;
original_command = other.original_command;
args = other.args;
}

KindType getKind() const override { return LldbEntryKind::CommandInfo; }

static bool classof(const TelemetryInfo *T) {
if (T == nullptr)
return false;
return T->getKind() == LldbEntryKind::CommandInfo;
}

llvm::json::Object serializeToJson() const override;

std::string ToString() const override;
};

// The "catch-all" entry to store a set of custom/non-standard
// data.
struct MiscTelemetryInfo : public LldbBaseTelemetryInfo {
// If the event is/can be associated with a target entry,
// this field contains that target's UUID.
// <EMPTY> otherwise.
std::string target_uuid;

// Set of key-value pairs for any optional (or impl-specific) data
std::unordered_map<std::string, std::string> meta_data;

MiscTelemetryInfo() = default;

MiscTelemetryInfo(const MiscTelemetryInfo &other) {
target_uuid = other.target_uuid;
meta_data = other.meta_data;
}

KindType getKind() const override { return LldbEntryKind::MiscInfo; }

static bool classof(const TelemetryInfo *T) {
if (T == nullptr)
return false;
return T->getKind() == LldbEntryKind::MiscInfo;
}

llvm::json::Object serializeToJson() const override;

std::string ToString() const override;
};

class LldbTelemeter : public llvm::telemetry::Telemeter {
public:
static std::unique_ptr<LldbTelemeter> CreateInstance(Debugger *);

virtual ~LldbTelemeter() = default;

// Invoked upon process exit
virtual void LogProcessExit(int status, llvm::StringRef exit_string,
llvm::telemetry::EventStats 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,
llvm::telemetry::EventStats stats) = 0;
virtual void LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
llvm::telemetry::EventStats 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,
llvm::telemetry::EventStats stats,
Target *target_ptr) = 0;
virtual void LogCommandEnd(llvm::StringRef uuid, llvm::StringRef command_name,
llvm::StringRef command_args,
llvm::telemetry::EventStats stats,
Target *target_ptr,
CommandReturnObject *result) = 0;

virtual std::string GetNextUUID() = 0;

// For client (eg., SB API) to send telemetry entries.
virtual void
LogClientTelemetry(const lldb_private::StructuredDataImpl &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>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is actual code, it should talk about the (real) syntax, not the proposed one.

// 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.
llvm::telemetry::Config *GetTelemetryConfig();

} // namespace lldb_private
#endif // LLDB_CORE_TELEMETRY_H
4 changes: 2 additions & 2 deletions lldb/include/lldb/lldb-enumerations.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ enum StopReason {
};

/// Command Return Status Types.
enum ReturnStatus {
eReturnStatusInvalid,
enum ReturnStatus : int {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this relate to the rest of the patch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to serialize the ReturnStatus from a command.

eReturnStatusInvalid = 0,
eReturnStatusSuccessFinishNoResult,
eReturnStatusSuccessFinishResult,
eReturnStatusSuccessContinuingNoResult,
Expand Down
10 changes: 10 additions & 0 deletions lldb/source/API/SBDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,16 @@ SBTarget SBDebugger::GetDummyTarget() {
return sb_target;
}

void SBDebugger::SendTelemetry(const lldb::SBStructuredData &entry) {
if (lldb_private::Debugger *debugger = this->get()) {
debugger->SendClientTelemetry(*(entry.m_impl_up.get()));
} else {
Log *log = GetLog(LLDBLog::API);
LLDB_LOGF(log,
"Could not send telemetry from SBDebugger - debugger was null.");
}
}

bool SBDebugger::DeleteTarget(lldb::SBTarget &target) {
LLDB_INSTRUMENT_VA(this, target);

Expand Down
Loading
Loading