diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2019-10-25 03:17:06 +0300 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2019-10-25 03:17:06 +0300 |
commit | 51f37839aa8b7b8a3888377ecd8e50791b2bd590 (patch) | |
tree | 3831930fe7715326e0a4e79a46e69ecd20d6b6c2 | |
parent | 73a34a5306f56c1ebe15a71f956480211fdf180c (diff) |
Remove thread.rs, do process list update sync
Do process list update synchronously instead of in a thread; that was
too slow after all.
-rw-r--r-- | src/main.rs | 9 | ||||
-rw-r--r-- | src/thread.rs | 221 | ||||
-rw-r--r-- | src/ui/components/processes.rs | 206 |
3 files changed, 191 insertions, 245 deletions
diff --git a/src/main.rs b/src/main.rs index 94df77a..28bb412 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,8 +38,6 @@ use std::time::Duration; mod ui; use ui::*; -mod thread; -use thread::start_thread; fn notify(signals: &[c_int]) -> Result<crossbeam::channel::Receiver<c_int>, Error> { let (s, r) = bounded(100); @@ -70,15 +68,14 @@ fn main() -> Result<(), Error> { let mut state = State::new(); let receiver = state.receiver(); - - /* Start data update thread (src/thread.rs) */ - let (s, r) = start_thread(); let window = Box::new(Window::new( Box::new(ui::components::KernelMetrics::new()), - Box::new(ui::components::ProcessList::new(s, r)), + Box::new(ui::components::ProcessList::new()), )); state.register_component(window); + state.render(); + state.redraw(true); /* Keep track of the input mode. See ui::UIMode for details */ 'main: loop { diff --git a/src/thread.rs b/src/thread.rs deleted file mode 100644 index c98d7f7..0000000 --- a/src/thread.rs +++ /dev/null @@ -1,221 +0,0 @@ -/* - * bb - * - * Copyright 2019 Manos Pitsidianakis - * - * This file is part of bb. - * - * bb is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * bb is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with bb. If not, see <http://www.gnu.org/licenses/>. - */ - -use crate::ui::components::processes::State; -use crate::ui::components::*; -use crossbeam::channel::bounded; -use crossbeam::{Receiver, Sender}; -use std::collections::HashMap; -use std::fs::File; -use std::io::prelude::*; -use std::path::PathBuf; -use std::str::FromStr; - -struct ProcessData { - cpu_stat: Stat, - processes_times: HashMap<Pid, usize>, -} - -pub fn start_thread() -> (Sender<Vec<ProcessDisplay>>, Receiver<Vec<ProcessDisplay>>) { - let (s, r): (Sender<Vec<ProcessDisplay>>, Receiver<Vec<ProcessDisplay>>) = bounded(1); /* ours */ - let (ts, tr): (Sender<Vec<ProcessDisplay>>, Receiver<Vec<ProcessDisplay>>) = bounded(1); /* theirs */ - ts.send(Vec::with_capacity(1024)).unwrap(); - std::thread::spawn(move || { - let mut data = ProcessData { - cpu_stat: get_stat(&mut 0).remove(0), - processes_times: Default::default(), - }; - loop { - let mut processes = match tr.recv() { - Ok(n) => n, - Err(e) => panic!(e), - }; - processes.clear(); - - let cpu_stat = get_stat(&mut 0).remove(0); - for entry in std::fs::read_dir("/proc/").unwrap() { - let dir = entry.unwrap(); - if let Some(fname) = dir.file_name().to_str() { - if !fname.chars().all(|c| c.is_numeric()) { - continue; - } - } else { - continue; - } - - let process = if let Ok(p) = get_pid_info(dir.path()) { - p - } else { - continue; - }; - - if process.cmd_line.is_empty() { - /* This is a kernel thread, skip for now */ - continue; - } - - let mut process_display = ProcessDisplay { - i: process.pid, - pid: PidString(process.pid.to_string()), - ppid: PpidString(process.ppid.to_string()), - vm_rss: VmRssString(Bytes(process.vm_rss * 1024).as_convenient_string()), - cpu_percent: (100.0 - * ((process.utime - - data - .processes_times - .get(&process.pid) - .map(|v| *v) - .unwrap_or(process.utime)) as f64 - / ((cpu_stat.total_time() - data.cpu_stat.total_time()) as f64))) - as usize, - utime: process.utime, - state: process.state, - cmd_line: CmdLineString(process.cmd_line), - username: UserString(crate::ui::username(process.uid)), - }; - if process_display.cpu_percent > 100 { - process_display.cpu_percent = 0; - } - - data.processes_times - .insert(process.pid, process_display.utime); - - processes.push(process_display); - } - data.cpu_stat = cpu_stat; - - s.send(processes).unwrap(); - } - }); - - (ts, r) -} - -/* Might return Error if process has disappeared - * during the function's run */ -fn get_pid_info(mut path: PathBuf) -> Result<Process, std::io::Error> { - /* proc file structure can be found in man 5 proc.*/ - path.push("status"); - let mut file: File = File::open(&path)?; - let mut res = String::with_capacity(2048); - file.read_to_string(&mut res)?; - let mut lines_iter = res.lines(); - let mut ret = Process { - pid: 0, - ppid: 0, - vm_rss: 0, - uid: 0, - utime: 0, - state: State::Waiting, - cmd_line: String::new(), - }; - let mut line; - - macro_rules! err { - ($res:expr) => { - match $res { - Ok(v) => v, - Err(_) => { - return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "")); - } - } - }; - } - - macro_rules! none_err { - ($res:expr) => { - if let Some(v) = $res { - v - } else { - return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "")); - } - }; - } - - let mut b = 0; - while b < 5 { - let line_opt = lines_iter.next(); - if line_opt.is_none() { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - format!("{} returned malformed input", path.display()), - )); - } - line = none_err!(line_opt); - let mut mut_value_iter = line.split_whitespace(); - match mut_value_iter.next() { - Some("VmRSS:") => { - ret.vm_rss = err!(usize::from_str(none_err!(mut_value_iter.next()))); - b += 1; - } - Some("State:") => { - ret.state = State::from(none_err!(none_err!(mut_value_iter.next()).chars().next())); - b += 1; - } - Some("Pid:") => { - ret.pid = err!(i32::from_str(none_err!(mut_value_iter.next()))); - b += 1; - } - Some("PPid:") => { - ret.ppid = err!(i32::from_str(none_err!(mut_value_iter.next()))); - b += 1; - } - Some("Uid:") => { - ret.uid = err!(u32::from_str(none_err!(mut_value_iter.next()))); - b += 1; - } - None => { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidData, - format!( - "{} returned malformed input. Original error was while parsing file", - path.display(), - ), - )); - } - _ => {} - } - } - - path.pop(); - path.push("cmdline"); - let mut file: File = File::open(&path)?; - res.clear(); - file.read_to_string(&mut res)?; - if !res.is_empty() { - /* values are separated by null bytes */ - ret.cmd_line = format!("{}", res.split('\0').collect::<Vec<&str>>().join(" ")); - } - path.pop(); - path.push("stat"); - let mut file: File = File::open(&path)?; - res.clear(); - file.read_to_string(&mut res)?; - /* values are separated by whitespace and are in a specific order */ - if !res.is_empty() { - let mut vals = res.split_whitespace().skip(13); - ret.utime = err!(usize::from_str(none_err!(vals.next()))); - ret.utime += err!(usize::from_str(none_err!(vals.next()))); - ret.utime += err!(usize::from_str(none_err!(vals.next()))); - ret.utime += err!(usize::from_str(none_err!(vals.next()))); - } - Ok(ret) -} diff --git a/src/ui/components/processes.rs b/src/ui/components/processes.rs index e563e3c..631240f 100644 --- a/src/ui/components/processes.rs +++ b/src/ui/components/processes.rs @@ -20,7 +20,16 @@ */ use super::*; -use crossbeam::{Receiver, Sender}; +use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; +use std::path::PathBuf; +use std::str::FromStr; +#[derive(Debug)] +pub struct ProcessData { + cpu_stat: Stat, + processes_times: HashMap<Pid, usize>, +} const SIGNAL_LIST: &[(i32, &'static str)] = &[ (1, "1 HUP"), @@ -132,10 +141,9 @@ pub struct ProcessDisplay { /* process list components */ #[derive(Debug)] pub struct ProcessList { - sender: Sender<Vec<ProcessDisplay>>, - rcver: Receiver<Vec<ProcessDisplay>>, page_movement: Option<PageMovement>, cpu_stat: Stat, + data: ProcessData, cursor: usize, height: usize, dirty: bool, @@ -225,13 +233,16 @@ impl fmt::Display for ProcessList { } impl ProcessList { - pub fn new(sender: Sender<Vec<ProcessDisplay>>, rcver: Receiver<Vec<ProcessDisplay>>) -> Self { + pub fn new() -> Self { + let data = ProcessData { + cpu_stat: get_stat(&mut 0).remove(0), + processes_times: Default::default(), + }; ProcessList { - sender, - rcver, cursor: 0, page_movement: None, cpu_stat: get_stat(&mut 0).remove(0), + data, processes: Vec::with_capacity(1024), processes_times: Default::default(), height: 0, @@ -302,18 +313,10 @@ impl Component for ProcessList { tick = true; } - let update_maxima = if tick && !self.freeze { - match self.rcver.recv_timeout(std::time::Duration::new(0, 0)) { - Ok(new_processes) => { - self.sender - .send(std::mem::replace(&mut self.processes, new_processes)) - .unwrap(); - true - } - Err(_) => false, - } - } else { - false + let update_maxima = tick && !self.freeze; + + if update_maxima { + self.processes = get(&mut self.data); }; if tick || self.freeze { @@ -815,3 +818,170 @@ fn executable_path_color(p: &CmdLineString) -> Result<(&str, &str, &str), (&str, } } } + +fn get(data: &mut ProcessData) -> Vec<ProcessDisplay> { + let mut processes = Vec::with_capacity(2048); + let cpu_stat = get_stat(&mut 0).remove(0); + for entry in std::fs::read_dir("/proc/").unwrap() { + let dir = entry.unwrap(); + if let Some(fname) = dir.file_name().to_str() { + if !fname.chars().all(|c| c.is_numeric()) { + continue; + } + } else { + continue; + } + + let process = if let Ok(p) = get_pid_info(dir.path()) { + p + } else { + continue; + }; + + if process.cmd_line.is_empty() { + /* This is a kernel thread, skip for now */ + continue; + } + + let mut process_display = ProcessDisplay { + i: process.pid, + pid: PidString(process.pid.to_string()), + ppid: PpidString(process.ppid.to_string()), + vm_rss: VmRssString(Bytes(process.vm_rss * 1024).as_convenient_string()), + cpu_percent: (100.0 + * ((process.utime + - data + .processes_times + .get(&process.pid) + .map(|v| *v) + .unwrap_or(process.utime)) as f64 + / ((cpu_stat.total_time() - data.cpu_stat.total_time()) as f64))) + as usize, + utime: process.utime, + state: process.state, + cmd_line: CmdLineString(process.cmd_line), + username: UserString(crate::ui::username(process.uid)), + }; + if process_display.cpu_percent > 100 { + process_display.cpu_percent = 0; + } + + data.processes_times + .insert(process.pid, process_display.utime); + + processes.push(process_display); + } + data.cpu_stat = cpu_stat; + processes +} + +/* Might return Error if process has disappeared + * during the function's run */ +fn get_pid_info(mut path: PathBuf) -> Result<Process, std::io::Error> { + /* proc file structure can be found in man 5 proc.*/ + path.push("status"); + let mut file: File = File::open(&path)?; + let mut res = String::with_capacity(2048); + file.read_to_string(&mut res)?; + let mut lines_iter = res.lines(); + let mut ret = Process { + pid: 0, + ppid: 0, + vm_rss: 0, + uid: 0, + utime: 0, + state: State::Waiting, + cmd_line: String::new(), + }; + let mut line; + + macro_rules! err { + ($res:expr) => { + match $res { + Ok(v) => v, + Err(_) => { + return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "")); + } + } + }; + } + + macro_rules! none_err { + ($res:expr) => { + if let Some(v) = $res { + v + } else { + return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "")); + } + }; + } + + let mut b = 0; + while b < 5 { + let line_opt = lines_iter.next(); + if line_opt.is_none() { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("{} returned malformed input", path.display()), + )); + } + line = none_err!(line_opt); + let mut mut_value_iter = line.split_whitespace(); + match mut_value_iter.next() { + Some("VmRSS:") => { + ret.vm_rss = err!(usize::from_str(none_err!(mut_value_iter.next()))); + b += 1; + } + Some("State:") => { + ret.state = State::from(none_err!(none_err!(mut_value_iter.next()).chars().next())); + b += 1; + } + Some("Pid:") => { + ret.pid = err!(i32::from_str(none_err!(mut_value_iter.next()))); + b += 1; + } + Some("PPid:") => { + ret.ppid = err!(i32::from_str(none_err!(mut_value_iter.next()))); + b += 1; + } + Some("Uid:") => { + ret.uid = err!(u32::from_str(none_err!(mut_value_iter.next()))); + b += 1; + } + None => { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!( + "{} returned malformed input. Original error was while parsing file", + path.display(), + ), + )); + } + _ => {} + } + } + + path.pop(); + path.push("cmdline"); + let mut file: File = File::open(&path)?; + res.clear(); + file.read_to_string(&mut res)?; + if !res.is_empty() { + /* values are separated by null bytes */ + ret.cmd_line = format!("{}", res.split('\0').collect::<Vec<&str>>().join(" ")); + } + path.pop(); + path.push("stat"); + let mut file: File = File::open(&path)?; + res.clear(); + file.read_to_string(&mut res)?; + /* values are separated by whitespace and are in a specific order */ + if !res.is_empty() { + let mut vals = res.split_whitespace().skip(13); + ret.utime = err!(usize::from_str(none_err!(vals.next()))); + ret.utime += err!(usize::from_str(none_err!(vals.next()))); + ret.utime += err!(usize::from_str(none_err!(vals.next()))); + ret.utime += err!(usize::from_str(none_err!(vals.next()))); + } + Ok(ret) +} |