summary refs log tree commit diff
path: root/crosvm_plugin
diff options
context:
space:
mode:
authorMatt Delco <delco@chromium.org>2019-10-01 18:10:37 -0700
committerCommit Bot <commit-bot@chromium.org>2019-11-02 03:56:58 +0000
commitfdcfd1d4398d4632333c0d3ed619539251d9986b (patch)
treed73d655932401ca2c0118088d3050fed9b9b24e0 /crosvm_plugin
parentc2d221804f2ba559d68e7fc9676e833d703cd9ee (diff)
downloadcrosvm-fdcfd1d4398d4632333c0d3ed619539251d9986b.tar
crosvm-fdcfd1d4398d4632333c0d3ed619539251d9986b.tar.gz
crosvm-fdcfd1d4398d4632333c0d3ed619539251d9986b.tar.bz2
crosvm-fdcfd1d4398d4632333c0d3ed619539251d9986b.tar.lz
crosvm-fdcfd1d4398d4632333c0d3ed619539251d9986b.tar.xz
crosvm-fdcfd1d4398d4632333c0d3ed619539251d9986b.tar.zst
crosvm-fdcfd1d4398d4632333c0d3ed619539251d9986b.zip
crosvm: compile out stats
The stats are typically alive in the plugin library, but we typically
don't care about or use these stats.

BUG=None.
TEST=Compiled and ran test.  Verified release binary size got smaller
by 25KB and debug by 50KB.

Change-Id: I2469ff83f91a3aebf86d70807088bba3edce6641
Signed-off-by: Matt Delco <delco@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1835034
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'crosvm_plugin')
-rw-r--r--crosvm_plugin/Cargo.toml3
-rw-r--r--crosvm_plugin/src/lib.rs196
-rw-r--r--crosvm_plugin/src/stats.rs84
3 files changed, 156 insertions, 127 deletions
diff --git a/crosvm_plugin/Cargo.toml b/crosvm_plugin/Cargo.toml
index 6805898..b9abc02 100644
--- a/crosvm_plugin/Cargo.toml
+++ b/crosvm_plugin/Cargo.toml
@@ -4,6 +4,9 @@ version = "0.17.0"
 authors = ["The Chromium OS Authors"]
 edition = "2018"
 
+[features]
+stats = []
+
 [lib]
 crate-type = ["cdylib"]
 
diff --git a/crosvm_plugin/src/lib.rs b/crosvm_plugin/src/lib.rs
index fa6ce2f..8def28d 100644
--- a/crosvm_plugin/src/lib.rs
+++ b/crosvm_plugin/src/lib.rs
@@ -28,7 +28,6 @@ use std::slice;
 use std::slice::{from_raw_parts, from_raw_parts_mut};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::sync::Arc;
-use std::time::Instant;
 
 use libc::{E2BIG, EINVAL, ENOENT, ENOTCONN, EPROTO};
 
