Revert "metrics: Add metrics crate."

This reverts commit 4c2017c757.

Reason for revert: dependency crrev.com/c/3622798 caused regression

Original change's description:
> metrics: Add metrics crate.
>
> This crate is effectively a collection of stubs in this repo,
> but will allow a downstream repo to implement those stubs.
>
> BUG=b:213152497
> FIXED=b:213152497
> TEST=build + kokoro
>
> Change-Id: I3c544644ec3d7917f9ebb2e8621042c35b556db8
> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3622799
> Reviewed-by: Noah Gold <nkgold@google.com>
> Commit-Queue: Michael Hoyle <mikehoyle@google.com>
> Tested-by: kokoro <noreply+kokoro@google.com>

BUG=b:213152497,b:232316549
TEST=CtsAccessibilityServiceTestCases

Change-Id: Idd636d7d34e68977fffc1f6c0a89924eb7be5d54
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3641218
Auto-Submit: Shao-Chuan Lee <shaochuan@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Reviewed-by: Michael Hoyle <mikehoyle@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
(cherry picked from commit 55a5c8d8f9)
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3650067
Tested-by: Shao-Chuan Lee <shaochuan@chromium.org>
Commit-Queue: Shao-Chuan Lee <shaochuan@chromium.org>
Reviewed-by: Junichi Uekawa <uekawa@chromium.org>
This commit is contained in:
Shao-Chuan Lee 2022-05-12 08:10:08 +00:00 committed by Chromeos LUCI
parent c0b4fbdce7
commit 4521376a0a
18 changed files with 0 additions and 1453 deletions

View file

@ -171,7 +171,6 @@ libc = "0.2.93"
libcras = "*"
# Compile out trace statements in release builds
log = { version = "0", features = ["release_max_level_debug"]}
metrics = { path = "metrics" }
minijail = "*" # provided by ebuild
net_util = { path = "net_util" }
p9 = "*"

View file

@ -1,28 +0,0 @@
[package]
name = "metrics"
version = "0.1.0"
authors = ["The Chromium OS Authors"]
edition = "2021"
[features]
kiwi = ["lazy_static", "libc", "serde_json", "sync"]
[dependencies]
anyhow = "*"
base = { path = "../base" }
cfg-if = "*"
lazy_static = { version = "*", optional = true }
libc = { version = "*", optional = true }
protobuf = { version = "2.24", features = [ "with-serde" ] }
serde = { version = "1", features = [ "derive" ] }
serde_json = { version = "*", optional = true }
sync = { path = "../common/sync", optional = true }
[target.'cfg(windows)'.dependencies]
chrono = { version = "*" }
winapi = { version = "*" }
wmi = { version = "^0.9" }
[build-dependencies]
protoc-rust = "2.24"

View file

@ -1,52 +0,0 @@
// Copyright 2022 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.
use protoc_rust::{Codegen, Customize};
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::{env, fs};
fn main() {
build_protos();
}
// TODO(mikehoyle): Unify all proto-building logic across crates into a common build dependency.
fn build_protos() {
let proto_files = vec!["protos/event_details.proto"];
let out_dir = format!(
"{}/metrics_protos",
env::var("OUT_DIR").expect("OUT_DIR env does not exist.")
);
fs::create_dir_all(&out_dir).unwrap();
Codegen::new()
.out_dir(&out_dir)
.inputs(&proto_files)
.customize(Customize {
serde_derive: Some(true),
..Default::default()
})
.run()
.unwrap();
create_gen_file(proto_files, &out_dir);
}
fn create_gen_file(proto_files: Vec<&str>, out_dir: &str) {
let generated = PathBuf::from(&out_dir).join("generated.rs");
let out = File::create(generated).expect("Failed to create generated file.");
for proto in proto_files {
let file_stem = PathBuf::from(proto)
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_owned();
let out_dir = out_dir.replace("\\", "/");
writeln!(&out, "#[path = \"{}/{}.rs\"]", out_dir, file_stem)
.expect("failed to write to generated.");
writeln!(&out, "pub mod {}_proto;", file_stem).expect("failed to write to generated.");
}
}

View file

