<commit_before>#[macro_export]
macro_rules! write_log {
    ($dest:expr, $debug:ident, $level:expr, $fmt:expr, $($arg:tt)*) => {
        if $debug {
            use std::io::Write;
            match writeln!($dest, concat!(concat!($level, ": "), $fmt), $($arg)*) {
                Ok(_) => {},
                Err(x) => panic!("Unable to write to stderr: {}", x),
            }
        }
    };
}

#[macro_export]
macro_rules! debug {
    ($debug:ident, $msg:expr) => { write_log!(&mut ::std::io::stderr(), $debug, "DEBUG", $msg, ) };

    ($debug:ident, $fmt:expr, $($arg:tt)*) => { write_log!(&mut ::std::io::stderr(), $debug, "DEBUG", $fmt, $($arg)*) };
}
<commit_msg>Figured out how to test the debug macro and added the appropriate tests.<commit_after>#[macro_export]
macro_rules! write_log {
    ($dest:expr, $debug:ident, $level:expr, $fmt:expr, $($arg:tt)*) => {
        if $debug {
            use std::io::Write;
            match writeln!($dest, concat!(concat!($level, ": "), $fmt), $($arg)*) {
                Ok(_) => {},
                Err(x) => panic!("Unable to write to stderr: {}", x),
            }
        }
    };
}

#[macro_export]
macro_rules! debug {
    ($debug:ident, $msg:expr) => { write_log!(&mut ::std::io::stderr(), $debug, "DEBUG", $msg, ) };

    ($debug:ident, $fmt:expr, $($arg:tt)*) => { write_log!(&mut ::std::io::stderr(), $debug, "DEBUG", $fmt, $($arg)*) };
}

#[cfg(test)]
mod test {
    use std::fmt::Write;

    #[test]
    fn debug_should_write_a_message_without_format_args_to_the_dest() {
        let mut buff = String::new();

        write_log!(&mut buff, true, "DEBUG", "This is a test without format args.", );

        assert_eq!(buff, "DEBUG: This is a test without format args.\n");
    }

    #[test]
    fn debug_should_write_a_message_with_one_format_arg_to_the_dest() {
        let mut buff = String::new();

        write_log!(&mut buff, true, "DEBUG", "This is a test with {} format arg.", "one");

        assert_eq!(buff, "DEBUG: This is a test with one format arg.\n");
    }

    #[test]
    fn debug_should_write_a_message_with_format_args_to_the_dest() {
        let mut buff = String::new();

        write_log!(&mut buff, true, "DEBUG", "This is a test with {} {}.", "format", "args");

        assert_eq!(buff, "DEBUG: This is a test with format args.\n");
    }
}
<|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2015 Guillaume Gomez
//

use sys::component::Component;
use sys::disk::Disk;
use sys::ffi;
use sys::network::Networks;
use sys::process::*;
use sys::processor::*;

use {LoadAvg, Pid, ProcessExt, ProcessorExt, RefreshKind, SystemExt};

use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::mem;
use std::sync::Arc;

use libc::{self, c_int, c_void, size_t, sysconf, _SC_PAGESIZE};

use rayon::prelude::*;

/// Structs containing system's information.
pub struct System {
    process_list: HashMap<Pid, Process>,
    mem_total: u64,
    mem_free: u64,
    swap_total: u64,
    swap_free: u64,
    global_processor: Processor,
    processors: Vec<Processor>,
    page_size_kb: u64,
    components: Vec<Component>,
    connection: Option<ffi::io_connect_t>,
    disks: Vec<Disk>,
    networks: Networks,
    uptime: u64,
    port: ffi::mach_port_t,
}

impl Drop for System {
    fn drop(&mut self) {
        if let Some(conn) = self.connection {
            unsafe {
                ffi::IOServiceClose(conn);
            }
        }
    }
}

pub(crate) struct Wrap<'a>(pub UnsafeCell<&'a mut HashMap<Pid, Process>>);

unsafe impl<'a> Send for Wrap<'a> {}
unsafe impl<'a> Sync for Wrap<'a> {}

impl System {
    fn clear_procs(&mut self) {
        let mut to_delete = Vec::new();

        for (pid, mut proc_) in &mut self.process_list {
            if !has_been_updated(&mut proc_) {
                to_delete.push(*pid);
            }
        }
        for pid in to_delete {
            self.process_list.remove(&pid);
        }
    }
}

impl SystemExt for System {
    fn new_with_specifics(refreshes: RefreshKind) -> System {
        let port = unsafe { ffi::mach_host_self() };
        let (global_processor, processors) = init_processors(port);

        let mut s = System {
            process_list: HashMap::with_capacity(200),
            mem_total: 0,
            mem_free: 0,
            swap_total: 0,
            swap_free: 0,
            global_processor,
            processors,
            page_size_kb: unsafe { sysconf(_SC_PAGESIZE) as u64 >> 10 }, // divide by 1024
            components: Vec::with_capacity(2),
            connection: get_io_service_connection(),
            disks: Vec::with_capacity(1),
            networks: Networks::new(),
            uptime: get_uptime(),
            port,
        };
        s.refresh_specifics(refreshes);
        s
    }

    fn refresh_memory(&mut self) {
        let mut mib = [0, 0];

        self.uptime = get_uptime();
        unsafe {
            // get system values
            // get swap info
            let mut xs: ffi::xsw_usage = mem::zeroed::<ffi::xsw_usage>();
            if get_sys_value(
                ffi::CTL_VM,
                ffi::VM_SWAPUSAGE,
                mem::size_of::<ffi::xsw_usage>(),
                &mut xs as *mut ffi::xsw_usage as *mut c_void,
                &mut mib,
            ) {
                self.swap_total = xs.xsu_total >> 10; // divide by 1024
                self.swap_free = xs.xsu_avail >> 10; // divide by 1024
            }
            // get ram info
            if self.mem_total < 1 {
                get_sys_value(
                    ffi::CTL_HW,
                    ffi::HW_MEMSIZE,
                    mem::size_of::<u64>(),
                    &mut self.mem_total as *mut u64 as *mut c_void,
                    &mut mib,
                );
                self.mem_total >>= 10; // divide by 1024
            }
            let count: u32 = ffi::HOST_VM_INFO64_COUNT;
            let mut stat = mem::zeroed::<ffi::vm_statistics64>();
            if ffi::host_statistics64(
                self.port,
                ffi::HOST_VM_INFO64,
                &mut stat as *mut ffi::vm_statistics64 as *mut c_void,
                &count,
            ) == ffi::KERN_SUCCESS
            {
                // From the apple documentation:
                //
                // /*
                //  * NB: speculative pages are already accounted for in "free_count",
                //  * so "speculative_count" is the number of "free" pages that are
                //  * used to hold data that was read speculatively from disk but
                //  * haven't actually been used by anyone so far.
                //  */
                // self.mem_free = u64::from(stat.free_count) * self.page_size_kb;
                self.mem_free = self.mem_total
                    - (u64::from(stat.active_count)
                        + u64::from(stat.inactive_count)
                        + u64::from(stat.wire_count)
                        + u64::from(stat.speculative_count)
                        - u64::from(stat.purgeable_count))
                        * self.page_size_kb;
            }
        }
    }

    fn refresh_components_list(&mut self) {
        if let Some(con) = self.connection {
            self.components.clear();
            // getting CPU critical temperature
            let critical_temp = crate::mac::component::get_temperature(
                con,
                &['T' as i8, 'C' as i8, '0' as i8, 'D' as i8, 0],
            );

            for (id, v) in crate::mac::component::COMPONENTS_TEMPERATURE_IDS.iter() {
                if let Some(c) = Component::new((*id).to_owned(), None, critical_temp, v, con) {
                    self.components.push(c);
                }
            }
        }
    }

