Skip to content

Commit 42781f7

Browse files
committed
[lldb]Implement LLDB Telemetry :
Provide the concrete implementation for telemetry in LLDB. (This is follow-up patch to PR/102323)
1 parent 990e1ca commit 42781f7

File tree

12 files changed

+1139
-13
lines changed

12 files changed

+1139
-13
lines changed

lldb/include/lldb/API/SBDebugger.h

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

1414
#include "lldb/API/SBDefines.h"
1515
#include "lldb/API/SBPlatform.h"
16+
#include "lldb/API/SBStructuredData.h"
17+
#include "llvm/Support/JSON.h"
1618

1719
namespace lldb_private {
1820
class CommandPluginInterfaceImplementation;
@@ -245,6 +247,8 @@ class LLDB_API SBDebugger {
245247

246248
lldb::SBTarget GetDummyTarget();
247249

250+
void SendTelemetry(const llvm::json::Object &entry);
251+
248252
// Return true if target is deleted from the target list of the debugger.
249253
bool DeleteTarget(lldb::SBTarget &target);
250254

lldb/include/lldb/Core/Debugger.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "lldb/Core/FormatEntity.h"
2020
#include "lldb/Core/IOHandler.h"
2121
#include "lldb/Core/SourceManager.h"
22+
#include "lldb/Core/Telemetry.h"
2223
#include "lldb/Core/UserSettingsController.h"
2324
#include "lldb/Host/HostThread.h"
2425
#include "lldb/Host/StreamFile.h"
@@ -31,6 +32,7 @@
3132
#include "lldb/Utility/Diagnostics.h"
3233
#include "lldb/Utility/FileSpec.h"
3334
#include "lldb/Utility/Status.h"
35+
#include "lldb/Utility/StructuredData.h"
3436
#include "lldb/Utility/UserID.h"
3537
#include "lldb/lldb-defines.h"
3638
#include "lldb/lldb-enumerations.h"
@@ -45,7 +47,9 @@
4547
#include "llvm/ADT/StringRef.h"
4648
#include "llvm/Support/DynamicLibrary.h"
4749
#include "llvm/Support/FormatVariadic.h"
50+
#include "llvm/Support/JSON.h"
4851
#include "llvm/Support/Threading.h"
52+
#include "llvm/Telemetry/Telemetry.h"
4953

5054
#include <cassert>
5155
#include <cstddef>
@@ -149,6 +153,10 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
149153

150154
repro::DataRecorder *GetInputRecorder();
151155

156+
LldbTelemeter *GetTelemeter() { return m_telemeter.get(); }
157+
158+
void SendClientTelemetry(const llvm::json::Object &entry);
159+
152160
Status SetInputString(const char *data);
153161

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

770+
std::unique_ptr<LldbTelemeter> m_telemeter;
771+
762772
private:
763773
// Use Debugger::CreateInstance() to get a shared pointer to a new debugger
764774
// object

lldb/include/lldb/Core/Telemetry.h

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
//===-- Telemetry.h ----------------------------------------------*- C++
2+
//-*-===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef LLDB_CORE_TELEMETRY_H
11+
#define LLDB_CORE_TELEMETRY_H
12+
13+
#include <chrono>
14+
#include <ctime>
15+
#include <memory>
16+
#include <optional>
17+
#include <string>
18+
#include <unordered_map>
19+
20+
#include "lldb/Interpreter/CommandReturnObject.h"
21+
#include "lldb/Utility/StructuredData.h"
22+
#include "lldb/lldb-forward.h"
23+
#include "llvm/ADT/StringExtras.h"
24+
#include "llvm/ADT/StringRef.h"
25+
#include "llvm/Support/JSON.h"
26+
#include "llvm/Telemetry/Telemetry.h"
27+
28+
namespace lldb_private {
29+
30+
using llvm::telemetry::KindType;
31+
32+
struct LldbEntryKind : public ::llvm::telemetry::EntryKind {
33+
static const KindType BaseInfo = 0b11000;
34+
static const KindType DebuggerInfo = 0b11001;
35+
static const KindType TargetInfo = 0b11010;
36+
static const KindType ClientInfo = 0b11100;
37+
static const KindType CommandInfo = 0b11101;
38+
static const KindType MiscInfo = 0b11110;
39+
};
40+
41+
struct LldbBaseTelemetryInfo : public ::llvm::telemetry::TelemetryInfo {
42+
// For dyn_cast, isa, etc operations.
43+
KindType getKind() const override { return LldbEntryKind::BaseInfo; }
44+
45+
static bool classof(const TelemetryInfo *T) {
46+
if (T == nullptr)
47+
return false;
48+
// Subclasses of this is also acceptable.
49+
return (T->getKind() & LldbEntryKind::BaseInfo) == LldbEntryKind::BaseInfo;
50+
}
51+
52+
// Returns a human-readable string description of the struct.
53+
// This is for debugging purposes only.
54+
// It is NOT meant as a data-serialisation method.
55+
virtual std::string ToString() const;
56+
};
57+
58+
struct DebuggerTelemetryInfo : public LldbBaseTelemetryInfo {
59+
std::string username;
60+
std::string lldb_git_sha;
61+
std::string lldb_path;
62+
std::string cwd;
63+
64+
DebuggerTelemetryInfo() = default;
65+
66+
// Provide a copy ctor because we may need to make a copy before
67+
// sanitizing the data.
68+
// (The sanitization might differ between different Destination classes).
69+
DebuggerTelemetryInfo(const DebuggerTelemetryInfo &other) {
70+
username = other.username;
71+
lldb_git_sha = other.lldb_git_sha;
72+
lldb_path = other.lldb_path;
73+
cwd = other.cwd;
74+
};
75+
76+
KindType getKind() const override { return LldbEntryKind::DebuggerInfo; }
77+
78+
static bool classof(const TelemetryInfo *T) {
79+
if (T == nullptr)
80+
return false;
81+
return T->getKind() == LldbEntryKind::DebuggerInfo;
82+
}
83+
84+
llvm::json::Object serializeToJson() const override;
85+
86+
std::string ToString() const override;
87+
};
88+
89+
struct TargetTelemetryInfo : public LldbBaseTelemetryInfo {
90+
// The same as the executable-module's UUID.
91+
std::string target_uuid;
92+
std::string file_format;
93+
94+
std::string binary_path;
95+
size_t binary_size;
96+
97+
TargetTelemetryInfo() = default;
98+
99+
TargetTelemetryInfo(const TargetTelemetryInfo &other) {
100+
target_uuid = other.target_uuid;
101+
file_format = other.file_format;
102+
binary_path = other.binary_path;
103+
binary_size = other.binary_size;
104+
}
105+
106+
KindType getKind() const override { return LldbEntryKind::TargetInfo; }
107+
108+
static bool classof(const TelemetryInfo *T) {
109+
if (T == nullptr)
110+
return false;
111+
return T->getKind() == LldbEntryKind::TargetInfo;
112+
}
113+
114+
llvm::json::Object serializeToJson() const override;
115+
116+
std::string ToString() const override;
117+
};
118+
119+
// Entry from client (eg., SB-API)
120+
struct ClientTelemetryInfo : public LldbBaseTelemetryInfo {
121+
std::string request_name;
122+
std::string error_msg;
123+
124+
ClientTelemetryInfo() = default;
125+
126+
ClientTelemetryInfo(const ClientTelemetryInfo &other) {
127+
request_name = other.request_name;
128+
error_msg = other.error_msg;
129+
}
130+
131+
KindType getKind() const override { return LldbEntryKind::ClientInfo; }
132+
133+
static bool classof(const TelemetryInfo *T) {
134+
if (T == nullptr)
135+
return false;
136+
return T->getKind() == LldbEntryKind::ClientInfo;
137+
}
138+
139+
llvm::json::Object serializeToJson() const override;
140+
141+
std::string ToString() const override;
142+
};
143+
144+
struct CommandExitDescription : public ::llvm::telemetry::ExitDescription {
145+
lldb::ReturnStatus ret_status;
146+
CommandExitDescription(int ret_code, std::string ret_desc,
147+
lldb::ReturnStatus status) {
148+
ExitCode = ret_code;
149+
Description = std::move(ret_desc);
150+
ret_status = status;
151+
}
152+
};
153+
154+
struct CommandTelemetryInfo : public LldbBaseTelemetryInfo {
155+
// If the command is/can be associated with a target entry,
156+
// this field contains that target's UUID.
157+
// <EMPTY> otherwise.
158+
std::string target_uuid;
159+
std::string command_uuid;
160+
161+
// Eg., "breakpoint set"
162+
std::string command_name;
163+
164+
// !!NOTE!!: The following fields may be omitted due to PII risk.
165+
// (Configurable via the telemery::Config struct)
166+
std::string original_command;
167+
std::string args;
168+
169+
lldb::ReturnStatus ret_status;
170+
171+
CommandTelemetryInfo() = default;
172+
173+
CommandTelemetryInfo(const CommandTelemetryInfo &other) {
174+
target_uuid = other.target_uuid;
175+
command_uuid = other.command_uuid;
176+
command_name = other.command_name;
177+
original_command = other.original_command;
178+
args = other.args;
179+
}
180+
181+
KindType getKind() const override { return LldbEntryKind::CommandInfo; }
182+
183+
static bool classof(const TelemetryInfo *T) {
184+
if (T == nullptr)
185+
return false;
186+
return T->getKind() == LldbEntryKind::CommandInfo;
187+
}
188+
189+
llvm::json::Object serializeToJson() const override;
190+
191+
std::string ToString() const override;
192+
};
193+
194+
// The "catch-all" entry to store a set of custom/non-standard
195+
// data.
196+
struct MiscTelemetryInfo : public LldbBaseTelemetryInfo {
197+
// If the event is/can be associated with a target entry,
198+
// this field contains that target's UUID.
199+
// <EMPTY> otherwise.
200+
std::string target_uuid;
201+
202+
// Set of key-value pairs for any optional (or impl-specific) data
203+
std::unordered_map<std::string, std::string> meta_data;
204+
205+
MiscTelemetryInfo() = default;
206+
207+
MiscTelemetryInfo(const MiscTelemetryInfo &other) {
208+
target_uuid = other.target_uuid;
209+
meta_data = other.meta_data;
210+
}
211+
212+
KindType getKind() const override { return LldbEntryKind::MiscInfo; }
213+
214+
static bool classof(const TelemetryInfo *T) {
215+
if (T == nullptr)
216+
return false;
217+
return T->getKind() == LldbEntryKind::MiscInfo;
218+
}
219+
220+
llvm::json::Object serializeToJson() const override;
221+
222+
std::string ToString() const override;
223+
};
224+
225+
class LldbTelemeter : public llvm::telemetry::Telemeter {
226+
public:
227+
static std::unique_ptr<LldbTelemeter> CreateInstance(Debugger *);
228+
229+
virtual ~LldbTelemeter() = default;
230+
231+
// Invoked upon process exit
232+
virtual void LogProcessExit(int status, llvm::StringRef exit_string,
233+
llvm::telemetry::EventStats stats,
234+
Target *target_ptr) = 0;
235+
236+
// Invoked upon loading the main executable module
237+
// We log in a fire-n-forget fashion so that if the load
238+
// crashes, we don't lose the entry.
239+
virtual void
240+
LogMainExecutableLoadStart(lldb::ModuleSP exec_mod,
241+
llvm::telemetry::EventStats stats) = 0;
242+
virtual void LogMainExecutableLoadEnd(lldb::ModuleSP exec_mod,
243+
llvm::telemetry::EventStats stats) = 0;
244+
245+
// Invoked for each command
246+
// We log in a fire-n-forget fashion so that if the command execution
247+
// crashes, we don't lose the entry.
248+
virtual void LogCommandStart(llvm::StringRef uuid,
249+
llvm::StringRef original_command,
250+
llvm::telemetry::EventStats stats,
251+
Target *target_ptr) = 0;
252+
virtual void LogCommandEnd(llvm::StringRef uuid, llvm::StringRef command_name,
253+
llvm::StringRef command_args,
254+
llvm::telemetry::EventStats stats,
255+
Target *target_ptr,
256+
CommandReturnObject *result) = 0;
257+
258+
virtual std::string GetNextUUID() = 0;
259+
260+
// For client (eg., SB API) to send telemetry entries.
261+
virtual void LogClientTelemetry(const llvm::json::Object &entry) = 0;
262+
};
263+
264+
// Logger configs: LLDB users can also supply their own configs via:
265+
// $HOME/.lldb_telemetry_config
266+
//
267+
// We can propose simple syntax: <field_name><colon><value>
268+
// Eg.,
269+
// enable_telemetry:true
270+
// destination:stdout
271+
// destination:stderr
272+
// destination:/path/to/some/file
273+
//
274+
// The allowed field_name values are:
275+
// * enable_telemetry
276+
// If the fields are specified more than once, the last line will take
277+
// precedence If enable_logging is set to false, no logging will occur.
278+
// * destination.
279+
// This is allowed to be specified multiple times - it will add to the
280+
// default (ie, specified by vendor) list of destinations.
281+
// The value can be either:
282+
// + one of the two magic values "stdout" or "stderr".
283+
// + a path to a local file
284+
// !!NOTE!!: We decided to use a separate file instead of the existing settings
285+
// file because that file is parsed too late in the process and by the
286+
// there might have been lots of telemetry-entries that need to be
287+
// sent already.
288+
// This approach avoid losing log entries if LLDB crashes during init.
289+
llvm::telemetry::Config *GetTelemetryConfig();
290+
291+
} // namespace lldb_private
292+
#endif // LLDB_CORE_TELEMETRY_H

lldb/include/lldb/lldb-enumerations.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,8 @@ enum StopReason {
257257
};
258258

259259
/// Command Return Status Types.
260-
enum ReturnStatus {
261-
eReturnStatusInvalid,
260+
enum ReturnStatus : int {
261+
eReturnStatusInvalid = 0,
262262
eReturnStatusSuccessFinishNoResult,
263263
eReturnStatusSuccessFinishResult,
264264
eReturnStatusSuccessContinuingNoResult,

lldb/source/API/SBDebugger.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,16 @@ SBTarget SBDebugger::GetDummyTarget() {
972972
return sb_target;
973973
}
974974

975+
void SBDebugger::SendTelemetry(const llvm::json::Object &entry) {
976+
if (lldb_private::Debugger *debugger = this->get()) {
977+
debugger->SendClientTelemetry(entry);
978+
} else {
979+
Log *log = GetLog(LLDBLog::API);
980+
LLDB_LOGF(log,
981+
"Could not send telemetry from SBDebugger - debugger was null.");
982+
}
983+
}
984+
975985
bool SBDebugger::DeleteTarget(lldb::SBTarget &target) {
976986
LLDB_INSTRUMENT_VA(this, target);
977987

0 commit comments

Comments
 (0)