@ -1,98 +0,0 @@
// Provides data structures for additional details for metrics events.
syntax = "proto2";
message RecordDetails {
reserved 1 to 11, 14 to 16;
// Additional details about an unexpected exit of a child process within
// the emulator.
optional EmulatorChildProcessExitDetails emulator_child_process_exit_details =
12;
// Additional details about wave formats from the Window's host system.
optional WaveFormatDetails wave_format_details = 13;
}
message WaveFormatDetails {
// Format requested by WASAPI `GetMixFormat` system call.
optional WaveFormat requested = 1;
// Originally the requested wave format that's modified by the emulator. Only
// populated if the emulator decides the requested wave format should not be
// used.
optional WaveFormat modified = 2;
// Format that is valid and closest matching to the modified format, if the
// modified was rejected. Should only be populated if modified is also
// non-null and was rejected by WASAPI `IsFormatSupported` system call.
optional WaveFormat closest_matched = 3;
}
// Defines the format of waveformat audio data. This information is used by
// WASAPI to determine how to process the audio playback data coming from the
// emulator.
//
// The fields in the structure come from WAVEFORMATEXTENSIBLE of win32 api.
// https://docs.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-waveformatextensible
message WaveFormat {
// Ex. 65534 (Maps to WAVE_FORMAT_EXTENSIBLE)
optional int32 format_tag = 1;
// Number of channels.
optional int32 channels = 2;
// Sample rate in Hz. Ex: 48000
optional int32 samples_per_sec = 3;
// Required average data-transfer rate for the format tag. Usually this will
// be samples_per_sec * block_align, since the format tag is usually
// WAVE_FORMAT_IEEE_FLOAT or it's extensible and SubFormat is
// KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.
optional int32 avg_bytes_per_sec = 4;
// Minimum atomic unit of data based on the format_tag. Usually this will
// just be bits_per_samples * channels.
optional int32 block_align = 5;
// Bits used per sample. Must be a multiple of 8.
optional int32 bits_per_sample = 6;
// Size in bytes of extra information appended to WAVEFORMATEX struct.
optional int32 size_bytes = 7;
// The next fields are part of the WAVEFORMATEXTENSIBLE struct. They will only
// be non-null if format_tag is WAVE_FORMAT_EXTENSIBLE.
// Bit depth. Can be any value. Ex. bits_per_sample is 24,
// but samples is 20. Note: This value is a union, so it could mean something
// slightly different, but most likely won't. Refer to doc for more info.
optional int32 samples = 8;
// Bitmask mapping channels in stream to speaker positions.
// Ex. 3 ( SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT )
optional int64 channel_mask = 9;
// Similar to format_tag, but for WAVEFORMATEXTENSIBLE structs.
optional WaveFormatSubFormat sub_format = 10;
// Subformat GUID mapping:
// https://github.com/retep998/winapi-rs/blob/2f76bdea3a79817ccfab496fbd1786d5a697387b/src/shared/ksmedia.rs
enum WaveFormatSubFormat {
KSDATAFORMAT_SUBTYPE_INVALID = 0;
KSDATAFORMAT_SUBTYPE_ANALOG = 1;
KSDATAFORMAT_SUBTYPE_PCM = 2;
KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = 3;
KSDATAFORMAT_SUBTYPE_DRM = 4;
KSDATAFORMAT_SUBTYPE_ALAW = 5;
KSDATAFORMAT_SUBTYPE_MULAW = 6;
KSDATAFORMAT_SUBTYPE_ADPCM = 7;
KSDATAFORMAT_SUBTYPE_MPEG = 8;
}
}
enum EmulatorProcessType {
PROCESS_TYPE_UNKNOWN = 0;
PROCESS_TYPE_MAIN = 1;
PROCESS_TYPE_BLOCK = 2;
PROCESS_TYPE_METRICS = 3;
PROCESS_TYPE_NET = 4;
PROCESS_TYPE_SLIRP = 5;
PROCESS_TYPE_GPU = 6;
PROCESS_TYPE_SOUND = 7;
}
message EmulatorChildProcessExitDetails {
// The Windows exit code of the child process
optional uint32 exit_code = 1;
// The process identifier, as defined by the ProcessType enum in the
// emulator code.
optional EmulatorProcessType process_type = 2;
}

View file

@ -1,85 +0,0 @@
// Copyright 2022 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.
//! Encapsulate the main runtime loop of a metrics process.
use crate::metrics_requests::MetricsRequest;
use crate::RequestHandler;
use anyhow::Result;
use base::{info, warn, CloseNotifier, PollToken, ReadNotifier, Tube, WaitContext};
/// Handles incoming requests to log metrics
pub(crate) trait MetricsRequestHandler {
fn new() -> Self;
fn handle_request(&self, request: MetricsRequest);
fn shutdown(&self);
}
/// Runs the metrics controller.
pub struct MetricsController {
agents: Vec<Tube>,
handler: RequestHandler,
}
#[derive(PollToken)]
enum Token {
/// Triggered when the agent's pipe is readable (e.g. read_notifier).
Agent(usize),
/// Triggered when the agent's pipe closes (e.g. close_notifier).
AgentExited(usize),
}
impl MetricsController {
pub fn new(agents: Vec<Tube>) -> Self {
Self {
agents,
handler: RequestHandler::new(),
}
}
/// Run the metrics controller until all clients exit & close their Tubes.
pub fn run(&mut self) -> Result<()> {
let wait_ctx: WaitContext<Token> = WaitContext::new()?;
let mut closed_tubes = 0;
for (agent_index, agent) in self.agents.iter().enumerate() {
wait_ctx.add(agent.get_read_notifier(), Token::Agent(agent_index))?;
wait_ctx.add(agent.get_close_notifier(), Token::AgentExited(agent_index))?;
}
'listen: loop {
let events = wait_ctx.wait()?;
for event in events.iter().filter(|e| e.is_readable) {
match event.token {
Token::Agent(client_index) => {
let client = &self.agents[client_index];
match client.recv::<MetricsRequest>() {
Ok(req) => self.handler.handle_request(req),
Err(e) => {
warn!("unexpected error receiving agent metrics request: {}", e)
}
}
}
Token::AgentExited(client_index) => {
let client = &self.agents[client_index];
wait_ctx.delete(client.get_read_notifier())?;
wait_ctx.delete(client.get_close_notifier())?;
closed_tubes += 1;
info!(
"metrics tube closed: {} out of {} closed",
closed_tubes,
self.agents.len(),
);
if closed_tubes == self.agents.len() {
info!("metrics run loop exiting: all tubes closed");
break 'listen;
}
}
}
}
}
self.handler.shutdown();
Ok(())
}
}

View file

