Repository: GuillaumeGomez/sysinfo (Rust)

Issue:
On Linux `ProcessExt::start_time()` does not return the time since the epoch as documented
It seems to just return `(22) starttime` from `/proc/[pid]/stat`, see `man proc`: "The time the process started after system boot."

`info.process(get_current_pid().unwrap()).unwrap().start_time()` is much smaller than the expected 1642369341.

Note that just adding `boot_time()` only works half the time and is otherwise off by one second, are fractions lost somewhere along the way?

    info.process(get_current_pid().unwrap()).unwrap().start_time() + info.boot_time() = 1642369340
    SystemTime::now().duration_since(UNIX_EPOCH).unwrap()                             = 1642369341


Discussion:
It's pretty bad in any case. Interested into sending a PR? Otherwise I'll try to fix it as soon as possible.
Nice, thanks. And it seems the remaining occasional one second difference also happens with `ps`:

```shell
$ date; ps -eo lstart,args | tail -2       
Mon 17 Jan 21:54:02 UTC 2022
Mon 17 Jan 21:54:01 2022 ps -eo lstart,args
Mon 17 Jan 21:54:01 2022 tail -2
```
It's because of the uptime apparently. Not sure why though...
If there is a difference between the integer component of `SystemTime::now().duration_since(UNIX_EPOCH)` (which `.as_secs()` truncates) and the `get_current_pid()....start_time()`, then it is between 1.010 and 1.350, I have never seen 1.4 or even 1.5, so it is not some rounding error. Ah well, good enough for seconds :)



Patch:
diff --git a/src/linux/process.rs b/src/linux/process.rs
index 704c59fc2..55a9d699f 100644
--- a/src/linux/process.rs
+++ b/src/linux/process.rs
@@ -10,7 +10,7 @@ use std::str::FromStr;
 
 use libc::{gid_t, kill, uid_t};
 
-use crate::sys::system::REMAINING_FILES;
+use crate::sys::system::{SystemInfo, REMAINING_FILES};
 use crate::sys::utils::{get_all_data, get_all_data_from_file, realpath};
 use crate::utils::into_iter;
 use crate::{DiskUsage, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal};
