Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Core/Telemetry.cpp
213764 views
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
27
namespace lldb_private {
28
namespace telemetry {
29
30
using namespace llvm::telemetry;
31
32
static 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.
41
static 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
47
void 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
55
void ClientInfo::serialize(Serializer &serializer) const {
56
LLDBBaseTelemetryInfo::serialize(serializer);
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
63
void CommandInfo::serialize(Serializer &serializer) const {
64
LLDBBaseTelemetryInfo::serialize(serializer);
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
79
std::atomic<uint64_t> CommandInfo::g_command_id_seed = 1;
80
uint64_t CommandInfo::GetNextID() { return g_command_id_seed.fetch_add(1); }
81
82
void DebuggerInfo::serialize(Serializer &serializer) const {
83
LLDBBaseTelemetryInfo::serialize(serializer);
84
85
serializer.write("lldb_version", lldb_version);
86
serializer.write("is_exit_entry", is_exit_entry);
87
}
88
89
void ExecutableModuleInfo::serialize(Serializer &serializer) const {
90
LLDBBaseTelemetryInfo::serialize(serializer);
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
98
void ProcessExitInfo::serialize(Serializer &serializer) const {
99
LLDBBaseTelemetryInfo::serialize(serializer);
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
110
TelemetryManager::TelemetryManager(std::unique_ptr<LLDBConfig> config)
111
: m_config(std::move(config)), m_id(MakeUUID()) {}
112
113
llvm::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>(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.
123
static std::optional<std::chrono::nanoseconds>
124
GetAsNanosec(StructuredData::Dictionary *dict, llvm::StringRef key) {
125
auto value = dict->GetValueForKey(key);
126
if (!value->IsValid()) {
127
LLDB_LOG(GetLog(LLDBLog::Object),
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
135
void TelemetryManager::DispatchClientTelemetry(
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
154
LLDB_LOG(GetLog(LLDBLog::Object),
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
161
LLDB_LOG(GetLog(LLDBLog::Object),
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
181
class NoOpTelemetryManager : public TelemetryManager {
182
public:
183
llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override {
184
// Does nothing.
185
return llvm::Error::success();
186
}
187
188
explicit NoOpTelemetryManager()
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
197
void DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry,
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
207
static NoOpTelemetryManager *GetInstance() {
208
static std::unique_ptr<NoOpTelemetryManager> g_ins =
209
std::make_unique<NoOpTelemetryManager>();
210
return g_ins.get();
211
}
212
};
213
214
std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;
215
TelemetryManager *TelemetryManager::GetInstance() {
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)
220
return NoOpTelemetryManager::GetInstance();
221
return g_instance.get();
222
}
223
224
void 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
231
232