blob: 66fac147daee703217c2bb8c2561c971a5c16f89 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/system_logs/reven_log_source.h"
#include <string_view>
#include "base/functional/bind.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/to_string.h"
#include "base/task/thread_pool.h"
#include "chromeos/ash/services/cros_healthd/public/cpp/service_connection.h"
#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom-shared.h"
#include "chromeos/ash/services/cros_healthd/public/mojom/cros_healthd_probe.mojom.h"
namespace system_logs {
namespace {
namespace healthd = ::ash::cros_healthd::mojom;
using healthd::TelemetryInfo;
using healthd::TelemetryInfoPtr;
using ProbeCategories = healthd::ProbeCategoryEnum;
constexpr char kNotAvailable[] = "<not available>";
constexpr char kRevenAvailableMemoryKey[] = "chromeosflex_available_memory";
constexpr char kRevenBiosVersionKey[] = "chromeosflex_bios_version";
constexpr char kRevenBluetoothDriverKey[] = "chromeosflex_bluetooth_driver";
constexpr char kRevenBluetoothIdKey[] = "chromeosflex_bluetooth_id";
constexpr char kRevenBluetoothNameKey[] = "chromeosflex_bluetooth_name";
constexpr char kRevenCpuNameKey[] = "chromeosflex_cpu_name";
constexpr char kRevenEthernetDriverKey[] = "chromeosflex_ethernet_driver";
constexpr char kRevenEthernetIdKey[] = "chromeosflex_ethernet_id";
constexpr char kRevenEthernetNameKey[] = "chromeosflex_ethernet_name";
constexpr char kRevenFreeMemoryKey[] = "chromeosflex_free_memory";
constexpr char kRevenGlExtensionsKey[] = "chromeosflex_gl_extensions";
constexpr char kRevenGlRendererKey[] = "chromeosflex_gl_renderer";
constexpr char kRevenGlShadingVersionKey[] = "chromeosflex_gl_shading_version";
constexpr char kRevenGlVendorKey[] = "chromeosflex_gl_vendor";
constexpr char kRevenGlVersionKey[] = "chromeosflex_gl_version";
constexpr char kRevenGpuDriverKey[] = "chromeosflex_gpu_driver";
constexpr char kRevenGpuIdKey[] = "chromeosflex_gpu_id";
constexpr char kRevenGpuNameKey[] = "chromeosflex_gpu_name";
constexpr char kRevenProductNameKey[] = "chromeosflex_product_name";
constexpr char kRevenProductVendorKey[] = "chromeosflex_product_vendor";
constexpr char kRevenProductVersionKey[] = "chromeosflex_product_version";
constexpr char kRevenSecurebootKey[] = "chromeosflex_secureboot";
constexpr char kRevenTotalMemoryKey[] = "chromeosflex_total_memory";
constexpr char kRevenTouchpadStack[] = "chromeosflex_touchpad";
constexpr char kRevenTpmAllowListedKey[] = "chromeosflex_tpm_allow_listed";
constexpr char kRevenTpmDidVidKey[] = "chromeosflex_tpm_did_vid";
constexpr char kRevenTpmManufacturerKey[] = "chromeosflex_tpm_manufacturer";
constexpr char kRevenTpmOwnedKey[] = "chromeosflex_tpm_owned";
constexpr char kRevenTpmSpecLevelKey[] = "chromeosflex_tpm_spec_level";
constexpr char kRevenTpmVersionKey[] = "chromeosflex_tpm_version";
constexpr char kRevenUefiKey[] = "chromeosflex_uefi";
constexpr char kRevenWirelessDriverKey[] = "chromeosflex_wireless_driver";
constexpr char kRevenWirelessIdKey[] = "chromeosflex_wireless_id";
constexpr char kRevenWirelessNameKey[] = "chromeosflex_wireless_name";
// Format a gradually-accumulated, comma-separated list.
void StrListAppend(std::string* list, std::string_view value) {
if (!list->empty()) {
base::StrAppend(list, {", "});
}
base::StrAppend(list, {value});
}
// Format the combination of bus, vendor_id, and product_id or device_id.
std::string FormatDeviceIDs(const std::string& bus,
uint16_t vendor_id,
uint16_t product_id) {
return base::StringPrintf("%s:%04x:%04x", bus.c_str(), vendor_id, product_id);
}
// TPM family. We use the TPM 2.0 style encoding, e.g.:
// * TPM 1.2: "1.2" -> 0x312e3200
// * TPM 2.0: "2.0" -> 0x322e3000
std::string ToTpmVersionStr(uint32_t tpm_family) {
if (tpm_family == (uint32_t)0x312e3200) {
return "1.2";
} else if (tpm_family == (uint32_t)0x322e3000) {
return "2.0";
} else {
return "unknown";
}
}
void PopulateCpuInfo(SystemLogsResponse& psd, const TelemetryInfoPtr& info) {
if (info->cpu_result.is_null() || info->cpu_result->is_error()) {
DVLOG(1) << "CpuResult not found in croshealthd response";
return;
}
std::vector<healthd::PhysicalCpuInfoPtr>& physical_cpus =
info->cpu_result->get_cpu_info()->physical_cpus;
DCHECK_GE(physical_cpus.size(), 1u);
std::string cpu_names;
for (const auto& cpu : physical_cpus) {
// Missing/empty values will be distinguishable in the final list.
StrListAppend(&cpu_names, cpu->model_name.value_or(kNotAvailable));
}
psd.emplace(kRevenCpuNameKey, cpu_names);
}
void PopulateMemoryInfo(SystemLogsResponse& psd, const TelemetryInfoPtr& info) {
if (info->memory_result.is_null() || info->memory_result->is_error()) {
DVLOG(1) << "MemoryResult not found in croshealthd response";
return;
}
healthd::MemoryInfoPtr& memory_info = info->memory_result->get_memory_info();
psd.emplace(kRevenTotalMemoryKey,
base::NumberToString(memory_info->total_memory_kib));
psd.emplace(kRevenFreeMemoryKey,
base::NumberToString(memory_info->free_memory_kib));
psd.emplace(kRevenAvailableMemoryKey,
base::NumberToString(memory_info->available_memory_kib));
}
void PopulateSystemInfo(SystemLogsResponse& psd, const TelemetryInfoPtr& info) {
if (info->system_result.is_null() || info->system_result->is_error()) {
DVLOG(1) << "SystemResult not found in croshealthd response";
return;
}
healthd::DmiInfoPtr& dmi_info =
info->system_result->get_system_info()->dmi_info;
if (!dmi_info.is_null()) {
psd.emplace(kRevenProductVendorKey,
dmi_info->sys_vendor.value_or(kNotAvailable));
psd.emplace(kRevenProductNameKey,
dmi_info->product_name.value_or(kNotAvailable));
psd.emplace(kRevenProductVersionKey,
dmi_info->product_version.value_or(kNotAvailable));
psd.emplace(kRevenBiosVersionKey,
dmi_info->bios_version.value_or(kNotAvailable));
}
healthd::OsInfoPtr& os_info = info->system_result->get_system_info()->os_info;
if (!os_info.is_null()) {
psd.emplace(kRevenSecurebootKey,
base::ToString(os_info->boot_mode ==
healthd::BootMode::kCrosEfiSecure));
psd.emplace(kRevenUefiKey,
base::ToString(
os_info->boot_mode == healthd::BootMode::kCrosEfi ||
os_info->boot_mode == healthd::BootMode::kCrosEfiSecure));
}
}
// Constructs key names based on the passed label. Collects data from all passed
// devices into each value.
void PopulateBusDevicesInfo(SystemLogsResponse& psd,
std::string_view label,
const std::vector<healthd::BusDevicePtr>& devices) {
if (devices.empty()) {
return;
}
const std::string name_key = base::StrCat({"chromeosflex_", label, "_name"});
std::string names;
const std::string id_key = base::StrCat({"chromeosflex_", label, "_id"});
std::string ids;
const std::string driver_key =
base::StrCat({"chromeosflex_", label, "_driver"});
std::string drivers;
for (const auto& device : devices) {
StrListAppend(
&names, base::StrCat({device->vendor_name, " ", device->product_name}));
if (device->bus_info->is_pci_bus_info()) {
const healthd::PciBusInfoPtr& pci_info =
device->bus_info->get_pci_bus_info();
StrListAppend(&ids, FormatDeviceIDs("pci", pci_info->vendor_id,
pci_info->device_id));
// Missing/empty values will be distinguishable in the final list.
StrListAppend(&drivers, pci_info->driver.value_or(kNotAvailable));
}
if (device->bus_info->is_usb_bus_info()) {
const healthd::UsbBusInfoPtr& usb_info =
device->bus_info->get_usb_bus_info();
StrListAppend(&ids, FormatDeviceIDs("usb", usb_info->vendor_id,
usb_info->product_id));
const std::vector<healthd::UsbBusInterfaceInfoPtr>& usb_interfaces =
usb_info->interfaces;
for (const auto& interface : usb_interfaces) {
// Missing/empty values will be distinguishable in the final list.
StrListAppend(&drivers, interface->driver.value_or(kNotAvailable));
}
}
}
psd.emplace(name_key, names);
psd.emplace(id_key, ids);
psd.emplace(driver_key, drivers);
}
void PopulateBusDevicesInfo(SystemLogsResponse& psd,
const TelemetryInfoPtr& info) {
if (info->bus_result.is_null() || info->bus_result->is_error()) {
DVLOG(1) << "BusResult not found in croshealthd response";
return;
}
std::vector<healthd::BusDevicePtr>& bus_devices =
info->bus_result->get_bus_devices();
// When looking at bus devices there can be multiple of any given class
// (though in practice we mostly see this with gpu). We need to accumulate
// these values on a per-class basis, so: sort by class we care about, then
// process in a group.
std::vector<healthd::BusDevicePtr> ethernet_devices;
std::vector<healthd::BusDevicePtr> wireless_devices;
std::vector<healthd::BusDevicePtr> bluetooth_devices;
std::vector<healthd::BusDevicePtr> gpu_devices;
for (auto& device : bus_devices) {
switch (device->device_class) {
case healthd::BusDeviceClass::kEthernetController:
ethernet_devices.push_back(std::move(device));
break;
case healthd::BusDeviceClass::kWirelessController:
wireless_devices.push_back(std::move(device));
break;
case healthd::BusDeviceClass::kBluetoothAdapter:
bluetooth_devices.push_back(std::move(device));
break;
case healthd::BusDeviceClass::kDisplayController:
gpu_devices.push_back(std::move(device));
break;
default:
break;
}
}
// See kRevenEthernet*Key, kRevenWireless*Key,
// kRevenBluetooth*Key, and kRevenGpu*Key.
PopulateBusDevicesInfo(psd, "ethernet", ethernet_devices);
PopulateBusDevicesInfo(psd, "wireless", wireless_devices);
PopulateBusDevicesInfo(psd, "bluetooth", bluetooth_devices);
PopulateBusDevicesInfo(psd, "gpu", gpu_devices);
}
void PopulateTpmInfo(SystemLogsResponse& psd, const TelemetryInfoPtr& info) {
if (info->tpm_result.is_null() || info->tpm_result->is_error()) {
DVLOG(1) << "TpmResult not found in croshealthd response";
return;
}
const healthd::TpmInfoPtr& tpm_info = info->tpm_result->get_tpm_info();
if (tpm_info.is_null()) {
return;
}
psd.emplace(kRevenTpmDidVidKey, tpm_info->did_vid.value_or(kNotAvailable));
psd.emplace(kRevenTpmAllowListedKey,
base::ToString(tpm_info->supported_features->is_allowed));
psd.emplace(kRevenTpmOwnedKey, base::ToString(tpm_info->status->owned));
const healthd::TpmVersionPtr& version = tpm_info->version;
if (version.is_null()) {
return;
}
psd.emplace(kRevenTpmVersionKey, ToTpmVersionStr(version->family));
psd.emplace(kRevenTpmSpecLevelKey, base::NumberToString(version->spec_level));
psd.emplace(kRevenTpmManufacturerKey,
base::NumberToString(version->manufacturer));
}
void PopulateGraphicsInfo(SystemLogsResponse& psd,
const TelemetryInfoPtr& info) {
if (info->graphics_result.is_null() || info->graphics_result->is_error()) {
DVLOG(1) << "GraphicsResult not found in croshealthd response";
return;
}
const healthd::GraphicsInfoPtr& graphics_info =
info->graphics_result->get_graphics_info();
const healthd::GLESInfoPtr& gles_info = graphics_info->gles_info;
psd.emplace(kRevenGlVersionKey, gles_info->version);
psd.emplace(kRevenGlShadingVersionKey, gles_info->shading_version);
psd.emplace(kRevenGlVendorKey, gles_info->vendor);
psd.emplace(kRevenGlRendererKey, gles_info->renderer);
psd.emplace(kRevenGlExtensionsKey,
base::JoinString(gles_info->extensions, ", "));
}
std::string FetchTouchpadLibraryName() {
return ash::cros_healthd::ServiceConnection::GetInstance()
->FetchTouchpadLibraryName();
}
// For any key not set, set it to "<not available>".
// We use the sentinel value to distinguish between "empty" data and data that
// couldn't be retrieved. We set all values to help keep reporting consistent.
void SetNotAvailablePsd(SystemLogsResponse& psd) {
psd.try_emplace(kRevenAvailableMemoryKey, kNotAvailable);
psd.try_emplace(kRevenBiosVersionKey, kNotAvailable);
psd.try_emplace(kRevenBluetoothDriverKey, kNotAvailable);
psd.try_emplace(kRevenBluetoothIdKey, kNotAvailable);
psd.try_emplace(kRevenBluetoothNameKey, kNotAvailable);
psd.try_emplace(kRevenCpuNameKey, kNotAvailable);
psd.try_emplace(kRevenEthernetDriverKey, kNotAvailable);
psd.try_emplace(kRevenEthernetIdKey, kNotAvailable);
psd.try_emplace(kRevenEthernetNameKey, kNotAvailable);
psd.try_emplace(kRevenFreeMemoryKey, kNotAvailable);
psd.try_emplace(kRevenGlExtensionsKey, kNotAvailable);
psd.try_emplace(kRevenGlRendererKey, kNotAvailable);
psd.try_emplace(kRevenGlShadingVersionKey, kNotAvailable);
psd.try_emplace(kRevenGlVendorKey, kNotAvailable);
psd.try_emplace(kRevenGlVersionKey, kNotAvailable);
psd.try_emplace(kRevenGpuDriverKey, kNotAvailable);
psd.try_emplace(kRevenGpuIdKey, kNotAvailable);
psd.try_emplace(kRevenGpuNameKey, kNotAvailable);
psd.try_emplace(kRevenProductNameKey, kNotAvailable);
psd.try_emplace(kRevenProductVendorKey, kNotAvailable);
psd.try_emplace(kRevenProductVersionKey, kNotAvailable);
psd.try_emplace(kRevenSecurebootKey, kNotAvailable);
psd.try_emplace(kRevenTotalMemoryKey, kNotAvailable);
psd.try_emplace(kRevenTpmAllowListedKey, kNotAvailable);
psd.try_emplace(kRevenTpmDidVidKey, kNotAvailable);
psd.try_emplace(kRevenTpmManufacturerKey, kNotAvailable);
psd.try_emplace(kRevenTpmOwnedKey, kNotAvailable);
psd.try_emplace(kRevenTpmSpecLevelKey, kNotAvailable);
psd.try_emplace(kRevenTpmVersionKey, kNotAvailable);
psd.try_emplace(kRevenUefiKey, kNotAvailable);
psd.try_emplace(kRevenWirelessDriverKey, kNotAvailable);
psd.try_emplace(kRevenWirelessIdKey, kNotAvailable);
psd.try_emplace(kRevenWirelessNameKey, kNotAvailable);
}
} // namespace
RevenLogSource::RevenLogSource() : SystemLogsSource("Reven") {
ash::cros_healthd::ServiceConnection::GetInstance()->BindProbeService(
probe_service_.BindNewPipeAndPassReceiver());
}
RevenLogSource::~RevenLogSource() = default;
void RevenLogSource::Fetch(SysLogsSourceCallback callback) {
probe_service_->ProbeTelemetryInfo(
{ProbeCategories::kBluetooth, ProbeCategories::kBus,
ProbeCategories::kCpu, ProbeCategories::kGraphics,
ProbeCategories::kMemory, ProbeCategories::kSystem,
ProbeCategories::kTpm},
base::BindOnce(&RevenLogSource::OnTelemetryInfoProbeResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void RevenLogSource::OnTelemetryInfoProbeResponse(
SysLogsSourceCallback callback,
TelemetryInfoPtr info_ptr) {
SystemLogsResponse product_specific_data;
if (info_ptr.is_null()) {
DVLOG(1) << "Null response from croshealthd::ProbeTelemetryInfo.";
}
PopulateSystemInfo(product_specific_data, info_ptr);
PopulateCpuInfo(product_specific_data, info_ptr);
PopulateMemoryInfo(product_specific_data, info_ptr);
PopulateBusDevicesInfo(product_specific_data, info_ptr);
PopulateTpmInfo(product_specific_data, info_ptr);
PopulateGraphicsInfo(product_specific_data, info_ptr);
// Make sure all keys are present.
SetNotAvailablePsd(product_specific_data);
base::OnceCallback<void(const std::string&)> reply_cb = base::BindOnce(
[](SysLogsSourceCallback callback, const SystemLogsResponse& psd,
const std::string& touchpad_lib_name) {
auto response = std::make_unique<SystemLogsResponse>(psd);
// Make sure to overwrite the "not available".
response->emplace(kRevenTouchpadStack, touchpad_lib_name);
std::move(callback).Run(std::move(response));
},
std::move(callback), product_specific_data);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&FetchTouchpadLibraryName), std::move(reply_cb));
}
} // namespace system_logs