| // Copyright 2017 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/system_data/system_data.h" |
| |
| #include <stdint.h> |
| |
| #include <cstring> |
| #include <map> |
| #include <set> |
| #include <string> |
| #include <utility> |
| |
| #include "src/logger/internal_metrics_config.cb.h" |
| #include "src/logging.h" |
| #include "src/pb/common.pb.h" |
| #include "src/pb/observation_batch.pb.h" |
| #include "src/registry/report_definition.pb.h" |
| |
| namespace cobalt::system_data { |
| |
| namespace { |
| |
| #if defined(__x86_64__) || defined(__i386__) |
| |
| // This identifies board names for x86 Systems. |
| // If the signature of the CPU matches a known signature, then we use the name, |
| // otherwise we encode the signature as a string so we can easily identify when |
| // new signatures start to become popular. |
| std::string getBoardName(int signature) { |
| // This function will only be run once per system boot, so this map will only |
| // be created once. |
| static const std::map<int, std::string> knownCPUSignatures = { |
| {0x806e9, "Eve"}, |
| }; |
| |
| auto name = knownCPUSignatures.find(signature); |
| if (name == knownCPUSignatures.end()) { |
| char sigstr[20]; // NOLINT readability-magic-numbers |
| snprintf(sigstr, sizeof(sigstr), "unknown:0x%X", signature); |
| return sigstr; |
| } |
| |
| return name->second; |
| } |
| |
| // Invokes the cpuid instruction on X86. |info_type| specifies which query |
| // we are performing. This is written into register EAX prior to invoking |
| // cpuid. (The sub-type specifier in register ECX is alwyas set to zero.) The |
| // results from registers EAX, EBX, ECX, EDX respectively are writtent into the |
| // four entries of |cpu_info|. See for example the wikipedia article on |
| // cpuid for more info. |
| void Cpuid(int info_type, |
| int cpu_info[4]) { // NOLINT readability-non-const-parameter |
| __asm__ volatile("cpuid\n" |
| : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) |
| : "a"(info_type), "c"(0)); |
| } |
| |
| // Invokes Cpuid() to determine the board_name. |
| void PopulateBoardName(SystemProfile* profile) { |
| // "": means that the calling system has no idea. |
| // "pc": is the current placeholder value that fuchsia reports for x86 |
| // devices, so we think we can do better. |
| // Anything else is considered to be "better" than just raw Cpuid or a lookup |
| // table. |
| if (profile->board_name().empty() || profile->board_name() == "pc") { |
| // First we invoke Cpuid with info_type = 0 in order to obtain num_ids |
| // and vendor_name. |
| int cpu_info[4] = {-1}; |
| Cpuid(0, cpu_info); |
| int num_ids = cpu_info[0]; |
| |
| if (num_ids > 0) { |
| // Then invoke Cpuid again with info_type = 1 in order to obtain |
| // |signature|. |
| Cpuid(1, cpu_info); |
| profile->set_board_name(getBoardName(cpu_info[0])); |
| } |
| } |
| } |
| |
| #elif defined(__aarch64__) || defined(__arm__) |
| |
| void PopulateBoardName(SystemProfile* profile) { |
| if (profile->board_name() == "") { |
| profile->set_board_name("Generic ARM"); |
| } |
| } |
| |
| #else |
| |
| void PopulateBoardName(SystemProfile* profile) {} |
| |
| #endif |
| |
| } // namespace |
| |
| // TODO(fxbug.dev/85571): NOLINTNEXTLINE(bugprone-easily-swappable-parameters) |
| SystemData::SystemData(const std::string& product_name, const std::string& board_name_suggestion, |
| ReleaseStage release_stage, const std::string& version, |
| SystemProfile::BuildType build_type, |
| const std::vector<int64_t>& experiment_ids) |
| : release_stage_(release_stage) { |
| system_profile_.set_product_name(product_name); |
| system_profile_.set_board_name(board_name_suggestion); |
| system_profile_.set_system_version(version); |
| system_profile_.set_build_type(build_type); |
| system_profile_.set_app_version("<unset>"); |
| system_profile_.set_channel("<unset>"); |
| |
| for (const auto& experiment_id : experiment_ids) { |
| system_profile_.add_experiment_ids(experiment_id); |
| } |
| |
| PopulateSystemProfile(); |
| } |
| |
| void SystemData::SetAppVersion(const std::string& app_version) { |
| system_profile_.set_app_version(app_version); |
| NotifyChange(); |
| } |
| |
| void SystemData::SetChannel(const std::string& channel) { |
| system_profile_.set_channel(channel); |
| NotifyChange(); |
| } |
| |
| void SystemData::SetSoftwareDistributionInfo(SoftwareDistributionInfo info) { |
| using EventCode = logger::SetSoftwareDistributionInfoCalledMigratedMetricDimensionPreviousChannel; |
| logger::SetSoftwareDistributionInfoCalledMigratedEventCodes event_codes; |
| |
| if (system_profile_.channel() == "<unset>") { |
| event_codes.previous_channel = EventCode::Unset; |
| } else if (system_profile_.channel() == "<unknown>") { |
| event_codes.previous_channel = EventCode::Unknown; |
| } else { |
| event_codes.previous_channel = EventCode::Valid; |
| } |
| |
| event_codes.new_channel = EventCode::Unset; |
| if (info.channel) { |
| if (info.channel.value().empty()) { |
| system_profile_.set_channel("<unknown>"); |
| event_codes.new_channel = EventCode::Unknown; |
| } else { |
| system_profile_.set_channel(info.channel.value()); |
| event_codes.new_channel = EventCode::Valid; |
| } |
| } |
| |
| logger::InternalMetrics::InternalMetricsFlusher flusher = internal_metrics_->Flusher(); |
| internal_metrics_->SetSoftwareDistributionInfoCalled(event_codes); |
| NotifyChange(); |
| } |
| |
| void SystemData::OverrideSystemProfile(const SystemProfile& profile) { |
| system_profile_ = profile; |
| NotifyChange(); |
| } |
| |
| void SystemData::FilteredSystemProfile(const ReportDefinition& report, |
| const SystemProfile& profile_in, |
| SystemProfile* profile_out) { |
| for (const int field : report.system_profile_field()) { |
| switch (field) { |
| case SystemProfileField::OS: |
| profile_out->set_os(profile_in.os()); |
| break; |
| case SystemProfileField::ARCH: |
| profile_out->set_arch(profile_in.arch()); |
| break; |
| case SystemProfileField::BOARD_NAME: |
| profile_out->set_board_name(profile_in.board_name()); |
| break; |
| case SystemProfileField::PRODUCT_NAME: |
| profile_out->set_product_name(profile_in.product_name()); |
| break; |
| case SystemProfileField::SYSTEM_VERSION: |
| profile_out->set_system_version(profile_in.system_version()); |
| break; |
| case SystemProfileField::APP_VERSION: |
| profile_out->set_app_version(profile_in.app_version()); |
| break; |
| case SystemProfileField::CHANNEL: |
| profile_out->set_channel(profile_in.channel()); |
| break; |
| case SystemProfileField::BUILD_TYPE: |
| profile_out->set_build_type(profile_in.build_type()); |
| break; |
| case SystemProfileField::EXPERIMENT_IDS: |
| std::set<int64_t> report_experiment_ids(report.experiment_id().begin(), |
| report.experiment_id().end()); |
| for (const int64_t experiment_id : profile_in.experiment_ids()) { |
| if (report_experiment_ids.count(experiment_id) > 0) { |
| profile_out->add_experiment_ids(experiment_id); |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| void SystemData::ResetInternalMetrics(logger::InternalMetrics* internal_metrics) { |
| internal_metrics_.reset(internal_metrics); |
| } |
| |
| void SystemData::OnChange(std::function<void()> callback) { |
| change_callbacks_.push_back(std::move(callback)); |
| } |
| |
| void SystemData::NotifyChange() { |
| for (const auto& callback : change_callbacks_) { |
| callback(); |
| } |
| } |
| |
| void SystemData::PopulateSystemProfile() { |
| #if defined(__ANDROID__) |
| |
| system_profile_.set_os(SystemProfile::ANDROID); |
| |
| #elif defined(__linux__) |
| |
| system_profile_.set_os(SystemProfile::LINUX); |
| |
| #elif defined(__Fuchsia__) |
| |
| system_profile_.set_os(SystemProfile::FUCHSIA); |
| |
| #else |
| |
| system_profile_.set_os(SystemProfile::UNKNOWN_OS); |
| |
| #endif |
| |
| #if defined(__x86_64__) |
| |
| system_profile_.set_arch(SystemProfile::X86_64); |
| |
| #elif defined(__i386__) |
| |
| system_profile_.set_arch(SystemProfile::X86_32); |
| #elif defined(__aarch64__) |
| |
| system_profile_.set_arch(SystemProfile::ARM_64); |
| |
| #elif defined(__arm__) |
| |
| system_profile_.set_arch(SystemProfile::ARM_32); |
| |
| #else |
| |
| system_profile_.set_arch(SystemProfile::UNKNOWN_ARCH); |
| |
| #endif |
| |
| PopulateBoardName(&system_profile_); |
| } |
| |
| } // namespace cobalt::system_data |