summaryrefslogtreecommitdiffstats
path: root/src/data_collection/processes/unix/process_ext.rs
blob: feabfea13db60f73f6c8d66fc4237c3b4cbeeb84 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! Shared process data harvesting code from macOS and FreeBSD via sysinfo.

use std::{io, time::Duration};

use hashbrown::HashMap;
use sysinfo::{ProcessStatus, System};

use super::ProcessHarvest;
use crate::{data_collection::processes::UserTable, utils::error, Pid};

pub(crate) trait UnixProcessExt {
    fn sysinfo_process_data(
        sys: &System, use_current_cpu_total: bool, unnormalized_cpu: bool, total_memory: u64,
        user_table: &mut UserTable,
    ) -> error::Result<Vec<ProcessHarvest>> {
        let mut process_vector: Vec<ProcessHarvest> = Vec::new();
        let process_hashmap = sys.processes();
        let cpu_usage = sys.global_cpu_info().cpu_usage() as f64 / 100.0;
        let num_processors = sys.cpus().len() as f64;

        for process_val in process_hashmap.values() {
            let name = if process_val.name().is_empty() {
                let process_cmd = process_val.cmd();
                if process_cmd.len() > 1 {
                    process_cmd[0].clone()
                } else {
                    process_val
                        .exe()
                        .and_then(|exe| exe.file_stem())
                        .and_then(|stem| stem.to_str())
                        .map(|s| s.to_string())
                        .unwrap_or(String::new())
                }
            } else {
                process_val.name().to_string()
            };
            let command = {
                let command = process_val.cmd().join(" ");
                if command.is_empty() {
                    name.to_string()
                } else {
                    command
                }
            };

            let pcu = {
                let usage = process_val.cpu_usage() as f64;
                if unnormalized_cpu || num_processors == 0.0 {
                    usage
                } else {
                    usage / num_processors
                }
            };
            let process_cpu_usage = if use_current_cpu_total && cpu_usage > 0.0 {
                pcu / cpu_usage
            } else {
                pcu
            } as f32;

            let disk_usage = process_val.disk_usage();
            let process_state = {
                let ps = process_val.status();
                (ps.to_string(), convert_process_status_to_char(ps))
            };
            let uid = process_val.user_id().map(|u| **u);
            let pid = process_val.pid().as_u32() as Pid;
            process_vector.push(ProcessHarvest {
                pid,
                parent_pid: Self::parent_pid(process_val),
                name,
                command,
                mem_usage_percent: if total_memory > 0 {
                    (process_val.memory() as f64 * 100.0 / total_memory as f64) as f32
                } else {
                    0.0
                },
                mem_usage_bytes: process_val.memory(),
                cpu_usage_percent: process_cpu_usage,
                read_bytes_per_sec: disk_usage.read_bytes,
                write_bytes_per_sec: disk_usage.written_bytes,
                total_read_bytes: disk_usage.total_read_bytes,
                total_write_bytes: disk_usage.total_written_bytes,
                process_state,
                uid,
                user: uid
                    .and_then(|uid| {
                        user_table
                            .get_uid_to_username_mapping(uid)
                            .map(Into::into)
                            .ok()
                    })
                    .unwrap_or_else(|| "N/A".into()),
                time: Duration::from_secs(process_val.run_time()),
                #[cfg(feature = "gpu")]
                gpu_mem: 0,
                #[cfg(feature = "gpu")]
                gpu_mem_percent: 0.0,
                #[cfg(feature = "gpu")]
                gpu_util: 0,
            });
        }

        if Self::has_backup_proc_cpu_fn() {
            let unknown_state = ProcessStatus::Unknown(0).to_string();
            let cpu_usage_unknown_pids: Vec<Pid> = process_vector
                .iter()
                .filter(|process| process.process_state.0 == unknown_state)
                .map(|process| process.pid)
                .collect();
            let cpu_usages = Self::backup_proc_cpu(&cpu_usage_unknown_pids)?;
            for process in &mut process_vector {
                if cpu_usages.contains_key(&process.pid) {
                    process.cpu_usage_percent = if unnormalized_cpu || num_processors == 0.0 {
                        *cpu_usages.get(&process.pid).unwrap()
                    } else {
                        *cpu_usages.get(&process.pid).unwrap() / num_processors
                    } as f32;
                }
            }
        }

        Ok(process_vector)
    }

    #[inline]
    fn has_backup_proc_cpu_fn() -> bool {
        false
    }

    fn backup_proc_cpu(_pids: &[Pid]) -> io::Result<HashMap<Pid, f64>> {
        Ok(HashMap::default())
    }

    fn parent_pid(process_val: &sysinfo::Process) -> Option<Pid> {
        process_val.parent().map(|p| p.as_u32() as _)
    }
}

fn convert_process_status_to_char(status: ProcessStatus) -> char {
    match status {
        ProcessStatus::Run => 'R',
        ProcessStatus::Sleep => 'S',
        ProcessStatus::Idle => 'D',
        ProcessStatus::Zombie => 'Z',
        _ => '?',
    }
}