LLDB mainline
Telemetry.cpp
Go to the documentation of this file.
1//===-- Telemetry.cpp -----------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
11#include "lldb/Utility/Log.h"
12#include "lldb/Utility/UUID.h"
14#include "lldb/lldb-forward.h"
15#include "llvm/ADT/StringRef.h"
16#include "llvm/Support/Error.h"
17#include "llvm/Support/Format.h"
18#include "llvm/Support/RandomNumberGenerator.h"
19#include "llvm/Telemetry/Telemetry.h"
20#include <chrono>
21#include <cstdlib>
22#include <ctime>
23#include <memory>
24#include <string>
25#include <utility>
26
27namespace lldb_private {
28namespace telemetry {
29
30using namespace llvm::telemetry;
31
32static uint64_t ToNanosec(const SteadyTimePoint Point) {
33 return std::chrono::nanoseconds(Point.time_since_epoch()).count();
34}
35
36// Generate a unique string. This should be unique across different runs.
37// We build such string by combining three parts:
38// <16 random bytes>_<timestamp>
39// This reduces the chances of getting the same UUID, even when the same
40// user runs the two copies of binary at the same time.
41static std::string MakeUUID() {
42 auto timestmap = std::chrono::steady_clock::now().time_since_epoch().count();
43 UUID uuid = UUID::Generate();
44 return llvm::formatv("{0}_{1}", uuid.GetAsString(), timestmap);
45}
46
47void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
48 serializer.write("entry_kind", getKind());
49 serializer.write("session_id", SessionId);
50 serializer.write("start_time", ToNanosec(start_time));
51 if (end_time.has_value())
52 serializer.write("end_time", ToNanosec(end_time.value()));
53}
54
55void ClientInfo::serialize(Serializer &serializer) const {
57 serializer.write("client_data", client_data);
58 serializer.write("client_name", client_name);
59 if (error_msg.has_value())
60 serializer.write("error_msg", error_msg.value());
61}
62
63void CommandInfo::serialize(Serializer &serializer) const {
65
66 serializer.write("target_uuid", target_uuid.GetAsString());
67 serializer.write("command_id", command_id);
68 serializer.write("command_name", command_name);
69 if (original_command.has_value())
70 serializer.write("original_command", original_command.value());
71 if (args.has_value())
72 serializer.write("args", args.value());
73 if (ret_status.has_value())
74 serializer.write("ret_status", ret_status.value());
75 if (error_data.has_value())
76 serializer.write("error_data", error_data.value());
77}
78
79std::atomic<uint64_t> CommandInfo::g_command_id_seed = 1;
80uint64_t CommandInfo::GetNextID() { return g_command_id_seed.fetch_add(1); }
81
82void DebuggerInfo::serialize(Serializer &serializer) const {
84
85 serializer.write("lldb_version", lldb_version);
86 serializer.write("is_exit_entry", is_exit_entry);
87}
88
89void ExecutableModuleInfo::serialize(Serializer &serializer) const {
91
92 serializer.write("uuid", uuid.GetAsString());
93 serializer.write("pid", pid);
94 serializer.write("triple", triple);
95 serializer.write("is_start_entry", is_start_entry);
96}
97
98void ProcessExitInfo::serialize(Serializer &serializer) const {
100
101 serializer.write("module_uuid", module_uuid.GetAsString());
102 serializer.write("pid", pid);
103 serializer.write("is_start_entry", is_start_entry);
104 if (exit_desc.has_value()) {
105 serializer.write("exit_code", exit_desc->exit_code);
106 serializer.write("exit_desc", exit_desc->description);
107 }
108}
109
110TelemetryManager::TelemetryManager(std::unique_ptr<LLDBConfig> config)
111 : m_config(std::move(config)), m_id(MakeUUID()) {}
112
114 // Assign the manager_id, and debugger_id, if available, to this entry.
115 LLDBBaseTelemetryInfo *lldb_entry = llvm::cast<LLDBBaseTelemetryInfo>(entry);
116 lldb_entry->SessionId = m_id;
117 if (Debugger *debugger = lldb_entry->debugger)
118 lldb_entry->debugger_id = debugger->GetID();
119 return llvm::Error::success();
120}
121
122// Helper for extracting time field from a Dictionary.
123static std::optional<std::chrono::nanoseconds>
124GetAsNanosec(StructuredData::Dictionary *dict, llvm::StringRef key) {
125 auto value = dict->GetValueForKey(key);
126 if (!value->IsValid()) {
128 "Cannot determine {0} from client-telemetry entry", key);
129 return std::nullopt;
130 }
131
132 return std::chrono::nanoseconds(value->GetUnsignedIntegerValue(0));
133}
134
136 const lldb_private::StructuredDataImpl &entry, Debugger *debugger) {
137 if (!m_config->enable_client_telemetry)
138 return;
139
140 ClientInfo client_info;
141 client_info.debugger = debugger;
142 if (entry.GetObjectSP()->GetType() != lldb::eStructuredDataTypeDictionary) {
143 LLDB_LOG(GetLog(LLDBLog::Object), "Expected Dictionary type but got {0}.",
144 entry.GetObjectSP()->GetType());
145 return;
146 }
147
148 auto *dict = entry.GetObjectSP()->GetAsDictionary();
149
150 llvm::StringRef client_name;
151 if (dict->GetValueForKeyAsString("client_name", client_name))
152 client_info.client_name = client_name.str();
153 else
155 "Cannot determine client_name from client-telemetry entry");
156
157 llvm::StringRef client_data;
158 if (dict->GetValueForKeyAsString("client_data", client_data))
159 client_info.client_data = client_data.str();
160 else
162 "Cannot determine client_data from client-telemetry entry");
163
164 if (auto maybe_start_time = GetAsNanosec(dict, "start_time"))
165 client_info.start_time += *maybe_start_time;
166
167 if (auto maybe_end_time = GetAsNanosec(dict, "end_time")) {
168 SteadyTimePoint epoch;
169 client_info.end_time = epoch + *maybe_end_time;
170 }
171
172 llvm::StringRef error_msg;
173 if (dict->GetValueForKeyAsString("error", error_msg))
174 client_info.error_msg = error_msg.str();
175
176 if (llvm::Error er = dispatch(&client_info))
177 LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(er),
178 "Failed to dispatch client telemetry");
179}
180
182public:
183 llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override {
184 // Does nothing.
185 return llvm::Error::success();
186 }
187
189 : TelemetryManager(std::make_unique<LLDBConfig>(
190 /*EnableTelemetry=*/false, /*DetailedCommand=*/false,
191 /*ClientTelemery=*/false)) {}
192
193 virtual llvm::StringRef GetInstanceName() const override {
194 return "NoOpTelemetryManager";
195 }
196
198 Debugger *debugger) override {
199 // Does nothing.
200 }
201
202 llvm::Error dispatch(llvm::telemetry::TelemetryInfo *entry) override {
203 // Does nothing.
204 return llvm::Error::success();
205 }
206
208 static std::unique_ptr<NoOpTelemetryManager> g_ins =
209 std::make_unique<NoOpTelemetryManager>();
210 return g_ins.get();
211 }
212};
213
214std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;
216 // If Telemetry is disabled or if there is no default instance, then use the
217 // NoOp manager. We use a dummy instance to avoid having to do nullchecks in
218 // various places.
219 if (!Config::BuildTimeEnableTelemetry || !g_instance)
221 return g_instance.get();
222}
223
224void TelemetryManager::SetInstance(std::unique_ptr<TelemetryManager> manager) {
225 if (Config::BuildTimeEnableTelemetry)
226 g_instance = std::move(manager);
227}
228
229} // namespace telemetry
230} // namespace lldb_private
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:369
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:392
A class to manage flag bits.
Definition Debugger.h:80
StructuredData::ObjectSP GetObjectSP()
ObjectSP GetValueForKey(llvm::StringRef key) const
Represents UUID's of various sizes.
Definition UUID.h:27
static UUID Generate(uint32_t num_bytes=16)
Create a random UUID.
Definition UUID.cpp:119
std::string GetAsString(llvm::StringRef separator="-") const
Definition UUID.cpp:54
llvm::Error dispatch(llvm::telemetry::TelemetryInfo *entry) override
void DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry, Debugger *debugger) override
virtual llvm::StringRef GetInstanceName() const override
static NoOpTelemetryManager * GetInstance()
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override
virtual void DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry, Debugger *debugger)
static void SetInstance(std::unique_ptr< TelemetryManager > manger)
TelemetryManager(std::unique_ptr< LLDBConfig > config)
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override
static TelemetryManager * GetInstance()
static std::unique_ptr< TelemetryManager > g_instance
Definition Telemetry.h:265
std::unique_ptr< LLDBConfig > m_config
Definition Telemetry.h:262
static std::optional< std::chrono::nanoseconds > GetAsNanosec(StructuredData::Dictionary *dict, llvm::StringRef key)
std::chrono::time_point< std::chrono::steady_clock, std::chrono::nanoseconds > SteadyTimePoint
Defines a convenient type for timestamp of various events.
Definition Telemetry.h:75
static uint64_t ToNanosec(const SteadyTimePoint Point)
Definition Telemetry.cpp:32
static std::string MakeUUID()
Definition Telemetry.cpp:41
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:332
@ eStructuredDataTypeDictionary
void serialize(llvm::telemetry::Serializer &serializer) const override
Definition Telemetry.cpp:55
std::optional< std::string > error_msg
Definition Telemetry.h:103
void serialize(llvm::telemetry::Serializer &serializer) const override
Definition Telemetry.cpp:63
std::optional< std::string > original_command
These two fields are not collected by default due to PII risks.
Definition Telemetry.h:135
std::string command_name
The command name(eg., "breakpoint set")
Definition Telemetry.h:130
std::optional< lldb::ReturnStatus > ret_status
Return status of a command and any error description in case of error.
Definition Telemetry.h:139
UUID target_uuid
If the command is/can be associated with a target entry this field contains that target's UUID.
Definition Telemetry.h:122
std::optional< std::string > args
Definition Telemetry.h:136
uint64_t command_id
A unique ID for a command so the manager can match the start entry with its end entry.
Definition Telemetry.h:128
static std::atomic< uint64_t > g_command_id_seed
Definition Telemetry.h:162
std::optional< std::string > error_data
Definition Telemetry.h:140
void serialize(llvm::telemetry::Serializer &serializer) const override
Definition Telemetry.cpp:82
void serialize(llvm::telemetry::Serializer &serializer) const override
Definition Telemetry.cpp:89
std::string triple
The triple of this executable module.
Definition Telemetry.h:192
bool is_start_entry
If true, this entry was emitted at the beginning of an event (eg., before the executable is set).
Definition Telemetry.h:197
UUID uuid
The same as the executable-module's UUID.
Definition Telemetry.h:188
lldb::pid_t pid
PID of the process owned by this target.
Definition Telemetry.h:190
SteadyTimePoint start_time
Start time of an event.
Definition Telemetry.h:79
void serialize(llvm::telemetry::Serializer &serializer) const override
Definition Telemetry.cpp:47
llvm::telemetry::KindType getKind() const override
Definition Telemetry.h:88
std::optional< SteadyTimePoint > end_time
End time of an event - may be empty if not meaningful.
Definition Telemetry.h:81
std::optional< ExitDescription > exit_desc
Definition Telemetry.h:224
void serialize(llvm::telemetry::Serializer &serializer) const override
Definition Telemetry.cpp:98