    fn refresh_cpu(&mut self) {
        self.uptime = get_uptime();

        // get processor values
        let mut num_cpu_u = 0u32;
        let mut cpu_info: *mut i32 = ::std::ptr::null_mut();
        let mut num_cpu_info = 0u32;

        let mut pourcent = 0f32;

        unsafe {
            if ffi::host_processor_info(
                self.port,
                ffi::PROCESSOR_CPU_LOAD_INFO,
                &mut num_cpu_u as *mut u32,
                &mut cpu_info as *mut *mut i32,
                &mut num_cpu_info as *mut u32,
            ) == ffi::KERN_SUCCESS
            {
                let proc_data = Arc::new(ProcessorData::new(cpu_info, num_cpu_info));
                for (i, proc_) in self.processors.iter_mut().enumerate() {
                    let old_proc_data = &*proc_.get_data();
                    let in_use =
                        (*cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_USER as isize,
                        ) - *old_proc_data.cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_USER as isize,
                        )) + (*cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_SYSTEM as isize,
                        ) - *old_proc_data.cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_SYSTEM as isize,
                        )) + (*cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_NICE as isize,
                        ) - *old_proc_data.cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_NICE as isize,
                        ));
                    let total = in_use
                        + (*cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_IDLE as isize,
                        ) - *old_proc_data.cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_IDLE as isize,
                        ));
                    proc_.update(in_use as f32 / total as f32, Arc::clone(&proc_data));
                    pourcent += proc_.get_cpu_usage();
                }
            }
        }
        self.global_processor
            .set_cpu_usage(pourcent / self.processors.len() as f32);
    }

    fn refresh_processes(&mut self) {
        let count = unsafe { ffi::proc_listallpids(::std::ptr::null_mut(), 0) };
        if count < 1 {
            return;
        }
        if let Some(pids) = get_proc_list() {
            let arg_max = get_arg_max();
            let entries: Vec<Process> = {
                let wrap = &Wrap(UnsafeCell::new(&mut self.process_list));
                pids.par_iter()
                    .flat_map(|pid| match update_process(wrap, *pid, arg_max as size_t) {
                        Ok(x) => x,
                        Err(_) => None,
                    })
                    .collect()
            };
            entries.into_iter().for_each(|entry| {
                self.process_list.insert(entry.pid(), entry);
            });
            self.clear_procs();
        }
    }

    fn refresh_process(&mut self, pid: Pid) -> bool {
        let arg_max = get_arg_max();
        match {
            let wrap = Wrap(UnsafeCell::new(&mut self.process_list));
            update_process(&wrap, pid, arg_max as size_t)
        } {
            Ok(Some(p)) => {
                self.process_list.insert(p.pid(), p);
                true
            }
            Ok(_) => true,
            Err(_) => false,
        }
    }

    fn refresh_disks_list(&mut self) {
        self.disks = crate::mac::disk::get_disks();
    }

    // COMMON PART
    //
    // Need to be moved into a "common" file to avoid duplication.

    fn get_process_list(&self) -> &HashMap<Pid, Process> {
        &self.process_list
    }

    fn get_process(&self, pid: Pid) -> Option<&Process> {
        self.process_list.get(&pid)
    }

    fn get_global_processor_info(&self) -> &Processor {
        &self.global_processor
    }

    fn get_processors(&self) -> &[Processor] {
        &self.processors
    }

    fn get_networks(&self) -> &Networks {
        &self.networks
    }

    fn get_networks_mut(&mut self) -> &mut Networks {
        &mut self.networks
    }

    fn get_total_memory(&self) -> u64 {
        self.mem_total
    }

    fn get_free_memory(&self) -> u64 {
        self.mem_free
    }

    fn get_used_memory(&self) -> u64 {
        self.mem_total - self.mem_free
    }

    fn get_total_swap(&self) -> u64 {
        self.swap_total
    }

    fn get_free_swap(&self) -> u64 {
        self.swap_free
    }

    // need to be checked
    fn get_used_swap(&self) -> u64 {
        self.swap_total - self.swap_free
    }

    fn get_components(&self) -> &[Component] {
        &self.components
    }

    fn get_components_mut(&mut self) -> &mut [Component] {
        &mut self.components
    }

    fn get_disks(&self) -> &[Disk] {
        &self.disks
    }

    fn get_disks_mut(&mut self) -> &mut [Disk] {
        &mut self.disks
    }

    fn get_uptime(&self) -> u64 {
        self.uptime
    }

    fn get_load_average(&self) -> LoadAvg {
        let loads = vec![0f64; 3];
        unsafe {
            ffi::getloadavg(loads.as_ptr() as *const f64, 3);
        }
        LoadAvg {
            one: loads[0],
            five: loads[1],
            fifteen: loads[2],
        }
    }
}

impl Default for System {
    fn default() -> System {
        System::new()
    }
}

// code from https://github.com/Chris911/iStats
fn get_io_service_connection() -> Option<ffi::io_connect_t> {
    let mut master_port: ffi::mach_port_t = 0;
    let mut iterator: ffi::io_iterator_t = 0;

    unsafe {
        ffi::IOMasterPort(ffi::MACH_PORT_NULL, &mut master_port);

        let matching_dictionary = ffi::IOServiceMatching(b"AppleSMC\0".as_ptr() as *const i8);
        let result =
            ffi::IOServiceGetMatchingServices(master_port, matching_dictionary, &mut iterator);
        if result != ffi::KIO_RETURN_SUCCESS {
            //println!("Error: IOServiceGetMatchingServices() = {}", result);
            return None;
        }

        let device = ffi::IOIteratorNext(iterator);
        ffi::IOObjectRelease(iterator);
        if device == 0 {
            //println!("Error: no SMC found");
            return None;
        }

        let mut conn = 0;
        let result = ffi::IOServiceOpen(device, ffi::mach_task_self(), 0, &mut conn);
        ffi::IOObjectRelease(device);
        if result != ffi::KIO_RETURN_SUCCESS {
            //println!("Error: IOServiceOpen() = {}", result);
            return None;
        }

        Some(conn)
    }
}

fn get_uptime() -> u64 {
    let mut boottime: libc::timeval = unsafe { mem::zeroed() };
    let mut len = mem::size_of::<libc::timeval>();
    let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME];
    unsafe {
        if libc::sysctl(
            mib.as_mut_ptr(),
            2,
            &mut boottime as *mut libc::timeval as *mut _,
            &mut len,
            ::std::ptr::null_mut(),
            0,
        ) < 0
        {
            return 0;
        }
    }
    let bsec = boottime.tv_sec;
    let csec = unsafe { libc::time(::std::ptr::null_mut()) };

    unsafe { libc::difftime(csec, bsec) as u64 }
}

fn get_arg_max() -> usize {
    let mut mib: [c_int; 3] = [libc::CTL_KERN, libc::KERN_ARGMAX, 0];
    let mut arg_max = 0i32;
    let mut size = mem::size_of::<c_int>();
    unsafe {
        if libc::sysctl(
            mib.as_mut_ptr(),
            2,
            (&mut arg_max) as *mut i32 as *mut c_void,
            &mut size,
            ::std::ptr::null_mut(),
            0,
        ) == -1
        {
            4096 // We default to this value
        } else {
            arg_max as usize
        }
    }
}

pub(crate) unsafe fn get_sys_value(
    high: u32,
    low: u32,
    mut len: usize,
    value: *mut c_void,
    mib: &mut [i32; 2],
) -> bool {
    mib[0] = high as i32;
    mib[1] = low as i32;
    libc::sysctl(
        mib.as_mut_ptr(),
        2,
        value,
        &mut len as *mut usize,
        ::std::ptr::null_mut(),
        0,
    ) == 0
}
<commit_msg>Fix CPU usage computation on mac<commit_after>//
// Sysinfo
//
// Copyright (c) 2015 Guillaume Gomez
//

use sys::component::Component;
use sys::disk::Disk;
use sys::ffi;
use sys::network::Networks;
use sys::process::*;
use sys::processor::*;

use {LoadAvg, Pid, ProcessExt, ProcessorExt, RefreshKind, SystemExt};

use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::mem;
use std::sync::Arc;

use libc::{self, c_int, c_void, size_t, sysconf, _SC_PAGESIZE};

use rayon::prelude::*;

/// Structs containing system's information.
pub struct System {
    process_list: HashMap<Pid, Process>,
    mem_total: u64,
    mem_free: u64,
    swap_total: u64,
    swap_free: u64,
    global_processor: Processor,
    processors: Vec<Processor>,
    page_size_kb: u64,
    components: Vec<Component>,
    connection: Option<ffi::io_connect_t>,
    disks: Vec<Disk>,
    networks: Networks,
    uptime: u64,
    port: ffi::mach_port_t,
}

impl Drop for System {
    fn drop(&mut self) {
        if let Some(conn) = self.connection {
            unsafe {
                ffi::IOServiceClose(conn);
            }
        }
    }
}

pub(crate) struct Wrap<'a>(pub UnsafeCell<&'a mut HashMap<Pid, Process>>);

unsafe impl<'a> Send for Wrap<'a> {}
unsafe impl<'a> Sync for Wrap<'a> {}

impl System {
    fn clear_procs(&mut self) {
        let mut to_delete = Vec::new();

        for (pid, mut proc_) in &mut self.process_list {
            if !has_been_updated(&mut proc_) {
                to_delete.push(*pid);
            }
        }
        for pid in to_delete {
            self.process_list.remove(&pid);
        }
    }
}

