Path: blob/main/contrib/llvm-project/lldb/source/Core/Telemetry.cpp
213764 views
//===-- Telemetry.cpp -----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7#include "lldb/Core/Telemetry.h"8#include "lldb/Core/Debugger.h"9#include "lldb/Utility/LLDBLog.h"10#include "lldb/Utility/Log.h"11#include "lldb/Utility/UUID.h"12#include "lldb/lldb-enumerations.h"13#include "lldb/lldb-forward.h"14#include "llvm/ADT/StringRef.h"15#include "llvm/Support/Error.h"16#include "llvm/Support/Format.h"17#include "llvm/Support/RandomNumberGenerator.h"18#include "llvm/Telemetry/Telemetry.h"19#include <chrono>20#include <cstdlib>21#include <ctime>22#include <memory>23#include <string>24#include <utility>2526namespace lldb_private {27namespace telemetry {2829using namespace llvm::telemetry;3031static uint64_t ToNanosec(const SteadyTimePoint Point) {32return std::chrono::nanoseconds(Point.time_since_epoch()).count();33}3435// Generate a unique string. This should be unique across different runs.36// We build such string by combining three parts:37// <16 random bytes>_<timestamp>38// This reduces the chances of getting the same UUID, even when the same39// user runs the two copies of binary at the same time.40static std::string MakeUUID() {41auto timestmap = std::chrono::steady_clock::now().time_since_epoch().count();42UUID uuid = UUID::Generate();43return llvm::formatv("{0}_{1}", uuid.GetAsString(), timestmap);44}4546void LLDBBaseTelemetryInfo::serialize(Serializer &serializer) const {47serializer.write("entry_kind", getKind());48serializer.write("session_id", SessionId);49serializer.write("start_time", ToNanosec(start_time));50if (end_time.has_value())51serializer.write("end_time", ToNanosec(end_time.value()));52}5354void ClientInfo::serialize(Serializer &serializer) const {55LLDBBaseTelemetryInfo::serialize(serializer);56serializer.write("client_data", client_data);57serializer.write("client_name", client_name);58if (error_msg.has_value())59serializer.write("error_msg", error_msg.value());60}6162void CommandInfo::serialize(Serializer &serializer) const {63LLDBBaseTelemetryInfo::serialize(serializer);6465serializer.write("target_uuid", target_uuid.GetAsString());66serializer.write("command_id", command_id);67serializer.write("command_name", command_name);68if (original_command.has_value())69serializer.write("original_command", original_command.value());70if (args.has_value())71serializer.write("args", args.value());72if (ret_status.has_value())73serializer.write("ret_status", ret_status.value());74if (error_data.has_value())75serializer.write("error_data", error_data.value());76}7778std::atomic<uint64_t> CommandInfo::g_command_id_seed = 1;79uint64_t CommandInfo::GetNextID() { return g_command_id_seed.fetch_add(1); }8081void DebuggerInfo::serialize(Serializer &serializer) const {82LLDBBaseTelemetryInfo::serialize(serializer);8384serializer.write("lldb_version", lldb_version);85serializer.write("is_exit_entry", is_exit_entry);86}8788void ExecutableModuleInfo::serialize(Serializer &serializer) const {89LLDBBaseTelemetryInfo::serialize(serializer);9091serializer.write("uuid", uuid.GetAsString());92serializer.write("pid", pid);93serializer.write("triple", triple);94serializer.write("is_start_entry", is_start_entry);95}9697void ProcessExitInfo::serialize(Serializer &serializer) const {98LLDBBaseTelemetryInfo::serialize(serializer);99100serializer.write("module_uuid", module_uuid.GetAsString());101serializer.write("pid", pid);102serializer.write("is_start_entry", is_start_entry);103if (exit_desc.has_value()) {104serializer.write("exit_code", exit_desc->exit_code);105serializer.write("exit_desc", exit_desc->description);106}107}108109TelemetryManager::TelemetryManager(std::unique_ptr<LLDBConfig> config)110: m_config(std::move(config)), m_id(MakeUUID()) {}111112llvm::Error TelemetryManager::preDispatch(TelemetryInfo *entry) {113// Assign the manager_id, and debugger_id, if available, to this entry.114LLDBBaseTelemetryInfo *lldb_entry = llvm::cast<LLDBBaseTelemetryInfo>(entry);115lldb_entry->SessionId = m_id;116if (Debugger *debugger = lldb_entry->debugger)117lldb_entry->debugger_id = debugger->GetID();118return llvm::Error::success();119}120121// Helper for extracting time field from a Dictionary.122static std::optional<std::chrono::nanoseconds>123GetAsNanosec(StructuredData::Dictionary *dict, llvm::StringRef key) {124auto value = dict->GetValueForKey(key);125if (!value->IsValid()) {126LLDB_LOG(GetLog(LLDBLog::Object),127"Cannot determine {0} from client-telemetry entry", key);128return std::nullopt;129}130131return std::chrono::nanoseconds(value->GetUnsignedIntegerValue(0));132}133134void TelemetryManager::DispatchClientTelemetry(135const lldb_private::StructuredDataImpl &entry, Debugger *debugger) {136if (!m_config->enable_client_telemetry)137return;138139ClientInfo client_info;140client_info.debugger = debugger;141if (entry.GetObjectSP()->GetType() != lldb::eStructuredDataTypeDictionary) {142LLDB_LOG(GetLog(LLDBLog::Object), "Expected Dictionary type but got {0}.",143entry.GetObjectSP()->GetType());144return;145}146147auto *dict = entry.GetObjectSP()->GetAsDictionary();148149llvm::StringRef client_name;150if (dict->GetValueForKeyAsString("client_name", client_name))151client_info.client_name = client_name.str();152else153LLDB_LOG(GetLog(LLDBLog::Object),154"Cannot determine client_name from client-telemetry entry");155156llvm::StringRef client_data;157if (dict->GetValueForKeyAsString("client_data", client_data))158client_info.client_data = client_data.str();159else160LLDB_LOG(GetLog(LLDBLog::Object),161"Cannot determine client_data from client-telemetry entry");162163if (auto maybe_start_time = GetAsNanosec(dict, "start_time"))164client_info.start_time += *maybe_start_time;165166if (auto maybe_end_time = GetAsNanosec(dict, "end_time")) {167SteadyTimePoint epoch;168client_info.end_time = epoch + *maybe_end_time;169}170171llvm::StringRef error_msg;172if (dict->GetValueForKeyAsString("error", error_msg))173client_info.error_msg = error_msg.str();174175if (llvm::Error er = dispatch(&client_info))176LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(er),177"Failed to dispatch client telemetry");178}179180class NoOpTelemetryManager : public TelemetryManager {181public:182llvm::Error preDispatch(llvm::telemetry::TelemetryInfo *entry) override {183// Does nothing.184return llvm::Error::success();185}186187explicit NoOpTelemetryManager()188: TelemetryManager(std::make_unique<LLDBConfig>(189/*EnableTelemetry=*/false, /*DetailedCommand=*/false,190/*ClientTelemery=*/false)) {}191192virtual llvm::StringRef GetInstanceName() const override {193return "NoOpTelemetryManager";194}195196void DispatchClientTelemetry(const lldb_private::StructuredDataImpl &entry,197Debugger *debugger) override {198// Does nothing.199}200201llvm::Error dispatch(llvm::telemetry::TelemetryInfo *entry) override {202// Does nothing.203return llvm::Error::success();204}205206static NoOpTelemetryManager *GetInstance() {207static std::unique_ptr<NoOpTelemetryManager> g_ins =208std::make_unique<NoOpTelemetryManager>();209return g_ins.get();210}211};212213std::unique_ptr<TelemetryManager> TelemetryManager::g_instance = nullptr;214TelemetryManager *TelemetryManager::GetInstance() {215// If Telemetry is disabled or if there is no default instance, then use the216// NoOp manager. We use a dummy instance to avoid having to do nullchecks in217// various places.218if (!Config::BuildTimeEnableTelemetry || !g_instance)219return NoOpTelemetryManager::GetInstance();220return g_instance.get();221}222223void TelemetryManager::SetInstance(std::unique_ptr<TelemetryManager> manager) {224if (Config::BuildTimeEnableTelemetry)225g_instance = std::move(manager);226}227228} // namespace telemetry229} // namespace lldb_private230231232