@@ -46,6 +45,9 @@ use kvm_sys::{
 
 use protos::plugin::*;
 
+#[cfg(feature = "stats")]
+mod stats;
+
 // Needs to be large enough to receive all the VCPU sockets.
 const MAX_DATAGRAM_FD: usize = 32;
 // Needs to be large enough for a sizable dirty log.
@@ -155,7 +157,7 @@ impl IdAllocator {
 
 #[repr(u8)]
 #[derive(Debug, Clone, Copy)]
-enum Stat {
+pub enum Stat {
     IoEventFd,
     MemoryGetDirtyLog,
     IrqEventGetFd,
@@ -210,84 +212,29 @@ enum Stat {
     Count,
 }
 
-#[derive(Clone, Copy)]
-struct StatEntry {
-    count: u64,
-    total: u64,
-    max: u64,
-}
-
-struct StatUpdater {
-    idx: usize,
-    start: Instant,
-}
-
-struct GlobalStats {
-    entries: [StatEntry; Stat::Count as usize],
+#[cfg(feature = "stats")]
+fn record(a: Stat) -> stats::StatUpdater {
+    unsafe { stats::STATS.record(a) }
 }
 
-static mut STATS: GlobalStats = GlobalStats {
-    entries: [StatEntry {
-        count: 0,
-        total: 0,
-        max: 0,
-    }; Stat::Count as usize],
-};
-
-impl GlobalStats {
-    // Record latency from this call until the end of block/function
-    // Example:
-    // pub fn foo() {
-    //     let _u = STATS.record(Stat::Foo);
-    //     // ... some operation ...
-    // }
-    // The added STATS.record will record latency of "some operation" and will
-    // update max and average latencies for it under Stats::Foo. Subsequent
-    // call to STATS.print() will print out max and average latencies for all
-    // operations that were performed.
-    fn record(&mut self, idx: Stat) -> StatUpdater {
-        StatUpdater {
-            idx: idx as usize,
-            start: Instant::now(),
-        }
-    }
-
-    fn print(&self) {
-        for idx in 0..Stat::Count as usize {
-            let e = &self.entries[idx as usize];
-            let stat = unsafe { std::mem::transmute::<u8, Stat>(idx as u8) };
-            if e.count > 0 {
-                println!(
-                    "Stat::{:?}: avg {}ns max {}ns",
-                    stat,
-                    e.total / e.count,
-                    e.max
-                );
-            }
-        }
-    }
-
-    fn update(&mut self, idx: usize, elapsed_nanos: u64) {
-        let e = &mut self.entries[idx as usize];
-        e.total += elapsed_nanos;
-        if e.max < elapsed_nanos {
-            e.max = elapsed_nanos;
-        }
-        e.count += 1;
-    }
+#[cfg(not(feature = "stats"))]
+fn record(_a: Stat) -> u32 {
+    0
 }
 
-impl Drop for StatUpdater {
-    fn drop(&mut self) {
-        let elapsed = self.start.elapsed();
-        let elapsed_nanos = elapsed.as_secs() * 1000000000 + elapsed.subsec_nanos() as u64;
-        // Unsafe due to racy access - OK for stats
+#[cfg(feature = "stats")]
+fn printstats() {
+    // Unsafe due to racy access - OK for stats
+    if std::env::var("CROSVM_STATS").is_ok() {
         unsafe {
-            STATS.update(self.idx, elapsed_nanos);
+            stats::STATS.print();
         }
     }
 }
 
+#[cfg(not(feature = "stats"))]
+fn printstats() {}
+
 pub struct crosvm {
     id_allocator: Arc<IdAllocator>,
     socket: UnixDatagram,
@@ -378,12 +325,7 @@ impl crosvm {
         r.mut_destroy().id = id;
         self.main_transaction(&r, &[])?;
         self.get_id_allocator().free(id);
-        // Unsafe due to racy access - OK for stats
-        if std::env::var("CROSVM_STATS").is_ok() {
-            unsafe {
-                STATS.print();
-            }
-        }
+        printstats();
         Ok(())
     }
 
@@ -813,7 +755,7 @@ impl_ctor_dtor!(
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_io_event_fd(this: *mut crosvm_io_event) -> c_int {
-    let _u = STATS.record(Stat::IoEventFd);
+    let _u = record(Stat::IoEventFd);
     (*this).evt.as_raw_fd()
 }
 
@@ -886,7 +828,7 @@ pub unsafe extern "C" fn crosvm_memory_get_dirty_log(
     this: *mut crosvm_memory,
     log: *mut u8,
 ) -> c_int {
-    let _u = STATS.record(Stat::MemoryGetDirtyLog);
+    let _u = record(Stat::MemoryGetDirtyLog);
     let crosvm = &mut *crosvm;
     let this = &mut *this;
     let log_slice = slice::from_raw_parts_mut(log, dirty_log_bitmap_size(this.length as usize));
@@ -948,13 +890,13 @@ impl_ctor_dtor!(
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_irq_event_get_fd(this: *mut crosvm_irq_event) -> c_int {
-    let _u = STATS.record(Stat::IrqEventGetFd);
+    let _u = record(Stat::IrqEventGetFd);
     (*this).trigger_evt.as_raw_fd()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_irq_event_get_resample_fd(this: *mut crosvm_irq_event) -> c_int {
-    let _u = STATS.record(Stat::IrqEventGetResampleFd);
+    let _u = record(Stat::IrqEventGetResampleFd);
     (*this).resample_evt.as_raw_fd()
 }
 
@@ -1276,7 +1218,7 @@ fn to_crosvm_rc<T>(r: result::Result<T, c_int>) -> c_int {
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_connect(out: *mut *mut crosvm) -> c_int {
-    let _u = STATS.record(Stat::Connect);
+    let _u = record(Stat::Connect);
     let socket_name = match env::var("CROSVM_SOCKET") {
         Ok(v) => v,
         _ => return -ENOTCONN,
@@ -1299,7 +1241,7 @@ pub unsafe extern "C" fn crosvm_connect(out: *mut *mut crosvm) -> c_int {
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_new_connection(self_: *mut crosvm, out: *mut *mut crosvm) -> c_int {
-    let _u = STATS.record(Stat::NewConnection);
+    let _u = record(Stat::NewConnection);
     let self_ = &mut (*self_);
     match self_.try_clone() {
         Ok(cloned) => {
@@ -1312,7 +1254,7 @@ pub unsafe extern "C" fn crosvm_new_connection(self_: *mut crosvm, out: *mut *mu
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_destroy_connection(self_: *mut *mut crosvm) -> c_int {
-    let _u = STATS.record(Stat::DestroyConnection);
+    let _u = record(Stat::DestroyConnection);
     Box::from_raw(*self_);
     *self_ = null_mut();
     0
@@ -1320,7 +1262,7 @@ pub unsafe extern "C" fn crosvm_destroy_connection(self_: *mut *mut crosvm) -> c
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_get_shutdown_eventfd(self_: *mut crosvm) -> c_int {
-    let _u = STATS.record(Stat::GetShutdownEventFd);
+    let _u = record(Stat::GetShutdownEventFd);
     let self_ = &mut (*self_);
     match self_.get_shutdown_eventfd() {
         Ok(f) => f.into_raw_fd(),
@@ -1334,7 +1276,7 @@ pub unsafe extern "C" fn crosvm_check_extension(
     extension: u32,
     has_extension: *mut bool,
 ) -> c_int {
-    let _u = STATS.record(Stat::CheckExtentsion);
+    let _u = record(Stat::CheckExtentsion);
     let self_ = &mut (*self_);
     let ret = self_.check_extension(extension);
 
@@ -1351,7 +1293,7 @@ pub unsafe extern "C" fn crosvm_get_supported_cpuid(
     cpuid_entries: *mut kvm_cpuid_entry2,
     out_count: *mut u32,
 ) -> c_int {
-    let _u = STATS.record(Stat::GetSupportedCpuid);
+    let _u = record(Stat::GetSupportedCpuid);
     let this = &mut *this;
     let cpuid_entries = from_raw_parts_mut(cpuid_entries, entry_count as usize);
     let mut cpuid_count: usize = 0;
@@ -1367,7 +1309,7 @@ pub unsafe extern "C" fn crosvm_get_emulated_cpuid(
     cpuid_entries: *mut kvm_cpuid_entry2,
     out_count: *mut u32,
 ) -> c_int {
-    let _u = STATS.record(Stat::GetEmulatedCpuid);
+    let _u = record(Stat::GetEmulatedCpuid);
     let this = &mut *this;
     let cpuid_entries = from_raw_parts_mut(cpuid_entries, entry_count as usize);
     let mut cpuid_count: usize = 0;
@@ -1383,7 +1325,7 @@ pub unsafe extern "C" fn crosvm_get_msr_index_list(
     msr_indices: *mut u32,
     out_count: *mut u32,
 ) -> c_int {
-    let _u = STATS.record(Stat::GetMsrIndexList);
+    let _u = record(Stat::GetMsrIndexList);
     let this = &mut *this;
     let msr_indices = from_raw_parts_mut(msr_indices, entry_count as usize);
     let mut msr_count: usize = 0;
@@ -1397,7 +1339,7 @@ pub unsafe extern "C" fn crosvm_net_get_config(
     self_: *mut crosvm,
     config: *mut crosvm_net_config,
 ) -> c_int {
-    let _u = STATS.record(Stat::NetGetConfig);
+    let _u = record(Stat::NetGetConfig);
     let self_ = &mut (*self_);
     let ret = self_.get_net_config();
 
@@ -1415,7 +1357,7 @@ pub unsafe extern "C" fn crosvm_reserve_range(
     start: u64,
     length: u64,
 ) -> c_int {
-    let _u = STATS.record(Stat::ReserveRange);
+    let _u = record(Stat::ReserveRange);
     let self_ = &mut (*self_);
     let ret = self_.reserve_range(space, start, length);
     to_crosvm_rc(ret)
@@ -1423,7 +1365,7 @@ pub unsafe extern "C" fn crosvm_reserve_range(
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_set_irq(self_: *mut crosvm, irq_id: u32, active: bool) -> c_int {
-    let _u = STATS.record(Stat::SetIrq);
+    let _u = record(Stat::SetIrq);
     let self_ = &mut (*self_);
     let ret = self_.set_irq(irq_id, active);
     to_crosvm_rc(ret)
@@ -1435,7 +1377,7 @@ pub unsafe extern "C" fn crosvm_set_irq_routing(
     route_count: u32,
     routes: *const crosvm_irq_route,
 ) -> c_int {
-    let _u = STATS.record(Stat::SetIrqRouting);
+    let _u = record(Stat::SetIrqRouting);
     let self_ = &mut (*self_);
     let ret = self_.set_irq_routing(slice::from_raw_parts(routes, route_count as usize));
     to_crosvm_rc(ret)
@@ -1447,7 +1389,7 @@ pub unsafe extern "C" fn crosvm_set_hypercall_hint(
     hints_count: u32,
     hints: *const crosvm_hint,
 ) -> c_int {
-    let _u = STATS.record(Stat::SetHypercallHint);
+    let _u = record(Stat::SetHypercallHint);
     let self_ = &mut (*self_);
 
     if hints_count < 1 {
@@ -1482,7 +1424,7 @@ pub unsafe extern "C" fn crosvm_get_pic_state(
     primary: bool,
     state: *mut kvm_pic_state,
 ) -> c_int {
-    let _u = STATS.record(Stat::GetPicState);
+    let _u = record(Stat::GetPicState);
     let this = &mut *this;
     let state_set = if primary {
         MainRequest_StateSet::PIC0
@@ -1500,7 +1442,7 @@ pub unsafe extern "C" fn crosvm_set_pic_state(
     primary: bool,
     state: *mut kvm_pic_state,
 ) -> c_int {
-    let _u = STATS.record(Stat::SetPicState);
+    let _u = record(Stat::SetPicState);
     let this = &mut *this;
     let state_set = if primary {
         MainRequest_StateSet::PIC0
@@ -1517,7 +1459,7 @@ pub unsafe extern "C" fn crosvm_get_ioapic_state(
     this: *mut crosvm,
     state: *mut kvm_ioapic_state,
 ) -> c_int {
-    let _u = STATS.record(Stat::GetIoapicState);
+    let _u = record(Stat::GetIoapicState);
     let this = &mut *this;
     let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_ioapic_state>());
     let ret = this.get_state(MainRequest_StateSet::IOAPIC, state);
@@ -1529,7 +1471,7 @@ pub unsafe extern "C" fn crosvm_set_ioapic_state(
     this: *mut crosvm,
     state: *const kvm_ioapic_state,
 ) -> c_int {
-    let _u = STATS.record(Stat::SetIoapicState);
+    let _u = record(Stat::SetIoapicState);
     let this = &mut *this;
     let state = from_raw_parts(state as *mut u8, size_of::<kvm_ioapic_state>());
     let ret = this.set_state(MainRequest_StateSet::IOAPIC, state);
@@ -1541,7 +1483,7 @@ pub unsafe extern "C" fn crosvm_get_pit_state(
     this: *mut crosvm,
     state: *mut kvm_pit_state2,
 ) -> c_int {
-    let _u = STATS.record(Stat::GetPitState);
+    let _u = record(Stat::GetPitState);
     let this = &mut *this;
     let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_pit_state2>());
     let ret = this.get_state(MainRequest_StateSet::PIT, state);
@@ -1553,7 +1495,7 @@ pub unsafe extern "C" fn crosvm_set_pit_state(
     this: *mut crosvm,
     state: *const kvm_pit_state2,
 ) -> c_int {
-    let _u = STATS.record(Stat::SetPitState);
+    let _u = record(Stat::SetPitState);
     let this = &mut *this;
     let state = from_raw_parts(state as *mut u8, size_of::<kvm_pit_state2>());
     let ret = this.set_state(MainRequest_StateSet::PIT, state);
@@ -1565,7 +1507,7 @@ pub unsafe extern "C" fn crosvm_get_clock(
     this: *mut crosvm,
     clock_data: *mut kvm_clock_data,
 ) -> c_int {
-    let _u = STATS.record(Stat::GetClock);
+    let _u = record(Stat::GetClock);
     let this = &mut *this;
     let state = from_raw_parts_mut(clock_data as *mut u8, size_of::<kvm_clock_data>());
     let ret = this.get_state(MainRequest_StateSet::CLOCK, state);
@@ -1577,7 +1519,7 @@ pub unsafe extern "C" fn crosvm_set_clock(
     this: *mut crosvm,
     clock_data: *const kvm_clock_data,
 ) -> c_int {
-    let _u = STATS.record(Stat::SetClock);
+    let _u = record(Stat::SetClock);
     let this = &mut *this;
     let state = from_raw_parts(clock_data as *mut u8, size_of::<kvm_clock_data>());
     let ret = this.set_state(MainRequest_StateSet::CLOCK, state);
@@ -1586,7 +1528,7 @@ pub unsafe extern "C" fn crosvm_set_clock(
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_set_identity_map_addr(self_: *mut crosvm, addr: u32) -> c_int {
-    let _u = STATS.record(Stat::SetIdentityMapAddr);
+    let _u = record(Stat::SetIdentityMapAddr);
     let self_ = &mut (*self_);
     let ret = self_.set_identity_map_addr(addr);
     to_crosvm_rc(ret)
@@ -1598,7 +1540,7 @@ pub unsafe extern "C" fn crosvm_pause_vcpus(
     cpu_mask: u64,
     user: *mut c_void,
 ) -> c_int {
-    let _u = STATS.record(Stat::PauseVcpus);
+    let _u = record(Stat::PauseVcpus);
     let self_ = &mut (*self_);
     let ret = self_.pause_vcpus(cpu_mask, user);
     to_crosvm_rc(ret)
@@ -1606,7 +1548,7 @@ pub unsafe extern "C" fn crosvm_pause_vcpus(
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_start(self_: *mut crosvm) -> c_int {
-    let _u = STATS.record(Stat::Start);
+    let _u = record(Stat::Start);
     let self_ = &mut (*self_);
     let ret = self_.start();
     to_crosvm_rc(ret)
@@ -1618,7 +1560,7 @@ pub unsafe extern "C" fn crosvm_get_vcpu(
     cpu_id: u32,
     out: *mut *mut crosvm_vcpu,
 ) -> c_int {
-    let _u = STATS.record(Stat::GetVcpu);
+    let _u = record(Stat::GetVcpu);
     let self_ = &mut (*self_);
     let ret = self_.get_vcpu(cpu_id);
 
@@ -1633,7 +1575,7 @@ pub unsafe extern "C" fn crosvm_vcpu_wait(
     this: *mut crosvm_vcpu,
     event: *mut crosvm_vcpu_event,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuWait);
+    let _u = record(Stat::VcpuWait);
     let this = &mut *this;
     let event = &mut *event;
     let ret = this.wait(event);
@@ -1642,7 +1584,7 @@ pub unsafe extern "C" fn crosvm_vcpu_wait(
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_vcpu_resume(this: *mut crosvm_vcpu) -> c_int {
-    let _u = STATS.record(Stat::VcpuResume);
+    let _u = record(Stat::VcpuResume);
     let this = &mut *this;
     let ret = this.resume();
     to_crosvm_rc(ret)
@@ -1653,7 +1595,7 @@ pub unsafe extern "C" fn crosvm_vcpu_get_regs(
     this: *mut crosvm_vcpu,
     regs: *mut kvm_regs,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuGetRegs);
+    let _u = record(Stat::VcpuGetRegs);
     let this = &mut *this;
     if this.regs.set {
         if let Err(e) = this.set_state_from_cache(VcpuRequest_StateSet::REGS) {
@@ -1675,7 +1617,7 @@ pub unsafe extern "C" fn crosvm_vcpu_set_regs(
     this: *mut crosvm_vcpu,
     regs: *const kvm_regs,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuSetRegs);
+    let _u = record(Stat::VcpuSetRegs);
     let this = &mut *this;
     this.regs.get = false;
     let regs = from_raw_parts(regs as *mut u8, size_of::<kvm_regs>());
@@ -1689,7 +1631,7 @@ pub unsafe extern "C" fn crosvm_vcpu_get_sregs(
     this: *mut crosvm_vcpu,
     sregs: *mut kvm_sregs,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuGetSregs);
+    let _u = record(Stat::VcpuGetSregs);
     let this = &mut *this;
     if this.sregs.set {
         if let Err(e) = this.set_state_from_cache(VcpuRequest_StateSet::SREGS) {
@@ -1711,7 +1653,7 @@ pub unsafe extern "C" fn crosvm_vcpu_set_sregs(
     this: *mut crosvm_vcpu,
     sregs: *const kvm_sregs,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuSetSregs);
+    let _u = record(Stat::VcpuSetSregs);
     let this = &mut *this;
     this.sregs.get = false;
     let sregs = from_raw_parts(sregs as *mut u8, size_of::<kvm_sregs>());
@@ -1722,7 +1664,7 @@ pub unsafe extern "C" fn crosvm_vcpu_set_sregs(
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_vcpu_get_fpu(this: *mut crosvm_vcpu, fpu: *mut kvm_fpu) -> c_int {
-    let _u = STATS.record(Stat::GetFpu);
+    let _u = record(Stat::GetFpu);
     let this = &mut *this;
     let fpu = from_raw_parts_mut(fpu as *mut u8, size_of::<kvm_fpu>());
     let ret = this.get_state(VcpuRequest_StateSet::FPU, fpu);
@@ -1731,7 +1673,7 @@ pub unsafe extern "C" fn crosvm_vcpu_get_fpu(this: *mut crosvm_vcpu, fpu: *mut k
 
 #[no_mangle]
 pub unsafe extern "C" fn crosvm_vcpu_set_fpu(this: *mut crosvm_vcpu, fpu: *const kvm_fpu) -> c_int {
-    let _u = STATS.record(Stat::SetFpu);
+    let _u = record(Stat::SetFpu);
     let this = &mut *this;
     let fpu = from_raw_parts(fpu as *mut u8, size_of::<kvm_fpu>());
     let ret = this.set_state(VcpuRequest_StateSet::FPU, fpu);
@@ -1743,7 +1685,7 @@ pub unsafe extern "C" fn crosvm_vcpu_get_debugregs(
     this: *mut crosvm_vcpu,
     dregs: *mut kvm_debugregs,
 ) -> c_int {
-    let _u = STATS.record(Stat::GetDebugRegs);
+    let _u = record(Stat::GetDebugRegs);
     let this = &mut *this;
     if this.debugregs.set {
         if let Err(e) = this.set_state_from_cache(VcpuRequest_StateSet::DEBUGREGS) {
@@ -1765,7 +1707,7 @@ pub unsafe extern "C" fn crosvm_vcpu_set_debugregs(
     this: *mut crosvm_vcpu,
     dregs: *const kvm_debugregs,
 ) -> c_int {
-    let _u = STATS.record(Stat::SetDebugRegs);
+    let _u = record(Stat::SetDebugRegs);
     let this = &mut *this;
     this.debugregs.get = false;
     let dregs = from_raw_parts(dregs as *mut u8, size_of::<kvm_debugregs>());
@@ -1779,7 +1721,7 @@ pub unsafe extern "C" fn crosvm_vcpu_get_xcrs(
     this: *mut crosvm_vcpu,
     xcrs: *mut kvm_xcrs,
 ) -> c_int {
-    let _u = STATS.record(Stat::GetXCRegs);
+    let _u = record(Stat::GetXCRegs);
     let this = &mut *this;
     let xcrs = from_raw_parts_mut(xcrs as *mut u8, size_of::<kvm_xcrs>());
     let ret = this.get_state(VcpuRequest_StateSet::XCREGS, xcrs);
@@ -1791,7 +1733,7 @@ pub unsafe extern "C" fn crosvm_vcpu_set_xcrs(
     this: *mut crosvm_vcpu,
     xcrs: *const kvm_xcrs,
 ) -> c_int {
-    let _u = STATS.record(Stat::SetXCRegs);
+    let _u = record(Stat::SetXCRegs);
     let this = &mut *this;
     let xcrs = from_raw_parts(xcrs as *mut u8, size_of::<kvm_xcrs>());
     let ret = this.set_state(VcpuRequest_StateSet::XCREGS, xcrs);
@@ -1805,7 +1747,7 @@ pub unsafe extern "C" fn crosvm_vcpu_get_msrs(
     msr_entries: *mut kvm_msr_entry,
     out_count: *mut u32,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuGetMsrs);
+    let _u = record(Stat::VcpuGetMsrs);
     let this = &mut *this;
     let msr_entries = from_raw_parts_mut(msr_entries, msr_count as usize);
     let mut count: usize = 0;
@@ -1820,7 +1762,7 @@ pub unsafe extern "C" fn crosvm_vcpu_set_msrs(
     msr_count: u32,
     msr_entries: *const kvm_msr_entry,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuSetMsrs);
+    let _u = record(Stat::VcpuSetMsrs);
     let this = &mut *this;
     let msr_entries = from_raw_parts(msr_entries, msr_count as usize);
     let ret = this.set_msrs(msr_entries);
@@ -1833,7 +1775,7 @@ pub unsafe extern "C" fn crosvm_vcpu_set_cpuid(
     cpuid_count: u32,
     cpuid_entries: *const kvm_cpuid_entry2,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuSetCpuid);
+    let _u = record(Stat::VcpuSetCpuid);
     let this = &mut *this;
     let cpuid_entries = from_raw_parts(cpuid_entries, cpuid_count as usize);
     let ret = this.set_cpuid(cpuid_entries);
@@ -1845,7 +1787,7 @@ pub unsafe extern "C" fn crosvm_vcpu_get_lapic_state(
     this: *mut crosvm_vcpu,
     state: *mut kvm_lapic_state,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuGetLapicState);
+    let _u = record(Stat::VcpuGetLapicState);
     let this = &mut *this;
     let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_lapic_state>());
     let ret = this.get_state(VcpuRequest_StateSet::LAPIC, state);
@@ -1857,7 +1799,7 @@ pub unsafe extern "C" fn crosvm_vcpu_set_lapic_state(
     this: *mut crosvm_vcpu,
     state: *const kvm_lapic_state,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuSetLapicState);
+    let _u = record(Stat::VcpuSetLapicState);
     let this = &mut *this;
     let state = from_raw_parts(state as *mut u8, size_of::<kvm_lapic_state>());
     let ret = this.set_state(VcpuRequest_StateSet::LAPIC, state);
@@ -1869,7 +1811,7 @@ pub unsafe extern "C" fn crosvm_vcpu_get_mp_state(
     this: *mut crosvm_vcpu,
     state: *mut kvm_mp_state,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuGetMpState);
+    let _u = record(Stat::VcpuGetMpState);
     let this = &mut *this;
     let state = from_raw_parts_mut(state as *mut u8, size_of::<kvm_mp_state>());
     let ret = this.get_state(VcpuRequest_StateSet::MP, state);
@@ -1881,7 +1823,7 @@ pub unsafe extern "C" fn crosvm_vcpu_set_mp_state(
     this: *mut crosvm_vcpu,
     state: *const kvm_mp_state,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuSetMpState);
+    let _u = record(Stat::VcpuSetMpState);
     let this = &mut *this;
     let state = from_raw_parts(state as *mut u8, size_of::<kvm_mp_state>());
     let ret = this.set_state(VcpuRequest_StateSet::MP, state);
@@ -1893,7 +1835,7 @@ pub unsafe extern "C" fn crosvm_vcpu_get_vcpu_events(
     this: *mut crosvm_vcpu,
     events: *mut kvm_vcpu_events,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuGetVcpuEvents);
+    let _u = record(Stat::VcpuGetVcpuEvents);
     let this = &mut *this;
     let events = from_raw_parts_mut(events as *mut u8, size_of::<kvm_vcpu_events>());
     let ret = this.get_state(VcpuRequest_StateSet::EVENTS, events);
@@ -1905,7 +1847,7 @@ pub unsafe extern "C" fn crosvm_vcpu_set_vcpu_events(
     this: *mut crosvm_vcpu,
     events: *const kvm_vcpu_events,
 ) -> c_int {
-    let _u = STATS.record(Stat::VcpuSetVcpuEvents);
+    let _u = record(Stat::VcpuSetVcpuEvents);
     let this = &mut *this;
     let events = from_raw_parts(events as *mut u8, size_of::<kvm_vcpu_events>());
     let ret = this.set_state(VcpuRequest_StateSet::EVENTS, events);
diff --git a/crosvm_plugin/src/stats.rs b/crosvm_plugin/src/stats.rs
new file mode 100644
index 0000000..0406e2f
--- /dev/null
+++ b/crosvm_plugin/src/stats.rs
@@ -0,0 +1,84 @@
+// 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.
+
+use crate::Stat;
+use std::time::Instant;
+
+#[derive(Clone, Copy)]
+struct StatEntry {
+    count: u64,
+    total: u64,
+    max: u64,
+}
+
+pub struct StatUpdater {
+    idx: usize,
+    start: Instant,
+}
+
+pub struct GlobalStats {
+    entries: [StatEntry; Stat::Count as usize],
+}
+
+pub static mut STATS: GlobalStats = GlobalStats {
+    entries: [StatEntry {
+        count: 0,
+        total: 0,
+        max: 0,
+    }; Stat::Count as usize],
+};
+
+impl GlobalStats {
+    // Record latency from this call until the end of block/function
+    // Example:
+    // pub fn foo() {
+    //     let _u = STATS.record(Stat::Foo);
+    //     // ... some operation ...
+    // }
+    // The added STATS.record will record latency of "some operation" and will
+    // update max and average latencies for it under Stats::Foo. Subsequent
+    // call to STATS.print() will print out max and average latencies for all
+    // operations that were performed.
+    pub fn record(&mut self, idx: Stat) -> StatUpdater {
+        StatUpdater {
+            idx: idx as usize,
+            start: Instant::now(),
+        }
+    }
+
+    pub fn print(&self) {
+        for idx in 0..Stat::Count as usize {
+            let e = &self.entries[idx as usize];
+            let stat = unsafe { std::mem::transmute::<u8, Stat>(idx as u8) };
+            if e.count > 0 {
+                println!(
+                    "Stat::{:?}: avg {}ns max {}ns",
+                    stat,
+                    e.total / e.count,
+                    e.max
+                );
+            }
+        }
+    }
+
+    fn update(&mut self, idx: usize, elapsed_nanos: u64) {
+        let e = &mut self.entries[idx as usize];
+        e.total += elapsed_nanos;
+        if e.max < elapsed_nanos {
+            e.max = elapsed_nanos;
+        }
+        e.count += 1;
+    }
+}
+
+impl Drop for StatUpdater {
+    fn drop(&mut self) {
+        let elapsed = self.start.elapsed();
+        let elapsed_nanos = elapsed.as_secs() * 1000000000 + elapsed.subsec_nanos() as u64;
+        // Unsafe due to racy access - OK for stats
+        unsafe {
+            STATS.update(self.idx, elapsed_nanos);
+        }
+    }
+}