| // |
| // 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 |