impl SystemExt for System {
    fn new_with_specifics(refreshes: RefreshKind) -> System {
        let port = unsafe { ffi::mach_host_self() };
        let (global_processor, processors) = init_processors(port);

        let mut s = System {
            process_list: HashMap::with_capacity(200),
            mem_total: 0,
            mem_free: 0,
            swap_total: 0,
            swap_free: 0,
            global_processor,
            processors,
            page_size_kb: unsafe { sysconf(_SC_PAGESIZE) as u64 >> 10 }, // divide by 1024
            components: Vec::with_capacity(2),
            connection: get_io_service_connection(),
            disks: Vec::with_capacity(1),
            networks: Networks::new(),
            uptime: get_uptime(),
            port,
        };
        s.refresh_specifics(refreshes);
        s
    }

    fn refresh_memory(&mut self) {
        let mut mib = [0, 0];

        self.uptime = get_uptime();
        unsafe {
            // get system values
            // get swap info
            let mut xs: ffi::xsw_usage = mem::zeroed::<ffi::xsw_usage>();
            if get_sys_value(
                ffi::CTL_VM,
                ffi::VM_SWAPUSAGE,
                mem::size_of::<ffi::xsw_usage>(),
                &mut xs as *mut ffi::xsw_usage as *mut c_void,
                &mut mib,
            ) {
                self.swap_total = xs.xsu_total >> 10; // divide by 1024
                self.swap_free = xs.xsu_avail >> 10; // divide by 1024
            }
            // get ram info
            if self.mem_total < 1 {
                get_sys_value(
                    ffi::CTL_HW,
                    ffi::HW_MEMSIZE,
                    mem::size_of::<u64>(),
                    &mut self.mem_total as *mut u64 as *mut c_void,
                    &mut mib,
                );
                self.mem_total >>= 10; // divide by 1024
            }
            let count: u32 = ffi::HOST_VM_INFO64_COUNT;
            let mut stat = mem::zeroed::<ffi::vm_statistics64>();
            if ffi::host_statistics64(
                self.port,
                ffi::HOST_VM_INFO64,
                &mut stat as *mut ffi::vm_statistics64 as *mut c_void,
                &count,
            ) == ffi::KERN_SUCCESS
            {
                // From the apple documentation:
                //
                // /*
                //  * NB: speculative pages are already accounted for in "free_count",
                //  * so "speculative_count" is the number of "free" pages that are
                //  * used to hold data that was read speculatively from disk but
                //  * haven't actually been used by anyone so far.
                //  */
                // self.mem_free = u64::from(stat.free_count) * self.page_size_kb;
                self.mem_free = self.mem_total
                    - (u64::from(stat.active_count)
                        + u64::from(stat.inactive_count)
                        + u64::from(stat.wire_count)
                        + u64::from(stat.speculative_count)
                        - u64::from(stat.purgeable_count))
                        * self.page_size_kb;
            }
        }
    }

    fn refresh_components_list(&mut self) {
        if let Some(con) = self.connection {
            self.components.clear();
            // getting CPU critical temperature
            let critical_temp = crate::mac::component::get_temperature(
                con,
                &['T' as i8, 'C' as i8, '0' as i8, 'D' as i8, 0],
            );

            for (id, v) in crate::mac::component::COMPONENTS_TEMPERATURE_IDS.iter() {
                if let Some(c) = Component::new((*id).to_owned(), None, critical_temp, v, con) {
                    self.components.push(c);
                }
            }
        }
    }

    fn refresh_cpu(&mut self) {
        self.uptime = get_uptime();

        // get processor values
        let mut num_cpu_u = 0u32;
        let mut cpu_info: *mut i32 = ::std::ptr::null_mut();
        let mut num_cpu_info = 0u32;

        let mut pourcent = 0f32;

        unsafe {
            if ffi::host_processor_info(
                self.port,
                ffi::PROCESSOR_CPU_LOAD_INFO,
                &mut num_cpu_u as *mut u32,
                &mut cpu_info as *mut *mut i32,
                &mut num_cpu_info as *mut u32,
            ) == ffi::KERN_SUCCESS
            {
                let proc_data = Arc::new(ProcessorData::new(cpu_info, num_cpu_info));
                for (i, proc_) in self.processors.iter_mut().enumerate() {
                    let old_proc_data = &*proc_.get_data();
                    let in_use =
                        (*cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_USER as isize,
                        ) - *old_proc_data.cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_USER as isize,
                        )) + (*cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_SYSTEM as isize,
                        ) - *old_proc_data.cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_SYSTEM as isize,
                        )) + (*cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_NICE as isize,
                        ) - *old_proc_data.cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_NICE as isize,
                        ));
                    let total = in_use
                        + (*cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_IDLE as isize,
                        ) - *old_proc_data.cpu_info.offset(
                            (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_IDLE as isize,
                        ));
                    proc_.update(in_use as f32 / total as f32 * 100., Arc::clone(&proc_data));
                    pourcent += proc_.get_cpu_usage();
                }
            }
        }
        self.global_processor
            .set_cpu_usage(pourcent / self.processors.len() as f32);
    }

    fn refresh_processes(&mut self) {
        let count = unsafe { ffi::proc_listallpids(::std::ptr::null_mut(), 0) };
        if count < 1 {
            return;
        }
        if let Some(pids) = get_proc_list() {
            let arg_max = get_arg_max();
            let entries: Vec<Process> = {
                let wrap = &Wrap(UnsafeCell::new(&mut self.process_list));
                pids.par_iter()
                    .flat_map(|pid| match update_process(wrap, *pid, arg_max as size_t) {
                        Ok(x) => x,
                        Err(_) => None,
                    })
                    .collect()
            };
            entries.into_iter().for_each(|entry| {
                self.process_list.insert(entry.pid(), entry);
            });
            self.clear_procs();
        }
    }

    fn refresh_process(&mut self, pid: Pid) -> bool {
        let arg_max = get_arg_max();
        match {
            let wrap = Wrap(UnsafeCell::new(&mut self.process_list));
            update_process(&wrap, pid, arg_max as size_t)
        } {
            Ok(Some(p)) => {
                self.process_list.insert(p.pid(), p);
                true
            }
            Ok(_) => true,
            Err(_) => false,
        }
    }

    fn refresh_disks_list(&mut self) {
        self.disks = crate::mac::disk::get_disks();
    }

    // COMMON PART
    //
    // Need to be moved into a "common" file to avoid duplication.

    fn get_process_list(&self) -> &HashMap<Pid, Process> {
        &self.process_list
    }

    fn get_process(&self, pid: Pid) -> Option<&Process> {
        self.process_list.get(&pid)
    }

    fn get_global_processor_info(&self) -> &Processor {
        &self.global_processor
    }

    fn get_processors(&self) -> &[Processor] {
        &self.processors
    }

    fn get_networks(&self) -> &Networks {
        &self.networks
    }

    fn get_networks_mut(&mut self) -> &mut Networks {
        &mut self.networks
    }

    fn get_total_memory(&self) -> u64 {
        self.mem_total
    }

    fn get_free_memory(&self) -> u64 {
        self.mem_free
    }

    fn get_used_memory(&self) -> u64 {
        self.mem_total - self.mem_free
    }

    fn get_total_swap(&self) -> u64 {
        self.swap_total
    }

    fn get_free_swap(&self) -> u64 {
        self.swap_free
    }

    // need to be checked
    fn get_used_swap(&self) -> u64 {
        self.swap_total - self.swap_free
    }

    fn get_components(&self) -> &[Component] {
        &self.components
    }

    fn get_components_mut(&mut self) -> &mut [Component] {
        &mut self.components
    }

    fn get_disks(&self) -> &[Disk] {
        &self.disks
    }

    fn get_disks_mut(&mut self) -> &mut [Disk] {
        &mut self.disks
    }

    fn get_uptime(&self) -> u64 {
        self.uptime
    }

    fn get_load_average(&self) -> LoadAvg {
        let loads = vec![0f64; 3];
        unsafe {
            ffi::getloadavg(loads.as_ptr() as *const f64, 3);
        }
        LoadAvg {
            one: loads[0],
            five: loads[1],
            fifteen: loads[2],
        }
    }
}

impl Default for System {
    fn default() -> System {
        System::new()
    }
}

// code from https://github.com/Chris911/iStats
fn get_io_service_connection() -> Option<ffi::io_connect_t> {
    let mut master_port: ffi::mach_port_t = 0;
    let mut iterator: ffi::io_iterator_t = 0;

    unsafe {
        ffi::IOMasterPort(ffi::MACH_PORT_NULL, &mut master_port);

        let matching_dictionary = ffi::IOServiceMatching(b"AppleSMC\0".as_ptr() as *const i8);
        let result =
            ffi::IOServiceGetMatchingServices(master_port, matching_dictionary, &mut iterator);
        if result != ffi::KIO_RETURN_SUCCESS {
            //println!("Error: IOServiceGetMatchingServices() = {}", result);
            return None;
        }

        let device = ffi::IOIteratorNext(iterator);
        ffi::IOObjectRelease(iterator);
        if device == 0 {
            //println!("Error: no SMC found");
            return None;
        }

        let mut conn = 0;
        let result = ffi::IOServiceOpen(device, ffi::mach_task_self(), 0, &mut conn);
        ffi::IOObjectRelease(device);
        if result != ffi::KIO_RETURN_SUCCESS {
            //println!("Error: IOServiceOpen() = {}", result);
            return None;
        }

        Some(conn)
    }
}