@ -1,93 +0,0 @@
// Copyright 2022 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.
use anyhow::Error;
use serde::{Deserialize, Serialize};
use std::convert::From;
use std::convert::TryFrom;
// TODO(mikehoyle): Create a way to generate these directly from the
// proto for a single source-of-truth.
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum MetricEventType {
CpuUsage,
MemoryUsage,
Fps,
JankyFps,
NetworkTxRate,
NetworkRxRate,
Interrupts,
FrameTime,
EmulatorGraphicsFreeze,
EmulatorGraphicsUnfreeze,
EmulatorGfxstreamVkAbortReason,
ChildProcessExit,
ReadIo,
WriteIo,
AudioFormatRequestOk,
AudioFormatModifiedOk,
AudioFormatFailed,
TscCoresOutOfSync,
NetworkTxRateSummarized,
NetworkRxRateSummarized,
Other(i64),
}
impl From<MetricEventType> for i64 {
fn from(event_code: MetricEventType) -> Self {
match event_code {
MetricEventType::CpuUsage => 10001,
MetricEventType::MemoryUsage => 10002,
MetricEventType::Fps => 10003,
MetricEventType::JankyFps => 10004,
MetricEventType::NetworkTxRate => 10005,
MetricEventType::NetworkRxRate => 10006,
MetricEventType::Interrupts => 10007,
MetricEventType::FrameTime => 10008,
MetricEventType::EmulatorGraphicsFreeze => 10009,
MetricEventType::EmulatorGraphicsUnfreeze => 10010,
MetricEventType::EmulatorGfxstreamVkAbortReason => 10011,
MetricEventType::ChildProcessExit => 10012,
MetricEventType::ReadIo => 10013,
MetricEventType::WriteIo => 10014,
MetricEventType::AudioFormatRequestOk => 10015,
MetricEventType::AudioFormatModifiedOk => 10016,
MetricEventType::AudioFormatFailed => 10017,
MetricEventType::TscCoresOutOfSync => 10018,
MetricEventType::NetworkTxRateSummarized => 10019,
MetricEventType::NetworkRxRateSummarized => 10020,
MetricEventType::Other(code) => code,
}
}
}
impl TryFrom<i64> for MetricEventType {
type Error = Error;
fn try_from(event_code: i64) -> Result<Self, Self::Error> {
match event_code {
10001 => Ok(MetricEventType::CpuUsage),
10002 => Ok(MetricEventType::MemoryUsage),
10003 => Ok(MetricEventType::Fps),
10004 => Ok(MetricEventType::JankyFps),
10005 => Ok(MetricEventType::NetworkTxRate),
10006 => Ok(MetricEventType::NetworkRxRate),
10007 => Ok(MetricEventType::Interrupts),
10008 => Ok(MetricEventType::FrameTime),
10009 => Ok(MetricEventType::EmulatorGraphicsFreeze),
10010 => Ok(MetricEventType::EmulatorGraphicsUnfreeze),
10011 => Ok(MetricEventType::EmulatorGfxstreamVkAbortReason),
10012 => Ok(MetricEventType::ChildProcessExit),
10013 => Ok(MetricEventType::ReadIo),
10014 => Ok(MetricEventType::WriteIo),
10015 => Ok(MetricEventType::AudioFormatRequestOk),
10016 => Ok(MetricEventType::AudioFormatModifiedOk),
10017 => Ok(MetricEventType::AudioFormatFailed),
10018 => Ok(MetricEventType::TscCoresOutOfSync),
10019 => Ok(MetricEventType::NetworkTxRateSummarized),
10020 => Ok(MetricEventType::NetworkRxRateSummarized),
_ => Ok(MetricEventType::Other(event_code)),
}
}
}

View file

@ -1,29 +0,0 @@
// Copyright 2022 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.
//! This crate serves to provide metrics bindings to be used throughout the codebase.
//! For binaries that wish to use metrics, the intention is that an independent metrics
//! process will run (main loop in the controller mod), and receive requests via a tube from
//! another process.
//!
//! At head, metrics requests are ignored. However, a branching codebase can choose to implement
//! their own handler which processes and uploads metrics requests as it sees fit, by setting the
//! appropriate RequestHandler.
mod controller;
mod event_types;
mod metrics_cleanup;
mod metrics_requests;
mod noop;
mod sys;
// Exports a <name>_proto module for each proto file
include!(concat!(env!("OUT_DIR"), "/metrics_protos/generated.rs"));
pub use controller::MetricsController;
pub use event_types::MetricEventType;
pub use metrics_cleanup::MetricsClientDestructor;
pub use noop::*;
pub use sys::*;
pub type RequestHandler = NoopMetricsRequestHandler;

View file

@ -1,22 +0,0 @@
// Copyright 2022 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.
//! Provides an tool for metrics client cleanup which may hold global state.
/// Ensures any cleanup necessary is performed on drop. Can be used to ensure cleanup is done
/// regardless of how the caller exits. Should be idempotent.
pub struct MetricsClientDestructor(Box<dyn FnMut()>);
impl MetricsClientDestructor {
pub fn new<T: 'static + FnMut()>(cleanup: T) -> Self {
MetricsClientDestructor(Box::new(cleanup))
}
/// A convenience method for immediately dropping self and invoking drop logic on the contained
/// object.
pub fn cleanup(self) {}
}
impl Drop for MetricsClientDestructor {
fn drop(&mut self) {
self.0();
}
}

View file

