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//===----------------------------------------------------------------------===//
8#include "lldb/Core/Telemetry.h"
9#include "lldb/Core/Debugger.h"
10#include "lldb/Utility/LLDBLog.h"
11#include "lldb/Utility/Log.h"
12#include "lldb/Utility/UUID.h"
13#include "lldb/lldb-enumerations.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(Fmt: "{0}_{1}", Vals: uuid.GetAsString(), Vals&: timestmap);
45}
46
47void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {
48 serializer.write(KeyName: "entry_kind", Value: getKind());
49 serializer.write(KeyName: "session_id", Value: SessionId);
50 serializer.write(KeyName: "start_time", Value: ToNanosec(Point: start_time));
51 if (end_time.has_value())
52 serializer.write(KeyName: "end_time", Value: ToNanosec(Point: end_time.value()));
53}
54
55void ClientInfo::serialize(Serializer &serializer) const {
56 LLDBBaseTelemetryInfo::serialize(serializer);
57 serializer.write(KeyName: "client_data", Value: client_data);
58 serializer.write(KeyName: "client_name", Value: client_name);
59 if (error_msg.has_value())
60 serializer.write(KeyName: "error_msg", Value: error_msg.value());
61}
62
63void CommandInfo::serialize(Serializer &serializer) const {
64 LLDBBaseTelemetryInfo::serialize(serializer);
65
66 serializer.write(KeyName: "target_uuid", Value: target_uuid.GetAsString());
67 serializer.write(KeyName: "command_id", Value: command_id);
68 serializer.write(KeyName: "command_name", Value: command_name);
69 if (original_command.has_value())
70 serializer.write(KeyName: "original_command", Value: original_command.value());
71 if (args.has_value())
72 serializer.write(KeyName: "args", Value: args.value());
73 if (ret_status.has_value())
74 serializer.write(KeyName: "ret_status", Value: ret_status.value());
75 if (error_data.has_value())
76 serializer.write(KeyName: "error_data", Value: 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(i: 1); }
81
82void DebuggerInfo::serialize(Serializer &serializer) const {
83 LLDBBaseTelemetryInfo::serialize(serializer);
84
85 serializer.write(KeyName: "lldb_version", Value: lldb_version);
86 serializer.write(KeyName: "is_exit_entry", Value: is_exit_entry);
87}
88
89void ExecutableModuleInfo::serialize(Serializer &serializer) const {
90 LLDBBaseTelemetryInfo::serialize(serializer);
91
92 serializer.write(KeyName: "uuid", Value: uuid.GetAsString());
93 serializer.write(KeyName: "pid", Value: pid);
94 serializer.write(KeyName: "triple", Value: triple);
95 serializer.write(KeyName: "is_start_entry", Value: is_start_entry);
96}
97
98void ProcessExitInfo::serialize(Serializer &serializer) const {
99 LLDBBaseTelemetryInfo::serialize(serializer);
100
101 serializer.write(KeyName: "module_uuid", Value: module_uuid.GetAsString());
102 serializer.write(KeyName: "pid", Value: pid);
103 serializer.write(KeyName: "is_start_entry", Value: is_start_entry);
104 if (exit_desc.has_value()) {
105 serializer.write(KeyName: "exit_code", Value: exit_desc->exit_code);
106 serializer.write(KeyName: "exit_desc", Value: exit_desc->description);
107 }
108}
109
110TelemetryManager::TelemetryManager(std::unique_ptr<LLDBConfig> config)
111 : m_config(std::move(config)), m_id(MakeUUID()) {}
112
113llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {
114 // Assign the manager_id, and debugger_id, if available, to this entry.
115 LLDBBaseTelemetryInfo *lldb_entry = llvm::cast<LLDBBaseTelemetryInfo>(Val: 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
122void TelemetryManager::DispatchClientTelemetry(
123 const lldb_private::StructuredDataImpl &entry, Debugger *debugger) {
124 if (!m_config->enable_client_telemetry)
125 return;
126
127 ClientInfo client_info;
128 client_info.debugger = debugger;
129 if (entry.GetObjectSP()->GetType() != lldb::eStructuredDataTypeDictionary) {
130 LLDB_LOG(GetLog(LLDBLog::Object), "Expected Dictionary type but got {0}.",
131 entry.GetObjectSP()->GetType());
132 return;
133 }
134
135 auto *dict = entry.GetObjectSP()->GetAsDictionary();
136
137 llvm::StringRef client_name;
138 if (dict->GetValueForKeyAsString(key: "client_name", result&: client_name))
139 client_info.client_name = client_name.str();
140 else
141 LLDB_LOG(GetLog(LLDBLog::Object),
142 "Cannot determine client_name from client-telemetry entry");
143
144 llvm::StringRef client_data;
145 if (dict->GetValueForKeyAsString(key: "client_data", result&: client_data))
146 client_info.client_data = client_data.str();
147 else
148 LLDB_LOG(GetLog(LLDBLog::Object),
149 "Cannot determine client_data from client-telemetry entry");
150
151 int64_t start_time;
152 if (dict->GetValueForKeyAsInteger(key: "start_time", result&: start_time)) {
153 client_info.start_time +=
154 std::chrono::nanoseconds(static_cast<size_t>(start_time));
155 } else {
156 LLDB_LOG(GetLog(LLDBLog::Object),
157 "Cannot determine start-time from client-telemetry entry");
158 }
159
160 int64_t end_time;
161 if (dict->GetValueForKeyAsInteger(key: "end_time", result&: end_time)) {
162 SteadyTimePoint epoch;
163 client_info.end_time =
164 epoch + std::chrono::nanoseconds(static_cast<size_t>(end_time));
165 } else {
166 LLDB_LOG(GetLog(LLDBLog::Object),
167 "Cannot determine end-time from client-telemetry entry");
168 }
169
170 llvm::StringRef error_msg;
171 if (dict->GetValueForKeyAsString(key: "error", result&: error_msg))
172 client_info.error_msg = error_msg.str();
173
174 if (llvm::Error er = dispatch(Entry: &client_info))
175 LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(er),
176 "Failed to dispatch client telemetry");
177}
178
179class NoOpTelemetryManager : public TelemetryManager {
180public:
181 llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override {
182 // Does nothing.
183 return llvm::Error::success();
184 }
185
186 explicit NoOpTelemetryManager()
187 : TelemetryManager(std::make_unique<LLDBConfig>(
188 /*EnableTelemetry=*/args: false, /*DetailedCommand=*/args: false,
189 /*ClientTelemery=*/args: false)) {}
190
191 virtual llvm::StringRef GetInstanceName() const override {
192 return "NoOpTelemetryManager";
193 }
194
195 void DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry,
196 Debugger *debugger) override {
197 // Does nothing.
198 }
199
200 llvm::Error dispatch(llvm::telemetry::TelemetryInfo *entry) override {
201 // Does nothing.
202 return llvm::Error::success();
203 }
204
205 static NoOpTelemetryManager *GetInstance() {
206 static std::unique_ptr<NoOpTelemetryManager> g_ins =
207 std::make_unique<NoOpTelemetryManager>();
208 return g_ins.get();
209 }
210};
211
212std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;
213TelemetryManager *TelemetryManager::GetInstance() {
214 // If Telemetry is disabled or if there is no default instance, then use the
215 // NoOp manager. We use a dummy instance to avoid having to do nullchecks in
216 // various places.
217 if (!Config::BuildTimeEnableTelemetry || !g_instance)
218 return NoOpTelemetryManager::GetInstance();
219 return g_instance.get();
220}
221
222void TelemetryManager::SetInstance(std::unique_ptr<TelemetryManager> manager) {
223 if (Config::BuildTimeEnableTelemetry)
224 g_instance = std::move(manager);
225}
226
227} // namespace telemetry
228} // namespace lldb_private
229

source code of lldb/source/Core/Telemetry.cpp