pperf 0.1.2

query process performance
Documentation
use std::{fs::File, io::Read};

use crate::error::Error;

/// 进程性能查询工具
pub struct PPerfTool {
    pid: u32,
    last_time: u64,
}

impl PPerfTool {
    /// 创建进程性能查询工具实例
    ///
    /// 该实例目前仅支持linux系统
    #[cfg(target_os = "linux")]
    pub fn new() -> Result<PPerfTool, Error> {
        Ok(PPerfTool {
            pid: std::process::id(),
            last_time: 0,
        })
    }

    #[cfg(not(target_os = "linux"))]
    pub fn new() -> Result<PPerfTool, Error> {
        Err(Error::OSNotSupportError)
    }

    /// 获取cpu时间(单位jiffies,即1/Hz)
    ///
    /// ```rust
    /// use crate::pperf::tool;
    /// let t = tool::PPerfTool::new().unwrap();
    /// let cputime = t.get_process_cpu_time();
    /// println!("get_process_cpu_time: {:?}", cputime)
    /// ```
    pub fn get_process_cpu_time(&self) -> Result<u64, Error> {
        let mut raw_stat = String::new();
        let path = format!("/proc/{}/stat", self.pid);
        let mut f = File::open(path)?;
        f.read_to_string(&mut raw_stat)?;
        let fields = raw_stat.split(" ");
        let utime = fields
            .clone()
            .nth(13)
            .ok_or(Error::GetFieldError)?
            .trim()
            .parse::<u64>()?;
        let stime = fields
            .clone()
            .nth(14)
            .ok_or(Error::GetFieldError)?
            .trim()
            .parse::<u64>()?;
        let cutime = fields
            .clone()
            .nth(15)
            .ok_or(Error::GetFieldError)?
            .trim()
            .parse::<u64>()?;
        let cstime = fields
            .clone()
            .nth(16)
            .ok_or(Error::GetFieldError)?
            .trim()
            .parse::<u64>()?;
        return Ok(utime + stime + cutime + cstime);
    }

    /// 获取cpu增量时间(单位jiffies,即1/Hz,与上次获取cpu时间的增量)
    ///
    /// ```rust
    /// use crate::pperf::tool;
    /// let mut t = tool::PPerfTool::new().unwrap();
    /// let incr_cputime = t.get_process_cpu_incr_time();
    /// println!("get_process_cpu_incr_time: {:?}", incr_cputime)
    /// ```
    pub fn get_process_cpu_incr_time(&mut self) -> Result<u64, Error> {
        let cpu_time = self.get_process_cpu_time()?;
        let incr_time = cpu_time - self.last_time;
        self.last_time = cpu_time;
        Ok(incr_time)
    }

    /// 获取进程rss内存
    ///
    /// ```rust
    /// use crate::pperf::tool;
    /// let mut t = tool::PPerfTool::new().unwrap();
    /// let rss = t.get_process_mem_rss();
    /// println!("get_process_mem_rss: {:?}", rss)
    /// ```
    pub fn get_process_mem_rss(&self) -> Result<u64, Error> {
        let mut raw_stat = String::new();
        let path = format!("/proc/{}/stat", self.pid);
        let mut f = File::open(path)?;
        f.read_to_string(&mut raw_stat)?;
        let fields = raw_stat.split(" ");
        Ok(fields
            .clone()
            .nth(23)
            .ok_or(Error::GetFieldError)?
            .trim()
            .parse::<u64>()?)
    }

    /// 获取进程vsize内存
    ///
    /// ```rust
    /// use crate::pperf::tool;
    /// let mut t = tool::PPerfTool::new().unwrap();
    /// let vsize = t.get_process_mem_vsize();
    /// println!("get_process_mem_vsize: {:?}", vsize)
    /// ```
    pub fn get_process_mem_vsize(&self) -> Result<u64, Error> {
        let mut raw_stat = String::new();
        let path = format!("/proc/{}/stat", self.pid);
        let mut f = File::open(path)?;
        f.read_to_string(&mut raw_stat)?;
        let fields = raw_stat.split(" ");
        Ok(fields
            .clone()
            .nth(22)
            .ok_or(Error::GetFieldError)?
            .trim()
            .parse::<u64>()?)
    }

    /// 获取进程uss内存
    ///
    /// ```rust
    /// use crate::pperf::tool;
    /// let mut t = tool::PPerfTool::new().unwrap();
    /// let uss = t.get_process_mem_uss();
    /// println!("get_process_mem_uss: {:?}", uss)
    /// ```
    pub fn get_process_mem_uss(&self) -> Result<u64, Error> {
        let mut uss = 0u64;
        let mut raw_smaps = String::new();
        let path = format!("/proc/{}/smaps", self.pid);
        let mut f = File::open(path)?;
        f.read_to_string(&mut raw_smaps)?;
        let lines = raw_smaps.split("\n");
        for line in lines {
            if line.starts_with("Private_Clean:") {
                let val = line
                    .strip_prefix("Private_Clean:")
                    .ok_or(Error::ParseError)?
                    .strip_suffix("kB")
                    .ok_or(Error::ParseError)?
                    .trim()
                    .parse::<u64>()?;
                uss = uss + val;
            }
            if line.starts_with("Private_Dirty:") {
                let val = line
                    .strip_prefix("Private_Dirty:")
                    .ok_or(Error::ParseError)?
                    .strip_suffix("kB")
                    .ok_or(Error::ParseError)?
                    .trim()
                    .parse::<u64>()?;
                uss = uss + val;
            }
        }

        Ok(uss)
    }
}

#[test]
fn test_get_process_cpu_time() {
    let tool = PPerfTool::new();
    println!(
        "get_process_cpu_incr_time: {:?}",
        tool.unwrap().get_process_cpu_incr_time().unwrap()
    )
}