Skip to content

Commit a74e4ac

Browse files
oontvoolabath
andauthored
[LLDB][Telemetry] Collect telemetry from client when allowed. (#129728)
This patch is slightly different from other impl in that we dispatch client-telemetry via a different helper method. This is to make it easier for vendor to opt-out (simply by overriding the method to do nothing). There is also a configuration option to disallow collecting client telemetry. --------- Co-authored-by: Pavel Labath <pavel@labath.sk>
1 parent cfc5baf commit a74e4ac

File tree

9 files changed

+175
-4
lines changed

9 files changed

+175
-4
lines changed

lldb/include/lldb/API/SBDebugger.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include "lldb/API/SBDefines.h"
1515
#include "lldb/API/SBPlatform.h"
16+
#include "lldb/API/SBStructuredData.h"
1617

1718
namespace lldb_private {
1819
class CommandPluginInterfaceImplementation;
@@ -250,6 +251,13 @@ class LLDB_API SBDebugger {
250251

251252
lldb::SBTarget GetDummyTarget();
252253

254+
#ifndef SWIG
255+
// Dispatch telemery from client to server if client-telemetry is enabled
256+
// (by vendor), otherwise the data is ignored.
257+
// Invoking this from python client (with SWIG) is not supported.
258+
void DispatchClientTelemetry(const lldb::SBStructuredData &data);
259+
#endif
260+
253261
// Return true if target is deleted from the target list of the debugger.
254262
bool DeleteTarget(lldb::SBTarget &target);
255263

lldb/include/lldb/Core/Debugger.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "lldb/Core/IOHandler.h"
2121
#include "lldb/Core/SourceManager.h"
2222
#include "lldb/Core/Statusline.h"
23+
#include "lldb/Core/StructuredDataImpl.h"
24+
#include "lldb/Core/Telemetry.h"
2325
#include "lldb/Core/UserSettingsController.h"
2426
#include "lldb/Host/HostThread.h"
2527
#include "lldb/Host/StreamFile.h"
@@ -32,6 +34,7 @@
3234
#include "lldb/Utility/Diagnostics.h"
3335
#include "lldb/Utility/FileSpec.h"
3436
#include "lldb/Utility/Status.h"
37+
#include "lldb/Utility/StructuredData.h"
3538
#include "lldb/Utility/UserID.h"
3639
#include "lldb/lldb-defines.h"
3740
#include "lldb/lldb-enumerations.h"
@@ -124,6 +127,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
124127

125128
void Clear();
126129

130+
void DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry);
131+
127132
bool GetAsyncExecution();
128133

129134
void SetAsyncExecution(bool async);

lldb/include/lldb/Core/Telemetry.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,16 @@ struct LLDBConfig : public ::llvm::telemetry::Config {
3939
// the vendor while creating the Manager.
4040
const bool detailed_command_telemetry;
4141

42-
explicit LLDBConfig(bool enable_telemetry, bool detailed_command_telemetry)
42+
// If true, we will collect telemetry from LLDB's clients (eg., lldb-dap) via
43+
// the SB interface. Must also be enabled by the vendor while creating the
44+
// manager.
45+
const bool enable_client_telemetry;
46+
47+
explicit LLDBConfig(bool enable_telemetry, bool detailed_command_telemetry,
48+
bool enable_client_telemetry)
4349
: ::llvm::telemetry::Config(enable_telemetry),
44-
detailed_command_telemetry(detailed_command_telemetry) {}
50+
detailed_command_telemetry(detailed_command_telemetry),
51+
enable_client_telemetry(enable_client_telemetry) {}
4552
};
4653

4754
// We expect each (direct) subclass of LLDBTelemetryInfo to
@@ -56,6 +63,7 @@ struct LLDBConfig : public ::llvm::telemetry::Config {
5663
struct LLDBEntryKind : public ::llvm::telemetry::EntryKind {
5764
// clang-format off
5865
static const llvm::telemetry::KindType BaseInfo = 0b11000000;
66+
static const llvm::telemetry::KindType ClientInfo = 0b11100000;
5967
static const llvm::telemetry::KindType CommandInfo = 0b11010000;
6068
static const llvm::telemetry::KindType DebuggerInfo = 0b11001000;
6169
static const llvm::telemetry::KindType ExecModuleInfo = 0b11000100;
@@ -89,6 +97,14 @@ struct LLDBBaseTelemetryInfo : public llvm::telemetry::TelemetryInfo {
8997
void serialize(llvm::telemetry::Serializer &serializer) const override;
9098
};
9199

100+
struct ClientInfo : public LLDBBaseTelemetryInfo {
101+
std::string client_name;
102+
std::string client_data;
103+
std::optional<std::string> error_msg;
104+
105+
void serialize(llvm::telemetry::Serializer &serializer) const override;
106+
};
107+
92108
struct CommandInfo : public LLDBBaseTelemetryInfo {
93109
/// If the command is/can be associated with a target entry this field
94110
/// contains that target's UUID. <EMPTY> otherwise.
@@ -217,6 +233,9 @@ class TelemetryManager : public llvm::telemetry::Manager {
217233

218234
const LLDBConfig *GetConfig() { return m_config.get(); }
219235

236+
virtual void
237+
DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry,
238+
Debugger *debugger);
220239
virtual llvm::StringRef GetInstanceName() const = 0;
221240

222241
static TelemetryManager *GetInstance();

lldb/source/API/SBDebugger.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,17 @@ SBTarget SBDebugger::GetDummyTarget() {
926926
return sb_target;
927927
}
928928

929+
void SBDebugger::DispatchClientTelemetry(const lldb::SBStructuredData &entry) {
930+
LLDB_INSTRUMENT_VA(this);
931+
if (m_opaque_sp) {
932+
m_opaque_sp->DispatchClientTelemetry(*entry.m_impl_up);
933+
} else {
934+
Log *log = GetLog(LLDBLog::API);
935+
LLDB_LOGF(log,
936+
"Could not send telemetry from SBDebugger - debugger was null.");
937+
}
938+
}
939+
929940
bool SBDebugger::DeleteTarget(lldb::SBTarget &target) {
930941
LLDB_INSTRUMENT_VA(this, target);
931942

lldb/source/Core/Debugger.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,12 @@ DebuggerSP Debugger::CreateInstance(lldb::LogOutputCallback log_callback,
841841
return debugger_sp;
842842
}
843843

844+
void Debugger::DispatchClientTelemetry(
845+
const lldb_private::StructuredDataImpl &entry) {
846+
lldb_private::telemetry::TelemetryManager::GetInstance()
847+
->DispatchClientTelemetry(entry, this);
848+
}
849+
844850
void Debugger::HandleDestroyCallback() {
845851
const lldb::user_id_t user_id = GetID();
846852
// Invoke and remove all the callbacks in an FIFO order. Callbacks which are

lldb/source/Core/Telemetry.cpp

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
5353
serializer.write("end_time", ToNanosec(end_time.value()));
5454
}
5555

56+
void ClientInfo::serialize(Serializer &serializer) const {
57+
LLDBBaseTelemetryInfo::serialize(serializer);
58+
serializer.write("client_data", client_data);
59+
serializer.write("client_name", client_name);
60+
if (error_msg.has_value())
61+
serializer.write("error_msg", error_msg.value());
62+
}
63+
5664
void CommandInfo::serialize(Serializer &serializer) const {
5765
LLDBBaseTelemetryInfo::serialize(serializer);
5866

@@ -112,6 +120,63 @@ llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {
112120
return llvm::Error::success();
113121
}
114122

123+
void TelemetryManager::DispatchClientTelemetry(
124+
const lldb_private::StructuredDataImpl &entry, Debugger *debugger) {
125+
if (!m_config->enable_client_telemetry)
126+
return;
127+
128+
ClientInfo client_info;
129+
client_info.debugger = debugger;
130+
if (entry.GetObjectSP()->GetType() != lldb::eStructuredDataTypeDictionary) {
131+
LLDB_LOG(GetLog(LLDBLog::Object), "Expected Dictionary type but got {0}.",
132+
entry.GetObjectSP()->GetType());
133+
return;
134+
}
135+
136+
auto *dict = entry.GetObjectSP()->GetAsDictionary();
137+
138+
llvm::StringRef client_name;
139+
if (dict->GetValueForKeyAsString("client_name", client_name))
140+
client_info.client_name = client_name.str();
141+
else
142+
LLDB_LOG(GetLog(LLDBLog::Object),
143+
"Cannot determine client_name from client-telemetry entry");
144+
145+
llvm::StringRef client_data;
146+
if (dict->GetValueForKeyAsString("client_data", client_data))
147+
client_info.client_data = client_data.str();
148+
else
149+
LLDB_LOG(GetLog(LLDBLog::Object),
150+
"Cannot determine client_data from client-telemetry entry");
151+
152+
int64_t start_time;
153+
if (dict->GetValueForKeyAsInteger("start_time", start_time)) {
154+
client_info.start_time +=
155+
std::chrono::nanoseconds(static_cast<size_t>(start_time));
156+
} else {
157+
LLDB_LOG(GetLog(LLDBLog::Object),
158+
"Cannot determine start-time from client-telemetry entry");
159+
}
160+
161+
int64_t end_time;
162+
if (dict->GetValueForKeyAsInteger("end_time", end_time)) {
163+
SteadyTimePoint epoch;
164+
client_info.end_time =
165+
epoch + std::chrono::nanoseconds(static_cast<size_t>(end_time));
166+
} else {
167+
LLDB_LOG(GetLog(LLDBLog::Object),
168+
"Cannot determine end-time from client-telemetry entry");
169+
}
170+
171+
llvm::StringRef error_msg;
172+
if (dict->GetValueForKeyAsString("error", error_msg))
173+
client_info.error_msg = error_msg.str();
174+
175+
if (llvm::Error er = dispatch(&client_info))
176+
LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(er),
177+
"Failed to dispatch client telemetry");
178+
}
179+
115180
class NoOpTelemetryManager : public TelemetryManager {
116181
public:
117182
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override {
@@ -121,12 +186,18 @@ class NoOpTelemetryManager : public TelemetryManager {
121186

122187
explicit NoOpTelemetryManager()
123188
: TelemetryManager(std::make_unique<LLDBConfig>(
124-
/*EnableTelemetry*/ false, /*DetailedCommand*/ false)) {}
189+
/*EnableTelemetry=*/false, /*DetailedCommand=*/false,
190+
/*ClientTelemery=*/false)) {}
125191

126192
virtual llvm::StringRef GetInstanceName() const override {
127193
return "NoOpTelemetryManager";
128194
}
129195

196+
void DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry,
197+
Debugger *debugger) override {
198+
// Does nothing.
199+
}
200+
130201
llvm::Error dispatch(llvm::telemetry::TelemetryInfo *entry) override {
131202
// Does nothing.
132203
return llvm::Error::success();

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,8 @@ void DAP::SetTarget(const lldb::SBTarget target) {
700700
}
701701

702702
bool DAP::HandleObject(const Message &M) {
703+
TelemetryDispatcher dispatcher(&debugger);
704+
dispatcher.Set("client_name", transport.GetClientName().str());
703705
if (const auto *req = std::get_if<Request>(&M)) {
704706
{
705707
std::lock_guard<std::mutex> guard(m_active_request_mutex);
@@ -716,11 +718,15 @@ bool DAP::HandleObject(const Message &M) {
716718
});
717719

718720
auto handler_pos = request_handlers.find(req->command);
721+
dispatcher.Set("client_data",
722+
llvm::Twine("request_command:", req->command).str());
719723
if (handler_pos != request_handlers.end()) {
720724
handler_pos->second->Run(*req);
721725
return true; // Success
722726
}
723727

728+
dispatcher.Set("error",
729+
llvm::Twine("unhandled-command:" + req->command).str());
724730
DAP_LOG(log, "({0}) error: unhandled command '{1}'",
725731
transport.GetClientName(), req->command);
726732
return false; // Fail
@@ -744,6 +750,8 @@ bool DAP::HandleObject(const Message &M) {
744750
// Result should be given, use null if not.
745751
if (resp->success) {
746752
(*response_handler)(resp->body);
753+
dispatcher.Set("client_data",
754+
llvm::Twine("response_command:", resp->command).str());
747755
} else {
748756
llvm::StringRef message = "Unknown error, response failed";
749757
if (resp->message) {
@@ -764,6 +772,7 @@ bool DAP::HandleObject(const Message &M) {
764772
}),
765773
*resp->message);
766774
}
775+
dispatcher.Set("error", message.str());
767776

768777
(*response_handler)(llvm::createStringError(
769778
std::error_code(-1, std::generic_category()), message));
@@ -772,6 +781,7 @@ bool DAP::HandleObject(const Message &M) {
772781
return true;
773782
}
774783

784+
dispatcher.Set("error", "Unsupported protocol message");
775785
DAP_LOG(log, "Unsupported protocol message");
776786

777787
return false;

lldb/tools/lldb-dap/LLDBUtils.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
#include "llvm/ADT/StringRef.h"
1818
#include "llvm/Support/Error.h"
1919
#include "llvm/Support/JSON.h"
20+
#include "llvm/Support/ScopedPrinter.h"
2021
#include "llvm/Support/raw_ostream.h"
22+
#include <chrono>
2123
#include <string>
2224

2325
namespace lldb_dap {
@@ -159,6 +161,43 @@ uint32_t GetLLDBFrameID(uint64_t dap_frame_id);
159161
lldb::SBEnvironment
160162
GetEnvironmentFromArguments(const llvm::json::Object &arguments);
161163

164+
/// Helper for sending telemetry to lldb server, if client-telemetry is enabled.
165+
class TelemetryDispatcher {
166+
public:
167+
TelemetryDispatcher(lldb::SBDebugger *debugger) {
168+
m_telemetry_json = llvm::json::Object();
169+
m_telemetry_json.try_emplace(
170+
"start_time",
171+
std::chrono::steady_clock::now().time_since_epoch().count());
172+
this->debugger = debugger;
173+
}
174+
175+
void Set(std::string key, std::string value) {
176+
m_telemetry_json.try_emplace(key, value);
177+
}
178+
179+
void Set(std::string key, int64_t value) {
180+
m_telemetry_json.try_emplace(key, value);
181+
}
182+
183+
~TelemetryDispatcher() {
184+
m_telemetry_json.try_emplace(
185+
"end_time",
186+
std::chrono::steady_clock::now().time_since_epoch().count());
187+
188+
lldb::SBStructuredData telemetry_entry;
189+
llvm::json::Value val(std::move(m_telemetry_json));
190+
191+
std::string string_rep = llvm::to_string(val);
192+
telemetry_entry.SetFromJSON(string_rep.c_str());
193+
debugger->DispatchClientTelemetry(telemetry_entry);
194+
}
195+
196+
private:
197+
llvm::json::Object m_telemetry_json;
198+
lldb::SBDebugger *debugger;
199+
};
200+
162201
/// Get the stop-disassembly-display settings
163202
///
164203
/// \param[in] debugger
@@ -168,6 +207,7 @@ GetEnvironmentFromArguments(const llvm::json::Object &arguments);
168207
/// The value of the stop-disassembly-display setting
169208
lldb::StopDisassemblyType GetStopDisassemblyDisplay(lldb::SBDebugger &debugger);
170209

210+
171211
/// Take ownership of the stored error.
172212
llvm::Error ToError(const lldb::SBError &error);
173213

lldb/unittests/Core/TelemetryTest.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ class FakePlugin : public telemetry::TelemetryManager {
5353
public:
5454
FakePlugin()
5555
: telemetry::TelemetryManager(std::make_unique<telemetry::LLDBConfig>(
56-
/*enable_telemetry=*/true, /*detailed_command_telemetry=*/true)) {}
56+
/*enable_telemetry=*/true, /*detailed_command_telemetry=*/true,
57+
/*enable_client_telemetry=*/true)) {}
5758

5859
// TelemetryManager interface
5960
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override {

0 commit comments

Comments
 (0)