@@ -82,6 +82,7 @@ pub struct Process {
     stime: u64,
     old_utime: u64,
     old_stime: u64,
+    start_time_without_boot_time: u64,
     start_time: u64,
     run_time: u64,
     pub(crate) updated: bool,
@@ -101,7 +102,12 @@ pub struct Process {
 }
 
 impl Process {
-    pub(crate) fn new(pid: Pid, parent: Option<Pid>, start_time: u64) -> Process {
+    pub(crate) fn new(
+        pid: Pid,
+        parent: Option<Pid>,
+        start_time_without_boot_time: u64,
+        info: &SystemInfo,
+    ) -> Process {
         Process {
             name: String::with_capacity(20),
             pid,
@@ -119,7 +125,8 @@ impl Process {
             old_utime: 0,
             old_stime: 0,
             updated: true,
-            start_time,
+            start_time_without_boot_time,
+            start_time: start_time_without_boot_time + info.boot_time,
             run_time: 0,
             uid: 0,
             gid: 0,
@@ -293,10 +300,9 @@ unsafe impl<'a, T> Sync for Wrap<'a, T> {}
 pub(crate) fn _get_process_data(
     path: &Path,
     proc_list: &mut Process,
-    page_size_kb: u64,
     pid: Pid,
     uptime: u64,
-    clock_cycle: u64,
+    info: &SystemInfo,
     refresh_kind: ProcessRefreshKind,
 ) -> Result<(Option<Process>, Pid), ()> {
     let pid = match path.file_name().and_then(|x| x.to_str()).map(Pid::from_str) {
@@ -330,11 +336,10 @@ pub(crate) fn _get_process_data(
             path,
             entry,
             &parts,
-            page_size_kb,
             parent_memory,
             parent_virtual_memory,
             uptime,
-            clock_cycle,
+            info,
             refresh_kind,
         );
         if refresh_kind.disk_usage() {
@@ -361,8 +366,9 @@ pub(crate) fn _get_process_data(
         }
     };
 
-    let start_time = u64::from_str(parts[21]).unwrap_or(0) / clock_cycle;
-    let mut p = Process::new(pid, parent_pid, start_time);
+    // To be noted that the start time is invalid here, it still needs
+    let start_time = u64::from_str(parts[21]).unwrap_or(0) / info.clock_cycle;
+    let mut p = Process::new(pid, parent_pid, start_time, info);
 
     p.stat_file = stat_file;
     get_status(&mut p, parts[2]);
@@ -419,11 +425,10 @@ pub(crate) fn _get_process_data(
         path,
         &mut p,
         &parts,
-        page_size_kb,
         proc_list.memory,
         proc_list.virtual_memory,
         uptime,
-        clock_cycle,
+        info,
         refresh_kind,
     );
     if refresh_kind.disk_usage() {
@@ -437,16 +442,15 @@ fn update_time_and_memory(
     path: &Path,
     entry: &mut Process,
     parts: &[&str],
-    page_size_kb: u64,
     parent_memory: u64,
     parent_virtual_memory: u64,
     uptime: u64,
-    clock_cycle: u64,
+    info: &SystemInfo,
     refresh_kind: ProcessRefreshKind,
 ) {
     {
         // rss
-        entry.memory = u64::from_str(parts[23]).unwrap_or(0) * page_size_kb;
+        entry.memory = u64::from_str(parts[23]).unwrap_or(0) * info.page_size_kb;
         if entry.memory >= parent_memory {
             entry.memory -= parent_memory;
         }
@@ -460,15 +464,14 @@ fn update_time_and_memory(
             u64::from_str(parts[13]).unwrap_or(0),
             u64::from_str(parts[14]).unwrap_or(0),
         );
-        entry.run_time = uptime.saturating_sub(entry.start_time);
+        entry.run_time = uptime.saturating_sub(entry.start_time_without_boot_time);
     }
     refresh_procs(
         entry,
         &path.join("task"),
-        page_size_kb,
         entry.pid,
         uptime,
-        clock_cycle,
+        info,
         refresh_kind,
     );
 }
@@ -476,10 +479,9 @@ fn update_time_and_memory(
 pub(crate) fn refresh_procs(
     proc_list: &mut Process,
     path: &Path,
-    page_size_kb: u64,
     pid: Pid,
     uptime: u64,
-    clock_cycle: u64,
+    info: &SystemInfo,
     refresh_kind: ProcessRefreshKind,
 ) -> bool {
     if let Ok(d) = fs::read_dir(path) {
@@ -509,10 +511,9 @@ pub(crate) fn refresh_procs(
                     if let Ok((p, _)) = _get_process_data(
                         e.as_path(),
                         proc_list.get(),
-                        page_size_kb,
                         pid,
                         uptime,
-                        clock_cycle,
+                        info,
                         refresh_kind,
                     ) {
                         p
@@ -526,15 +527,9 @@ pub(crate) fn refresh_procs(
             let new_tasks = folders
                 .iter()
                 .filter_map(|e| {
-                    if let Ok((p, pid)) = _get_process_data(
-                        e.as_path(),
-                        proc_list,
-                        page_size_kb,
-                        pid,
-                        uptime,
-                        clock_cycle,
-                        refresh_kind,
-                    ) {
+                    if let Ok((p, pid)) =
+                        _get_process_data(e.as_path(), proc_list, pid, uptime, info, refresh_kind)
+                    {
                         updated_pids.push(pid);
                         p
                     } else {
diff --git a/src/linux/system.rs b/src/linux/system.rs
index fbd32b116..0643c619a 100644
--- a/src/linux/system.rs
+++ b/src/linux/system.rs
@@ -97,6 +97,22 @@ fn boot_time() -> u64 {
     }
 }
 
+pub(crate) struct SystemInfo {
+    pub(crate) page_size_kb: u64,
+    pub(crate) clock_cycle: u64,
+    pub(crate) boot_time: u64,
+}
+
+impl SystemInfo {
+    fn new() -> Self {
+        Self {
+            page_size_kb: unsafe { sysconf(_SC_PAGESIZE) / 1024 } as _,
+            clock_cycle: unsafe { sysconf(_SC_CLK_TCK) } as _,
+            boot_time: boot_time(),
+        }
+    }
+}
+
 declare_signals! {
     c_int,
     Signal::Hangup => libc::SIGHUP,
@@ -146,18 +162,16 @@ pub struct System {
     swap_free: u64,
     global_processor: Processor,
     processors: Vec<Processor>,
-    page_size_kb: u64,
     components: Vec<Component>,
     disks: Vec<Disk>,
     networks: Networks,
     users: Vec<User>,
-    boot_time: u64,
     /// Field set to `false` in `update_processors` and to `true` in `refresh_processes_specifics`.
     ///
     /// The reason behind this is to avoid calling the `update_processors` more than necessary.
     /// For example when running `refresh_all` or `refresh_specifics`.
     need_processors_update: bool,
-    clock_cycle: u64,
+    info: SystemInfo,
 }
 
 impl System {
@@ -311,8 +325,10 @@ impl SystemExt for System {
     const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals();
 
     fn new_with_specifics(refreshes: RefreshKind) -> System {
+        let info = SystemInfo::new();
+        let process_list = Process::new(Pid(0), None, 0, &info);
         let mut s = System {
-            process_list: Process::new(Pid(0), None, 0),
+            process_list,
             mem_total: 0,
             mem_free: 0,
             mem_available: 0,
@@ -338,14 +354,12 @@ impl SystemExt for System {
                 String::new(),
             ),
             processors: Vec::with_capacity(4),
-            page_size_kb: unsafe { sysconf(_SC_PAGESIZE) / 1024 } as _,
             components: Vec::new(),
             disks: Vec::with_capacity(2),
             networks: Networks::new(),
             users: Vec::new(),
-            boot_time: boot_time(),
             need_processors_update: true,
-            clock_cycle: unsafe { sysconf(_SC_CLK_TCK) } as _,
+            info,
         };
         s.refresh_specifics(refreshes);
         s
@@ -389,10 +403,9 @@ impl SystemExt for System {
         if refresh_procs(
             &mut self.process_list,
             Path::new("/proc"),
-            self.page_size_kb,
             Pid(0),
             uptime,
-            self.clock_cycle,
+            &self.info,
             refresh_kind,
         ) {
             self.clear_procs(refresh_kind);
@@ -405,10 +418,9 @@ impl SystemExt for System {
         let found = match _get_process_data(
             &Path::new("/proc/").join(pid.to_string()),
             &mut self.process_list,
-            self.page_size_kb,
             Pid(0),
             uptime,
-            self.clock_cycle,
+            &self.info,
             refresh_kind,
         ) {
             Ok((Some(p), pid)) => {
@@ -539,7 +551,7 @@ impl SystemExt for System {
     }
 
     fn boot_time(&self) -> u64 {
-        self.boot_time
+        self.info.boot_time
     }
 
     fn load_average(&self) -> LoadAvg {
diff --git a/src/windows/tools.rs b/src/windows/tools.rs
index 9b9428e82..2e28197a5 100644
--- a/src/windows/tools.rs
+++ b/src/windows/tools.rs
@@ -95,7 +95,7 @@ pub unsafe fn get_disks() -> Vec<Disk> {
     #[cfg(feature = "multithread")]
     use rayon::iter::ParallelIterator;
 
-    crate::utils::into_iter(0..size_of::<DWORD>() * 8)
+    crate::utils::into_iter(0..DWORD::BITS)
         .filter_map(|x| {
             if (drives >> x) & 1 == 0 {
                 return None;


Test patch:
diff --git a/tests/process.rs b/tests/process.rs
index 7c689fa1c..270cd04d4 100644
--- a/tests/process.rs
+++ b/tests/process.rs
@@ -245,6 +245,8 @@ fn cpu_usage_is_not_nan() {
 
 #[test]
 fn test_process_times() {
+    use std::time::{SystemTime, UNIX_EPOCH};
+
     if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
         return;
     }
@@ -275,6 +277,16 @@ fn test_process_times() {
         assert!(p.run_time() >= 1);
         assert!(p.run_time() <= 2);
         assert!(p.start_time() > p.run_time());
+        // On linux, for whatever reason, the uptime seems to be older than the boot time, leading
+        // to this weird `+ 3` to ensure the test is passing as it should...
+        assert!(
+            p.start_time() + 3
+                > SystemTime::now()
+                    .duration_since(UNIX_EPOCH)
+                    .unwrap()
+                    .as_secs(),
+        );
+        assert!(p.start_time() >= s.boot_time());
     } else {
         panic!("Process not found!");
     }