| // Copyright 2024 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/developer/debug/zxdb/client/analytics_event.h" | |
| #include <cstdlib> | |
| #include "src/developer/debug/shared/logging/logging.h" | |
| #include "src/lib/fxl/strings/string_printf.h" | |
| namespace { | |
| // Searches for the current user name in the given string and replaces it with a literal "$USER". | |
| // If not present, returns the input string unmodified. | |
| std::string ObfuscateUser(const std::string& string) { | |
| const char* username = std::getenv("USER"); | |
| if (!username) { | |
| // If the username is not set in the environment, give up. | |
| return string; | |
| } | |
| size_t pos = string.find(username); | |
| const size_t username_len = strlen(username); | |
| std::string ret = string; | |
| while (pos < ret.size()) { | |
| std::string to_end; | |
| size_t replace_end = pos + username_len; | |
| // We have to special case the username being followed immediately by an escaped double-quote, | |
| // which will be treated as the end of the string by std::string::replace below. We don't want | |
| // to lose the rest of the string, so it's saved in a temporary. | |
| if (replace_end < ret.size() && ret[replace_end] == '\"') { | |
| to_end = ret.substr(replace_end); | |
| } | |
| ret.replace(pos, replace_end, "$USER"); | |
| if (!to_end.empty()) { | |
| ret.append(to_end); | |
| } | |
| pos = ret.find(username); | |
| } | |
| return ret; | |
| } | |
| } // namespace | |
| namespace zxdb { | |
| // AnalyticsEvent ---------------------------------------------------------------------------------- | |
| AnalyticsEvent::AnalyticsEvent(const std::string& name, const std::string& session_id) | |
| : analytics::core_dev_tools::Ga4Event(name) { | |
| // Note: this is a special name, and is processed by analytics differently than typical custom | |
| // parameters. This surfaces in the data as "ga_session_id", which (should) associate it with | |
| // other built-in session metrics. | |
| SetParameter("session_id", session_id); | |
| } | |
| // SessionStarted ---------------------------------------------------------------------------------- | |
| SessionStarted::SessionStarted(const std::string& session_id) | |
| : AnalyticsEvent("session_started", session_id) {} | |
| SessionConnected::SessionConnected(const std::string& session_id) | |
| : AnalyticsEvent("session_connected", session_id) {} | |
| // SessionConnected -------------------------------------------------------------------------------- | |
| void SessionConnected::SetRemoteType(SessionConnected::RemoteType type) { | |
| std::string remote_type_string; | |
| switch (type) { | |
| case RemoteType::kRemoteAgent: | |
| remote_type_string = "remote agent"; | |
| break; | |
| case RemoteType::kLocalAgent: | |
| remote_type_string = "local agent"; | |
| break; | |
| case RemoteType::kMinidump: | |
| remote_type_string = "minidump"; | |
| break; | |
| } | |
| SetParameter("remote_type", remote_type_string); | |
| } | |
| // SessionEnded ------------------------------------------------------------------------------------ | |
| SessionEnded::SessionEnded(const std::string& session_id) | |
| : AnalyticsEvent("session_ended", session_id) {} | |
| void SessionEnded::SetSessionTime(std::chrono::milliseconds session_time) { | |
| SetParameter("session_length_ms", session_time.count()); | |
| } | |
| // CommandEvent ------------------------------------------------------------------------------------ | |
| CommandEvent::CommandEvent(const std::string& session_id) : AnalyticsEvent("command", session_id) {} | |
| // The CommandReport only fills in verbs, nouns, and switches using the well known strings for the | |
| // defined set of the respective group. We will never receive user information in any of those | |
| // strings. Arguments and error messages however, can contain arbitrary text, which must not contain | |
| // user identifiable data. | |
| void CommandEvent::FromCommandReport(const CommandReport& report) { | |
| SetParameter("verb_id", report.verb_id); | |
| SetParameter("verb", report.verb); | |
| SetParameter("command_group", report.command_group); | |
| SetParameter("noun_count", static_cast<int64_t>(report.nouns.size())); | |
| for (size_t i = 0; i < report.nouns.size(); i++) { | |
| SetParameter(fxl::StringPrintf("noun%zu", i), report.nouns[i].name); | |
| SetParameter(fxl::StringPrintf("noun%zu_id", i), report.nouns[i].id); | |
| SetParameter(fxl::StringPrintf("noun%zu_index", i), report.nouns[i].index); | |
| } | |
| SetParameter("argument_count", static_cast<int64_t>(report.arguments.size())); | |
| for (size_t i = 0; i < report.arguments.size(); i++) { | |
| SetParameter(fxl::StringPrintf("argument%zu", i), ObfuscateUser(report.arguments[i])); | |
| } | |
| SetParameter("switch_count", static_cast<int64_t>(report.switches.size())); | |
| for (size_t i = 0; i < report.switches.size(); i++) { | |
| SetParameter(fxl::StringPrintf("switch%zu", i), report.switches[i].name); | |
| SetParameter(fxl::StringPrintf("switch%zu_id", i), report.switches[i].id); | |
| SetParameter(fxl::StringPrintf("switch%zu_value", i), report.switches[i].value); | |
| } | |
| SetParameter("has_error", report.err.has_error()); | |
| SetParameter("error_code", static_cast<int>(report.err.type())); | |
| SetParameter("error_string", ErrTypeToString(report.err.type())); | |
| SetParameter("error_message", ObfuscateUser(report.err.msg())); | |
| } | |
| // ConsoleType ------------------------------------------------------------------------------------- | |
| ConsoleType::ConsoleType(const std::string& session_id) | |
| : AnalyticsEvent("console_type", session_id) {} | |
| void ConsoleType::SetConsoleType(Type type) { | |
| std::string console_type_string; | |
| switch (type) { | |
| case Type::kCommandLine: { | |
| console_type_string = "command_line"; | |
| break; | |
| } | |
| case Type::kCommandLineEmbedded: { | |
| console_type_string = "command_line_embedded"; | |
| break; | |
| } | |
| case Type::kDebugAdapter: { | |
| console_type_string = "debug_adapter"; | |
| break; | |
| } | |
| } | |
| SetParameter("type", console_type_string); | |
| } | |
| } // namespace zxdb |