| // 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 <cstring> |
| #include <map> |
| #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" |
| |
| namespace cobalt::system_data { |
| |
| namespace { |
| |
| #if defined(__x86_64__) |
| |
| // 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__) |
| |
| void PopulateBoardName(SystemProfile* profile) { |
| if (profile->board_name() == "") { |
| profile->set_board_name("Generic ARM"); |
| } |
| } |
| |
| #else |
| |
| void PopulateBoardName(SystemProfile* profile) {} |
| |
| #endif |
| |
| } // namespace |
| |
| SystemData::SystemData(const std::string& product_name, const std::string& board_name_suggestion, |
| ReleaseStage release_stage, const std::string& version) |
| : internal_metrics_(logger::InternalMetrics::NewWithLogger(nullptr)), |
| 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_realm("<unset>"); |
| system_profile_.set_channel("<unset>"); |
| PopulateSystemProfile(); |
| } |
| |
| void SystemData::SetChannel(const std::string& channel) { |
| system_profile_.set_channel(channel); |
| NotifyChange(); |
| } |
| |
| void SystemData::SetRealm(const std::string& realm) { |
| system_profile_.set_realm(realm); |
| NotifyChange(); |
| } |
| |
| void SystemData::SetSoftwareDistributionInfo(SoftwareDistributionInfo info) { |
| using EventCode = logger::SetSoftwareDistributionInfoCalledMetricDimensionPreviousChannel; |
| logger::SetSoftwareDistributionInfoCalledEventCodes 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; |
| } |
| |
| if (system_profile_.realm() == "<unset>") { |
| event_codes.previous_realm = EventCode::Unset; |
| } else if (system_profile_.realm() == "<unknown>") { |
| event_codes.previous_realm = EventCode::Unknown; |
| } else { |
| event_codes.previous_realm = 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; |
| } |
| } |
| |
| event_codes.new_realm = EventCode::Unset; |
| if (info.realm) { |
| if (info.realm.value().empty()) { |
| system_profile_.set_realm("<unknown>"); |
| event_codes.new_realm = EventCode::Unknown; |
| } else { |
| system_profile_.set_realm(info.realm.value()); |
| event_codes.new_realm = EventCode::Valid; |
| } |
| } |
| |
| internal_metrics_->SetSoftwareDistributionInfoCalled(event_codes); |
| NotifyChange(); |
| } |
| |
| void SystemData::OverrideSystemProfile(const SystemProfile& profile) { |
| system_profile_ = profile; |
| NotifyChange(); |
| } |
| |
| void SystemData::ResetInternalMetrics(logger::LoggerInterface* internal_logger) { |
| internal_metrics_ = logger::InternalMetrics::NewWithLogger(internal_logger); |
| }; |
| |
| 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(__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(__aarch64__) |
| |
| system_profile_.set_arch(SystemProfile::ARM_64); |
| |
| #else |
| |
| system_profile_.set_arch(SystemProfile::UNKNOWN_ARCH); |
| |
| #endif |
| |
| PopulateBoardName(&system_profile_); |
| } |
| |
| } // namespace cobalt::system_data |