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 }