summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2019-10-25 03:17:06 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2019-10-25 03:17:06 +0300
commit51f37839aa8b7b8a3888377ecd8e50791b2bd590 (patch)
tree3831930fe7715326e0a4e79a46e69ecd20d6b6c2 /src
parent73a34a5306f56c1ebe15a71f956480211fdf180c (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.
Diffstat (limited to 'src')
-rw-r--r--src/main.rs9
-rw-r--r--src/thread.rs221
-rw-r--r--src/ui/components/processes.rs206
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)
+}