Warning, /sdk/rust-qt-binding-generator/demo/rust/src/implementation/processes.rs is written in an unsupported language. File is not indexed.

0001 // Copyright 2017  Jos van den Oever <jos@vandenoever.info>
0002 //
0003 // This program is free software; you can redistribute it and/or
0004 // modify it under the terms of the GNU General Public License as
0005 // published by the Free Software Foundation; either version 2 of
0006 // the License or (at your option) version 3 or any later version
0007 // accepted by the membership of KDE e.V. (or its successor approved
0008 // by the membership of KDE e.V.), which shall act as a proxy
0009 // defined in Section 14 of version 3 of the license.
0010 //
0011 // This program is distributed in the hope that it will be useful,
0012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 // GNU General Public License for more details.
0015 //
0016 // You should have received a copy of the GNU General Public License
0017 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018 
0019 use interface::*;
0020 use sysinfo::*;
0021 use std::sync::{Arc, Mutex};
0022 use std::collections::HashMap;
0023 use libc::pid_t;
0024 use std::thread;
0025 use std::time::Duration;
0026 use std::sync::mpsc::{channel, Sender, Receiver, RecvTimeoutError};
0027 
0028 struct ProcessItem {
0029     row: usize,
0030     tasks: Vec<pid_t>,
0031     process: Process,
0032 }
0033 
0034 #[derive(Default)]
0035 struct ProcessTree {
0036     top: Vec<pid_t>,
0037     processes: HashMap<pid_t, ProcessItem>,
0038     cpusum: f32,
0039 }
0040 
0041 enum ChangeState {
0042     Active,
0043     Inactive,
0044 }
0045 
0046 pub struct Processes {
0047     emit: ProcessesEmitter,
0048     model: ProcessesTree,
0049     p: ProcessTree,
0050     incoming: Arc<Mutex<Option<ProcessTree>>>,
0051     active: bool,
0052     channel: Sender<ChangeState>
0053 }
0054 
0055 fn check_process_hierarchy(parent: Option<pid_t>, processes: &HashMap<pid_t, Process>) {
0056     for (pid, process) in processes {
0057         assert_eq!(process.pid, *pid);
0058         if parent.is_some() {
0059             assert_eq!(process.parent, parent);
0060         }
0061         check_process_hierarchy(Some(*pid), &process.tasks);
0062     }
0063 }
0064 
0065 fn collect_processes(
0066     tasks: &HashMap<pid_t, Process>,
0067     mut processes: &mut HashMap<pid_t, ProcessItem>,
0068 ) -> f32 {
0069     let mut cpusum = 0.0;
0070     for process in tasks.values() {
0071         processes.insert(
0072             process.pid,
0073             ProcessItem {
0074                 row: 0,
0075                 tasks: Vec::new(),
0076                 process: process.clone(),
0077             },
0078         );
0079         let s = collect_processes(&process.tasks, &mut processes);
0080         cpusum += process.cpu_usage + s;
0081     }
0082     cpusum
0083 }
0084 
0085 // reconstruct process hierarchy
0086 fn handle_tasks(processes: &mut HashMap<pid_t, ProcessItem>) -> Vec<pid_t> {
0087     let mut top = Vec::new();
0088     let pids: Vec<pid_t> = processes.keys().cloned().collect();
0089     for pid in pids {
0090         if let Some(parent) = processes[&pid].process.parent {
0091             if let Some(p) = processes.get_mut(&parent) {
0092                 p.tasks.push(pid);
0093             } else {
0094                 top.push(pid);
0095             }
0096         } else {
0097             top.push(pid);
0098         }
0099     }
0100     top
0101 }
0102 
0103 fn update_rows(list: &[pid_t], processes: &mut HashMap<pid_t, ProcessItem>) {
0104     for (row, pid) in list.iter().enumerate() {
0105         processes.get_mut(pid).unwrap().row = row;
0106         let l = processes[pid].tasks.clone();
0107         update_rows(&l, processes);
0108     }
0109 }
0110 
0111 fn sort_tasks(p: &mut ProcessTree) {
0112     for process in p.processes.values_mut() {
0113         process.tasks.sort();
0114     }
0115     p.top.sort();
0116     update_rows(&p.top, &mut p.processes);
0117 }
0118 
0119 fn update() -> ProcessTree {
0120     let mut p = ProcessTree::default();
0121     let mut sysinfo = System::new();
0122     sysinfo.refresh_processes();
0123     let list = sysinfo.get_process_list();
0124     check_process_hierarchy(None, list);
0125     p.cpusum = collect_processes(list, &mut p.processes);
0126     p.top = handle_tasks(&mut p.processes);
0127     sort_tasks(&mut p);
0128     p
0129 }
0130 
0131 fn update_thread(
0132     mut emit: ProcessesEmitter,
0133     incoming: Arc<Mutex<Option<ProcessTree>>>,
0134     mut active: bool,
0135     status_channel: Receiver<ChangeState>,
0136 ) {
0137     thread::spawn(move || {
0138         loop {
0139             let timeout = if active {
0140                 *incoming.lock().unwrap() = Some(update());
0141                 emit.new_data_ready(None);
0142                 Duration::from_secs(1)
0143             } else {
0144                 Duration::from_secs(10_000)
0145             };
0146             match status_channel.recv_timeout(timeout) {
0147                 Err(RecvTimeoutError::Timeout) => {},
0148                 Err(RecvTimeoutError::Disconnected) => { return; },
0149                 Ok(ChangeState::Active) => { active = true; },
0150                 Ok(ChangeState::Inactive) => { active = false; },
0151             }
0152         }
0153     });
0154 }
0155 
0156 impl Processes {
0157     fn get(&self, index: usize) -> &ProcessItem {
0158         let pid = index as pid_t;
0159         &self.p.processes[&pid]
0160     }
0161     fn process(&self, index: usize) -> &Process {
0162         let pid = index as pid_t;
0163         &self.p.processes[&pid].process
0164     }
0165 }
0166 
0167 fn move_process(
0168     pid: pid_t,
0169     amap: &mut HashMap<pid_t, ProcessItem>,
0170     bmap: &mut HashMap<pid_t, ProcessItem>,
0171 ) {
0172     if let Some(e) = bmap.remove(&pid) {
0173         amap.insert(pid, e);
0174         let ts = amap[&pid].tasks.clone();
0175         for t in ts {
0176             move_process(t, amap, bmap);
0177         }
0178     }
0179 }
0180 
0181 fn remove_row(
0182     model: &mut ProcessesTree,
0183     parent: pid_t,
0184     row: usize,
0185     map: &mut HashMap<pid_t, ProcessItem>,
0186 ) {
0187     let pid = map[&parent].tasks[row];
0188     println!(
0189         "removing {} '{}' {}",
0190         pid,
0191         map[&pid].process.exe,
0192         map[&pid].process.cmd.join(" ")
0193     );
0194     model.begin_remove_rows(Some(parent as usize), row, row);
0195     map.remove(&pid);
0196     let len = {
0197         let tasks = &mut map.get_mut(&parent).unwrap().tasks;
0198         tasks.remove(row);
0199         tasks.len()
0200     };
0201     for r in row..len {
0202         let pid = map.get_mut(&parent).unwrap().tasks[r];
0203         map.get_mut(&pid).unwrap().row = r;
0204     }
0205     model.end_remove_rows();
0206 }
0207 
0208 fn insert_row(
0209     model: &mut ProcessesTree,
0210     parent: pid_t,
0211     row: usize,
0212     map: &mut HashMap<pid_t, ProcessItem>,
0213     pid: pid_t,
0214     source: &mut HashMap<pid_t, ProcessItem>,
0215 ) {
0216     println!(
0217         "adding {} '{}' {}",
0218         pid,
0219         source[&pid].process.exe,
0220         source[&pid].process.cmd.join(" ")
0221     );
0222     model.begin_insert_rows(Some(parent as usize), row, row);
0223     move_process(pid, map, source);
0224     let len = {
0225         let tasks = &mut map.get_mut(&parent).unwrap().tasks;
0226         tasks.insert(row, pid);
0227         tasks.len()
0228     };
0229     for r in row..len {
0230         let pid = map.get_mut(&parent).unwrap().tasks[r];
0231         map.get_mut(&pid).unwrap().row = r;
0232     }
0233     model.end_insert_rows();
0234 }
0235 
0236 fn cmp_f32(a: f32, b: f32) -> bool {
0237     ((a - b) / a).abs() < 0.01
0238 }
0239 
0240 fn sync_row(model: &mut ProcessesTree, pid: pid_t, a: &mut Process, b: &Process) -> f32 {
0241     let mut changed = a.name != b.name;
0242     if changed {
0243         a.name.clone_from(&b.name);
0244     }
0245     if !cmp_f32(a.cpu_usage, b.cpu_usage) {
0246         a.cpu_usage = b.cpu_usage;
0247         changed = true;
0248     }
0249     if a.cmd != b.cmd {
0250         a.cmd.clone_from(&b.cmd);
0251         changed = true;
0252     }
0253     if a.exe != b.exe {
0254         a.exe.clone_from(&b.exe);
0255         changed = true;
0256     }
0257     if a.memory != b.memory {
0258         a.memory = b.memory;
0259         changed = true;
0260     }
0261     if changed {
0262         model.data_changed(pid as usize, pid as usize);
0263     }
0264     b.cpu_usage
0265 }
0266 
0267 fn sync_tree(
0268     model: &mut ProcessesTree,
0269     parent: pid_t,
0270     amap: &mut HashMap<pid_t, ProcessItem>,
0271     bmap: &mut HashMap<pid_t, ProcessItem>,
0272 ) -> f32 {
0273     let mut a = 0;
0274     let mut b = 0;
0275     let mut alen = amap[&parent].tasks.len();
0276     let blen = bmap[&parent].tasks.len();
0277     let mut cpu_total = bmap[&parent].process.cpu_usage;
0278 
0279     while a < alen && b < blen {
0280         let apid = amap[&parent].tasks[a];
0281         let bpid = bmap[&parent].tasks[b];
0282         if apid < bpid {
0283             // a process has disappeared
0284             remove_row(model, parent, a, amap);
0285             alen -= 1;
0286         } else if apid > bpid {
0287             // a process has appeared
0288             insert_row(model, parent, a, amap, bpid, bmap);
0289             cpu_total += amap[&bpid].process.cpu_usage;
0290             a += 1;
0291             alen += 1;
0292             b += 1;
0293         } else {
0294             cpu_total += sync_row(
0295                 model,
0296                 apid,
0297                 &mut amap.get_mut(&apid).unwrap().process,
0298                 &bmap[&apid].process,
0299             );
0300             cpu_total += sync_tree(model, apid, amap, bmap);
0301             a += 1;
0302             b += 1;
0303         }
0304     }
0305     while a < blen {
0306         let bpid = bmap[&parent].tasks[b];
0307         insert_row(model, parent, a, amap, bpid, bmap);
0308         a += 1;
0309         alen += 1;
0310         b += 1;
0311     }
0312     while b < alen {
0313         remove_row(model, parent, a, amap);
0314         alen -= 1;
0315     }
0316     if !cmp_f32(cpu_total, bmap[&parent].process.cpu_usage) {
0317         amap.get_mut(&parent).unwrap().process.cpu_usage = cpu_total;
0318         model.data_changed(parent as usize, parent as usize);
0319     }
0320     assert_eq!(a, b);
0321     cpu_total
0322 }
0323 
0324 impl ProcessesTrait for Processes {
0325     fn new(mut emit: ProcessesEmitter, model: ProcessesTree) -> Processes {
0326         let (tx, rx) = channel();
0327         let p = Processes {
0328             emit: emit.clone(),
0329             model,
0330             p: ProcessTree::default(),
0331             incoming: Arc::new(Mutex::new(None)),
0332             active: false,
0333             channel: tx,
0334         };
0335         update_thread(emit, p.incoming.clone(), p.active, rx);
0336         p
0337     }
0338     fn emit(&mut self) -> &mut ProcessesEmitter {
0339         &mut self.emit
0340     }
0341     fn row_count(&self, index: Option<usize>) -> usize {
0342         if let Some(index) = index {
0343             self.get(index).tasks.len()
0344         } else {
0345             self.p.top.len()
0346         }
0347     }
0348     fn index(&self, index: Option<usize>, row: usize) -> usize {
0349         if let Some(index) = index {
0350             self.get(index).tasks[row] as usize
0351         } else {
0352             self.p.top[row] as usize
0353         }
0354     }
0355     fn parent(&self, index: usize) -> Option<usize> {
0356         let pid = index as pid_t;
0357         if self.p.top.contains(&pid) {
0358             None
0359         } else {
0360             self.get(index).process.parent.map(|pid| pid as usize)
0361         }
0362     }
0363     fn can_fetch_more(&self, index: Option<usize>) -> bool {
0364         if index.is_some() || !self.active {
0365             return false;
0366         }
0367         if let Ok(ref incoming) = self.incoming.try_lock() {
0368             incoming.is_some()
0369         } else {
0370             false
0371         }
0372     }
0373     fn fetch_more(&mut self, index: Option<usize>) {
0374         if index.is_some() || !self.active {
0375             return;
0376         }
0377         let new = if let Ok(ref mut incoming) = self.incoming.try_lock() {
0378             incoming.take()
0379         } else {
0380             None
0381         };
0382         if let Some(mut new) = new {
0383             // alert! at the top level, only adding is supported!
0384             if self.p.top.is_empty() {
0385                 self.model.begin_reset_model();
0386                 self.p = new;
0387                 self.model.end_reset_model();
0388             } else {
0389                 let top = self.p.top.clone();
0390                 for pid in top {
0391                     sync_tree(&mut self.model, pid, &mut self.p.processes, &mut new.processes);
0392                 }
0393             }
0394         }
0395     }
0396     fn row(&self, index: usize) -> usize {
0397         self.get(index).row
0398     }
0399     fn check_row(&self, index: usize, _row: usize) -> Option<usize> {
0400         let pid = index as pid_t;
0401         if self.p.processes.contains_key(&pid) {
0402             Some(self.row(index))
0403         } else {
0404             None
0405         }
0406     }
0407     fn pid(&self, index: usize) -> u32 {
0408         self.process(index).pid as u32
0409     }
0410     fn uid(&self, index: usize) -> u32 {
0411         self.process(index).uid as u32
0412     }
0413     fn cpu_usage(&self, index: usize) -> f32 {
0414         self.process(index).cpu_usage
0415     }
0416     fn cpu_percentage(&self, index: usize) -> u8 {
0417         let cpu = self.process(index).cpu_usage / self.p.cpusum;
0418         (cpu * 100.0) as u8
0419     }
0420     fn memory(&self, index: usize) -> u64 {
0421         self.process(index).memory
0422     }
0423     fn name(&self, index: usize) -> &str {
0424         &self.process(index).name
0425     }
0426     fn cmd(&self, index: usize) -> String {
0427         self.process(index).cmd.join(" ")
0428     }
0429     fn active(&self) -> bool {
0430         self.active
0431     }
0432     fn set_active(&mut self, active: bool) {
0433         if self.active != active {
0434             self.active = active;
0435             if active {
0436                 self.channel.send(ChangeState::Active)
0437             } else {
0438                 self.channel.send(ChangeState::Inactive)
0439             }.expect("Process thread died.");
0440         }
0441     }
0442 }