@ -1,47 +0,0 @@
// Copyright 2022 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.
//! Structs used to transport log requests between client processes and the logging controller
use crate::MetricEventType;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct LogMetric {
pub event_code: MetricEventType,
pub value: i64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct LogDescriptor {
pub event_code: MetricEventType,
pub descriptor: i64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct LogHighFrequencyDescriptorMetric {
pub event_code: MetricEventType,
pub descriptor: i64,
pub step: i64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct EventWithSerializedDetails {
pub event_code: MetricEventType,
pub serialized_details: Box<[u8]>,
}
#[derive(Serialize, Deserialize, Debug)]
pub enum MetricsRequest {
LogDescriptor(LogDescriptor),
LogEvent(MetricEventType),
LogMetric(LogMetric),
LogHistogram(LogMetric),
SetAuthToken(String),
SetGraphicsApi(String),
SetPackageName(String),
MergeSessionInvariants(Vec<u8>),
LogHighFrequencyDescriptorMetric(LogHighFrequencyDescriptorMetric),
LogEventWithSerializedDetails(EventWithSerializedDetails),
}

View file

@ -1,25 +0,0 @@
// Copyright 2022 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.
use crate::{event_details_proto::RecordDetails, MetricEventType, MetricsClientDestructor};
use base::Tube;
/// This interface exists to be used and re-implemented by downstream forks. Updates shouldn't be
/// done without ensuring they won't cause breakages in dependent codebases.
pub fn initialize(_: Tube) {}
#[cfg(test)]
pub fn force_initialize(_: Tube) {}
pub fn get_destructor() -> MetricsClientDestructor {
MetricsClientDestructor::new(|| {})
}
pub fn set_auth_token(_: &str) {}
pub fn set_graphics_api(_: &str) {}
pub fn set_package_name(_: &str) {}
pub fn merge_session_invariants(_: &[u8]) {}
pub fn log_descriptor(_: MetricEventType, _: i64) {}
pub fn log_event(_: MetricEventType) {}
pub fn log_metric(_: MetricEventType, _: i64) {}
pub fn log_histogram_metric(_: MetricEventType, _: i64) {}
pub fn log_high_frequency_descriptor_event(_: MetricEventType, _: i64, _: i64) {}
pub fn log_event_with_details(_: MetricEventType, _: &RecordDetails) {}

View file

@ -1,14 +0,0 @@
// Copyright 2022 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.
//! Provides noop implementations of metrics interfaces, to be used by builds which don't wish
//! to log metrics.
mod client;
mod periodic_logger;
mod request_handler;
pub use client::*;
pub use periodic_logger::PeriodicLogger;
pub use request_handler::NoopMetricsRequestHandler;

View file

@ -1,22 +0,0 @@
// Copyright 2022 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.
use crate::MetricEventType;
use std::result::Result;
use std::time::Duration;
/// A logging struct meant for use in tracking and periodically
/// logging a single metric. The metric is aggregated over the
/// designated time period. Intended for use with high-frequency metrics.
pub struct PeriodicLogger;
impl PeriodicLogger {
pub fn new(_event: MetricEventType, _period: Duration) -> Result<PeriodicLogger, String> {
Ok(PeriodicLogger)
}
/// Indicate the event has occurred with the given
/// value to be aggregated over the given time period.
pub fn log(&self, _value: i64) {}
}

View file

@ -1,15 +0,0 @@
// Copyright 2022 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.
use crate::controller::MetricsRequestHandler;
use crate::metrics_requests::MetricsRequest;
pub struct NoopMetricsRequestHandler;
impl MetricsRequestHandler for NoopMetricsRequestHandler {
fn new() -> Self {
NoopMetricsRequestHandler
}
fn handle_request(&self, _req: MetricsRequest) {}
fn shutdown(&self) {}
}

View file

@ -1,10 +0,0 @@
// Copyright 2022 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.
cfg_if::cfg_if! {
if #[cfg(windows)] {
pub(crate) mod windows;
pub use windows::*;
}
}

View file

@ -1,23 +0,0 @@
// Copyright 2022 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.
pub mod gpu_metrics;
pub(crate) mod system_metrics;
pub mod wmi;
pub use gpu_metrics::*;
pub const METRIC_UPLOAD_INTERVAL_SECONDS: i64 = 60;
pub const API_GUEST_ANGLE_VK_ENUM_NAME: &str = "API_GUEST_ANGLE_VK";
pub const API_HOST_ANGLE_D3D_ENUM_NAME: &str = "API_HOST_ANGLE_D3D";
pub const API_UNKNOWN_ENUM_NAME: &str = "API_UNKNOWN";
#[derive(Debug)]
pub enum Error {
CannotCloneEvent,
CannotInstantiateEvent,
InstanceAlreadyExists,
}
pub type Result<T> = std::result::Result<T, Error>;

View file

@ -1,52 +0,0 @@
// Copyright 2022 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.
use crate::windows::system_metrics::CoreWinMetrics;
use crate::windows::{Error, Result};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
static INSTANCE_EXISTS: AtomicBool = AtomicBool::new(false);
/// Used by gpu_display to show metrics in the CrosVm performance overlay.
pub struct Metrics {
metrics: Vec<Box<dyn ToString + Send + Sync>>,
// more_metrics is for metrics which have multiple owners (e.g., device dependent).
more_metrics: Vec<Arc<dyn ToString + Send + Sync>>,
}
impl Metrics {
pub fn new() -> Result<Self> {
if INSTANCE_EXISTS
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_err()
{
return Err(Error::InstanceAlreadyExists);
}
Ok(Metrics {
metrics: vec![
#[cfg(windows)]
Box::new(CoreWinMetrics::new()?),
],
more_metrics: vec![],
})
}
pub fn add_gpu_metrics(&mut self, t: Arc<dyn ToString + Send + Sync>) {
self.more_metrics.push(t);
}
pub fn get_metric_string(&self) -> String {
let mut buf = String::new();
for collector in self.metrics.iter() {
buf.push_str(&collector.to_string());
buf.push('\n');
}
for collector in self.more_metrics.iter() {
buf.push_str(&collector.to_string());
buf.push('\n');
}
buf
}
}

View file

@ -1,596 +0,0 @@
// Copyright 2022 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.
use crate::windows::{Error, Result, METRIC_UPLOAD_INTERVAL_SECONDS};
use crate::{log_metric, MetricEventType};
use base::{
error, AsRawDescriptor, Error as SysError, Event, FromRawDescriptor, PollToken, SafeDescriptor,
WaitContext,
};
use chrono::{DateTime, Local};
use std::fmt;
use std::mem;
use std::sync::{Arc, Mutex, Weak};
use std::thread;
use std::thread::JoinHandle;
use std::time::Duration;
use winapi::shared::minwindef::{DWORD, FILETIME};
use winapi::um::processthreadsapi::{GetProcessTimes, GetSystemTimes, OpenProcess};
use winapi::um::psapi::{
GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS, PROCESS_MEMORY_COUNTERS_EX,
};
use winapi::um::winbase::GetProcessIoCounters;
use winapi::um::winnt::{
IO_COUNTERS, LARGE_INTEGER, LONGLONG, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ,
SYNCHRONIZE,
};
const BYTES_PER_MB: usize = 1024 * 1024;
const WORKER_REPORT_INTERVAL: Duration = Duration::from_secs(1);
type SysResult<T> = std::result::Result<T, SysError>;
/// A worker job which periodically logs system metrics.
struct Worker {
exit_evt: Event,
io: Arc<Mutex<Option<ProcessIoRecord>>>,
measurements: Arc<Mutex<Option<Measurements>>>,
memory: Arc<Mutex<ProcessMemory>>,
memory_acc: Arc<Mutex<Option<ProcessMemoryAccumulated>>>,
metrics_string: Arc<Mutex<String>>,
}
impl Worker {
fn run(&mut self) {
#[derive(PollToken)]
enum Token {
Exit,
}
let event_ctx: WaitContext<Token> =
match WaitContext::build_with(&[(&self.exit_evt, Token::Exit)]) {
Ok(event_ctx) => event_ctx,
Err(e) => {
error!("failed creating WaitContext: {}", e);
return;
}
};
let mut last_metric_upload_time = Local::now();
'poll: loop {
let events = match event_ctx.wait_timeout(WORKER_REPORT_INTERVAL) {
Ok(events) => events,
Err(e) => {
error!("failed polling for events: {}", e);
return;
}
};
if events.is_empty() {
self.collect_metrics();
// Time budget for UI thread is very limited.
// We make the metric string for displaying in UI in the
// worker thread for best performance.
self.make_metrics_string();
self.upload_metrics(&mut last_metric_upload_time);
}
if events.into_iter().any(|e| e.is_readable) {
break 'poll;
}
}
}
fn make_metrics_string(&mut self) {
let mut metrics_string = self.metrics_string.lock().unwrap();
*metrics_string = format!(
"{}\n{}",
self.cpu_metrics_string(),
self.mem_metrics_string()
);
}
fn upload_metrics(&self, last_metric_upload_time: &mut DateTime<Local>) {
let time_elapsed = (Local::now() - *last_metric_upload_time).num_seconds();
if time_elapsed >= METRIC_UPLOAD_INTERVAL_SECONDS {
let mut memory_acc = self.memory_acc.lock().unwrap();
if let Some(acc) = &*memory_acc {
let mem = acc.accumulated.physical / acc.accumulated_count / BYTES_PER_MB;
// The i64 cast will not cause overflow because the mem is at most 10TB for
// Windows 10.
log_metric(MetricEventType::MemoryUsage, mem as i64);
}
*memory_acc = None;
let mut cpu_measurements = self.measurements.lock().unwrap();
if let Some(measurements) = &*cpu_measurements {
let sys_time = measurements.current.sys_time;
let process_time = measurements.current.process_time;
let prev_sys_time = measurements.last_upload.sys_time;
let prev_process_time = measurements.last_upload.process_time;
let diff_systime_kernel =
compute_filetime_subtraction(sys_time.kernel, prev_sys_time.kernel);
let diff_systime_user =
compute_filetime_subtraction(sys_time.user, prev_sys_time.user);
let diff_processtime_kernel =
compute_filetime_subtraction(process_time.kernel, prev_process_time.kernel);
let diff_processtime_user =
compute_filetime_subtraction(process_time.user, prev_process_time.user);
let total_systime = diff_systime_kernel + diff_systime_user;
let total_processtime = diff_processtime_kernel + diff_processtime_user;
if total_systime > 0 {
let cpu_usage = 100 * total_processtime / total_systime;
// The i64 cast will not cause overflow because the usage is at most 100.
log_metric(MetricEventType::CpuUsage, cpu_usage as i64);
}
}
*cpu_measurements = None;
let mut io = self.io.lock().unwrap();
if let Some(io_record) = &*io {
let new_io_read_bytes =
io_record.current.read_bytes - io_record.last_upload.read_bytes;
let new_io_write_bytes =
io_record.current.write_bytes - io_record.last_upload.write_bytes;
let ms_elapsed =
(io_record.current_time - io_record.last_upload_time).num_milliseconds();
if ms_elapsed > 0 {
let io_read_bytes_per_sec =
(new_io_read_bytes as f32) / (ms_elapsed as f32) * 1000.0;
let io_write_bytes_per_sec =
(new_io_write_bytes as f32) / (ms_elapsed as f32) * 1000.0;
log_metric(MetricEventType::ReadIo, io_read_bytes_per_sec as i64);
log_metric(MetricEventType::WriteIo, io_write_bytes_per_sec as i64);
}
}
*io = None;
*last_metric_upload_time = Local::now();
}
}
fn collect_metrics(&mut self) {
match self.get_cpu_metrics() {
Ok(new_measurement) => {
let mut measurements = self.measurements.lock().unwrap();
let next_measurements = match *measurements {
Some(Measurements {
current,
last_upload,
..
}) => Measurements {
current: new_measurement,
previous: current,
last_upload,
},
None => Measurements {
current: new_measurement,
previous: new_measurement,
last_upload: new_measurement,
},
};
*measurements = Some(next_measurements);
}
Err(e) => {
// Do not panic because of cpu query related failures.
error!("Get cpu measurement failed: {}", e);
}
}
match self.get_mem_metrics() {
Ok(mem) => {
// Keep running sum and count to calculate averages.
let mut memory_acc = self.memory_acc.lock().unwrap();
let updated_memory_acc = match *memory_acc {
Some(acc) => accumulate_process_memory(acc, mem),
None => ProcessMemoryAccumulated {
accumulated: mem,
accumulated_count: 1,
},
};
*memory_acc = Some(updated_memory_acc);
*self.memory.lock().unwrap() = mem
}
Err(e) => {
// Do not panic because of memory query failures.
error!("Get cpu measurement failed: {}", e);
}
}
match self.get_io_metrics() {
Ok(new_io) => {
let mut io_record = self.io.lock().unwrap();
let updated_io = match *io_record {
Some(io) => ProcessIoRecord {
current: new_io,
current_time: Local::now(),
last_upload: io.last_upload,
last_upload_time: io.last_upload_time,
},
None => ProcessIoRecord {
current: new_io,
current_time: Local::now(),
last_upload: new_io,
last_upload_time: Local::now(),
},
};
*io_record = Some(updated_io);
}
Err(e) => {
// Do not panic because of io query failures.
error!("Get io measurement failed: {}", e);
}
}
}
fn get_mem_metrics(&self) -> SysResult<ProcessMemory> {
let process_handle = CoreWinMetrics::get_process_handle()?;
let mut counters = PROCESS_MEMORY_COUNTERS_EX::default();
// Safe because we own the process handle and all memory was allocated.
let result = unsafe {
GetProcessMemoryInfo(
process_handle.as_raw_descriptor(),
// Converting is necessary because the `winapi`' GetProcessMemoryInfo
// does NOT support `PROCESS_MEMORY_COUNTERS_EX`, but only
// 'PROCESS_MEMORY_COUNTERS'. The casting is safe because the underlining
// Windows api does `PROCESS_MEMORY_COUNTERS_EX`.
&mut counters as *mut PROCESS_MEMORY_COUNTERS_EX as *mut PROCESS_MEMORY_COUNTERS,
mem::size_of::<PROCESS_MEMORY_COUNTERS_EX>() as DWORD,
)
};
if result == 0 {
return Err(SysError::last());
}
Ok(ProcessMemory {
page_fault_count: counters.PageFaultCount,
working_set_size: counters.WorkingSetSize,
working_set_peak: counters.PeakWorkingSetSize,
page_file_usage: counters.PagefileUsage,
page_file_peak: counters.PeakPagefileUsage,
physical: counters.PrivateUsage,
})
}
fn get_cpu_metrics(&self) -> SysResult<Measurement> {
let mut sys_time: SystemCpuTime = Default::default();
let mut process_time: ProcessCpuTime = Default::default();
let sys_time_success: i32;
// Safe because memory is allocated for sys_time before the windows call.
// And the value were initilized to 0s.
unsafe {
// First get kernel cpu time.
sys_time_success =
GetSystemTimes(&mut sys_time.idle, &mut sys_time.kernel, &mut sys_time.user);
}
if sys_time_success == 0 {
error!("Systime collection failed.\n");
return Err(SysError::last());
} else {
// Query current process cpu time.
let process_handle = CoreWinMetrics::get_process_handle()?;
let process_time_success: i32;
// Safe because memory is allocated for process_time before the windows call.
// And the value were initilized to 0s.
unsafe {
process_time_success = GetProcessTimes(
process_handle.as_raw_descriptor(),
&mut process_time.create,
&mut process_time.exit,
&mut process_time.kernel,
&mut process_time.user,
);
}
if process_time_success == 0 {
error!("Systime collection failed.\n");
return Err(SysError::last());
}
}
Ok(Measurement {
sys_time: SystemCpuTime {
idle: sys_time.idle,
kernel: sys_time.kernel,
user: sys_time.user,
},
process_time: ProcessCpuTime {
create: process_time.create,
exit: process_time.exit,
kernel: process_time.kernel,
user: process_time.user,
},
})
}
fn get_io_metrics(&self) -> SysResult<ProcessIo> {
let process_handle = CoreWinMetrics::get_process_handle()?;
let mut io_counters = IO_COUNTERS::default();
// Safe because we own the process handle and all memory was allocated.
let result = unsafe {
GetProcessIoCounters(
process_handle.as_raw_descriptor(),
&mut io_counters as *mut IO_COUNTERS,
)
};
if result == 0 {
return Err(SysError::last());
}
Ok(ProcessIo {
read_bytes: io_counters.ReadTransferCount,
write_bytes: io_counters.WriteTransferCount,
})
}
fn mem_metrics_string(&self) -> String {
let mut buf: String;
let guard = self.memory.lock().unwrap();
let memory: ProcessMemory = *guard;
buf = format!(
"Physical memory used: {} mb.\n",
memory.physical / BYTES_PER_MB
);
buf.push_str(&format!(
"Total working memory: {} mb.\n",
memory.working_set_size / BYTES_PER_MB
));
buf.push_str(&format!(
"Peak working memory: {} mb.\n",
memory.working_set_peak / BYTES_PER_MB
));
buf.push_str(&format!("Page fault count: {}.\n", memory.page_fault_count));
buf.push_str(&format!(
"Page file used: {} mb.\n",
memory.page_file_usage / BYTES_PER_MB
));
buf.push_str(&format!(
"Peak page file used: {} mb.\n",
memory.page_file_peak / BYTES_PER_MB
));
buf
}
fn cpu_metrics_string(&self) -> String {
let guard = self.measurements.lock().unwrap();
let mut buf = String::new();
// Now we use current and last cpu measurment data to calculate cpu usage
// as a percentage.
if let Some(measurements) = &*guard {
let sys_time = measurements.current.sys_time;
let process_time = measurements.current.process_time;
let prev_sys_time = measurements.previous.sys_time;
let prev_process_time = measurements.previous.process_time;
let diff_systime_kernel =
compute_filetime_subtraction(sys_time.kernel, prev_sys_time.kernel);
let diff_systime_user = compute_filetime_subtraction(sys_time.user, prev_sys_time.user);
let diff_processtime_kernel =
compute_filetime_subtraction(process_time.kernel, prev_process_time.kernel);
let diff_processtime_user =
compute_filetime_subtraction(process_time.user, prev_process_time.user);
let total_systime = diff_systime_kernel + diff_systime_user;
let total_processtime = diff_processtime_kernel + diff_processtime_user;
let mut process_cpu = String::from("still calculating...");
if total_systime > 0 {
process_cpu = format!("{}%", (100 * total_processtime / total_systime));
}
buf.push_str(&format!("Process cpu usage is: {}\n", process_cpu));
#[cfg(debug_assertions)]
{
// Show data supporting our cpu usage calculation.
// Output system cpu time.
buf.push_str(&format!(
"Systime Idle: low {} / high {}\n",
sys_time.idle.dwLowDateTime, sys_time.idle.dwHighDateTime
));
buf.push_str(&format!(
"Systime User: low {} / high {}\n",
sys_time.user.dwLowDateTime, sys_time.user.dwHighDateTime
));
buf.push_str(&format!(
"Systime kernel: low {} / high {}\n",
sys_time.kernel.dwLowDateTime, sys_time.kernel.dwHighDateTime
));
// Output process cpu time.
buf.push_str(&format!(
"Process Create: low {} / high {}\n",
process_time.create.dwLowDateTime, process_time.create.dwHighDateTime
));
buf.push_str(&format!(
"Process Exit: low {} / high {}\n",
process_time.exit.dwLowDateTime, process_time.exit.dwHighDateTime
));
buf.push_str(&format!(
"Process kernel: low {} / high {}\n",
process_time.kernel.dwLowDateTime, process_time.kernel.dwHighDateTime
));
buf.push_str(&format!(
"Process user: low {} / high {}\n",
process_time.user.dwLowDateTime, process_time.user.dwHighDateTime
));
}
} else {
buf.push_str("Calculating cpu usage...");
}
buf
}
}
fn compute_filetime_subtraction(fta: FILETIME, ftb: FILETIME) -> LONGLONG {
// safe because we are initializing the struct to 0s.
unsafe {
let mut a: LARGE_INTEGER = mem::zeroed::<LARGE_INTEGER>();
a.u_mut().LowPart = fta.dwLowDateTime;
a.u_mut().HighPart = fta.dwHighDateTime as i32;
let mut b: LARGE_INTEGER = mem::zeroed::<LARGE_INTEGER>();
b.u_mut().LowPart = ftb.dwLowDateTime;
b.u_mut().HighPart = ftb.dwHighDateTime as i32;
a.QuadPart() - b.QuadPart()
}
}
// Adds to a running total of memory metrics over the course of a collection period.
// Can divide these sums to calculate averages.
fn accumulate_process_memory(
acc: ProcessMemoryAccumulated,
mem: ProcessMemory,
) -> ProcessMemoryAccumulated {
ProcessMemoryAccumulated {
accumulated: ProcessMemory {
page_fault_count: mem.page_fault_count,
working_set_size: acc.accumulated.working_set_size + mem.working_set_size,
working_set_peak: mem.working_set_peak,
page_file_usage: acc.accumulated.page_file_usage + mem.page_file_usage,
page_file_peak: mem.page_file_peak,
physical: acc.accumulated.physical + mem.physical,
},
accumulated_count: acc.accumulated_count + 1,
}
}
#[derive(Copy, Clone, Default)]
struct SystemCpuTime {
idle: FILETIME,
kernel: FILETIME,
user: FILETIME,
}
#[derive(Copy, Clone, Default)]
struct ProcessCpuTime {
create: FILETIME,
exit: FILETIME,
kernel: FILETIME,
user: FILETIME,
}
#[derive(Copy, Clone, Default)]
struct ProcessMemory {
page_fault_count: u32,
working_set_size: usize,
working_set_peak: usize,
page_file_usage: usize,
page_file_peak: usize,
physical: usize,
}
#[derive(Copy, Clone)]
struct ProcessMemoryAccumulated {
accumulated: ProcessMemory,
accumulated_count: usize,
}
#[derive(Copy, Clone, Default)]
struct ProcessIo {
read_bytes: u64,
write_bytes: u64,
}
#[derive(Copy, Clone)]
struct ProcessIoRecord {
current: ProcessIo,
current_time: DateTime<Local>,
last_upload: ProcessIo,
last_upload_time: DateTime<Local>,
}
#[derive(Copy, Clone)]
struct Measurement {
sys_time: SystemCpuTime,
process_time: ProcessCpuTime,
}
struct Measurements {
current: Measurement,
previous: Measurement,
last_upload: Measurement,
}
/// A managing struct for a job which defines regular logging of core Windows system metrics.
pub(crate) struct CoreWinMetrics {
metrics_string: Weak<Mutex<String>>,
exit_evt: Event,
worker_thread: Option<JoinHandle<()>>,
}
impl CoreWinMetrics {
pub fn new() -> Result<Self> {
let exit_evt = match Event::new() {
Ok(evt) => evt,
Err(_e) => return Err(Error::CannotInstantiateEvent),
};
let metrics_string = String::new();
let arc_metrics_memory = Arc::new(Mutex::new(metrics_string));
let weak_metrics_memory = Arc::downgrade(&arc_metrics_memory);
let mut me = Self {
metrics_string: weak_metrics_memory,
exit_evt,
worker_thread: None,
};
let exit_evt_clone = match me.exit_evt.try_clone() {
Ok(evt) => evt,
Err(_) => return Err(Error::CannotCloneEvent),
};
me.worker_thread.replace(thread::spawn(|| {
Worker {
exit_evt: exit_evt_clone,
io: Arc::new(Mutex::new(None)),
measurements: Arc::new(Mutex::new(None)),
memory: Arc::new(Mutex::new(Default::default())),
memory_acc: Arc::new(Mutex::new(None)),
metrics_string: arc_metrics_memory,
}
.run();
}));
Ok(me)
}
fn get_process_handle() -> SysResult<SafeDescriptor> {
// Safe because we own the current process.
let process_handle = unsafe {
OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ | SYNCHRONIZE,
0,
std::process::id(),
)
};
if process_handle.is_null() {
return Err(SysError::last());
}
// Safe as the SafeDescriptor is the only thing with access to the handle after this.
Ok(unsafe { SafeDescriptor::from_raw_descriptor(process_handle) })
}
}
impl Drop for CoreWinMetrics {
fn drop(&mut self) {
if let Some(join_handle) = self.worker_thread.take() {
let _ = self.exit_evt.write(1);
join_handle
.join()
.expect("fail to join the worker thread of a win core metrics collector.");
}
}
}
impl fmt::Display for CoreWinMetrics {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.metrics_string.upgrade() {
Some(metrics_string) => write!(f, "{}", *metrics_string.lock().unwrap()),
None => write!(f, ""),
}
}
}

