blob: 852c9ccf3359bebd686767ff30ee6c61b8830c3b [file] [log] [blame]
// Copyright 2019 The Chromium OS 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 "diagnostics/cros_health_tool/telem/telem.h"
#include <sys/types.h>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <base/at_exit.h>
#include <base/json/json_writer.h>
#include <base/logging.h>
#include <base/optional.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/task/single_thread_task_executor.h>
#include <base/values.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include "diagnostics/cros_healthd_mojo_adapter/cros_healthd_mojo_adapter.h"
#include "mojo/cros_healthd_probe.mojom.h"
#include "mojo/network_health.mojom.h"
#include "mojo/network_types.mojom.h"
namespace diagnostics {
namespace {
using chromeos::cros_healthd::mojom::AudioResultPtr;
using chromeos::cros_healthd::mojom::BacklightResultPtr;
using chromeos::cros_healthd::mojom::BatteryResultPtr;
using chromeos::cros_healthd::mojom::BluetoothResultPtr;
using chromeos::cros_healthd::mojom::BootMode;
using chromeos::cros_healthd::mojom::BootPerformanceResultPtr;
using chromeos::cros_healthd::mojom::BusDeviceClass;
using chromeos::cros_healthd::mojom::BusInfo;
using chromeos::cros_healthd::mojom::BusResultPtr;
using chromeos::cros_healthd::mojom::CpuArchitectureEnum;
using chromeos::cros_healthd::mojom::CpuResultPtr;
using chromeos::cros_healthd::mojom::ErrorType;
using chromeos::cros_healthd::mojom::FanResultPtr;
using chromeos::cros_healthd::mojom::GraphicsResultPtr;
using chromeos::cros_healthd::mojom::MemoryResultPtr;
using chromeos::cros_healthd::mojom::NetworkResultPtr;
using chromeos::cros_healthd::mojom::NonRemovableBlockDeviceResultPtr;
using chromeos::cros_healthd::mojom::NullableUint64Ptr;
using chromeos::cros_healthd::mojom::OsInfoPtr;
using chromeos::cros_healthd::mojom::ProbeCategoryEnum;
using chromeos::cros_healthd::mojom::ProbeErrorPtr;
using chromeos::cros_healthd::mojom::ProcessResultPtr;
using chromeos::cros_healthd::mojom::ProcessState;
using chromeos::cros_healthd::mojom::StatefulPartitionResultPtr;
using chromeos::cros_healthd::mojom::SystemResultPtr;
using chromeos::cros_healthd::mojom::SystemResultV2Ptr;
using chromeos::cros_healthd::mojom::TelemetryInfoPtr;
using chromeos::cros_healthd::mojom::TimezoneResultPtr;
using chromeos::cros_healthd::mojom::TpmGSCVersion;
using chromeos::cros_healthd::mojom::TpmResultPtr;
using chromeos::network_config::mojom::NetworkType;
using chromeos::network_config::mojom::PortalState;
using chromeos::network_health::mojom::NetworkState;
using chromeos::network_health::mojom::SignalStrengthStatsPtr;
using chromeos::network_health::mojom::UInt32ValuePtr;
// Value printed for optional fields when they aren't populated.
constexpr char kNotApplicableString[] = "N/A";
constexpr std::pair<const char*, ProbeCategoryEnum> kCategorySwitches[] = {
{"battery", ProbeCategoryEnum::kBattery},
{"storage", ProbeCategoryEnum::kNonRemovableBlockDevices},
{"cpu", ProbeCategoryEnum::kCpu},
{"timezone", ProbeCategoryEnum::kTimezone},
{"memory", ProbeCategoryEnum::kMemory},
{"backlight", ProbeCategoryEnum::kBacklight},
{"fan", ProbeCategoryEnum::kFan},
{"stateful_partition", ProbeCategoryEnum::kStatefulPartition},
{"bluetooth", ProbeCategoryEnum::kBluetooth},
{"system", ProbeCategoryEnum::kSystem},
{"system2", ProbeCategoryEnum::kSystem2},
{"network", ProbeCategoryEnum::kNetwork},
{"audio", ProbeCategoryEnum::kAudio},
{"boot_performance", ProbeCategoryEnum::kBootPerformance},
{"bus", ProbeCategoryEnum::kBus},
{"tpm", ProbeCategoryEnum::kTpm},
{"graphics", ProbeCategoryEnum::kGraphics},
};
std::string EnumToString(ProcessState state) {
switch (state) {
case ProcessState::kRunning:
return "Running";
case ProcessState::kSleeping:
return "Sleeping";
case ProcessState::kWaiting:
return "Waiting";
case ProcessState::kZombie:
return "Zombie";
case ProcessState::kStopped:
return "Stopped";
case ProcessState::kTracingStop:
return "Tracing Stop";
case ProcessState::kDead:
return "Dead";
}
}
std::string EnumToString(ErrorType type) {
switch (type) {
case ErrorType::kFileReadError:
return "File Read Error";
case ErrorType::kParseError:
return "Parse Error";
case ErrorType::kSystemUtilityError:
return "Error running system utility";
case ErrorType::kServiceUnavailable:
return "External service not aviailable";
}
}
std::string EnumToString(CpuArchitectureEnum architecture) {
switch (architecture) {
case CpuArchitectureEnum::kUnknown:
return "unknown";
case CpuArchitectureEnum::kX86_64:
return "x86_64";
case CpuArchitectureEnum::kAArch64:
return "aarch64";
case CpuArchitectureEnum::kArmv7l:
return "armv7l";
}
}
std::string EnumToString(NetworkType type) {
switch (type) {
case NetworkType::kAll:
return "Unknown";
case NetworkType::kCellular:
return "Cellular";
case NetworkType::kEthernet:
return "Ethernet";
case NetworkType::kMobile:
return "Mobile";
case NetworkType::kTether:
return "Tether";
case NetworkType::kVPN:
return "VPN";
case NetworkType::kWireless:
return "Wireless";
case NetworkType::kWiFi:
return "WiFi";
}
}
std::string EnumToString(NetworkState state) {
switch (state) {
case NetworkState::kUninitialized:
return "Uninitialized";
case NetworkState::kDisabled:
return "Disabled";
case NetworkState::kProhibited:
return "Prohibited";
case NetworkState::kNotConnected:
return "Not Connected";
case NetworkState::kConnecting:
return "Connecting";
case NetworkState::kPortal:
return "Portal";
case NetworkState::kConnected:
return "Connected";
case NetworkState::kOnline:
return "Online";
}
}
std::string EnumToString(PortalState state) {
switch (state) {
case PortalState::kUnknown:
return "Unknown";
case PortalState::kOnline:
return "Online";
case PortalState::kPortalSuspected:
return "Portal Suspected";
case PortalState::kPortal:
return "Portal Detected";
case PortalState::kProxyAuthRequired:
return "Proxy Auth Required";
case PortalState::kNoInternet:
return "No Internet";
}
}
std::string EnumToString(BusDeviceClass device_class) {
switch (device_class) {
case BusDeviceClass::kOthers:
return "others";
case BusDeviceClass::kDisplayController:
return "display controller";
case BusDeviceClass::kEthernetController:
return "ethernet controller";
case BusDeviceClass::kWirelessController:
return "wireless controller";
case BusDeviceClass::kBluetoothAdapter:
return "bluetooth controller";
}
}
std::string EnumToString(BootMode mode) {
switch (mode) {
case BootMode::kUnknown:
return "Unknown";
case BootMode::kCrosSecure:
return "cros_secure";
case BootMode::kCrosEfi:
return "cros_efi";
case BootMode::kCrosLegacy:
return "cros_legacy";
case BootMode::kCrosEfiSecure:
return "cros_efi_secure";
}
}
std::string EnumToString(TpmGSCVersion version) {
switch (version) {
case TpmGSCVersion::kNotGSC:
return "NotGSC";
case TpmGSCVersion::kCr50:
return "Cr50";
case TpmGSCVersion::kTi50:
return "Ti50";
}
}
#define SET_DICT(key, info, output) SetJsonDictValue(#key, info->key, output);
template <typename T>
void SetJsonDictValue(const std::string& key,
const T& value,
base::Value* output) {
if constexpr (std::is_same_v<T, uint32_t> || std::is_same_v<T, int64_t> ||
std::is_same_v<T, uint64_t>) {
// |base::Value| doesn't support these types, we need to convert them to
// string.
SetJsonDictValue(key, std::to_string(value), output);
} else if constexpr (std::is_same_v<T, base::Optional<std::string>>) {
if (value.has_value())
SetJsonDictValue(key, value.value(), output);
} else if constexpr (std::is_same_v<T, NullableUint64Ptr>) {
if (value)
SetJsonDictValue(key, value->value, output);
} else if constexpr (std::is_same_v<T, UInt32ValuePtr>) {
if (value)
SetJsonDictValue(key, value->value, output);
} else if constexpr (std::is_enum_v<T>) {
SetJsonDictValue(key, EnumToString(value), output);
} else {
output->SetKey(key, base::Value(value));
}
}
void OutputCSVLine(const std::vector<std::string>& datas,
const std::string separator = ",") {
bool is_first = true;
for (const auto& data : datas) {
if (!is_first) {
std::cout << separator;
}
is_first = false;
std::cout << data;
}
std::cout << std::endl;
}
void OutputCSV(const std::vector<std::string>& headers,
const std::vector<std::vector<std::string>>& values) {
OutputCSVLine(headers);
for (const auto& value : values) {
OutputCSVLine(value);
}
}
void OutputJson(const base::Value& output) {
std::string json;
base::JSONWriter::WriteWithOptions(
output, base::JSONWriter::Options::OPTIONS_PRETTY_PRINT, &json);
std::cout << json << std::endl;
}
void DisplayError(const ProbeErrorPtr& error) {
base::Value output{base::Value::Type::DICTIONARY};
SET_DICT(type, error, &output);
SET_DICT(msg, error, &output);
OutputJson(output);
}
void DisplayProcessInfo(const ProcessResultPtr& result) {
if (result.is_null())
return;
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& info = result->get_process_info();
base::Value output{base::Value::Type::DICTIONARY};
SET_DICT(bytes_read, info, &output);
SET_DICT(bytes_written, info, &output);
SET_DICT(cancelled_bytes_written, info, &output);
SET_DICT(command, info, &output);
SET_DICT(free_memory_kib, info, &output);
SET_DICT(nice, info, &output);
SET_DICT(physical_bytes_read, info, &output);
SET_DICT(physical_bytes_written, info, &output);
SET_DICT(priority, info, &output);
SET_DICT(read_system_calls, info, &output);
SET_DICT(resident_memory_kib, info, &output);
SET_DICT(state, info, &output);
SET_DICT(total_memory_kib, info, &output);
SET_DICT(uptime_ticks, info, &output);
SET_DICT(user_id, info, &output);
SET_DICT(write_system_calls, info, &output);
OutputJson(output);
}
void DisplayBatteryInfo(const BatteryResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& info = result->get_battery_info();
// There might be no battery if it's AC only.
// Run the following command on DUT to see if the device is configured to AC
// only.
// # cros_config /hardware-properties psu-type
if (info.is_null()) {
return;
}
base::Value output{base::Value::Type::DICTIONARY};
SET_DICT(charge_full, info, &output);
SET_DICT(charge_full_design, info, &output);
SET_DICT(charge_now, info, &output);
SET_DICT(current_now, info, &output);
SET_DICT(cycle_count, info, &output);
SET_DICT(model_name, info, &output);
SET_DICT(serial_number, info, &output);
SET_DICT(status, info, &output);
SET_DICT(technology, info, &output);
SET_DICT(vendor, info, &output);
SET_DICT(voltage_min_design, info, &output);
SET_DICT(voltage_now, info, &output);
// Optional fields
SET_DICT(manufacture_date, info, &output);
SET_DICT(temperature, info, &output);
OutputJson(output);
}
void DisplayAudioInfo(const AudioResultPtr& audio_result) {
if (audio_result->is_error()) {
DisplayError(audio_result->get_error());
return;
}
const auto& audio = audio_result->get_audio_info();
if (audio.is_null()) {
std::cout << "Device does not have audio info" << std::endl;
return;
}
base::Value output{base::Value::Type::DICTIONARY};
output.SetStringKey("input_device_name", audio->input_device_name);
output.SetStringKey("output_device_name", audio->output_device_name);
output.SetBoolKey("input_mute", audio->input_mute);
output.SetBoolKey("output_mute", audio->output_mute);
output.SetIntKey("input_gain", audio->input_gain);
output.SetIntKey("output_volume", audio->output_volume);
output.SetIntKey("severe_underruns", audio->severe_underruns);
output.SetIntKey("underruns", audio->underruns);
OutputJson(output);
}
void DisplayBootPerformanceInfo(const BootPerformanceResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& info = result->get_boot_performance_info();
CHECK(!info.is_null());
base::Value output{base::Value::Type::DICTIONARY};
output.SetStringKey("shutdown_reason", info->shutdown_reason);
output.SetDoubleKey("boot_up_seconds", info->boot_up_seconds);
output.SetDoubleKey("boot_up_timestamp", info->boot_up_timestamp);
output.SetDoubleKey("shutdown_seconds", info->shutdown_seconds);
output.SetDoubleKey("shutdown_timestamp", info->shutdown_timestamp);
OutputJson(output);
}
void DisplayBlockDeviceInfo(const NonRemovableBlockDeviceResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& infos = result->get_block_device_info();
base::Value output{base::Value::Type::DICTIONARY};
auto* block_devices =
output.SetKey("block_devices", base::Value{base::Value::Type::LIST});
for (const auto& info : infos) {
base::Value data{base::Value::Type::DICTIONARY};
SET_DICT(bytes_read_since_last_boot, info, &data);
SET_DICT(bytes_written_since_last_boot, info, &data);
SET_DICT(io_time_seconds_since_last_boot, info, &data);
SET_DICT(name, info, &data);
SET_DICT(path, info, &data);
SET_DICT(read_time_seconds_since_last_boot, info, &data);
SET_DICT(serial, info, &data);
SET_DICT(size, info, &data);
SET_DICT(type, info, &data);
SET_DICT(write_time_seconds_since_last_boot, info, &data);
SET_DICT(manufacturer_id, info, &data);
// optional field
SET_DICT(discard_time_seconds_since_last_boot, info, &data);
block_devices->Append(std::move(data));
}
OutputJson(output);
}
void DisplayBluetoothInfo(const BluetoothResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& infos = result->get_bluetooth_adapter_info();
base::Value output{base::Value::Type::DICTIONARY};
auto* adapters =
output.SetKey("adapters", base::Value{base::Value::Type::LIST});
for (const auto& info : infos) {
base::Value data{base::Value::Type::DICTIONARY};
SET_DICT(address, info, &data);
SET_DICT(name, info, &data);
SET_DICT(num_connected_devices, info, &data);
SET_DICT(powered, info, &data);
adapters->Append(std::move(data));
}
OutputJson(output);
}
void DisplayCpuInfo(const CpuResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& info = result->get_cpu_info();
base::Value output{base::Value::Type::DICTIONARY};
auto* physical_cpus =
output.SetKey("physical_cpus", base::Value{base::Value::Type::LIST});
for (const auto& physical_cpu : info->physical_cpus) {
base::Value physical_cpu_data{base::Value::Type::DICTIONARY};
auto* logical_cpus = physical_cpu_data.SetKey(
"logical_cpus", base::Value{base::Value::Type::LIST});
for (const auto& logical_cpu : physical_cpu->logical_cpus) {
base::Value logical_cpu_data{base::Value::Type::DICTIONARY};
SET_DICT(idle_time_user_hz, logical_cpu, &logical_cpu_data);
SET_DICT(max_clock_speed_khz, logical_cpu, &logical_cpu_data);
SET_DICT(scaling_current_frequency_khz, logical_cpu, &logical_cpu_data);
SET_DICT(scaling_max_frequency_khz, logical_cpu, &logical_cpu_data);
SET_DICT(system_time_user_hz, logical_cpu, &logical_cpu_data);
SET_DICT(user_time_user_hz, logical_cpu, &logical_cpu_data);
auto* c_states = logical_cpu_data.SetKey(
"c_states", base::Value{base::Value::Type::LIST});
for (const auto& c_state : logical_cpu->c_states) {
base::Value c_state_data{base::Value::Type::DICTIONARY};
SET_DICT(name, c_state, &c_state_data);
SET_DICT(time_in_state_since_last_boot_us, c_state, &c_state_data);
c_states->Append(std::move(c_state_data));
}
logical_cpus->Append(std::move(logical_cpu_data));
}
// Optional field
SET_DICT(model_name, physical_cpu, &physical_cpu_data);
physical_cpus->Append(std::move(physical_cpu_data));
}
auto* temperature_channels = output.SetKey(
"temperature_channels", base::Value{base::Value::Type::LIST});
for (const auto& channel : info->temperature_channels) {
base::Value data{base::Value::Type::DICTIONARY};
SET_DICT(temperature_celsius, channel, &data);
// Optional field
SET_DICT(label, channel, &data);
temperature_channels->Append(std::move(data));
}
SET_DICT(num_total_threads, info, &output);
SET_DICT(architecture, info, &output);
if (info->keylocker_info) {
auto* out_keylocker = output.SetKey(
"keylocker_info", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(keylocker_configured, info->keylocker_info, out_keylocker);
}
OutputJson(output);
}
void DisplayFanInfo(const FanResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& infos = result->get_fan_info();
base::Value output{base::Value::Type::DICTIONARY};
auto* fans = output.SetKey("fans", base::Value{base::Value::Type::LIST});
for (const auto& info : infos) {
base::Value data{base::Value::Type::DICTIONARY};
SET_DICT(speed_rpm, info, &data);
fans->Append(std::move(data));
}
OutputJson(output);
}
void DisplayNetworkInfo(const NetworkResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& infos = result->get_network_health()->networks;
base::Value output{base::Value::Type::DICTIONARY};
auto* networks =
output.SetKey("networks", base::Value{base::Value::Type::LIST});
for (const auto& info : infos) {
base::Value data{base::Value::Type::DICTIONARY};
SET_DICT(portal_state, info, &data);
SET_DICT(state, info, &data);
SET_DICT(type, info, &data);
// Optional fields
SET_DICT(guid, info, &data);
SET_DICT(name, info, &data);
SET_DICT(mac_address, info, &data);
SET_DICT(ipv4_address, info, &data);
SET_DICT(signal_strength, info, &data);
if (info->signal_strength_stats) {
auto* stats = data.SetKey("signal_strength_stats",
base::Value{base::Value::Type::DICTIONARY});
SET_DICT(average, info->signal_strength_stats, stats);
SET_DICT(deviation, info->signal_strength_stats, stats);
}
if (info->ipv6_addresses.size()) {
SetJsonDictValue("ipv6_addresses",
base::JoinString(info->ipv6_addresses, ":"), &data);
}
networks->Append(std::move(data));
}
OutputJson(output);
}
void DisplayTimezoneInfo(const TimezoneResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& info = result->get_timezone_info();
CHECK(!info.is_null());
base::Value output{base::Value::Type::DICTIONARY};
output.SetStringKey("posix", info->posix);
output.SetStringKey("region", info->region);
OutputJson(output);
}
void DisplayMemoryInfo(const MemoryResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& info = result->get_memory_info();
CHECK(!info.is_null());
base::Value output{base::Value::Type::DICTIONARY};
SET_DICT(available_memory_kib, info, &output);
SET_DICT(free_memory_kib, info, &output);
SET_DICT(page_faults_since_last_boot, info, &output);
SET_DICT(total_memory_kib, info, &output);
OutputJson(output);
}
void DisplayBacklightInfo(const BacklightResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& infos = result->get_backlight_info();
base::Value output{base::Value::Type::DICTIONARY};
auto* backlights =
output.SetKey("backlights", base::Value{base::Value::Type::LIST});
for (const auto& info : infos) {
base::Value data{base::Value::Type::DICTIONARY};
SET_DICT(brightness, info, &data);
SET_DICT(max_brightness, info, &data);
SET_DICT(path, info, &data);
backlights->Append(std::move(data));
}
OutputJson(output);
}
void DisplayStatefulPartitionInfo(const StatefulPartitionResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& info = result->get_partition_info();
CHECK(!info.is_null());
base::Value output{base::Value::Type::DICTIONARY};
SET_DICT(available_space, info, &output);
SET_DICT(filesystem, info, &output);
SET_DICT(mount_source, info, &output);
SET_DICT(total_space, info, &output);
OutputJson(output);
}
void DisplaySystemInfo(const SystemResultPtr& system_result) {
if (system_result->is_error()) {
DisplayError(system_result->get_error());
return;
}
const auto& system_info = system_result->get_system_info();
const std::vector<std::string> headers = {
"first_power_date", "manufacture_date",
"product_sku_number", "product_serial_number",
"marketing_name", "bios_version",
"board_name", "board_version",
"chassis_type", "product_name",
"os_version", "os_channel"};
std::string chassis_type =
!system_info->chassis_type.is_null()
? std::to_string(system_info->chassis_type->value)
: kNotApplicableString;
std::string os_version =
base::JoinString({system_info->os_version->release_milestone,
system_info->os_version->build_number,
system_info->os_version->patch_number},
".");
// The marketing name sometimes has a comma, for example:
// "Acer Chromebook Spin 11 (CP311-H1, CP311-1HN)"
// This messes up the tast logic, which splits on commas. To fix it, we
// replace any ", " patterns found with "/".
std::string marketing_name = system_info->marketing_name;
base::ReplaceSubstringsAfterOffset(&marketing_name, 0, ", ", "/");
const std::vector<std::vector<std::string>> values = {
{system_info->first_power_date.value_or(kNotApplicableString),
system_info->manufacture_date.value_or(kNotApplicableString),
system_info->product_sku_number.value_or(kNotApplicableString),
system_info->product_serial_number.value_or(kNotApplicableString),
marketing_name, system_info->bios_version.value_or(kNotApplicableString),
system_info->board_name.value_or(kNotApplicableString),
system_info->board_version.value_or(kNotApplicableString), chassis_type,
system_info->product_name.value_or(kNotApplicableString), os_version,
system_info->os_version->release_channel}};
OutputCSV(headers, values);
}
void DisplaySystemInfoV2(const SystemResultV2Ptr& system_result) {
if (system_result->is_error()) {
DisplayError(system_result->get_error());
return;
}
const auto& system_info = system_result->get_system_info_v2();
base::Value output{base::Value::Type::DICTIONARY};
const auto& os_info = system_info->os_info;
auto* out_os_info =
output.SetKey("os_info", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(code_name, os_info, out_os_info);
SET_DICT(marketing_name, os_info, out_os_info);
SET_DICT(boot_mode, os_info, out_os_info);
const auto& os_version = os_info->os_version;
auto* out_os_version = out_os_info->SetKey(
"os_version", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(release_milestone, os_version, out_os_version);
SET_DICT(build_number, os_version, out_os_version);
SET_DICT(patch_number, os_version, out_os_version);
SET_DICT(release_channel, os_version, out_os_version);
const auto& vpd_info = system_info->vpd_info;
if (vpd_info) {
auto* out_vpd_info =
output.SetKey("vpd_info", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(serial_number, vpd_info, out_vpd_info);
SET_DICT(region, vpd_info, out_vpd_info);
SET_DICT(mfg_date, vpd_info, out_vpd_info);
SET_DICT(activate_date, vpd_info, out_vpd_info);
SET_DICT(sku_number, vpd_info, out_vpd_info);
SET_DICT(model_name, vpd_info, out_vpd_info);
}
const auto& dmi_info = system_info->dmi_info;
if (dmi_info) {
auto* out_dmi_info =
output.SetKey("dmi_info", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(bios_vendor, dmi_info, out_dmi_info);
SET_DICT(bios_version, dmi_info, out_dmi_info);
SET_DICT(board_name, dmi_info, out_dmi_info);
SET_DICT(board_vendor, dmi_info, out_dmi_info);
SET_DICT(board_version, dmi_info, out_dmi_info);
SET_DICT(chassis_vendor, dmi_info, out_dmi_info);
SET_DICT(chassis_type, dmi_info, out_dmi_info);
SET_DICT(product_family, dmi_info, out_dmi_info);
SET_DICT(product_name, dmi_info, out_dmi_info);
SET_DICT(product_version, dmi_info, out_dmi_info);
SET_DICT(sys_vendor, dmi_info, out_dmi_info);
}
OutputJson(output);
}
void DisplayBusDevices(const BusResultPtr& bus_result) {
if (bus_result->is_error()) {
DisplayError(bus_result->get_error());
return;
}
const auto& devices = bus_result->get_bus_devices();
base::Value output{base::Value::Type::DICTIONARY};
auto* out_devices =
output.SetKey("devices", base::Value{base::Value::Type::LIST});
for (const auto& device : devices) {
base::Value out_device{base::Value::Type::DICTIONARY};
SET_DICT(vendor_name, device, &out_device);
SET_DICT(product_name, device, &out_device);
SET_DICT(device_class, device, &out_device);
auto* out_bus_info = out_device.SetKey(
"bus_info", base::Value{base::Value::Type::DICTIONARY});
switch (device->bus_info->which()) {
case BusInfo::Tag::PCI_BUS_INFO: {
auto* out_pci_info = out_bus_info->SetKey(
"pci_bus_info", base::Value{base::Value::Type::DICTIONARY});
const auto& pci_info = device->bus_info->get_pci_bus_info();
SET_DICT(class_id, pci_info, out_pci_info);
SET_DICT(subclass_id, pci_info, out_pci_info);
SET_DICT(prog_if_id, pci_info, out_pci_info);
SET_DICT(vendor_id, pci_info, out_pci_info);
SET_DICT(device_id, pci_info, out_pci_info);
SET_DICT(driver, pci_info, out_pci_info);
break;
}
case BusInfo::Tag::USB_BUS_INFO: {
const auto& usb_info = device->bus_info->get_usb_bus_info();
auto* out_usb_info = out_bus_info->SetKey(
"usb_bus_info", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(class_id, usb_info, out_usb_info);
SET_DICT(subclass_id, usb_info, out_usb_info);
SET_DICT(protocol_id, usb_info, out_usb_info);
SET_DICT(vendor_id, usb_info, out_usb_info);
SET_DICT(product_id, usb_info, out_usb_info);
auto* out_usb_ifs = out_usb_info->SetKey(
"interfaces", base::Value{base::Value::Type::LIST});
for (const auto& usb_if_info : usb_info->interfaces) {
base::Value out_usb_if{base::Value::Type::DICTIONARY};
SET_DICT(interface_number, usb_if_info, &out_usb_if);
SET_DICT(class_id, usb_if_info, &out_usb_if);
SET_DICT(subclass_id, usb_if_info, &out_usb_if);
SET_DICT(protocol_id, usb_if_info, &out_usb_if);
SET_DICT(driver, usb_if_info, &out_usb_if);
out_usb_ifs->Append(std::move(out_usb_if));
}
break;
}
}
out_devices->Append(std::move(out_device));
}
OutputJson(output);
}
void DisplayTpmInfo(const TpmResultPtr& result) {
if (result->is_error()) {
DisplayError(result->get_error());
return;
}
const auto& info = result->get_tpm_info();
base::Value output{base::Value::Type::DICTIONARY};
const auto& version = info->version;
auto* out_version =
output.SetKey("version", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(gsc_version, version, out_version);
SET_DICT(family, version, out_version);
SET_DICT(spec_level, version, out_version);
SET_DICT(manufacturer, version, out_version);
SET_DICT(tpm_model, version, out_version);
SET_DICT(firmware_version, version, out_version);
SET_DICT(vendor_specific, version, out_version);
const auto& status = info->status;
auto* out_status =
output.SetKey("status", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(enabled, status, out_status);
SET_DICT(owned, status, out_status);
SET_DICT(owner_password_is_present, status, out_status);
const auto& dictionary_attack = info->dictionary_attack;
auto* out_dictionary_attack = output.SetKey(
"dictionary_attack", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(counter, dictionary_attack, out_dictionary_attack);
SET_DICT(threshold, dictionary_attack, out_dictionary_attack);
SET_DICT(lockout_in_effect, dictionary_attack, out_dictionary_attack);
SET_DICT(lockout_seconds_remaining, dictionary_attack, out_dictionary_attack);
const auto& attestation = info->attestation;
auto* out_attestation =
output.SetKey("attestation", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(prepared_for_enrollment, attestation, out_attestation);
SET_DICT(enrolled, attestation, out_attestation);
const auto& supported_features = info->supported_features;
auto* out_supported_features = output.SetKey(
"supported_features", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(support_u2f, supported_features, out_supported_features);
SET_DICT(support_pinweaver, supported_features, out_supported_features);
SET_DICT(support_runtime_selection, supported_features,
out_supported_features);
SET_DICT(is_allowed, supported_features, out_supported_features);
SET_DICT(did_vid, info, &output);
OutputJson(output);
}
void DisplayGraphicsInfo(const GraphicsResultPtr& graphics_result) {
if (graphics_result->is_error()) {
DisplayError(graphics_result->get_error());
return;
}
const auto& info = graphics_result->get_graphics_info();
CHECK(!info.is_null());
base::Value output{base::Value::Type::DICTIONARY};
const auto& gles_info = info->gles_info;
auto* out_gles_info =
output.SetKey("gles_info", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(version, gles_info, out_gles_info);
SET_DICT(shading_version, gles_info, out_gles_info);
SET_DICT(vendor, gles_info, out_gles_info);
SET_DICT(renderer, gles_info, out_gles_info);
auto* gles_extensions =
out_gles_info->SetKey("extensions", base::Value{base::Value::Type::LIST});
for (const auto& ext : gles_info->extensions) {
gles_extensions->Append(ext);
}
const auto& egl_info = info->egl_info;
auto* out_egl_info =
output.SetKey("egl_info", base::Value{base::Value::Type::DICTIONARY});
SET_DICT(version, egl_info, out_egl_info);
SET_DICT(vendor, egl_info, out_egl_info);
SET_DICT(client_api, egl_info, out_egl_info);
auto* egl_extensions =
out_egl_info->SetKey("extensions", base::Value{base::Value::Type::LIST});
for (const auto& ext : egl_info->extensions) {
egl_extensions->Append(ext);
}
OutputJson(output);
}
// Displays the retrieved telemetry information to the console.
void DisplayTelemetryInfo(const TelemetryInfoPtr& info) {
const auto& battery_result = info->battery_result;
if (battery_result)
DisplayBatteryInfo(battery_result);
const auto& block_device_result = info->block_device_result;
if (block_device_result)
DisplayBlockDeviceInfo(block_device_result);
const auto& cpu_result = info->cpu_result;
if (cpu_result)
DisplayCpuInfo(cpu_result);
const auto& timezone_result = info->timezone_result;
if (timezone_result)
DisplayTimezoneInfo(timezone_result);
const auto& memory_result = info->memory_result;
if (memory_result)
DisplayMemoryInfo(memory_result);
const auto& backlight_result = info->backlight_result;
if (backlight_result)
DisplayBacklightInfo(backlight_result);
const auto& fan_result = info->fan_result;
if (fan_result)
DisplayFanInfo(fan_result);
const auto& stateful_partition_result = info->stateful_partition_result;
if (stateful_partition_result)
DisplayStatefulPartitionInfo(stateful_partition_result);
const auto& bluetooth_result = info->bluetooth_result;
if (bluetooth_result)
DisplayBluetoothInfo(bluetooth_result);
const auto& system_result = info->system_result;
if (system_result)
DisplaySystemInfo(system_result);
const auto& network_result = info->network_result;
if (network_result)
DisplayNetworkInfo(network_result);
const auto& audio_result = info->audio_result;
if (audio_result)
DisplayAudioInfo(audio_result);
const auto& boot_performance_result = info->boot_performance_result;
if (boot_performance_result)
DisplayBootPerformanceInfo(boot_performance_result);
const auto& bus_result = info->bus_result;
if (bus_result)
DisplayBusDevices(bus_result);
const auto& tpm_result = info->tpm_result;
if (tpm_result)
DisplayTpmInfo(tpm_result);
const auto& system_result_v2 = info->system_result_v2;
if (system_result_v2)
DisplaySystemInfoV2(system_result_v2);
const auto& graphics_result = info->graphics_result;
if (graphics_result)
DisplayGraphicsInfo(graphics_result);
}
// Create a stringified list of the category names for use in help.
std::string GetCategoryHelp() {
std::stringstream ss;
ss << "Category or categories to probe, as comma-separated list: [";
const char* sep = "";
for (auto pair : kCategorySwitches) {
ss << sep << pair.first;
sep = ", ";
}
ss << "]";
return ss.str();
}
} // namespace
// 'telem' sub-command for cros-health-tool:
//
// Test driver for cros_healthd's telemetry collection. Supports requesting a
// comma-separate list of categories and/or a single process at a time.
int telem_main(int argc, char** argv) {
std::string category_help = GetCategoryHelp();
DEFINE_string(category, "", category_help.c_str());
DEFINE_uint32(process, 0, "Process ID to probe.");
brillo::FlagHelper::Init(argc, argv, "telem - Device telemetry tool.");
brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderrIfTty);
base::AtExitManager at_exit_manager;
std::map<std::string, chromeos::cros_healthd::mojom::ProbeCategoryEnum>
switch_to_category(std::begin(kCategorySwitches),
std::end(kCategorySwitches));
logging::InitLogging(logging::LoggingSettings());
base::SingleThreadTaskExecutor task_executor(base::MessagePumpType::IO);
std::unique_ptr<CrosHealthdMojoAdapter> adapter =
CrosHealthdMojoAdapter::Create();
// Make sure at least one flag is specified.
if (FLAGS_category == "" && FLAGS_process == 0) {
LOG(ERROR) << "No category or process specified.";
return EXIT_FAILURE;
}
// Probe a process, if requested.
if (FLAGS_process != 0) {
DisplayProcessInfo(
adapter->GetProcessInfo(static_cast<pid_t>(FLAGS_process)));
}
// Probe category info, if requested.
if (FLAGS_category != "") {
// Validate the category flag.
std::vector<chromeos::cros_healthd::mojom::ProbeCategoryEnum>
categories_to_probe;
std::vector<std::string> input_categories = base::SplitString(
FLAGS_category, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (const auto& category : input_categories) {
auto iterator = switch_to_category.find(category);
if (iterator == switch_to_category.end()) {
LOG(ERROR) << "Invalid category: " << category;
return EXIT_FAILURE;
}
categories_to_probe.push_back(iterator->second);
}
// Probe and display the category or categories.
chromeos::cros_healthd::mojom::TelemetryInfoPtr result =
adapter->GetTelemetryInfo(categories_to_probe);
if (!result) {
LOG(ERROR) << "Unable to probe telemetry info";
return EXIT_FAILURE;
}
DisplayTelemetryInfo(result);
}
return EXIT_SUCCESS;
}
} // namespace diagnostics