Repository: GuillaumeGomez/sysinfo
Pull request #639 (2021-12-13 22:07:33)

Add ProcessExt::run_time
Part of #611.

Setting up the architecture.


Patch:
diff --git a/src/apple/app_store/process.rs b/src/apple/app_store/process.rs
index e248443..3c4aa20 100644
--- a/src/apple/app_store/process.rs
+++ b/src/apple/app_store/process.rs
@@ -9,10 +9,6 @@ use crate::{DiskUsage, Pid, ProcessExt, ProcessStatus, Signal};
 pub struct Process;
 
 impl ProcessExt for Process {
-    fn new(_pid: Pid, _parent: Option<Pid>, _start_time: u64) -> Process {
-        Process {}
-    }
-
     fn kill(&self) -> bool {
         false
     }
@@ -69,6 +65,10 @@ impl ProcessExt for Process {
         0
     }
 
+    fn run_time(&self) -> u64 {
+        0
+    }
+
     fn cpu_usage(&self) -> f32 {
         0.0
     }
diff --git a/src/apple/macos/process.rs b/src/apple/macos/process.rs
index 61a5388..6cc5e08 100644
--- a/src/apple/macos/process.rs
+++ b/src/apple/macos/process.rs
@@ -30,6 +30,7 @@ pub struct Process {
     old_utime: u64,
     old_stime: u64,
     start_time: u64,
+    run_time: u64,
     updated: bool,
     cpu_usage: f32,
     /// User id of the process owner.
@@ -66,6 +67,7 @@ impl Process {
             old_stime: 0,
             updated: true,
             start_time: 0,
+            run_time: 0,
             uid: 0,
             gid: 0,
             process_status: ProcessStatus::Unknown(0),
@@ -82,6 +84,7 @@ impl Process {
         pid: Pid,
         parent: Option<Pid>,
         start_time: u64,
+        run_time: u64,
         exe: PathBuf,
         name: String,
         cwd: PathBuf,
@@ -105,6 +108,7 @@ impl Process {
             old_stime: 0,
             updated: true,
             start_time,
+            run_time,
             uid: 0,
             gid: 0,
             process_status: ProcessStatus::Unknown(0),
@@ -115,10 +119,8 @@ impl Process {
             written_bytes: 0,
         }
     }
-}
 
-impl ProcessExt for Process {
-    fn new(pid: Pid, parent: Option<Pid>, start_time: u64) -> Process {
+    pub(crate) fn new(pid: Pid, parent: Option<Pid>, start_time: u64, run_time: u64) -> Process {
         Process {
             name: String::new(),
             pid,
@@ -135,6 +137,7 @@ impl ProcessExt for Process {
             old_stime: 0,
             updated: true,
             start_time,
+            run_time,
             uid: 0,
             gid: 0,
             process_status: ProcessStatus::Unknown(0),
@@ -145,7 +148,9 @@ impl ProcessExt for Process {
             written_bytes: 0,
         }
     }
+}
 
+impl ProcessExt for Process {
     fn kill(&self) -> bool {
         self.kill_with(Signal::Kill).unwrap()
     }
@@ -238,6 +243,10 @@ impl ProcessExt for Process {
         self.start_time
     }
 
+    fn run_time(&self) -> u64 {
+        self.run_time
+    }
+
     fn cpu_usage(&self) -> f32 {
         self.cpu_usage
     }
@@ -336,6 +345,7 @@ pub(crate) fn update_process(
     pid: Pid,
     mut size: size_t,
     time_interval: Option<f64>,
+    now: u64,
     refresh_kind: ProcessRefreshKind,
 ) -> Result<Option<Process>, ()> {
     let mut proc_args = Vec::with_capacity(size as usize);
@@ -485,6 +495,9 @@ pub(crate) fn update_process(
         let mut cp = ptr.add(mem::size_of::<c_int>());
         let mut start = cp;
 
+        let start_time = info.pbi_start_tvsec;
+        let run_time = now.saturating_sub(start_time);
+
         let mut p = if cp < ptr.add(size) {
             while cp < ptr.add(size) && *cp != 0 {
                 cp = cp.offset(1);
@@ -558,7 +571,8 @@ pub(crate) fn update_process(
             Process::new_with(
                 pid,
                 parent,
-                info.pbi_start_tvsec,
+                start_time,
+                run_time,
                 exe,
                 name,
                 cwd,
@@ -567,7 +581,7 @@ pub(crate) fn update_process(
                 root,
             )
         } else {
-            Process::new(pid, parent, info.pbi_start_tvsec)
+            Process::new(pid, parent, start_time, run_time)
         };
 
         let task_info = get_task_info(pid);
diff --git a/src/apple/system.rs b/src/apple/system.rs
index ed3aab0..cbfadc0 100644
--- a/src/apple/system.rs
+++ b/src/apple/system.rs
@@ -18,13 +18,14 @@ use std::cell::UnsafeCell;
 use std::collections::HashMap;
 use std::mem;
 use std::sync::Arc;
+use std::time::SystemTime;
 
 #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
 use libc::size_t;
 
 use libc::{
-    c_char, c_int, c_void, host_statistics64, mach_port_t, mach_task_self, sysconf, sysctl,
-    sysctlbyname, timeval, vm_statistics64, _SC_PAGESIZE,
+    c_char, c_int, c_void, host_statistics64, mach_port_t, sysconf, sysctl, sysctlbyname, timeval,
+    vm_statistics64, _SC_PAGESIZE,
 };
 
 #[doc = include_str!("../../md_doc/system.md")]
@@ -112,6 +113,13 @@ fn boot_time() -> u64 {
     }
 }
 
+fn get_now() -> u64 {
+    SystemTime::now()
+        .duration_since(SystemTime::UNIX_EPOCH)
+        .map(|n| n.as_secs())
+        .unwrap_or(0)
+}
+
 impl SystemExt for System {
     const IS_SUPPORTED: bool = true;
 
@@ -265,6 +273,7 @@ impl SystemExt for System {
             return;
         }
         if let Some(pids) = get_proc_list() {
+            let now = get_now();
             let arg_max = get_arg_max();
             let port = self.port;
             let time_interval = self.clock_info.as_mut().map(|c| c.get_time_interval(port));
@@ -281,6 +290,7 @@ impl SystemExt for System {
                             pid,
                             arg_max as size_t,
                             time_interval,
+                            now,
                             refresh_kind,
                         ) {
                             Ok(x) => x,
@@ -303,12 +313,20 @@ impl SystemExt for System {
 
     #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))]
     fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool {
+        let now = get_now();
         let arg_max = get_arg_max();
         let port = self.port;
         let time_interval = self.clock_info.as_mut().map(|c| c.get_time_interval(port));
         match {
             let wrap = Wrap(UnsafeCell::new(&mut self.process_list));
-            update_process(&wrap, pid, arg_max as size_t, time_interval, refresh_kind)
+            update_process(
+                &wrap,
+                pid,
+                arg_max as size_t,
+                time_interval,
+                now,
+                refresh_kind,
+            )
         } {
             Ok(Some(p)) => {
                 self.process_list.insert(p.pid(), p);
@@ -568,7 +586,7 @@ fn get_io_service_connection() -> Option<ffi::io_connect_t> {
         }
 
         let mut conn = 0;
-        let result = ffi::IOServiceOpen(device, mach_task_self(), 0, &mut conn);
+        let result = ffi::IOServiceOpen(device, libc::mach_task_self(), 0, &mut conn);
         ffi::IOObjectRelease(device);
         if result != ffi::KIO_RETURN_SUCCESS {
             sysinfo_debug!("Error: IOServiceOpen() = {}", result);
diff --git a/src/linux/process.rs b/src/linux/process.rs
index 508ca42..34aa191 100644
--- a/src/linux/process.rs
+++ b/src/linux/process.rs
@@ -8,7 +8,7 @@ use std::io::Read;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
-use libc::{gid_t, kill, sysconf, uid_t, _SC_CLK_TCK};
+use libc::{gid_t, kill, uid_t};
 
 use crate::sys::system::REMAINING_FILES;
 use crate::sys::utils::{get_all_data, get_all_data_from_file, realpath};
@@ -83,6 +83,7 @@ pub struct Process {
     old_utime: u64,
     old_stime: u64,
     start_time: u64,
+    run_time: u64,
     pub(crate) updated: bool,
     cpu_usage: f32,
     /// User id of the process owner.
@@ -99,8 +100,8 @@ pub struct Process {
     written_bytes: u64,
 }
 
-impl ProcessExt for Process {
-    fn new(pid: Pid, parent: Option<Pid>, start_time: u64) -> Process {
+impl Process {
+    pub(crate) fn new(pid: Pid, parent: Option<Pid>, start_time: u64) -> Process {
         Process {
             name: String::with_capacity(20),
             pid,
@@ -119,6 +120,7 @@ impl ProcessExt for Process {
             old_stime: 0,
             updated: true,
             start_time,
+            run_time: 0,
             uid: 0,
             gid: 0,
             status: ProcessStatus::Unknown(0),
@@ -134,7 +136,9 @@ impl ProcessExt for Process {
             written_bytes: 0,
         }
     }
+}
 
+impl ProcessExt for Process {
     fn kill(&self) -> bool {
         self.kill_with(Signal::Kill).unwrap()
     }
@@ -225,6 +229,10 @@ impl ProcessExt for Process {
         self.start_time
     }
 
+    fn run_time(&self) -> u64 {
+        self.run_time
+    }
+
     fn cpu_usage(&self) -> f32 {
         self.cpu_usage
     }
@@ -325,10 +333,10 @@ pub(crate) fn _get_process_data(
     page_size_kb: u64,
     pid: Pid,
     uptime: u64,
-    now: u64,
+    clock_cycle: u64,
     refresh_kind: ProcessRefreshKind,
 ) -> Result<(Option<Process>, Pid), ()> {
-    let nb = match path.file_name().and_then(|x| x.to_str()).map(Pid::from_str) {
+    let pid = match path.file_name().and_then(|x| x.to_str()).map(Pid::from_str) {
         Some(Ok(nb)) if nb != pid => nb,
         _ => return Err(()),
     };
@@ -342,7 +350,7 @@ pub(crate) fn _get_process_data(
     };
     let parent_memory = proc_list.memory;
     let parent_virtual_memory = proc_list.virtual_memory;
-    if let Some(ref mut entry) = proc_list.tasks.get_mut(&nb) {
+    if let Some(ref mut entry) = proc_list.tasks.get_mut(&pid) {
         let data = if let Some(ref mut f) = entry.stat_file {
             get_all_data_from_file(f, 1024).map_err(|_| ())?
         } else {
@@ -362,15 +370,14 @@ pub(crate) fn _get_process_data(
             page_size_kb,
             parent_memory,
             parent_virtual_memory,
-            nb,
             uptime,
-            now,
+            clock_cycle,
             refresh_kind,
         );
         if refresh_kind.disk_usage() {
             update_process_disk_activity(entry, path);
         }
-        return Ok((None, nb));
+        return Ok((None, pid));
     }
 
     let mut tmp = PathBuf::from(path);
@@ -391,10 +398,11 @@ pub(crate) fn _get_process_data(
         }
     };
 
-    let clock_cycle = unsafe { sysconf(_SC_CLK_TCK) } as u64;
-    let since_boot = u64::from_str(parts[21]).unwrap_or(0) / clock_cycle;
-    let start_time = now.saturating_sub(uptime.saturating_sub(since_boot));
-    let mut p = Process::new(nb, parent_pid, start_time);
+    let start_time = u64::from_str(parts[21]).unwrap_or(0) / clock_cycle;
+    if pid == crate::get_current_pid().unwrap() {
+        println!("=> {:?} {:?}", start_time, clock_cycle);
+    }
+    let mut p = Process::new(pid, parent_pid, start_time);
 
     p.stat_file = stat_file;
     get_status(&mut p, parts[2]);
@@ -454,15 +462,14 @@ pub(crate) fn _get_process_data(
         page_size_kb,
         proc_list.memory,
         proc_list.virtual_memory,
-        nb,
         uptime,
-        now,
+        clock_cycle,
         refresh_kind,
     );
     if refresh_kind.disk_usage() {
         update_process_disk_activity(&mut p, path);
     }
-    Ok((Some(p), nb))
+    Ok((Some(p), pid))
 }
 
 #[allow(clippy::too_many_arguments)]
@@ -473,9 +480,8 @@ fn update_time_and_memory(
     page_size_kb: u64,
     parent_memory: u64,
     parent_virtual_memory: u64,
-    pid: Pid,
     uptime: u64,
-    now: u64,
+    clock_cycle: u64,
     refresh_kind: ProcessRefreshKind,
 ) {
     {
@@ -494,14 +500,15 @@ 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);
     }
     refresh_procs(
         entry,
         &path.join("task"),
         page_size_kb,
-        pid,
+        entry.pid,
         uptime,
-        now,
+        clock_cycle,
         refresh_kind,
     );
 }
@@ -512,7 +519,7 @@ pub(crate) fn refresh_procs(
     page_size_kb: u64,
     pid: Pid,
     uptime: u64,
-    now: u64,
+    clock_cycle: u64,
     refresh_kind: ProcessRefreshKind,
 ) -> bool {
     if let Ok(d) = fs::read_dir(path) {
@@ -545,7 +552,7 @@ pub(crate) fn refresh_procs(
                         page_size_kb,
                         pid,
                         uptime,
-                        now,
+                        clock_cycle,
                         refresh_kind,
                     ) {
                         p
@@ -565,7 +572,7 @@ pub(crate) fn refresh_procs(
                         page_size_kb,
                         pid,
                         uptime,
-                        now,
+                        clock_cycle,
                         refresh_kind,
                     ) {
                         updated_pids.push(pid);
diff --git a/src/linux/system.rs b/src/linux/system.rs
index 683b249..363c66a 100644
--- a/src/linux/system.rs
+++ b/src/linux/system.rs
@@ -5,18 +5,15 @@ use crate::sys::disk;
 use crate::sys::process::*;
 use crate::sys::processor::*;
 use crate::sys::utils::get_all_data;
-use crate::{
-    Disk, LoadAvg, Networks, Pid, ProcessExt, ProcessRefreshKind, RefreshKind, SystemExt, User,
-};
+use crate::{Disk, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, SystemExt, User};
 
-use libc::{self, c_char, sysconf, _SC_HOST_NAME_MAX, _SC_PAGESIZE};
+use libc::{self, c_char, sysconf, _SC_CLK_TCK, _SC_HOST_NAME_MAX, _SC_PAGESIZE};
 use std::collections::HashMap;
 use std::fs::File;
 use std::io::{BufRead, BufReader, Read};
 use std::path::Path;
 use std::str::FromStr;
 use std::sync::{Arc, Mutex};
-use std::time::SystemTime;
 
 // This whole thing is to prevent having too many files open at once. It could be problematic
 // for processes using a lot of files and using sysinfo at the same time.
@@ -124,6 +121,7 @@ pub struct System {
     /// 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,
 }
 
 impl System {
@@ -303,13 +301,14 @@ impl SystemExt for System {
                 String::new(),
             ),
             processors: Vec::with_capacity(4),
-            page_size_kb: unsafe { sysconf(_SC_PAGESIZE) as u64 / 1024 },
+            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 _,
         };
         s.refresh_specifics(refreshes);
         s
@@ -356,7 +355,7 @@ impl SystemExt for System {
             self.page_size_kb,
             0,
             uptime,
-            get_secs_since_epoch(),
+            self.clock_cycle,
             refresh_kind,
         ) {
             self.clear_procs(refresh_kind);
@@ -372,7 +371,7 @@ impl SystemExt for System {
             self.page_size_kb,
             0,
             uptime,
-            get_secs_since_epoch(),
+            self.clock_cycle,
             refresh_kind,
         ) {
             Ok((Some(p), pid)) => {
@@ -697,13 +696,6 @@ fn get_system_info_android(info: InfoType) -> Option<String> {
     }
 }
 
-fn get_secs_since_epoch() -> u64 {
-    match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
-        Ok(n) => n.as_secs(),
-        _ => panic!("SystemTime before UNIX EPOCH!"),
-    }
-}
-
 #[cfg(test)]
 mod test {
     #[cfg(target_os = "android")]
diff --git a/src/traits.rs b/src/traits.rs
index 58dd1df..09980ed 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -124,12 +124,6 @@ pub trait DiskExt: Debug {
 
 /// Contains all the methods of the [`Process`][crate::Process] struct.
 pub trait ProcessExt: Debug {
-    /// Creates a new process only containing the given information.
-    ///
-    /// On windows, the `start_time` argument is ignored.
-    #[doc(hidden)]
-    fn new(pid: Pid, parent: Option<Pid>, start_time: u64) -> Self;
-
     /// Sends [`Signal::Kill`] to the process (which is the only signal supported on all platforms
     /// by this crate).
     ///
@@ -304,18 +298,30 @@ pub trait ProcessExt: Debug {
     /// ```
     fn status(&self) -> ProcessStatus;
 
-    /// Returns the time of process launch (in seconds).
+    /// Returns the time where the process was started (in seconds) from epoch.
     ///
     /// ```no_run
     /// use sysinfo::{ProcessExt, System, SystemExt};
     ///
     /// let s = System::new();
     /// if let Some(process) = s.process(1337) {
-    ///     println!("Running since {} seconds", process.start_time());
+    ///     println!("Started at {} seconds", process.start_time());
     /// }
     /// ```
     fn start_time(&self) -> u64;
 
+    /// Returns for how much time the process has been running (in seconds).
+    ///
+    /// ```no_run
+    /// use sysinfo::{ProcessExt, System, SystemExt};
+    ///
+    /// let s = System::new();
+    /// if let Some(process) = s.process(1337) {
+    ///     println!("Running since {} seconds", process.run_time());
+    /// }
+    /// ```
+    fn run_time(&self) -> u64;
+
     /// Returns the total CPU usage (in %). Notice that it might be bigger than 100 if run on a
     /// multicore machine.
     ///
diff --git a/src/unknown/process.rs b/src/unknown/process.rs
index cbb3982..7be1dc7 100644
--- a/src/unknown/process.rs
+++ b/src/unknown/process.rs
@@ -19,10 +19,6 @@ pub struct Process {
 }
 
 impl ProcessExt for Process {
-    fn new(pid: Pid, parent: Option<Pid>, _start_time: u64) -> Process {
-        Process { pid, parent }
-    }
-
     fn kill(&self) -> bool {
         false
     }
@@ -79,6 +75,10 @@ impl ProcessExt for Process {
         0
     }
 
+    fn run_time(&self) -> u64 {
+        0
+    }
+
     fn cpu_usage(&self) -> f32 {
         0.0
     }
diff --git a/src/windows/mod.rs b/src/windows/mod.rs
index 81b610d..49c44e5 100644
--- a/src/windows/mod.rs
+++ b/src/windows/mod.rs
@@ -10,6 +10,7 @@ mod processor;
 mod system;
 mod tools;
 mod users;
+mod utils;
 
 pub use self::component::Component;
 pub use self::disk::Disk;
diff --git a/src/windows/process.rs b/src/windows/process.rs
index facf3f0..113a18a 100644
--- a/src/windows/process.rs
+++ b/src/windows/process.rs
@@ -37,7 +37,6 @@ use winapi::um::psapi::{
     EnumProcessModulesEx, GetModuleBaseNameW, GetModuleFileNameExW, GetProcessMemoryInfo,
     LIST_MODULES_ALL, PROCESS_MEMORY_COUNTERS, PROCESS_MEMORY_COUNTERS_EX,
 };
-use winapi::um::sysinfoapi::GetSystemTimeAsFileTime;
 use winapi::um::winbase::{GetProcessIoCounters, CREATE_NO_WINDOW};
 use winapi::um::winnt::{
     HANDLE, IO_COUNTERS, MEMORY_BASIC_INFORMATION, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ,
@@ -97,6 +96,7 @@ pub struct Process {
     handle: PtrWrapper<HANDLE>,
     cpu_calc_values: CPUsageCalculationValues,
     start_time: u64,
+    pub(crate) run_time: u64,
     cpu_usage: f32,
     pub(crate) updated: bool,
     old_read_bytes: u64,
@@ -174,7 +174,7 @@ unsafe fn get_exe(process_handler: HANDLE, h_mod: *mut c_void) -> PathBuf {
 
 impl Process {
     #[allow(clippy::uninit_assumed_init)]
-    pub(crate) fn new_from_pid(pid: Pid) -> Option<Process> {
+    pub(crate) fn new_from_pid(pid: Pid, now: u64) -> Option<Process> {
         let process_handler = unsafe { OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid as _) };
         if process_handler.is_null() {
             return None;
@@ -201,6 +201,7 @@ impl Process {
                 None
             },
             process_handler,
+            now,
         ))
     }
 
@@ -210,6 +211,7 @@ impl Process {
         memory: u64,
         virtual_memory: u64,
         name: String,
+        now: u64,
     ) -> Process {
         if let Some(handle) = get_process_handler(pid) {
             let mut h_mod = null_mut();
@@ -224,6 +226,7 @@ impl Process {
                     (Vec::new(), Vec::new(), PathBuf::new())
                 }
             };
+            let (start_time, run_time) = unsafe { get_start_and_run_time(handle, now) };
             Process {
                 handle: PtrWrapper(handle),
                 name,
@@ -239,7 +242,8 @@ impl Process {
                 virtual_memory,
                 cpu_usage: 0.,
                 cpu_calc_values: CPUsageCalculationValues::new(),
-                start_time: unsafe { get_start_time(handle) },
+                start_time,
+                run_time,
                 updated: true,
                 old_read_bytes: 0,
                 old_written_bytes: 0,
@@ -263,6 +267,7 @@ impl Process {
                 cpu_usage: 0.,
                 cpu_calc_values: CPUsageCalculationValues::new(),
                 start_time: 0,
+                run_time: 0,
                 updated: true,
                 old_read_bytes: 0,
                 old_written_bytes: 0,
@@ -272,7 +277,12 @@ impl Process {
         }
     }
 
-    fn new_with_handle(pid: Pid, parent: Option<Pid>, process_handler: HANDLE) -> Process {
+    fn new_with_handle(
+        pid: Pid,
+        parent: Option<Pid>,
+        process_handler: HANDLE,
+        now: u64,
+    ) -> Process {
         let mut h_mod = null_mut();
 
         unsafe {
@@ -292,6 +302,7 @@ impl Process {
                     (Vec::new(), Vec::new(), PathBuf::new())
                 }
             };
+            let (start_time, run_time) = get_start_and_run_time(process_handler, now);
             Process {
                 handle: PtrWrapper(process_handler),
                 name,
@@ -307,7 +318,8 @@ impl Process {
                 virtual_memory: 0,
                 cpu_usage: 0.,
                 cpu_calc_values: CPUsageCalculationValues::new(),
-                start_time: get_start_time(process_handler),
+                start_time,
+                run_time,
                 updated: true,
                 old_read_bytes: 0,
                 old_written_bytes: 0,
@@ -316,38 +328,25 @@ impl Process {
             }
         }
     }
-}
 
-impl ProcessExt for Process {
-    fn new(pid: Pid, parent: Option<Pid>, _: u64) -> Process {
-        if let Some(process_handler) = get_process_handler(pid) {
-            Process::new_with_handle(pid, parent, process_handler)
-        } else {
-            Process {
-                handle: PtrWrapper(null_mut()),
-                name: String::new(),
-                pid,
-                parent,
-                cmd: Vec::new(),
-                environ: Vec::new(),
-                exe: get_executable_path(pid),
-                cwd: PathBuf::new(),
-                root: PathBuf::new(),
-                status: ProcessStatus::Run,
-                memory: 0,
-                virtual_memory: 0,
-                cpu_usage: 0.,
-                cpu_calc_values: CPUsageCalculationValues::new(),
-                start_time: 0,
-                updated: true,
-                old_read_bytes: 0,
-                old_written_bytes: 0,
-                read_bytes: 0,
-                written_bytes: 0,
-            }
+    pub(crate) fn update(
+        &mut self,
+        refresh_kind: crate::ProcessRefreshKind,
+        nb_processors: u64,
+        now: u64,
+    ) {
+        if refresh_kind.cpu() {
+            compute_cpu_usage(self, nb_processors);
+        }
+        if refresh_kind.disk_usage() {
+            update_disk_usage(self);
         }
+        self.run_time = now - self.start_time();
+        self.updated = true;
     }
+}
 
+impl ProcessExt for Process {
     fn kill(&self) -> bool {
         let mut kill = process::Command::new("taskkill.exe");
         kill.arg("/PID").arg(self.pid().to_string()).arg("/F");
@@ -414,6 +413,10 @@ impl ProcessExt for Process {
         self.start_time
     }
 
+    fn run_time(&self) -> u64 {
+        self.run_time
+    }
+
     fn cpu_usage(&self) -> f32 {
         self.cpu_usage
     }
@@ -439,7 +442,7 @@ impl Drop for Process {
     }
 }
 
-unsafe fn get_start_time(handle: HANDLE) -> u64 {
+unsafe fn get_start_and_run_time(handle: HANDLE, now: u64) -> (u64, u64) {
     let mut fstart: FILETIME = zeroed();
     let mut x = zeroed();
 
@@ -450,8 +453,12 @@ unsafe fn get_start_time(handle: HANDLE) -> u64 {
         &mut x as *mut FILETIME,
         &mut x as *mut FILETIME,
     );
-    let tmp = (fstart.dwHighDateTime as u64) << 32 | (fstart.dwLowDateTime as u64);
-    tmp / 10_000_000 - 11_644_473_600
+    let tmp = super::utils::filetime_to_u64(fstart);
+    // 11_644_473_600 is the number of seconds between the Windows epoch (1601-01-01) and
+    // the linux epoch (1970-01-01).
+    let start = tmp / 10_000_000 - 11_644_473_600;
+    let run_time = check_sub(now, start);
+    (start, run_time)
 }
 
 #[allow(clippy::uninit_vec)]
@@ -784,21 +791,6 @@ pub(crate) fn get_executable_path(_pid: Pid) -> PathBuf {
     PathBuf::new()
 }
 
-pub(crate) fn get_system_computation_time() -> ULARGE_INTEGER {
-    unsafe {
-        let mut now: ULARGE_INTEGER = std::mem::zeroed();
-        let mut ftime: FILETIME = zeroed();
-
-        GetSystemTimeAsFileTime(&mut ftime);
-        memcpy(
-            &mut now as *mut ULARGE_INTEGER as *mut c_void,
-            &mut ftime as *mut FILETIME as *mut c_void,
-            size_of::<FILETIME>(),
-        );
-        now
-    }
-}
-
 #[inline]
 fn check_sub(a: u64, b: u64) -> u64 {
     if a < b {
@@ -809,7 +801,7 @@ fn check_sub(a: u64, b: u64) -> u64 {
 }
 /// Before changing this function, you must consider the following:
 /// https://github.com/GuillaumeGomez/sysinfo/issues/459
-pub(crate) fn compute_cpu_usage(p: &mut Process, nb_processors: u64, _now: ULARGE_INTEGER) {
+pub(crate) fn compute_cpu_usage(p: &mut Process, nb_processors: u64) {
     unsafe {
         let mut ftime: FILETIME = zeroed();
         let mut fsys: FILETIME = zeroed();
diff --git a/src/windows/system.rs b/src/windows/system.rs
index 0243457..67e154d 100644
--- a/src/windows/system.rs
+++ b/src/windows/system.rs
@@ -5,13 +5,11 @@ use winapi::um::winreg::HKEY_LOCAL_MACHINE;
 
 use crate::sys::component::{self, Component};
 use crate::sys::disk::Disk;
-use crate::sys::process::{
-    compute_cpu_usage, get_handle, get_system_computation_time, update_disk_usage, update_memory,
-    Process,
-};
+use crate::sys::process::{get_handle, update_memory, Process};
 use crate::sys::processor::*;
 use crate::sys::tools::*;
 use crate::sys::users::get_users;
+use crate::sys::utils::get_now;
 
 use crate::utils::into_iter;
 
@@ -185,15 +183,11 @@ impl SystemExt for System {
                 self.process_list.remove(&pid);
                 return false;
             }
-            true
-        } else if let Some(mut p) = Process::new_from_pid(pid) {
-            let system_time = get_system_computation_time();
-            if refresh_kind.cpu() {
-                compute_cpu_usage(&mut p, self.processors.len() as u64, system_time);
-            }
-            if refresh_kind.disk_usage() {
-                update_disk_usage(&mut p);
-            }
+            return true;
+        }
+        let now = get_now();
+        if let Some(mut p) = Process::new_from_pid(pid, now) {
+            p.update(refresh_kind, self.processors.len() as u64, now);
             self.process_list.insert(pid, p);
             true
         } else {
@@ -205,6 +199,7 @@ impl SystemExt for System {
     fn refresh_processes_specifics(&mut self, refresh_kind: ProcessRefreshKind) {
         // Windows 10 notebook requires at least 512KiB of memory to make it in one go
         let mut buffer_size: usize = 512 * 1024;
+        let now = get_now();
 
         loop {
             let mut process_information: Vec<u8> = Vec::with_capacity(buffer_size);
@@ -249,10 +244,10 @@ impl SystemExt for System {
                     process_information_offset += pi.NextEntryOffset as isize;
                 }
                 let process_list = Wrap(UnsafeCell::new(&mut self.process_list));
-                let (nb_processors, system_time) = if refresh_kind.cpu() {
-                    (self.processors.len() as u64, get_system_computation_time())
+                let nb_processors = if refresh_kind.cpu() {
+                    self.processors.len() as u64
                 } else {
-                    (0, unsafe { zeroed() })
+                    0
                 };
 
                 #[cfg(feature = "multithread")]
@@ -267,13 +262,7 @@ impl SystemExt for System {
                         if let Some(proc_) = (*process_list.0.get()).get_mut(&pid) {
                             proc_.memory = (pi.WorkingSetSize as u64) / 1_000;
                             proc_.virtual_memory = (pi.VirtualSize as u64) / 1_000;
-                            if refresh_kind.cpu() {
-                                compute_cpu_usage(proc_, nb_processors, system_time);
-                            }
-                            if refresh_kind.disk_usage() {
-                                update_disk_usage(proc_);
-                            }
-                            proc_.updated = true;
+                            proc_.update(refresh_kind, nb_processors, now);
                             return None;
                         }
                         let name = get_process_name(&pi, pid);
@@ -287,13 +276,9 @@ impl SystemExt for System {
                             (pi.WorkingSetSize as u64) / 1_000,
                             (pi.VirtualSize as u64) / 1_000,
                             name,
+                            now,
                         );
-                        if refresh_kind.cpu() {
-                            compute_cpu_usage(&mut p, nb_processors, system_time);
-                        }
-                        if refresh_kind.disk_usage() {
-                            update_disk_usage(&mut p);
-                        }
+                        p.update(refresh_kind, nb_processors, now);
                         Some(p)
                     })
                     .collect::<Vec<_>>();
@@ -480,16 +465,7 @@ fn refresh_existing_process(s: &mut System, pid: Pid, refresh_kind: ProcessRefre
             return false;
         }
         update_memory(entry);
-        if refresh_kind.disk_usage() {
-            update_disk_usage(entry);
-        }
-        if refresh_kind.cpu() {
-            compute_cpu_usage(
-                entry,
-                s.processors.len() as u64,
-                get_system_computation_time(),
-            );
-        }
+        entry.update(refresh_kind, s.processors.len() as u64, get_now());
         true
     } else {
         false
diff --git a/src/windows/utils.rs b/src/windows/utils.rs
new file mode 100644
index 0000000..3ea7f51
--- /dev/null
+++ b/src/windows/utils.rs
@@ -0,0 +1,18 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use winapi::shared::minwindef::FILETIME;
+
+use std::time::SystemTime;
+
+#[inline]
+pub fn filetime_to_u64(f: FILETIME) -> u64 {
+    (f.dwHighDateTime as u64) << 32 | (f.dwLowDateTime as u64)
+}
+
+#[inline]
+pub fn get_now() -> u64 {
+    SystemTime::now()
+        .duration_since(SystemTime::UNIX_EPOCH)
+        .map(|n| n.as_secs())
+        .unwrap_or(0)
+}


Test patch:
diff --git a/tests/process.rs b/tests/process.rs
index 3dfc0a3..832725b 100644
--- a/tests/process.rs
+++ b/tests/process.rs
@@ -236,3 +236,42 @@ fn cpu_usage_is_not_nan() {
     });
     assert!(checked > 0);
 }
+
+#[test]
+fn test_process_times() {
+    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
+        return;
+    }
+    let mut p = if cfg!(target_os = "windows") {
+        std::process::Command::new("waitfor")
+            .arg("/t")
+            .arg("3")
+            .arg("CwdSignal")
+            .stdout(std::process::Stdio::null())
+            .spawn()
+            .unwrap()
+    } else {
+        std::process::Command::new("sleep")
+            .arg("3")
+            .stdout(std::process::Stdio::null())
+            .spawn()
+            .unwrap()
+    };
+
+    let pid = p.id() as sysinfo::Pid;
+    std::thread::sleep(std::time::Duration::from_secs(1));
+    let mut s = sysinfo::System::new();
+    s.refresh_processes();
+    p.kill().expect("Unable to kill process.");
+
+    let processes = s.processes();
+    let p = processes.get(&pid);
+
+    if let Some(p) = p {
+        assert_eq!(p.pid(), pid);
+        assert!(p.run_time() <= 2);
+        assert!(p.start_time() > p.run_time());
+    } else {
+        panic!("Process not found!");
+    }
+}