View file

@ -1,241 +0,0 @@
// Copyright 2022 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.
/*
The struct must be named in non_camel and non_snake because we want to query the windows wmi
interface and conform to the windows naming convension.
*/
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use serde::{de::DeserializeOwned, Deserialize};
use {
base::warn,
std::{collections::HashMap, error::Error, rc::Rc},
wmi::{query::FilterValue, COMLibrary, WMIConnection},
};
const VIDEO_CONTROLLER_AVAILABILITY_ENABLED: i64 = 3;
#[derive(Deserialize, Debug)]
pub struct Win32_Processor {
pub Manufacturer: String,
pub Name: String,
pub NumberOfCores: u32,
pub NumberOfLogicalProcessors: u32,
pub ThreadCount: u32,
}
#[derive(Deserialize, Debug)]
pub struct Win32_VideoController {
pub Name: String,
// TODO(b/191406729): re-enable.
// pub AdapterRAM: u64,
pub DriverVersion: String,
pub Availability: u16,
pub Description: String,
}
#[derive(Deserialize, Debug)]
struct MSFT_Partition {
DriveLetter: String,
}
#[derive(Deserialize, Debug)]
struct MSFT_Disk {
__Path: String,
UniqueId: String,
}
#[derive(Deserialize, Debug)]
struct MSFT_DiskToPartition {}
#[derive(Deserialize, Debug)]
struct MSFT_PhysicalDisk {
FriendlyName: String,
MediaType: u16,
BusType: u16,
Size: u64,
UniqueId: String,
}
// Keep the formatting so that the debug output string matches the proto field
// values.
#[derive(Debug)]
pub enum MediaType {
UNKNOWN,
HDD,
SSD,
SCM,
}
impl From<u16> for MediaType {
fn from(value: u16) -> Self {
match value {
3 => MediaType::HDD,
4 => MediaType::SSD,
5 => MediaType::SCM,
_ => MediaType::UNKNOWN,
}
}
}
// Keep the formatting so that the debug output string matches the proto field
// values.
#[derive(Debug)]
pub enum BusType {
UNKNOWN,
SCSI,
ATAPI,
ATA,
TYPE_1394,
SSA,
FIBRE_CHANNEL,
USB,
RAID,
ISCSI,
SAS,
SATA,
SD,
MMC,
FILE_BACKED_VIRTUAL,
STORAGE_SPACES,
NVME,
}
impl From<u16> for BusType {
fn from(value: u16) -> Self {
match value {
1 => BusType::SCSI,
2 => BusType::ATAPI,
3 => BusType::ATA,
4 => BusType::TYPE_1394,
5 => BusType::SSA,
6 => BusType::FIBRE_CHANNEL,
7 => BusType::USB,
8 => BusType::RAID,
9 => BusType::ISCSI,
10 => BusType::SAS,
11 => BusType::SATA,
12 => BusType::SD,
13 => BusType::MMC,
15 => BusType::FILE_BACKED_VIRTUAL,
16 => BusType::STORAGE_SPACES,
17 => BusType::NVME,
_ => BusType::UNKNOWN,
}
}
}
// Friendly format for MSFT_PhysicalDisk.
// Also includes the cross-referenced partitions within the disk.
#[derive(Debug)]
pub struct PhysicalDisk {
pub Name: String,
pub MediaType: MediaType,
pub BusType: BusType,
pub Size: u64,
pub DriveLetters: Vec<String>,
}
#[derive(Deserialize, Debug)]
pub struct Win32_PhysicalMemory {
pub Capacity: u64,
pub ConfiguredClockSpeed: u32,
pub PartNumber: String,
}
#[derive(Clone, Deserialize, Debug)]
pub struct Win32_BaseBoard {
pub Manufacturer: String,
pub Product: String,
}
#[derive(Debug)]
pub struct WmiMetrics {
pub cpus: Vec<Win32_Processor>,
pub gpus: Vec<Win32_VideoController>,
pub disks: Vec<PhysicalDisk>,
pub mems: Vec<Win32_PhysicalMemory>,
pub motherboard: Option<Win32_BaseBoard>,
}
pub fn get_wmi_metrics() -> Result<WmiMetrics, Box<dyn Error>> {
let com_con = Rc::new(COMLibrary::new()?);
let wmi_con = WMIConnection::new(Rc::clone(&com_con))?;
// Fetch WMI data, including all entries.
let cpus: Vec<Win32_Processor> = run_wmi_query(&wmi_con);
let disks = get_disks(Rc::clone(&com_con))?;
let mems: Vec<Win32_PhysicalMemory> = run_wmi_query(&wmi_con);
let motherboard: Option<Win32_BaseBoard> = run_wmi_query(&wmi_con).into_iter().next();
let gpus = get_gpus(&wmi_con);
let wmi_metrics = WmiMetrics {
cpus,
gpus,
disks,
mems,
motherboard,
};
Ok(wmi_metrics)
}
fn get_disks(com_con: Rc<COMLibrary>) -> Result<Vec<PhysicalDisk>, Box<dyn Error>> {
// For MSFT_PhysicalDisk, we need to connect with storage namespace.
let wmi_con = WMIConnection::with_namespace_path("Root\\Microsoft\\Windows\\Storage", com_con)?;
// First we get all instances of following classes:
// MSFT_Disk, MSFT_PhysicalDisk
// We use the WMI associator query to find mapping for each:
// MSFT_Disk -> MSFT_Partition (1:N)
// Then, we find the mapping from each:
// MSFT_Disk -> MSFT_PhysicalDisk (1:1)
// Finally, we construct each PhysicalDisk structure by combining the
// matched MSFT_PhysicalDisk and MSFT_Parition instances.
let msft_disks: Vec<MSFT_Disk> = run_wmi_query(&wmi_con);
let physical_disks: Vec<MSFT_PhysicalDisk> = run_wmi_query(&wmi_con);
let mut disks = Vec::with_capacity(physical_disks.len());
for msft_disk in msft_disks {
let partitions =
wmi_con.associators::<MSFT_Partition, MSFT_DiskToPartition>(&msft_disk.__Path)?;
let physical_disk = physical_disks
.iter()
.find(|d| d.UniqueId == msft_disk.UniqueId)
.ok_or("Could not find a matching MSFT_PhysicalDisk!")?;
disks.push(PhysicalDisk {
Name: physical_disk.FriendlyName.clone(),
MediaType: physical_disk.MediaType.into(),
BusType: physical_disk.BusType.into(),
Size: physical_disk.Size,
DriveLetters: partitions.into_iter().map(|p| p.DriveLetter).collect(),
});
}
Ok(disks)
}
fn get_gpus(wmi_con: &WMIConnection) -> Vec<Win32_VideoController> {
// TODO(b/191406729): Fix the query once the AdapterRAM can be correctly
// queried.
let mut filters = HashMap::new();
filters.insert(
"Availability".to_string(),
FilterValue::Number(VIDEO_CONTROLLER_AVAILABILITY_ENABLED),
);
wmi_con
.filtered_query(&filters)
.map_err(|e| warn!("wmi query failed: {}", e))
.unwrap_or_default()
}
fn run_wmi_query<T>(wmi_con: &WMIConnection) -> Vec<T>
where
T: DeserializeOwned,
{
wmi_con
.query()
.map_err(|e| warn!("wmi query failed: {}", e))
.unwrap_or_default()
}