blob: 3d3a7c14a4b183dbd90d987db85279d1ccd64ef2 [file] [log] [blame] [edit]
//
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <sys/utsname.h>
#include <uuid.h>
#include "common/libs/utils/files.h"
#include "common/libs/utils/flag_parser.h"
#include "host/commands/metrics/events.h"
#include "host/commands/metrics/metrics_defs.h"
#include "host/commands/metrics/proto/cf_metrics_protos.h"
#include "host/commands/metrics/utils.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/vm_manager/crosvm_manager.h"
#include "host/libs/vm_manager/qemu_manager.h"
#include "shared/api_level.h"
namespace cuttlefish {
namespace {
constexpr int kLogSourceId = 1753;
constexpr char kLogSourceStr[] = "CUTTLEFISH_METRICS";
constexpr int kCppClientType =
19; // C++ native client type (clientanalytics.proto)
std::pair<uint64_t, uint64_t> ConvertMillisToTime(uint64_t millis) {
uint64_t seconds = millis / 1000;
uint64_t nanos = (millis % 1000) * 1000000;
return {seconds, nanos};
}
std::unique_ptr<CuttlefishLogEvent> BuildCfLogEvent(
uint64_t now_ms, CuttlefishLogEvent::DeviceType device_type) {
auto [now_s, now_ns] = ConvertMillisToTime(now_ms);
// "cfEvent" is the top level CuttlefishLogEvent
auto cfEvent = std::make_unique<CuttlefishLogEvent>();
cfEvent->set_device_type(device_type);
cfEvent->set_session_id(metrics::GenerateSessionId(now_ms));
if (!metrics::GetCfVersion().empty()) {
cfEvent->set_cuttlefish_version(metrics::GetCfVersion());
}
Timestamp* timestamp = cfEvent->mutable_timestamp_ms();
timestamp->set_seconds(now_s);
timestamp->set_nanos(now_ns);
return cfEvent;
}
cuttlefish::MetricsEvent::OsType GetOsType() {
struct utsname buf;
if (uname(&buf) != 0) {
LOG(ERROR) << "failed to retrieve system information";
return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
}
std::string sysname(buf.sysname);
std::string machine(buf.machine);
if (sysname != "Linux") {
return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
}
if (machine == "x86_64") {
return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_X86_64;
}
if (machine == "x86") {
return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_X86;
}
if (machine == "aarch64" || machine == "arm64") {
return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_AARCH64;
}
if (machine[0] == 'a') {
return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_AARCH32;
}
return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
}
cuttlefish::MetricsEvent::VmmType GetVmmManager() {
auto config = cuttlefish::CuttlefishConfig::Get();
CHECK(config) << "Could not open cuttlefish config";
auto vmm = config->vm_manager();
if (vmm == cuttlefish::vm_manager::CrosvmManager::name()) {
return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_CROSVM;
}
if (vmm == cuttlefish::vm_manager::QemuManager::name()) {
return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_QEMU;
}
return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_UNSPECIFIED;
}
// Builds the 2nd level MetricsEvent.
void AddCfMetricsEventToLog(uint64_t now_ms, CuttlefishLogEvent* cfEvent,
MetricsEvent::EventType event_type) {
auto [now_s, now_ns] = ConvertMillisToTime(now_ms);
// "metrics_event" is the 2nd level MetricsEvent
cuttlefish::MetricsEvent* metrics_event = cfEvent->mutable_metrics_event();
metrics_event->set_event_type(event_type);
metrics_event->set_os_type(GetOsType());
metrics_event->set_os_version(metrics::GetOsVersion());
metrics_event->set_vmm_type(GetVmmManager());
if (!metrics::GetVmmVersion().empty()) {
metrics_event->set_vmm_version(metrics::GetVmmVersion());
}
metrics_event->set_company(metrics::GetCompany());
metrics_event->set_api_level(PRODUCT_SHIPPING_API_LEVEL);
Timestamp* metrics_timestamp = metrics_event->mutable_event_time_ms();
metrics_timestamp->set_seconds(now_s);
metrics_timestamp->set_nanos(now_ns);
}
std::unique_ptr<LogRequest> BuildLogRequest(uint64_t now_ms,
CuttlefishLogEvent* cfEvent) {
// "log_request" is the top level LogRequest
auto log_request = std::make_unique<LogRequest>();
log_request->set_request_time_ms(now_ms);
log_request->set_log_source(kLogSourceId);
log_request->set_log_source_name(kLogSourceStr);
ClientInfo* client_info = log_request->mutable_client_info();
client_info->set_client_type(kCppClientType);
std::string cfLogStr;
if (!cfEvent->SerializeToString(&cfLogStr)) {
LOG(ERROR) << "Serialization failed for event";
return nullptr;
}
LogEvent* logEvent = log_request->add_log_event();
logEvent->set_event_time_ms(now_ms);
logEvent->set_source_extension(cfLogStr);
return log_request;
}
} // namespace
int Clearcut::SendEvent(CuttlefishLogEvent::DeviceType device_type,
MetricsEvent::EventType event_type) {
uint64_t now_ms = metrics::GetEpochTimeMs();
auto cfEvent = BuildCfLogEvent(now_ms, device_type);
AddCfMetricsEventToLog(now_ms, cfEvent.get(), event_type);
auto logRequest = BuildLogRequest(now_ms, cfEvent.get());
if (!logRequest) {
LOG(ERROR) << "Failed to build LogRequest";
return MetricsExitCodes::kMetricsError;
}
std::string logRequestStr;
if (!logRequest->SerializeToString(&logRequestStr)) {
LOG(ERROR) << "Serialization failed for LogRequest";
return MetricsExitCodes::kMetricsError;
}
return metrics::PostRequest(logRequestStr, metrics::kProd);
}
int Clearcut::SendVMStart(CuttlefishLogEvent::DeviceType device) {
return SendEvent(device,
MetricsEvent::CUTTLEFISH_EVENT_TYPE_VM_INSTANTIATION);
}
int Clearcut::SendVMStop(CuttlefishLogEvent::DeviceType device) {
return SendEvent(device, MetricsEvent::CUTTLEFISH_EVENT_TYPE_VM_STOP);
}
int Clearcut::SendDeviceBoot(CuttlefishLogEvent::DeviceType device) {
return SendEvent(device, MetricsEvent::CUTTLEFISH_EVENT_TYPE_DEVICE_BOOT);
}
int Clearcut::SendLockScreen(CuttlefishLogEvent::DeviceType device) {
return SendEvent(device,
MetricsEvent::CUTTLEFISH_EVENT_TYPE_LOCK_SCREEN_AVAILABLE);
}
// TODO (moelsherif@): remove this function in the future since it is not used
cuttlefish::CuttlefishLogEvent* sampleEvent() {
cuttlefish::CuttlefishLogEvent* event = new cuttlefish::CuttlefishLogEvent();
event->set_device_type(
cuttlefish::CuttlefishLogEvent::CUTTLEFISH_DEVICE_TYPE_HOST);
return event;
}
// TODO (moelsherif@): remove this function in the future since it is not used
std::string ProtoToString(LogEvent* event) {
std::string output;
if (!event->SerializeToString(&output)) {
LOG(ERROR) << "failed to serialize proto LogEvent";
}
return output;
}
} // namespace cuttlefish