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 }