fn get_uptime() -> u64 {
    let mut boottime: libc::timeval = unsafe { mem::zeroed() };
    let mut len = mem::size_of::<libc::timeval>();
    let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME];
    unsafe {
        if libc::sysctl(
            mib.as_mut_ptr(),
            2,
            &mut boottime as *mut libc::timeval as *mut _,
            &mut len,
            ::std::ptr::null_mut(),
            0,
        ) < 0
        {
            return 0;
        }
    }
    let bsec = boottime.tv_sec;
    let csec = unsafe { libc::time(::std::ptr::null_mut()) };

    unsafe { libc::difftime(csec, bsec) as u64 }
}

fn get_arg_max() -> usize {
    let mut mib: [c_int; 3] = [libc::CTL_KERN, libc::KERN_ARGMAX, 0];
    let mut arg_max = 0i32;
    let mut size = mem::size_of::<c_int>();
    unsafe {
        if libc::sysctl(
            mib.as_mut_ptr(),
            2,
            (&mut arg_max) as *mut i32 as *mut c_void,
            &mut size,
            ::std::ptr::null_mut(),
            0,
        ) == -1
        {
            4096 // We default to this value
        } else {
            arg_max as usize
        }
    }
}

pub(crate) unsafe fn get_sys_value(
    high: u32,
    low: u32,
    mut len: usize,
    value: *mut c_void,
    mib: &mut [i32; 2],
) -> bool {
    mib[0] = high as i32;
    mib[1] = low as i32;
    libc::sysctl(
        mib.as_mut_ptr(),
        2,
        value,
        &mut len as *mut usize,
        ::std::ptr::null_mut(),
        0,
    ) == 0
}
<|endoftext|>


<commit_before>#![allow(unused_results)]
#![allow(clippy::print_stdout)]

use std::sync::atomic::AtomicUsize;

#[cfg(not(target_arch = "x86_64"))]
use std::time::{Duration, Instant};

#[cfg(feature = "no_metrics")]
use std::marker::PhantomData;

#[cfg(not(feature = "no_metrics"))]
use std::sync::atomic::Ordering::{Acquire, Relaxed};

use crate::Lazy;

use super::*;

/// A metric collector for all pagecache users running in this
/// process.
pub static M: Lazy<Metrics, fn() -> Metrics> = Lazy::new(Metrics::default);

#[allow(clippy::cast_precision_loss)]
pub(crate) fn clock() -> u64 {
    if cfg!(feature = "no_metrics") {
        0
    } else {
        #[cfg(target_arch = "x86_64")]
        #[allow(unsafe_code)]
        unsafe {
            let mut aux = 0;
            core::arch::x86_64::__rdtscp(&mut aux)
        }

        #[cfg(not(target_arch = "x86_64"))]
        {
            let u = uptime();
            (u.as_secs() * 1_000_000_000) + u64::from(u.subsec_nanos())
        }
    }
}

// not correct, since it starts counting at the first observance...
#[cfg(not(target_arch = "x86_64"))]
pub(crate) fn uptime() -> Duration {
    static START: Lazy<Instant, fn() -> Instant> = Lazy::new(Instant::now);

    if cfg!(feature = "no_metrics") {
        Duration::new(0, 0)
    } else {
        START.elapsed()
    }
}

/// Measure the duration of an event, and call `Histogram::measure()`.
pub struct Measure<'h> {
    _start: u64,
    #[cfg(not(feature = "no_metrics"))]
    histo: &'h Histogram,
    #[cfg(feature = "no_metrics")]
    _pd: PhantomData<&'h ()>,
}

impl<'h> Measure<'h> {
    /// The time delta from ctor to dtor is recorded in `histo`.
    #[inline]
    pub fn new(_histo: &'h Histogram) -> Measure<'h> {
        Measure {
            #[cfg(feature = "no_metrics")]
            _pd: PhantomData,
            #[cfg(not(feature = "no_metrics"))]
            histo: _histo,
            _start: clock(),
        }
    }
}

impl<'h> Drop for Measure<'h> {
    #[inline]
    fn drop(&mut self) {
        #[cfg(not(feature = "no_metrics"))]
        self.histo.measure(clock() - self._start);
    }
}

#[derive(Default, Debug)]
pub struct Metrics {
    pub accountant_bump_tip: Histogram,
    pub accountant_hold: Histogram,
    pub accountant_lock: Histogram,
    pub accountant_mark_link: Histogram,
    pub accountant_mark_replace: Histogram,
    pub accountant_next: Histogram,
    pub advance_snapshot: Histogram,
    pub assign_offset: Histogram,
    pub compress: Histogram,
    pub decompress: Histogram,
    pub deserialize: Histogram,
    pub get_page: Histogram,
    pub get_pagetable: Histogram,
    pub link_page: Histogram,
    pub log_reservation_attempts: CachePadded<AtomicUsize>,
    pub log_reservations: CachePadded<AtomicUsize>,
    pub make_stable: Histogram,
    pub page_out: Histogram,
    pub pull: Histogram,
    pub read: Histogram,
    pub read_segment_message: Histogram,
    pub replace_page: Histogram,
    pub reserve_lat: Histogram,
    pub reserve_sz: Histogram,
    pub rewrite_page: Histogram,
    pub segment_read: Histogram,
    pub segment_utilization_startup: Histogram,
    pub segment_utilization_shutdown: Histogram,
    pub serialize: Histogram,
    pub snapshot_apply: Histogram,
    pub start_pagecache: Histogram,
    pub start_segment_accountant: Histogram,
    pub tree_cas: Histogram,
    pub tree_child_split_attempt: CachePadded<AtomicUsize>,
    pub tree_child_split_success: CachePadded<AtomicUsize>,
    pub tree_del: Histogram,
    pub tree_get: Histogram,
    pub tree_loops: CachePadded<AtomicUsize>,
    pub tree_merge: Histogram,
    pub tree_parent_split_attempt: CachePadded<AtomicUsize>,
    pub tree_parent_split_success: CachePadded<AtomicUsize>,
    pub tree_reverse_scan: Histogram,
    pub tree_root_split_attempt: CachePadded<AtomicUsize>,
    pub tree_root_split_success: CachePadded<AtomicUsize>,
    pub tree_scan: Histogram,
    pub tree_set: Histogram,
    pub tree_start: Histogram,
    pub tree_traverse: Histogram,
    pub write_to_log: Histogram,
    pub written_bytes: Histogram,
    #[cfg(feature = "measure_allocs")]
    pub allocations: CachePadded<AtomicUsize>,
    #[cfg(feature = "measure_allocs")]
    pub allocated_bytes: CachePadded<AtomicUsize>,
}

#[cfg(not(feature = "no_metrics"))]
impl Metrics {
    #[inline]
    pub fn tree_looped(&self) {
        self.tree_loops.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_attempted(&self) {
        self.log_reservation_attempts.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_success(&self) {
        self.log_reservations.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_attempt(&self) {
        self.tree_child_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_success(&self) {
        self.tree_child_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_attempt(&self) {
        self.tree_parent_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_success(&self) {
        self.tree_parent_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_attempt(&self) {
        self.tree_root_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_success(&self) {
        self.tree_root_split_success.fetch_add(1, Relaxed);
    }

    pub fn print_profile(&self) {
        println!(
            "sled profile:\n\
             {0: >17} | {1: >10} | {2: >10} | {3: >10} | {4: >10} | {5: >10} | {6: >10} | {7: >10} | {8: >10} | {9: >10}",
            "op",
            "min (us)",
            "med (us)",
            "90 (us)",
            "99 (us)",
            "99.9 (us)",
            "99.99 (us)",
            "max (us)",
            "count",
            "sum (s)"
        );
        println!("{}", std::iter::repeat("-").take(134).collect::<String>());

        let p = |mut tuples: Vec<(String, _, _, _, _, _, _, _, _, _)>| {
            tuples.sort_by_key(|t| (t.9 * -1. * 1e3) as i64);
            for v in tuples {
                println!(
                    "{0: >17} | {1: >10.1} | {2: >10.1} | {3: >10.1} \
                     | {4: >10.1} | {5: >10.1} | {6: >10.1} | {7: >10.1} \
                     | {8: >10.1} | {9: >10.3}",
                    v.0, v.1, v.2, v.3, v.4, v.5, v.6, v.7, v.8, v.9,
                );
            }
        };

        let lat = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.) / 1e3,
                histo.percentile(50.) / 1e3,
                histo.percentile(90.) / 1e3,
                histo.percentile(99.) / 1e3,
                histo.percentile(99.9) / 1e3,
                histo.percentile(99.99) / 1e3,
                histo.percentile(100.) / 1e3,
                histo.count(),
                histo.sum() as f64 / 1e9,
            )
        };

        let sz = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.),
                histo.percentile(50.),
                histo.percentile(90.),
                histo.percentile(99.),
                histo.percentile(99.9),
                histo.percentile(99.99),
                histo.percentile(100.),
                histo.count(),
                histo.sum() as f64,
            )
        };

        println!("tree:");
        p(vec![
            lat("traverse", &self.tree_traverse),
            lat("get", &self.tree_get),
            lat("set", &self.tree_set),
            lat("merge", &self.tree_merge),
            lat("del", &self.tree_del),
            lat("cas", &self.tree_cas),
            lat("scan", &self.tree_scan),
            lat("rev scan", &self.tree_reverse_scan),
        ]);
        let total_loops = self.tree_loops.load(Acquire);
        let total_ops = self.tree_get.count()
            + self.tree_set.count()
            + self.tree_merge.count()
            + self.tree_del.count()
            + self.tree_cas.count()
            + self.tree_scan.count()
            + self.tree_reverse_scan.count();
        let loop_pct = total_loops * 100 / (total_ops + 1);
        println!(
            "tree contention loops: {} ({}% retry rate)",
            total_loops, loop_pct
        );
        println!(
            "tree split success rates: child({}/{}) parent({}/{}) root({}/{})",
            self.tree_child_split_success.load(Acquire),
            self.tree_child_split_attempt.load(Acquire),
            self.tree_parent_split_success.load(Acquire),
            self.tree_parent_split_attempt.load(Acquire),
            self.tree_root_split_success.load(Acquire),
            self.tree_root_split_attempt.load(Acquire),
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("pagecache:");
        p(vec![
            lat("get", &self.get_page),
            lat("get pt", &self.get_pagetable),
            lat("rewrite", &self.rewrite_page),
            lat("replace", &self.replace_page),
            lat("link", &self.link_page),
            lat("pull", &self.pull),
            lat("page_out", &self.page_out),
        ]);
        let hit_ratio = (self.get_page.count() - self.pull.count()) * 100
            / (self.get_page.count() + 1);
        println!("hit ratio: {}%", hit_ratio);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("serialization and compression:");
        p(vec![
            lat("serialize", &self.serialize),
            lat("deserialize", &self.deserialize),
            #[cfg(feature = "compression")]
            lat("compress", &self.compress),
            #[cfg(feature = "compression")]
            lat("decompress", &self.decompress),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("log:");
        p(vec![
            lat("make_stable", &self.make_stable),
            lat("read", &self.read),
            lat("write", &self.write_to_log),
            sz("written bytes", &self.written_bytes),
            lat("assign offset", &self.assign_offset),
            lat("reserve lat", &self.reserve_lat),
            sz("reserve sz", &self.reserve_sz),
        ]);
        let log_reservations =
            std::cmp::max(1, self.log_reservations.load(Acquire));
        let log_reservation_attempts =
            self.log_reservation_attempts.load(Acquire);
        let log_reservation_retry_rate =
            (log_reservation_attempts - log_reservations) * 100
                / (log_reservations + 1);
        println!("log reservations: {}", log_reservations);
        println!(
            "log res attempts: {}, ({}% retry rate)",
            log_reservation_attempts, log_reservation_retry_rate,
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("segment accountant:");
        p(vec![
            lat("acquire", &self.accountant_lock),
            lat("hold", &self.accountant_hold),
            lat("next", &self.accountant_next),
            lat("replace", &self.accountant_mark_replace),
            lat("link", &self.accountant_mark_link),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("recovery:");
        p(vec![
            lat("start", &self.tree_start),
            lat("advance snapshot", &self.advance_snapshot),
            lat("load SA", &self.start_segment_accountant),
            lat("load PC", &self.start_pagecache),
            lat("snap apply", &self.snapshot_apply),
            lat("segment read", &self.segment_read),
            lat("log message read", &self.read_segment_message),
            sz("seg util start", &self.segment_utilization_startup),
            sz("seg util end", &self.segment_utilization_shutdown),
        ]);

        #[cfg(feature = "measure_allocs")]
        {
            println!(
                "{}",
                std::iter::repeat("-").take(134).collect::<String>()
            );
            println!("allocation statistics:");
            println!(
                "total allocations: {}",
                measure_allocs::ALLOCATIONS.load(Acquire)
            );
            println!(
                "allocated bytes: {}",
                measure_allocs::ALLOCATED_BYTES.load(Acquire)
            );
        }
    }
}

#[cfg(feature = "no_metrics")]
impl Metrics {
    pub const fn log_reservation_attempted(&self) {}

    pub const fn log_reservation_success(&self) {}

    pub const fn tree_child_split_attempt(&self) {}

    pub const fn tree_child_split_success(&self) {}

    pub const fn tree_parent_split_attempt(&self) {}

    pub const fn tree_parent_split_success(&self) {}

    pub const fn tree_root_split_attempt(&self) {}

    pub const fn tree_root_split_success(&self) {}

    pub const fn tree_looped(&self) {}

    pub const fn print_profile(&self) {}
}
<commit_msg>metrics: fix log_reservation_attempts subtract overflow<commit_after>#![allow(unused_results)]
#![allow(clippy::print_stdout)]

use std::sync::atomic::AtomicUsize;

#[cfg(not(target_arch = "x86_64"))]
use std::time::{Duration, Instant};

#[cfg(feature = "no_metrics")]
use std::marker::PhantomData;

#[cfg(not(feature = "no_metrics"))]
use std::sync::atomic::Ordering::{Acquire, Relaxed};

use crate::Lazy;

use super::*;

/// A metric collector for all pagecache users running in this
/// process.
pub static M: Lazy<Metrics, fn() -> Metrics> = Lazy::new(Metrics::default);

#[allow(clippy::cast_precision_loss)]
pub(crate) fn clock() -> u64 {
    if cfg!(feature = "no_metrics") {
        0
    } else {
        #[cfg(target_arch = "x86_64")]
        #[allow(unsafe_code)]
        unsafe {
            let mut aux = 0;
            core::arch::x86_64::__rdtscp(&mut aux)
        }

        #[cfg(not(target_arch = "x86_64"))]
        {
            let u = uptime();
            (u.as_secs() * 1_000_000_000) + u64::from(u.subsec_nanos())
        }
    }
}

// not correct, since it starts counting at the first observance...
#[cfg(not(target_arch = "x86_64"))]
pub(crate) fn uptime() -> Duration {
    static START: Lazy<Instant, fn() -> Instant> = Lazy::new(Instant::now);

    if cfg!(feature = "no_metrics") {
        Duration::new(0, 0)
    } else {
        START.elapsed()
    }
}

/// Measure the duration of an event, and call `Histogram::measure()`.
pub struct Measure<'h> {
    _start: u64,
    #[cfg(not(feature = "no_metrics"))]
    histo: &'h Histogram,
    #[cfg(feature = "no_metrics")]
    _pd: PhantomData<&'h ()>,
}

impl<'h> Measure<'h> {
    /// The time delta from ctor to dtor is recorded in `histo`.
    #[inline]
    pub fn new(_histo: &'h Histogram) -> Measure<'h> {
        Measure {
            #[cfg(feature = "no_metrics")]
            _pd: PhantomData,
            #[cfg(not(feature = "no_metrics"))]
            histo: _histo,
            _start: clock(),
        }
    }
}

impl<'h> Drop for Measure<'h> {
    #[inline]
    fn drop(&mut self) {
        #[cfg(not(feature = "no_metrics"))]
        self.histo.measure(clock() - self._start);
    }
}

#[derive(Default, Debug)]
pub struct Metrics {
    pub accountant_bump_tip: Histogram,
    pub accountant_hold: Histogram,
    pub accountant_lock: Histogram,
    pub accountant_mark_link: Histogram,
    pub accountant_mark_replace: Histogram,
    pub accountant_next: Histogram,
    pub advance_snapshot: Histogram,
    pub assign_offset: Histogram,
    pub compress: Histogram,
    pub decompress: Histogram,
    pub deserialize: Histogram,
    pub get_page: Histogram,
    pub get_pagetable: Histogram,
    pub link_page: Histogram,
    pub log_reservation_attempts: CachePadded<AtomicUsize>,
    pub log_reservations: CachePadded<AtomicUsize>,
    pub make_stable: Histogram,
    pub page_out: Histogram,
    pub pull: Histogram,
    pub read: Histogram,
    pub read_segment_message: Histogram,
    pub replace_page: Histogram,
    pub reserve_lat: Histogram,
    pub reserve_sz: Histogram,
    pub rewrite_page: Histogram,
    pub segment_read: Histogram,
    pub segment_utilization_startup: Histogram,
    pub segment_utilization_shutdown: Histogram,
    pub serialize: Histogram,
    pub snapshot_apply: Histogram,
    pub start_pagecache: Histogram,
    pub start_segment_accountant: Histogram,
    pub tree_cas: Histogram,
    pub tree_child_split_attempt: CachePadded<AtomicUsize>,
    pub tree_child_split_success: CachePadded<AtomicUsize>,
    pub tree_del: Histogram,
    pub tree_get: Histogram,
    pub tree_loops: CachePadded<AtomicUsize>,
    pub tree_merge: Histogram,
    pub tree_parent_split_attempt: CachePadded<AtomicUsize>,
    pub tree_parent_split_success: CachePadded<AtomicUsize>,
    pub tree_reverse_scan: Histogram,
    pub tree_root_split_attempt: CachePadded<AtomicUsize>,
    pub tree_root_split_success: CachePadded<AtomicUsize>,
    pub tree_scan: Histogram,
    pub tree_set: Histogram,
    pub tree_start: Histogram,
    pub tree_traverse: Histogram,
    pub write_to_log: Histogram,
    pub written_bytes: Histogram,
    #[cfg(feature = "measure_allocs")]
    pub allocations: CachePadded<AtomicUsize>,
    #[cfg(feature = "measure_allocs")]
    pub allocated_bytes: CachePadded<AtomicUsize>,
}

#[cfg(not(feature = "no_metrics"))]
impl Metrics {
    #[inline]
    pub fn tree_looped(&self) {
        self.tree_loops.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_attempted(&self) {
        self.log_reservation_attempts.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn log_reservation_success(&self) {
        self.log_reservations.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_attempt(&self) {
        self.tree_child_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_child_split_success(&self) {
        self.tree_child_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_attempt(&self) {
        self.tree_parent_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_parent_split_success(&self) {
        self.tree_parent_split_success.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_attempt(&self) {
        self.tree_root_split_attempt.fetch_add(1, Relaxed);
    }

    #[inline]
    pub fn tree_root_split_success(&self) {
        self.tree_root_split_success.fetch_add(1, Relaxed);
    }

    pub fn print_profile(&self) {
        println!(
            "sled profile:\n\
             {0: >17} | {1: >10} | {2: >10} | {3: >10} | {4: >10} | {5: >10} | {6: >10} | {7: >10} | {8: >10} | {9: >10}",
            "op",
            "min (us)",
            "med (us)",
            "90 (us)",
            "99 (us)",
            "99.9 (us)",
            "99.99 (us)",
            "max (us)",
            "count",
            "sum (s)"
        );
        println!("{}", std::iter::repeat("-").take(134).collect::<String>());

        let p = |mut tuples: Vec<(String, _, _, _, _, _, _, _, _, _)>| {
            tuples.sort_by_key(|t| (t.9 * -1. * 1e3) as i64);
            for v in tuples {
                println!(
                    "{0: >17} | {1: >10.1} | {2: >10.1} | {3: >10.1} \
                     | {4: >10.1} | {5: >10.1} | {6: >10.1} | {7: >10.1} \
                     | {8: >10.1} | {9: >10.3}",
                    v.0, v.1, v.2, v.3, v.4, v.5, v.6, v.7, v.8, v.9,
                );
            }
        };

        let lat = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.) / 1e3,
                histo.percentile(50.) / 1e3,
                histo.percentile(90.) / 1e3,
                histo.percentile(99.) / 1e3,
                histo.percentile(99.9) / 1e3,
                histo.percentile(99.99) / 1e3,
                histo.percentile(100.) / 1e3,
                histo.count(),
                histo.sum() as f64 / 1e9,
            )
        };

        let sz = |name: &str, histo: &Histogram| {
            (
                name.to_string(),
                histo.percentile(0.),
                histo.percentile(50.),
                histo.percentile(90.),
                histo.percentile(99.),
                histo.percentile(99.9),
                histo.percentile(99.99),
                histo.percentile(100.),
                histo.count(),
                histo.sum() as f64,
            )
        };

        println!("tree:");
        p(vec![
            lat("traverse", &self.tree_traverse),
            lat("get", &self.tree_get),
            lat("set", &self.tree_set),
            lat("merge", &self.tree_merge),
            lat("del", &self.tree_del),
            lat("cas", &self.tree_cas),
            lat("scan", &self.tree_scan),
            lat("rev scan", &self.tree_reverse_scan),
        ]);
        let total_loops = self.tree_loops.load(Acquire);
        let total_ops = self.tree_get.count()
            + self.tree_set.count()
            + self.tree_merge.count()
            + self.tree_del.count()
            + self.tree_cas.count()
            + self.tree_scan.count()
            + self.tree_reverse_scan.count();
        let loop_pct = total_loops * 100 / (total_ops + 1);
        println!(
            "tree contention loops: {} ({}% retry rate)",
            total_loops, loop_pct
        );
        println!(
            "tree split success rates: child({}/{}) parent({}/{}) root({}/{})",
            self.tree_child_split_success.load(Acquire),
            self.tree_child_split_attempt.load(Acquire),
            self.tree_parent_split_success.load(Acquire),
            self.tree_parent_split_attempt.load(Acquire),
            self.tree_root_split_success.load(Acquire),
            self.tree_root_split_attempt.load(Acquire),
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("pagecache:");
        p(vec![
            lat("get", &self.get_page),
            lat("get pt", &self.get_pagetable),
            lat("rewrite", &self.rewrite_page),
            lat("replace", &self.replace_page),
            lat("link", &self.link_page),
            lat("pull", &self.pull),
            lat("page_out", &self.page_out),
        ]);
        let hit_ratio = (self.get_page.count() - self.pull.count()) * 100
            / (self.get_page.count() + 1);
        println!("hit ratio: {}%", hit_ratio);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("serialization and compression:");
        p(vec![
            lat("serialize", &self.serialize),
            lat("deserialize", &self.deserialize),
            #[cfg(feature = "compression")]
            lat("compress", &self.compress),
            #[cfg(feature = "compression")]
            lat("decompress", &self.decompress),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("log:");
        p(vec![
            lat("make_stable", &self.make_stable),
            lat("read", &self.read),
            lat("write", &self.write_to_log),
            sz("written bytes", &self.written_bytes),
            lat("assign offset", &self.assign_offset),
            lat("reserve lat", &self.reserve_lat),
            sz("reserve sz", &self.reserve_sz),
        ]);
        let log_reservations =
            std::cmp::max(1, self.log_reservations.load(Acquire));
        let log_reservation_attempts =
            std::cmp::max(1, self.log_reservation_attempts.load(Acquire));
        let log_reservation_retry_rate =
            (log_reservation_attempts - log_reservations) * 100
                / (log_reservations + 1);
        println!("log reservations: {}", log_reservations);
        println!(
            "log res attempts: {}, ({}% retry rate)",
            log_reservation_attempts, log_reservation_retry_rate,
        );

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("segment accountant:");
        p(vec![
            lat("acquire", &self.accountant_lock),
            lat("hold", &self.accountant_hold),
            lat("next", &self.accountant_next),
            lat("replace", &self.accountant_mark_replace),
            lat("link", &self.accountant_mark_link),
        ]);

        println!("{}", std::iter::repeat("-").take(134).collect::<String>());
        println!("recovery:");
        p(vec![
            lat("start", &self.tree_start),
            lat("advance snapshot", &self.advance_snapshot),
            lat("load SA", &self.start_segment_accountant),
            lat("load PC", &self.start_pagecache),
            lat("snap apply", &self.snapshot_apply),
            lat("segment read", &self.segment_read),
            lat("log message read", &self.read_segment_message),
            sz("seg util start", &self.segment_utilization_startup),
            sz("seg util end", &self.segment_utilization_shutdown),
        ]);

        #[cfg(feature = "measure_allocs")]
        {
            println!(
                "{}",
                std::iter::repeat("-").take(134).collect::<String>()
            );
            println!("allocation statistics:");
            println!(
                "total allocations: {}",
                measure_allocs::ALLOCATIONS.load(Acquire)
            );
            println!(
                "allocated bytes: {}",
                measure_allocs::ALLOCATED_BYTES.load(Acquire)
            );
        }
    }
}

#[cfg(feature = "no_metrics")]
impl Metrics {
    pub const fn log_reservation_attempted(&self) {}

    pub const fn log_reservation_success(&self) {}

    pub const fn tree_child_split_attempt(&self) {}

    pub const fn tree_child_split_success(&self) {}

    pub const fn tree_parent_split_attempt(&self) {}

    pub const fn tree_parent_split_success(&self) {}

    pub const fn tree_root_split_attempt(&self) {}

    pub const fn tree_root_split_success(&self) {}

    pub const fn tree_looped(&self) {}

    pub const fn print_profile(&self) {}
}
<|endoftext|>


<commit_before>// Integration with Musashi
extern crate libc;

// Register enum copied from Musashi's m68k_register_t enum
#[repr(C)]
#[derive(Copy, Clone)]
pub enum Register {
	/* Real registers */
	D0,		/* Data registers */
	D1,
	D2,
	D3,
	D4,
	D5,
	D6,
	D7,
	A0,		/* Address registers */
	A1,
	A2,
	A3,
	A4,
	A5,
	A6,
	A7,
	PC,		/* Program Counter */
	SR,		/* Status Register */
	SP,		/* The current Stack Pointer (located in A7) */
	USP,		/* User Stack Pointer */
	ISP,		/* Interrupt Stack Pointer */
	MSP,		/* Master Stack Pointer */
	SFC,		/* Source Function Code */
	DFC,		/* Destination Function Code */
	VBR,		/* Vector Base Register */
	CACR,		/* Cache Control Register */
	CAAR,		/* Cache Address Register */

	/* Assumed registers */
	/* These are cheat registers which emulate the 1-longword prefetch
	 * present in the 68000 and 68010.
	 */
	PrefAddr,	/* Last prefetch address */
	PrefData,	/* Last prefetch data */

	/* Convenience registers */
	PPC,		/* Previous value in the program counter */
	IR,			/* Instruction register */
	CpuType	/* Type of CPU being run */
}

#[repr(C)]
#[derive(Copy, Clone)]
enum CpuType
{
	Invalid,
	M68000,
	M68010,
	M68EC020,
	M68020,
	M68030,		/* Supported by disassembler ONLY */
	M68040		/* Supported by disassembler ONLY */
}

#[link(name = "musashi", kind = "static")]
extern {
	fn m68k_init();
	fn m68k_set_cpu_type(cputype: CpuType);
	fn m68k_pulse_reset();
	fn m68k_execute(num_cycles: i32) -> i32;
	fn m68k_get_reg(context: *mut libc::c_void, regnum: Register) -> u32;
	fn m68k_set_reg(regnum: Register, value: u32);
}

static mut musashi_memory:  [u8; 1024] = [0u8; 1024];

// callbacks from Musashi
#[no_mangle]
pub extern fn cpu_read_byte(address: u32) -> u32 {
	println!("read byte {:08x}", address);
	let address = address as usize;
	unsafe {
		musashi_memory[address] as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_word(address: u32) -> u32 {
	println!("read word {:08x}", address);
	let address = address as usize;
	unsafe {
		((musashi_memory[address+0] as u32) << 8
		|(musashi_memory[address+1] as u32) << 0) as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_long(address: u32) -> u32 {
	println!("read long {:08x}", address);
	let address = address as usize;
	unsafe {
		((musashi_memory[address+0] as u32) << 24
		|(musashi_memory[address+1] as u32) << 16
		|(musashi_memory[address+2] as u32) <<  8
		|(musashi_memory[address+3] as u32) <<  0) as u32
	}
}

#[no_mangle]
pub extern fn cpu_write_byte(address: u32, value: u32) {
	let address = address as usize;
	unsafe {
		musashi_memory[address+0] = (value & 0xff) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_word(address: u32, value: u32) {
	let address = address as usize;
	unsafe {
		musashi_memory[address+0] = (value & 0xff00 >> 8) as u8;
		musashi_memory[address+1] = (value & 0x00ff >> 0) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_long(address: u32, value: u32) {
	let address = address as usize;
	unsafe {
		musashi_memory[address+0] = (value & 0xff000000 >> 24) as u8;
		musashi_memory[address+1] = (value & 0x00ff0000 >> 16) as u8;
		musashi_memory[address+2] = (value & 0x0000ff00 >>  8) as u8;
		musashi_memory[address+3] = (value & 0x000000ff >>  0) as u8;
	}
}

#[no_mangle]
pub extern fn cpu_pulse_reset() {panic!("pr")}
#[no_mangle]
pub extern fn cpu_long_branch() {println!("lb")}
#[no_mangle]
pub extern fn cpu_set_fc(fc: u32) {println!("set_fc {}", fc)}
#[no_mangle]
pub extern fn cpu_irq_ack(level: i32) -> i32 {panic!("ia")}
#[no_mangle]
pub extern fn cpu_instr_callback() {println!("ic")}

use std::ptr;

pub fn experimental_communication() {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(Register::D0, 123);
		println!("D0: {}", m68k_get_reg(ptr::null_mut(), Register::D0));
	}
}

pub fn roundtrip_register(reg: Register, value: u32) -> u32 {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(reg, value);
		m68k_get_reg(ptr::null_mut(), reg)
	}
}
#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn roundtrip_D0() {
		assert_eq!(256, roundtrip_register(Register::D0, 256));
	}
}<commit_msg>Fix snake case warning<commit_after>// Integration with Musashi
extern crate libc;

// Register enum copied from Musashi's m68k_register_t enum
#[repr(C)]
#[derive(Copy, Clone)]
pub enum Register {
	/* Real registers */
	D0,		/* Data registers */
	D1,
	D2,
	D3,
	D4,
	D5,
	D6,
	D7,
	A0,		/* Address registers */
	A1,
	A2,
	A3,
	A4,
	A5,
	A6,
	A7,
	PC,		/* Program Counter */
	SR,		/* Status Register */
	SP,		/* The current Stack Pointer (located in A7) */
	USP,		/* User Stack Pointer */
	ISP,		/* Interrupt Stack Pointer */
	MSP,		/* Master Stack Pointer */
	SFC,		/* Source Function Code */
	DFC,		/* Destination Function Code */
	VBR,		/* Vector Base Register */
	CACR,		/* Cache Control Register */
	CAAR,		/* Cache Address Register */

	/* Assumed registers */
	/* These are cheat registers which emulate the 1-longword prefetch
	 * present in the 68000 and 68010.
	 */
	PrefAddr,	/* Last prefetch address */
	PrefData,	/* Last prefetch data */

	/* Convenience registers */
	PPC,		/* Previous value in the program counter */
	IR,			/* Instruction register */
	CpuType	/* Type of CPU being run */
}

#[repr(C)]
#[derive(Copy, Clone)]
enum CpuType
{
	Invalid,
	M68000,
	M68010,
	M68EC020,
	M68020,
	M68030,		/* Supported by disassembler ONLY */
	M68040		/* Supported by disassembler ONLY */
}

#[link(name = "musashi", kind = "static")]
extern {
	fn m68k_init();
	fn m68k_set_cpu_type(cputype: CpuType);
	fn m68k_pulse_reset();
	fn m68k_execute(num_cycles: i32) -> i32;
	fn m68k_get_reg(context: *mut libc::c_void, regnum: Register) -> u32;
	fn m68k_set_reg(regnum: Register, value: u32);
}

static mut musashi_memory:  [u8; 1024] = [0u8; 1024];

// callbacks from Musashi
#[no_mangle]
pub extern fn cpu_read_byte(address: u32) -> u32 {
	println!("read byte {:08x}", address);
	let address = address as usize;
	unsafe {
		musashi_memory[address] as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_word(address: u32) -> u32 {
	println!("read word {:08x}", address);
	let address = address as usize;
	unsafe {
		((musashi_memory[address+0] as u32) << 8
		|(musashi_memory[address+1] as u32) << 0) as u32
	}
}
#[no_mangle]
pub extern fn cpu_read_long(address: u32) -> u32 {
	println!("read long {:08x}", address);
	let address = address as usize;
	unsafe {
		((musashi_memory[address+0] as u32) << 24
		|(musashi_memory[address+1] as u32) << 16
		|(musashi_memory[address+2] as u32) <<  8
		|(musashi_memory[address+3] as u32) <<  0) as u32
	}
}

#[no_mangle]
pub extern fn cpu_write_byte(address: u32, value: u32) {
	let address = address as usize;
	unsafe {
		musashi_memory[address+0] = (value & 0xff) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_word(address: u32, value: u32) {
	let address = address as usize;
	unsafe {
		musashi_memory[address+0] = (value & 0xff00 >> 8) as u8;
		musashi_memory[address+1] = (value & 0x00ff >> 0) as u8;
	}
}
#[no_mangle]
pub extern fn cpu_write_long(address: u32, value: u32) {
	let address = address as usize;
	unsafe {
		musashi_memory[address+0] = (value & 0xff000000 >> 24) as u8;
		musashi_memory[address+1] = (value & 0x00ff0000 >> 16) as u8;
		musashi_memory[address+2] = (value & 0x0000ff00 >>  8) as u8;
		musashi_memory[address+3] = (value & 0x000000ff >>  0) as u8;
	}
}

#[no_mangle]
pub extern fn cpu_pulse_reset() {panic!("pr")}
#[no_mangle]
pub extern fn cpu_long_branch() {println!("lb")}
#[no_mangle]
pub extern fn cpu_set_fc(fc: u32) {println!("set_fc {}", fc)}
#[no_mangle]
pub extern fn cpu_irq_ack(level: i32) -> i32 {panic!("ia")}
#[no_mangle]
pub extern fn cpu_instr_callback() {println!("ic")}

use std::ptr;

pub fn experimental_communication() {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(Register::D0, 123);
		println!("D0: {}", m68k_get_reg(ptr::null_mut(), Register::D0));
	}
}

pub fn roundtrip_register(reg: Register, value: u32) -> u32 {
	unsafe {
		m68k_init();
		m68k_set_cpu_type(CpuType::M68000);
		m68k_set_reg(reg, value);
		m68k_get_reg(ptr::null_mut(), reg)
	}
}
#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn roundtrip_d0() {
		assert_eq!(256, roundtrip_register(Register::D0, 256));
	}
}<|endoftext|>


<commit_before>//! TCP/UDP bindings for `tokio-core`
//!
//! This module contains the TCP/UDP networking types, similar to the standard
//! library, which can be used to implement networking protocols.

mod tcp;
mod udp;

pub use self::tcp::{TcpStream, TcpStreamNew};
pub use self::tcp::{TcpListener, Incoming};
pub use self::udp::{UdpSocket, UdpCodec, UdpFramed};
<commit_msg>Add some more public exports<commit_after>//! TCP/UDP bindings for `tokio-core`
//!
//! This module contains the TCP/UDP networking types, similar to the standard
//! library, which can be used to implement networking protocols.

mod tcp;
mod udp;

pub use self::tcp::{TcpStream, TcpStreamNew};
pub use self::tcp::{TcpListener, Incoming};
pub use self::udp::{UdpSocket, UdpCodec, UdpFramed, SendDgram, RecvDgram};
<|endoftext|>


<commit_before>//! Network packet utility

use sync::{Arc, Weak};
use sync::spin::RwLock;

use net::Interface;

use alloc_uni::__rust_deallocate;

use hal::arch::defs::PAGE_SIZE;

/// A network packet
pub struct Packet {
    page: *mut u8,
    data: *mut u8,
    size: usize,
    intf: Option<Weak<RwLock<Interface>>>,
}

impl Packet {
    /// Creates a new packet from a memory `page`
    ///
    /// `offset` is the offset from the beginning of the page where the actual
    /// network data is
    ///
    /// `size` is the size in byte of the packet
    ///
    /// This method is unsafe because a few requirements are necessary:
    ///
    /// * `page` must be allocated with `__rust_allocate(PAGE_SIZE, PAGE_SIZE)`
    /// * `page` ownership is transferred to this packet (i.e. it will be
    ///   deallocated on `Drop`)
    pub unsafe fn new(page: *mut u8, offset: usize, size: usize) -> Self {
        Packet {
            page: page,
            data: page.offset(offset as isize),
            size: size,
            intf: None,
        }
    }

    #[inline]
    /// Returns the size of the packet
    pub fn size(&self) -> usize {
        self.size
    }

    #[inline]
    /// Returns the offset from `page()` where the data starts
    pub fn offset(&self) -> usize {
        self.data as usize - self.page as usize
    }

    #[inline]
    /// Returns the beginning of the page allocated
    pub fn page(&self) -> *const u8 {
        self.page
    }

    #[inline]
    /// Returns a pointer that points to the beginning of the network data
    pub fn start(&self) -> *const u8 {
        self.data
    }

    #[inline]
    /// Return the interface the packet is linked to
    pub fn interface(&self) -> Option<Arc<RwLock<Interface>>> {
        self.intf.as_ref().and_then(|weak| weak.upgrade())
    }

    #[inline]
    /// Set the interface the packet is linked to
    pub fn set_interface(&mut self, intf: Weak<RwLock<Interface>>) {
        self.intf = Some(intf);
    }
}

impl Drop for Packet {
    fn drop(&mut self) {
        __rust_deallocate(self.page, PAGE_SIZE, PAGE_SIZE);
    }
}
<commit_msg>net: Add size accessor for type Packet<commit_after>//! Network packet utility

use sync::{Arc, Weak};
use sync::spin::RwLock;

use net::Interface;

use alloc_uni::__rust_deallocate;

use hal::arch::defs::PAGE_SIZE;

/// A network packet
pub struct Packet {
    /// The page that contains the packet. This is aligned on PAGE_SIZE and
    /// must be allocated by __rust_allocate(PAGE_SIZE, PAGE_SIZE)
    page: *mut u8,
    /// Pointer to the start of the data.
    data: *mut u8,
    /// Size of the packet
    size: usize,
    /// The interface the packet was received on
    intf: Option<Weak<RwLock<Interface>>>,
    /// Size of the link header
    link_hdr_size: usize,
    /// Size of the network header
    net_hdr_size: usize,
    /// Size of the transport header
    tspt_hdr_size: usize,
}

impl Packet {
    /// Creates a new packet from a memory pointer `page`
    ///
    /// `offset` is the offset from the beginning of the page where the actual
    /// network data is
    ///
    /// `size` is the size in bytes of the packet
    ///
    /// This method is unsafe because a few requirements are necessary:
    ///
    /// * `page` must be allocated with `__rust_allocate(PAGE_SIZE, PAGE_SIZE)`
    /// * `page` ownership is transferred to this packet (i.e. it will be
    ///   deallocated on `Drop`)
    pub unsafe fn new(page: *mut u8, offset: usize, size: usize) -> Self {
        Packet {
            page: page,
            data: page.offset(offset as isize),
            size: size,
            intf: None,
            link_hdr_size: 0,
            net_hdr_size: 0,
            tspt_hdr_size: 0,
        }
    }

    #[inline]
    /// Returns the size of the packet
    pub fn size(&self) -> usize {
        self.size
    }

    #[inline]
    /// Returns the offset from `page()` where the data starts
    pub fn offset(&self) -> usize {
        self.data as usize - self.page as usize
    }

    #[inline]
    /// Returns the beginning of the allocated page
    pub fn page(&self) -> *const u8 {
        self.page
    }

    #[inline]
    /// Returns a pointer that points to the beginning of the network data
    pub fn start(&self) -> *const u8 {
        self.data
    }

    #[inline]
    /// Return the interface the packet is linked to
    pub fn interface(&self) -> Option<Arc<RwLock<Interface>>> {
        self.intf.as_ref().and_then(|weak| weak.upgrade())
    }

    #[inline]
    /// Set the interface the packet is linked to
    pub fn set_interface(&mut self, intf: Weak<RwLock<Interface>>) {
        self.intf = Some(intf);
    }

    #[inline]
    /// Returns the size of the link header
    ///
    /// This has to be set by the link layer handler
    pub fn link_hdr_size(&self) -> usize {
        self.link_hdr_size
    }

    #[inline]
    /// Returns a mutable reference to the size of the link header
    pub unsafe fn link_hdr_size_mut(&mut self) -> &mut usize {
        &mut self.link_hdr_size
    }

    #[inline]
    /// Returns the size of the network header
    ///
    /// This has to be set by the network layer handler
    pub fn net_hdr_size(&self) -> usize {
        self.net_hdr_size
    }

    #[inline]
    /// Returns a mutable reference to the size of the network header
    pub unsafe fn net_hdr_size_mut(&mut self) -> &mut usize {
        &mut self.net_hdr_size
    }

    #[inline]
    /// Returns the size of the transport header
    ///
    /// This has to be set by the transport layer handler
    pub fn tspt_hdr_size(&self) -> usize {
        self.tspt_hdr_size
    }

    #[inline]
    /// Returns a mutable reference to the size of the transport header
    pub unsafe fn tspt_hdr_size_mut(&mut self) -> &mut usize {
        &mut self.tspt_hdr_size
    }

    /// Returns the size of the payload.
    pub fn payload_size(&self) -> usize {
        self.size - self.link_hdr_size() - self.net_hdr_size() -
        self.tspt_hdr_size()
    }
}

impl Drop for Packet {
    fn drop(&mut self) {
        __rust_deallocate(self.page, PAGE_SIZE, PAGE_SIZE);
    }
}
<|endoftext|>