Repository: GuillaumeGomez/sysinfo
Pull request #287 (2020-04-05 12:13:37)

Process disk rw
Taking over #248.

cc @bvaisvi

On windows, the performance are still miserable. An alternative could be to use `GetProcessIoCounters`, however it returns all I/O (so disk+network+driver, whatever "driver" means). So to get a somewhat accurate information, it'd require to get the network usage for the process and subtract it from what `GetProcessIoCounters` returns. Quite annoying... As a result, windows performance is getting 3 times worst when adding new process:

<details>
```
test bench_new                     ... bench:   7,918,095 ns/iter (+/- 422,384)
test bench_new_all                 ... bench:  92,207,290 ns/iter (+/- 6,613,951)
test bench_refresh_all             ... bench:   1,561,685 ns/iter (+/- 132,639)
test bench_refresh_components      ... bench:           1 ns/iter (+/- 0)
test bench_refresh_components_list ... bench:      28,625 ns/iter (+/- 1,849)
test bench_refresh_cpu             ... bench:      34,676 ns/iter (+/- 9,526)
test bench_refresh_disks           ... bench:      56,971 ns/iter (+/- 2,548)
test bench_refresh_disks_lists     ... bench:     125,932 ns/iter (+/- 4,918)
test bench_refresh_memory          ... bench:       1,042 ns/iter (+/- 28)
test bench_refresh_networks        ... bench:      39,778 ns/iter (+/- 3,216)
test bench_refresh_networks_list   ... bench:   1,374,947 ns/iter (+/- 58,170)
test bench_refresh_process         ... bench:       1,288 ns/iter (+/- 125)
test bench_refresh_processes       ... bench:   1,135,397 ns/iter (+/- 254,835)
test bench_refresh_system          ... bench:      38,371 ns/iter (+/- 7,019)
test bench_refresh_users_list      ... bench:   3,427,415 ns/iter (+/- 108,865)
```
</details>

The only way I found to reduce this performance drop even more is to only compute disk usage when user requests it (otherwise, it's 70 times slower!!!).


Patch:
diff --git a/README.md b/README.md
index 74ebc4d..45551fe 100644
--- a/README.md
+++ b/README.md
@@ -108,20 +108,21 @@ Here are the current results:
 <details>
 
 ```text
-test bench_new                     ... bench:       3,741 ns/iter (+/- 252)
-test bench_new_all                 ... bench:  10,491,084 ns/iter (+/- 450,925)
-test bench_refresh_all             ... bench:   2,787,974 ns/iter (+/- 235,649)
-test bench_refresh_components      ... bench:      24,270 ns/iter (+/- 1,127)
-test bench_refresh_components_list ... bench:     370,693 ns/iter (+/- 51,925)
-test bench_refresh_cpu             ... bench:      13,367 ns/iter (+/- 1,858)
-test bench_refresh_disks           ... bench:       2,532 ns/iter (+/- 108)
-test bench_refresh_disks_lists     ... bench:      50,359 ns/iter (+/- 5,877)
-test bench_refresh_memory          ... bench:      11,713 ns/iter (+/- 1,006)
-test bench_refresh_networks        ... bench:     220,246 ns/iter (+/- 24,294)
-test bench_refresh_networks_list   ... bench:     229,648 ns/iter (+/- 82,050)
-test bench_refresh_process         ... bench:      77,375 ns/iter (+/- 10,657)
-test bench_refresh_processes       ... bench:   2,282,106 ns/iter (+/- 154,098)
-test bench_refresh_system          ... bench:      52,466 ns/iter (+/- 4,710)
+test bench_new                     ... bench:     186,638 ns/iter (+/- 8,603)
+test bench_new_all                 ... bench:  18,604,576 ns/iter (+/- 1,323,790)
+test bench_refresh_all             ... bench:   5,147,523 ns/iter (+/- 337,096)
+test bench_refresh_components      ... bench:      25,391 ns/iter (+/- 1,725)
+test bench_refresh_components_list ... bench:     381,490 ns/iter (+/- 18,237)
+test bench_refresh_cpu             ... bench:      13,397 ns/iter (+/- 880)
+test bench_refresh_disks           ... bench:       2,515 ns/iter (+/- 161)
+test bench_refresh_disks_list      ... bench:      50,986 ns/iter (+/- 5,301)
+test bench_refresh_memory          ... bench:      11,843 ns/iter (+/- 779)
+test bench_refresh_networks        ... bench:     250,242 ns/iter (+/- 68,482)
+test bench_refresh_networks_list   ... bench:     255,148 ns/iter (+/- 23,677)
+test bench_refresh_process         ... bench:     128,860 ns/iter (+/- 22,638)
+test bench_refresh_processes       ... bench:   4,714,965 ns/iter (+/- 309,573)
+test bench_refresh_system          ... bench:      52,044 ns/iter (+/- 4,510)
+test bench_refresh_users_list      ... bench:   2,095,043 ns/iter (+/- 648,385)
 ```
 </details>
 
@@ -130,20 +131,21 @@ test bench_refresh_system          ... bench:      52,466 ns/iter (+/- 4,710)
 <details>
 
 ```text
-test bench_new                     ... bench:   7,688,460 ns/iter (+/- 1,230,010)
-test bench_new_all                 ... bench:  24,098,860 ns/iter (+/- 5,260,950)
-test bench_refresh_all             ... bench:   3,096,107 ns/iter (+/- 94,257)
-test bench_refresh_components      ... bench:   1,205,378 ns/iter (+/- 40,071)
-test bench_refresh_components_list ... bench:   3,181,602 ns/iter (+/- 102,533)
-test bench_refresh_cpu             ... bench:         395 ns/iter (+/- 18)
-test bench_refresh_disks           ... bench:      53,082 ns/iter (+/- 1,834)
-test bench_refresh_disks_lists     ... bench:     114,080 ns/iter (+/- 1,920)
-test bench_refresh_memory          ... bench:         596 ns/iter (+/- 48)
-test bench_refresh_networks        ... bench:      37,549 ns/iter (+/- 1,622)
-test bench_refresh_networks_list   ... bench:     667,180 ns/iter (+/- 59,859)
-test bench_refresh_process         ... bench:         755 ns/iter (+/- 47)
-test bench_refresh_processes       ... bench:   1,217,488 ns/iter (+/- 69,041)
-test bench_refresh_system          ... bench:   1,214,780 ns/iter (+/- 52,013)
+test bench_new                     ... bench:   7,335,755 ns/iter (+/- 469,000)
+test bench_new_all                 ... bench:  32,233,480 ns/iter (+/- 1,567,239)
+test bench_refresh_all             ... bench:   1,433,015 ns/iter (+/- 126,322)
+test bench_refresh_components      ... bench:           1 ns/iter (+/- 0)
+test bench_refresh_components_list ... bench:   9,835,060 ns/iter (+/- 407,072)
+test bench_refresh_cpu             ... bench:      33,873 ns/iter (+/- 2,177)
+test bench_refresh_disks           ... bench:      58,951 ns/iter (+/- 6,128)
+test bench_refresh_disks_list      ... bench:     125,199 ns/iter (+/- 2,741)
+test bench_refresh_memory          ... bench:       1,004 ns/iter (+/- 56)
+test bench_refresh_networks        ... bench:      39,013 ns/iter (+/- 2,676)
+test bench_refresh_networks_list   ... bench:   1,341,850 ns/iter (+/- 78,258)
+test bench_refresh_process         ... bench:       2,116 ns/iter (+/- 58)
+test bench_refresh_processes       ... bench:   1,032,447 ns/iter (+/- 57,695)
+test bench_refresh_system          ... bench:      35,374 ns/iter (+/- 3,200)
+test bench_refresh_users_list      ... bench:   3,321,140 ns/iter (+/- 135,160)
 ```
 </details>
 
@@ -152,20 +154,21 @@ test bench_refresh_system          ... bench:   1,214,780 ns/iter (+/- 52,013)
 <details>
 
 ```text
-test bench_new                     ... bench:      56,861 ns/iter (+/- 5,653)
-test bench_new_all                 ... bench:   4,634,509 ns/iter (+/- 1,604,369)
-test bench_refresh_all             ... bench:   1,962,343 ns/iter (+/- 129,726)
-test bench_refresh_components      ... bench:     294,752 ns/iter (+/- 45,107)
-test bench_refresh_components_list ... bench:     895,672 ns/iter (+/- 112,586)
-test bench_refresh_cpu             ... bench:      11,187 ns/iter (+/- 2,483)
-test bench_refresh_disks           ... bench:         975 ns/iter (+/- 50)
-test bench_refresh_disks_lists     ... bench:      25,955 ns/iter (+/- 3,159)
-test bench_refresh_memory          ... bench:       3,440 ns/iter (+/- 198)
-test bench_refresh_networks        ... bench:     211,552 ns/iter (+/- 16,686)
-test bench_refresh_networks_list   ... bench:     211,138 ns/iter (+/- 22,644)
-test bench_refresh_process         ... bench:       4,174 ns/iter (+/- 1,249)
-test bench_refresh_processes       ... bench:     803,559 ns/iter (+/- 42,974)
-test bench_refresh_system          ... bench:     365,762 ns/iter (+/- 55,893)
+test bench_new                     ... bench:      86,404 ns/iter (+/- 9,402)
+test bench_new_all                 ... bench:  21,123,771 ns/iter (+/- 570,722)
+test bench_refresh_all             ... bench:   1,757,683 ns/iter (+/- 203,234)
+test bench_refresh_components      ... bench:     325,560 ns/iter (+/- 41,068)
+test bench_refresh_components_list ... bench:     989,827 ns/iter (+/- 221,093)
+test bench_refresh_cpu             ... bench:       8,535 ns/iter (+/- 487)
+test bench_refresh_disks           ... bench:         939 ns/iter (+/- 33)
+test bench_refresh_disks_lists     ... bench:      25,093 ns/iter (+/- 2,080)
+test bench_refresh_memory          ... bench:       2,174 ns/iter (+/- 55)
+test bench_refresh_networks        ... bench:     181,558 ns/iter (+/- 7,325)
+test bench_refresh_networks_list   ... bench:     180,410 ns/iter (+/- 2,414)
+test bench_refresh_process         ... bench:       5,570 ns/iter (+/- 431)
+test bench_refresh_processes       ... bench:     683,455 ns/iter (+/- 14,995)
+test bench_refresh_system          ... bench:     362,875 ns/iter (+/- 172,547)
+test bench_refresh_users_list      ... bench:  16,783,834 ns/iter (+/- 465,111)
 ```
 </details>
 
diff --git a/benches/basic.rs b/benches/basic.rs
index 0666a05..b146493 100644
--- a/benches/basic.rs
+++ b/benches/basic.rs
@@ -71,7 +71,7 @@ fn bench_refresh_disks(b: &mut test::Bencher) {
 }
 
 #[bench]
-fn bench_refresh_disks_lists(b: &mut test::Bencher) {
+fn bench_refresh_disks_list(b: &mut test::Bencher) {
     let mut s = sysinfo::System::new();
 
     b.iter(move || {
diff --git a/src/common.rs b/src/common.rs
index 632deba..3655cb2 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -7,6 +7,7 @@
 use NetworkData;
 use Networks;
 use NetworksExt;
+use UserExt;
 
 /// Trait to have a common fallback for the [`Pid`][crate::Pid] type.
 pub trait AsU32 {
@@ -273,3 +274,162 @@ impl From<isize> for DiskType {
         }
     }
 }
+
+/// An enum representing signal on UNIX-like systems.
+#[repr(C)]
+#[derive(Clone, PartialEq, PartialOrd, Debug, Copy)]
+pub enum Signal {
+    /// Hangup detected on controlling terminal or death of controlling process.
+    Hangup = 1,
+    /// Interrupt from keyboard.
+    Interrupt = 2,
+    /// Quit from keyboard.
+    Quit = 3,
+    /// Illegal instruction.
+    Illegal = 4,
+    /// Trace/breakpoint trap.
+    Trap = 5,
+    /// Abort signal from C abort function.
+    Abort = 6,
+    // IOT trap. A synonym for SIGABRT.
+    // IOT = 6,
+    /// Bus error (bad memory access).
+    Bus = 7,
+    /// Floating point exception.
+    FloatingPointException = 8,
+    /// Kill signal.
+    Kill = 9,
+    /// User-defined signal 1.
+    User1 = 10,
+    /// Invalid memory reference.
+    Segv = 11,
+    /// User-defined signal 2.
+    User2 = 12,
+    /// Broken pipe: write to pipe with no readers.
+    Pipe = 13,
+    /// Timer signal from C alarm function.
+    Alarm = 14,
+    /// Termination signal.
+    Term = 15,
+    /// Stack fault on coprocessor (unused).
+    Stklft = 16,
+    /// Child stopped or terminated.
+    Child = 17,
+    /// Continue if stopped.
+    Continue = 18,
+    /// Stop process.
+    Stop = 19,
+    /// Stop typed at terminal.
+    TSTP = 20,
+    /// Terminal input for background process.
+    TTIN = 21,
+    /// Terminal output for background process.
+    TTOU = 22,
+    /// Urgent condition on socket.
+    Urgent = 23,
+    /// CPU time limit exceeded.
+    XCPU = 24,
+    /// File size limit exceeded.
+    XFSZ = 25,
+    /// Virtual alarm clock.
+    VirtualAlarm = 26,
+    /// Profiling time expired.
+    Profiling = 27,
+    /// Windows resize signal.
+    Winch = 28,
+    /// I/O now possible.
+    IO = 29,
+    // Pollable event (Sys V). Synonym for IO
+    //Poll = 29,
+    /// Power failure (System V).
+    Power = 30,
+    /// Bad argument to routine (SVr4).
+    Sys = 31,
+}
+
+/// A struct representing system load average value.
+///
+/// It is returned by [`SystemExt::get_load_average`][crate::SystemExt::get_load_average].
+///
+/// ```no_run
+/// use sysinfo::{System, SystemExt};
+///
+/// let s = System::new_all();
+/// let load_avg = s.get_load_average();
+/// println!(
+///     "one minute: {}%, five minutes: {}%, fifteen minutes: {}%",
+///     load_avg.one,
+///     load_avg.five,
+///     load_avg.fifteen,
+/// );
+/// ```
+#[repr(C)]
+#[derive(Default, Debug, Clone)]
+pub struct LoadAvg {
+    /// Average load within one minute.
+    pub one: f64,
+    /// Average load within five minutes.
+    pub five: f64,
+    /// Average load within fifteen minutes.
+    pub fifteen: f64,
+}
+
+/// Type containing user information.
+///
+/// It is returned by [`SystemExt::get_users`][crate::SystemExt::get_users].
+///
+/// ```no_run
+/// use sysinfo::{System, SystemExt};
+///
+/// let s = System::new_all();
+/// println!("users: {:?}", s.get_users());
+/// ```
+#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub struct User {
+    pub(crate) name: String,
+    pub(crate) groups: Vec<String>,
+}
+
+impl UserExt for User {
+    fn get_name(&self) -> &str {
+        &self.name
+    }
+
+    fn get_groups(&self) -> &[String] {
+        &self.groups
+    }
+}
+
+/// Type containing read and written bytes.
+///
+/// It is returned by [`ProcessExt::get_disk_usage`][crate::ProcessExt::get_disk_usage].
+///
+/// ```no_run
+/// use sysinfo::{ProcessExt, System, SystemExt};
+///
+/// let s = System::new_all();
+/// for (pid, process) in s.get_processes() {
+///     let disk_usage = process.get_disk_usage();
+///     println!("[{}] read bytes   : new/total => {}/{}",
+///         pid,
+///         disk_usage.read_bytes,
+///         disk_usage.total_read_bytes,
+///     );
+///     println!("[{}] written bytes: new/total => {}/{}",
+///         pid,
+///         disk_usage.written_bytes,
+///         disk_usage.total_written_bytes,
+///     );
+/// }
+/// ```
+#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
+pub struct DiskUsage {
+    /// Total number of written bytes.
+    pub total_written_bytes: u64,
+    /// Number of written bytes.
+    pub written_bytes: u64,
+    /// Total number of read bytes.
+    pub total_read_bytes: u64,
+    /// Number of read bytes.
+    pub read_bytes: u64,
+}
diff --git a/src/debug.rs b/src/debug.rs
index 73ca273..e70e987 100644
--- a/src/debug.rs
+++ b/src/debug.rs
@@ -131,11 +131,17 @@ impl fmt::Debug for NetworkData {
             .field("packets income", &self.get_packets_received())
             .field("total packets income", &self.get_total_packets_received())
             .field("packets outcome", &self.get_packets_transmitted())
-            .field("total packets outcome", &self.get_total_packets_transmitted())
+            .field(
+                "total packets outcome",
+                &self.get_total_packets_transmitted(),
+            )
             .field("errors income", &self.get_errors_on_received())
             .field("total errors income", &self.get_total_errors_on_received())
             .field("errors outcome", &self.get_errors_on_transmitted())
-            .field("total errors outcome", &self.get_total_errors_on_transmitted())
+            .field(
+                "total errors outcome",
+                &self.get_total_errors_on_transmitted(),
+            )
             .finish()
     }
 }
diff --git a/src/linux/process.rs b/src/linux/process.rs
index f0eb027..f2fbe69 100644
--- a/src/linux/process.rs
+++ b/src/linux/process.rs
@@ -11,6 +11,7 @@ use std::path::{Path, PathBuf};
 
 use libc::{c_int, gid_t, kill, uid_t};
 
+use DiskUsage;
 use Pid;
 use ProcessExt;
 
@@ -124,6 +125,10 @@ pub struct Process {
     /// Tasks run by this process.
     pub tasks: HashMap<Pid, Process>,
     pub(crate) stat_file: Option<File>,
+    old_read_bytes: u64,
+    old_written_bytes: u64,
+    read_bytes: u64,
+    written_bytes: u64,
 }
 
 impl ProcessExt for Process {
@@ -155,6 +160,10 @@ impl ProcessExt for Process {
                 HashMap::new()
             },
             stat_file: None,
+            old_read_bytes: 0,
+            old_written_bytes: 0,
+            read_bytes: 0,
+            written_bytes: 0,
         }
     }
 
@@ -215,6 +224,15 @@ impl ProcessExt for Process {
     fn cpu_usage(&self) -> f32 {
         self.cpu_usage
     }
+
+    fn get_disk_usage(&self) -> DiskUsage {
+        DiskUsage {
+            written_bytes: self.written_bytes - self.old_written_bytes,
+            total_written_bytes: self.written_bytes,
+            read_bytes: self.read_bytes - self.old_read_bytes,
+            total_read_bytes: self.read_bytes,
+        }
+    }
 }
 
 impl Drop for Process {
@@ -244,3 +262,32 @@ pub fn set_time(p: &mut Process, utime: u64, stime: u64) {
 pub fn has_been_updated(p: &Process) -> bool {
     p.updated
 }
+
+pub(crate) fn update_process_disk_activity(p: &mut Process, path: &Path) {
+    let mut path = PathBuf::from(path);
+    path.push("io");
+    let data = match super::system::get_all_data(&path, 16_384) {
+        Ok(d) => d,
+        Err(_) => return,
+    };
+    let mut done = 0;
+    for line in data.split("\n") {
+        let mut parts = line.split(": ");
+        match parts.next() {
+            Some("read_bytes") => {
+                p.old_read_bytes = p.read_bytes;
+                p.read_bytes = parts.next().and_then(|x| x.parse::<u64>().ok()).unwrap_or(0);
+            }
+            Some("write_bytes") => {
+                p.old_written_bytes = p.written_bytes;
+                p.written_bytes = parts.next().and_then(|x| x.parse::<u64>().ok()).unwrap_or(0);
+            }
+            _ => continue,
+        }
+        done += 1;
+        if done > 1 {
+            // No need to continue the reading.
+            break;
+        }
+    }
+}
diff --git a/src/linux/system.rs b/src/linux/system.rs
index 7c23dc2..efd498a 100644
--- a/src/linux/system.rs
+++ b/src/linux/system.rs
@@ -120,10 +120,7 @@ fn boot_time() -> u64 {
     if unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut up) } == 0 {
         up.tv_sec as u64
     } else {
-        #[cfg(feature = "debug")]
-        {
-            println!("clock_gettime failed: boot time cannot be retrieve...");
-        }
+        sysinfo_debug!("clock_gettime failed: boot time cannot be retrieve...");
         0
     }
 }
@@ -738,6 +735,7 @@ fn _get_process_data(
             uptime,
             now,
         );
+        update_process_disk_activity(entry, path);
         return Ok(None);
     }
 
@@ -821,6 +819,7 @@ fn _get_process_data(
         uptime,
         now,
     );
+    update_process_disk_activity(&mut p, path);
     Ok(Some(p))
 }
 
diff --git a/src/mac/ffi.rs b/src/mac/ffi.rs
index f3e0223..8fc30d1 100644
--- a/src/mac/ffi.rs
+++ b/src/mac/ffi.rs
@@ -30,6 +30,7 @@ extern "C" {
     //pub fn proc_regionfilename(pid: c_int, address: u64, buffer: *mut c_void,
     //                           buffersize: u32) -> c_int;
     pub fn proc_pidpath(pid: c_int, buffer: *mut c_void, buffersize: u32) -> c_int;
+    pub fn proc_pid_rusage(pid: c_int, flavor: c_int, buffer: *mut c_void) -> c_int;
 
     pub fn IOMasterPort(a: i32, b: *mut mach_port_t) -> i32;
     pub fn IOServiceMatching(a: *const c_char) -> *mut c_void;
@@ -431,6 +432,31 @@ pub struct xsw_usage {
     pub xsu_encrypted: boolean_t,
 }
 
+//https://github.com/andrewdavidmackenzie/libproc-rs/blob/master/src/libproc/pid_rusage.rs
+#[derive(Debug, Default)]
+#[repr(C)]
+pub struct RUsageInfoV2 {
+    pub ri_uuid: [u8; 16],
+    pub ri_user_time: u64,
+    pub ri_system_time: u64,
+    pub ri_pkg_idle_wkups: u64,
+    pub ri_interrupt_wkups: u64,
+    pub ri_pageins: u64,
+    pub ri_wired_size: u64,
+    pub ri_resident_size: u64,
+    pub ri_phys_footprint: u64,
+    pub ri_proc_start_abstime: u64,
+    pub ri_proc_exit_abstime: u64,
+    pub ri_child_user_time: u64,
+    pub ri_child_system_time: u64,
+    pub ri_child_pkg_idle_wkups: u64,
+    pub ri_child_interrupt_wkups: u64,
+    pub ri_child_pageins: u64,
+    pub ri_child_elapsed_abstime: u64,
+    pub ri_diskio_bytesread: u64,
+    pub ri_diskio_byteswritten: u64,
+}
+
 //pub const HOST_CPU_LOAD_INFO_COUNT: usize = 4;
 //pub const HOST_CPU_LOAD_INFO: u32 = 3;
 pub const KERN_SUCCESS: kern_return_t = 0;
diff --git a/src/mac/process.rs b/src/mac/process.rs
index 56556ad..41adebf 100644
--- a/src/mac/process.rs
+++ b/src/mac/process.rs
@@ -7,12 +7,13 @@
 use std::borrow::Borrow;
 use std::ffi::OsStr;
 use std::fmt;
-use std::mem;
+use std::mem::{self, MaybeUninit};
 use std::ops::Deref;
 use std::path::{Path, PathBuf};
 
 use libc::{c_int, c_void, gid_t, kill, size_t, uid_t};
 
+use DiskUsage;
 use Pid;
 use ProcessExt;
 
@@ -149,6 +150,10 @@ pub struct Process {
     ///
     /// This is very likely this one that you want instead of `process_status`.
     pub status: Option<ThreadStatus>,
+    pub(crate) old_read_bytes: u64,
+    pub(crate) old_written_bytes: u64,
+    pub(crate) read_bytes: u64,
+    pub(crate) written_bytes: u64,
 }
 
 impl Process {
@@ -175,6 +180,10 @@ impl Process {
             gid: 0,
             process_status: ProcessStatus::Unknown(0),
             status: None,
+            old_read_bytes: 0,
+            old_written_bytes: 0,
+            read_bytes: 0,
+            written_bytes: 0,
         }
     }
 
@@ -210,6 +219,10 @@ impl Process {
             gid: 0,
             process_status: ProcessStatus::Unknown(0),
             status: None,
+            old_read_bytes: 0,
+            old_written_bytes: 0,
+            read_bytes: 0,
+            written_bytes: 0,
         }
     }
 }
@@ -238,6 +251,10 @@ impl ProcessExt for Process {
             gid: 0,
             process_status: ProcessStatus::Unknown(0),
             status: None,
+            old_read_bytes: 0,
+            old_written_bytes: 0,
+            read_bytes: 0,
+            written_bytes: 0,
         }
     }
 
@@ -296,6 +313,15 @@ impl ProcessExt for Process {
     fn cpu_usage(&self) -> f32 {
         self.cpu_usage
     }
+
+    fn get_disk_usage(&self) -> DiskUsage {
+        DiskUsage {
+            read_bytes: self.read_bytes - self.old_read_bytes,
+            total_read_bytes: self.read_bytes,
+            written_bytes: self.written_bytes - self.old_written_bytes,
+            total_written_bytes: self.written_bytes,
+        }
+    }
 }
 
 pub(crate) fn compute_cpu_usage(p: &mut Process, time: u64, task_time: u64) {
@@ -384,6 +410,7 @@ pub(crate) fn update_process(
 
             p.memory = task_info.pti_resident_size >> 10; // divide by 1024
             p.virtual_memory = task_info.pti_virtual_size >> 10; // divide by 1024
+            update_proc_disk_activity(p);
             return Ok(None);
         }
 
@@ -572,11 +599,26 @@ pub(crate) fn update_process(
         p.uid = info.pbi_uid;
         p.gid = info.pbi_gid;
         p.process_status = ProcessStatus::from(info.pbi_status);
-
+        update_proc_disk_activity(&mut p);
         Ok(Some(p))
     }
 }
 
+fn update_proc_disk_activity(p: &mut Process) {
+    p.old_read_bytes = p.read_bytes;
+    p.old_written_bytes = p.written_bytes;
+
+    let mut pidrusage: ffi::RUsageInfoV2 = unsafe { MaybeUninit::uninit().assume_init() };
+    let retval = unsafe { ffi::proc_pid_rusage(p.pid() as c_int, 2, &mut pidrusage as *mut _ as _) };
+
+    if retval < 0 {
+        sysinfo_debug!("proc_pid_rusage failed: {:?}", retval);
+    } else {
+        p.read_bytes = pidrusage.ri_diskio_bytesread;
+        p.written_bytes = pidrusage.ri_diskio_byteswritten;
+    }
+}
+
 pub(crate) fn get_proc_list() -> Option<Vec<Pid>> {
     let count = unsafe { ffi::proc_listallpids(::std::ptr::null_mut(), 0) };
     if count < 1 {
diff --git a/src/sysinfo.rs b/src/sysinfo.rs
index 12fae02..2f25810 100644
--- a/src/sysinfo.rs
+++ b/src/sysinfo.rs
@@ -61,6 +61,20 @@ extern crate doc_comment;
 #[cfg(doctest)]
 doctest!("../README.md");
 
+#[cfg(feature = "debug")]
+#[doc(hidden)]
+macro_rules! sysinfo_debug {
+    ($($x:tt)*) => {{
+        eprintln!($($x)*);
+    }}
+}
+
+#[cfg(not(feature = "debug"))]
+#[doc(hidden)]
+macro_rules! sysinfo_debug {
+    ($($x:tt)*) => {{}};
+}
+
 cfg_if! {
     if #[cfg(target_os = "macos")] {
         mod mac;
@@ -91,7 +105,9 @@ cfg_if! {
     }
 }
 
-pub use common::{AsU32, DiskType, NetworksIter, Pid, RefreshKind};
+pub use common::{
+    AsU32, DiskType, DiskUsage, LoadAvg, NetworksIter, Pid, RefreshKind, Signal, User,
+};
 pub use sys::{Component, Disk, NetworkData, Networks, Process, ProcessStatus, Processor, System};
 pub use traits::{
     ComponentExt, DiskExt, NetworkExt, NetworksExt, ProcessExt, ProcessorExt, SystemExt, UserExt,
@@ -158,131 +174,6 @@ pub fn set_open_files_limit(mut _new_limit: isize) -> bool {
     }
 }
 
-/// An enum representing signal on UNIX-like systems.
-#[repr(C)]
-#[derive(Clone, PartialEq, PartialOrd, Debug, Copy)]
-pub enum Signal {
-    /// Hangup detected on controlling terminal or death of controlling process.
-    Hangup = 1,
-    /// Interrupt from keyboard.
-    Interrupt = 2,
-    /// Quit from keyboard.
-    Quit = 3,
-    /// Illegal instruction.
-    Illegal = 4,
-    /// Trace/breakpoint trap.
-    Trap = 5,
-    /// Abort signal from C abort function.
-    Abort = 6,
-    // IOT trap. A synonym for SIGABRT.
-    // IOT = 6,
-    /// Bus error (bad memory access).
-    Bus = 7,
-    /// Floating point exception.
-    FloatingPointException = 8,
-    /// Kill signal.
-    Kill = 9,
-    /// User-defined signal 1.
-    User1 = 10,
-    /// Invalid memory reference.
-    Segv = 11,
-    /// User-defined signal 2.
-    User2 = 12,
-    /// Broken pipe: write to pipe with no readers.
-    Pipe = 13,
-    /// Timer signal from C alarm function.
-    Alarm = 14,
-    /// Termination signal.
-    Term = 15,
-    /// Stack fault on coprocessor (unused).
-    Stklft = 16,
-    /// Child stopped or terminated.
-    Child = 17,
-    /// Continue if stopped.
-    Continue = 18,
-    /// Stop process.
-    Stop = 19,
-    /// Stop typed at terminal.
-    TSTP = 20,
-    /// Terminal input for background process.
-    TTIN = 21,
-    /// Terminal output for background process.
-    TTOU = 22,
-    /// Urgent condition on socket.
-    Urgent = 23,
-    /// CPU time limit exceeded.
-    XCPU = 24,
-    /// File size limit exceeded.
-    XFSZ = 25,
-    /// Virtual alarm clock.
-    VirtualAlarm = 26,
-    /// Profiling time expired.
-    Profiling = 27,
-    /// Windows resize signal.
-    Winch = 28,
-    /// I/O now possible.
-    IO = 29,
-    // Pollable event (Sys V). Synonym for IO
-    //Poll = 29,
-    /// Power failure (System V).
-    Power = 30,
-    /// Bad argument to routine (SVr4).
-    Sys = 31,
-}
-
-/// A struct representing system load average value.
-///
-/// It is returned by [`SystemExt::get_load_average`].
-///
-/// ```no_run
-/// use sysinfo::{System, SystemExt};
-///
-/// let s = System::new_all();
-/// let load_avg = s.get_load_average();
-/// println!(
-///     "one minute: {}%, five minutes: {}%, fifteen minutes: {}%",
-///     load_avg.one,
-///     load_avg.five,
-///     load_avg.fifteen,
-/// );
-/// ```
-#[repr(C)]
-#[derive(Default, Debug, Clone)]
-pub struct LoadAvg {
-    /// Average load within one minute.
-    pub one: f64,
-    /// Average load within five minutes.
-    pub five: f64,
-    /// Average load within fifteen minutes.
-    pub fifteen: f64,
-}
-
-/// Type containing user information.
-///
-/// It is returned by [`SystemExt::get_users`].
-///
-/// ```no_run
-/// use sysinfo::{System, SystemExt};
-///
-/// let s = System::new_all();
-/// println!("users: {:?}", s.get_users());
-/// ```
-#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
-pub struct User {
-    name: String,
-    groups: Vec<String>,
-}
-
-impl UserExt for User {
-    fn get_name(&self) -> &str {
-        &self.name
-    }
-
-    fn get_groups(&self) -> &[String] {
-        &self.groups
-    }
-}
-
 #[cfg(test)]
 mod test {
     use super::*;
diff --git a/src/traits.rs b/src/traits.rs
index 88a3122..69ac35e 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -6,6 +6,7 @@
 
 use sys::{Component, Disk, Networks, Process, Processor};
 use DiskType;
+use DiskUsage;
 use LoadAvg;
 use NetworksIter;
 use Pid;
@@ -295,6 +296,28 @@ pub trait ProcessExt: Debug {
     /// }
     /// ```
     fn cpu_usage(&self) -> f32;
+
+    /// Returns number of bytes read and written to disk.
+    ///
+    /// /!\\ On Windows, this method actually returns **ALL** I/O read and written bytes.
+    ///
+    /// ```no_run
+    /// use sysinfo::{ProcessExt, System, SystemExt};
+    ///
+    /// let s = System::new();
+    /// if let Some(process) = s.get_process(1337) {
+    ///     let disk_usage = process.get_disk_usage();
+    ///     println!("read bytes   : new/total => {}/{}",
+    ///         disk_usage.read_bytes,
+    ///         disk_usage.total_read_bytes,
+    ///     );
+    ///     println!("written bytes: new/total => {}/{}",
+    ///         disk_usage.written_bytes,
+    ///         disk_usage.total_written_bytes,
+    ///     );
+    /// }
+    /// ```
+    fn get_disk_usage(&self) -> DiskUsage;
 }
 
 /// Contains all the methods of the [`Processor`][crate::Processor] struct.
diff --git a/src/unknown/process.rs b/src/unknown/process.rs
index d79bafd..b55884f 100644
--- a/src/unknown/process.rs
+++ b/src/unknown/process.rs
@@ -5,6 +5,7 @@
 //
 
 use std::path::Path;
+use DiskUsage;
 use Pid;
 use ProcessExt;
 
@@ -79,4 +80,8 @@ impl ProcessExt for Process {
     fn cpu_usage(&self) -> f32 {
         0.0
     }
+
+    fn get_disk_usage(&self) -> DiskUsage {
+        DiskUsage::default()
+    }
 }
diff --git a/src/windows/process.rs b/src/windows/process.rs
index cffac2d..a5e9945 100644
--- a/src/windows/process.rs
+++ b/src/windows/process.rs
@@ -14,6 +14,7 @@ use std::str;
 
 use libc::{c_void, memcpy};
 
+use DiskUsage;
 use Pid;
 use ProcessExt;
 
@@ -28,7 +29,8 @@ use winapi::um::psapi::{
     LIST_MODULES_ALL, PROCESS_MEMORY_COUNTERS, PROCESS_MEMORY_COUNTERS_EX,
 };
 use winapi::um::sysinfoapi::GetSystemTimeAsFileTime;
-use winapi::um::winnt::{HANDLE, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, ULARGE_INTEGER};
+use winapi::um::winbase::GetProcessIoCounters;
+use winapi::um::winnt::{IO_COUNTERS, HANDLE, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, ULARGE_INTEGER};
 
 /// Enum describing the different status of a process.
 #[derive(Clone, Copy, Debug)]
@@ -66,21 +68,20 @@ fn get_process_handler(pid: Pid) -> Option<HANDLE> {
 }
 
 #[derive(Clone)]
-struct HandleWrapper(HANDLE);
+struct PtrWrapper<T: Clone>(T);
 
-impl Deref for HandleWrapper {
-    type Target = HANDLE;
+impl<T: Clone> Deref for PtrWrapper<T> {
+    type Target = T;
 
-    fn deref(&self) -> &HANDLE {
+    fn deref(&self) -> &Self::Target {
         &self.0
     }
 }
 
-unsafe impl Send for HandleWrapper {}
-unsafe impl Sync for HandleWrapper {}
+unsafe impl<T: Clone> Send for PtrWrapper<T> {}
+unsafe impl<T: Clone> Sync for PtrWrapper<T> {}
 
 /// Struct containing a process' information.
-#[derive(Clone)]
 pub struct Process {
     name: String,
     cmd: Vec<String>,
@@ -93,13 +94,17 @@ pub struct Process {
     pub(crate) virtual_memory: u64,
     parent: Option<Pid>,
     status: ProcessStatus,
-    handle: HandleWrapper,
+    handle: PtrWrapper<HANDLE>,
     old_cpu: u64,
     old_sys_cpu: u64,
     old_user_cpu: u64,
     start_time: u64,
     cpu_usage: f32,
     pub(crate) updated: bool,
+    old_read_bytes: u64,
+    old_written_bytes: u64,
+    read_bytes: u64,
+    written_bytes: u64,
 }
 
 unsafe fn get_process_name(process_handler: HANDLE, h_mod: *mut c_void) -> String {
@@ -200,7 +205,7 @@ impl Process {
             let mut root = exe.clone();
             root.pop();
             Process {
-                handle: HandleWrapper(handle),
+                handle: PtrWrapper(handle),
                 name,
                 pid,
                 parent,
@@ -218,10 +223,14 @@ impl Process {
                 old_user_cpu: 0,
                 start_time: unsafe { get_start_time(handle) },
                 updated: true,
+                old_read_bytes: 0,
+                old_written_bytes: 0,
+                read_bytes: 0,
+                written_bytes: 0,
             }
         } else {
             Process {
-                handle: HandleWrapper(null_mut()),
+                handle: PtrWrapper(null_mut()),
                 name,
                 pid,
                 parent,
@@ -239,6 +248,10 @@ impl Process {
                 old_user_cpu: 0,
                 start_time: 0,
                 updated: true,
+                old_read_bytes: 0,
+                old_written_bytes: 0,
+                read_bytes: 0,
+                written_bytes: 0,
             }
         }
     }
@@ -258,7 +271,7 @@ impl Process {
             let mut root = exe.clone();
             root.pop();
             Process {
-                handle: HandleWrapper(process_handler),
+                handle: PtrWrapper(process_handler),
                 name,
                 pid,
                 parent,
@@ -276,6 +289,10 @@ impl Process {
                 old_user_cpu: 0,
                 start_time: get_start_time(process_handler),
                 updated: true,
+                old_read_bytes: 0,
+                old_written_bytes: 0,
+                read_bytes: 0,
+                written_bytes: 0,
             }
         }
     }
@@ -292,7 +309,7 @@ impl ProcessExt for Process {
             Process::new_with_handle(pid, parent, process_handler)
         } else {
             Process {
-                handle: HandleWrapper(null_mut()),
+                handle: PtrWrapper(null_mut()),
                 name: String::new(),
                 pid,
                 parent,
@@ -310,6 +327,10 @@ impl ProcessExt for Process {
                 old_user_cpu: 0,
                 start_time: 0,
                 updated: true,
+                old_read_bytes: 0,
+                old_written_bytes: 0,
+                read_bytes: 0,
+                written_bytes: 0,
             }
         }
     }
@@ -374,6 +395,15 @@ impl ProcessExt for Process {
     fn cpu_usage(&self) -> f32 {
         self.cpu_usage
     }
+
+    fn get_disk_usage(&self) -> DiskUsage {
+        DiskUsage {
+            written_bytes: self.written_bytes - self.old_written_bytes,
+            total_written_bytes: self.written_bytes,
+            read_bytes: self.read_bytes - self.old_read_bytes,
+            total_read_bytes: self.read_bytes,
+        }
+    }
 }
 
 impl Drop for Process {
@@ -409,7 +439,7 @@ fn get_cmd_line(handle: HANDLE) -> Vec<String> {
     use winapi::um::memoryapi::ReadProcessMemory;
 
     unsafe {
-        let mut pinfo = std::mem::MaybeUninit::<PROCESS_BASIC_INFORMATION>::uninit();
+        let mut pinfo = MaybeUninit::<PROCESS_BASIC_INFORMATION>::uninit();
         if NtQueryInformationProcess(
             handle,
             0, // ProcessBasicInformation
@@ -423,7 +453,7 @@ fn get_cmd_line(handle: HANDLE) -> Vec<String> {
         let pinfo = pinfo.assume_init();
 
         let ppeb: PPEB = pinfo.PebBaseAddress;
-        let mut peb_copy = std::mem::MaybeUninit::<PEB>::uninit();
+        let mut peb_copy = MaybeUninit::<PEB>::uninit();
         if ReadProcessMemory(
             handle,
             ppeb as *mut _,
@@ -437,8 +467,7 @@ fn get_cmd_line(handle: HANDLE) -> Vec<String> {
         let peb_copy = peb_copy.assume_init();
 
         let proc_param = peb_copy.ProcessParameters;
-        let mut rtl_proc_param_copy =
-            std::mem::MaybeUninit::<RTL_USER_PROCESS_PARAMETERS>::uninit();
+        let mut rtl_proc_param_copy = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS>::uninit();
         if ReadProcessMemory(
             handle,
             proc_param as *mut PRTL_USER_PROCESS_PARAMETERS as *mut _,
@@ -627,6 +656,21 @@ pub fn get_handle(p: &Process) -> HANDLE {
 
 pub fn update_proc_info(p: &mut Process) {
     update_memory(p);
+    update_disk_usage(p);
+}
+
+pub fn update_disk_usage(p: &mut Process) {
+    let mut counters = MaybeUninit::<IO_COUNTERS>::uninit();
+    let ret = unsafe { GetProcessIoCounters(*p.handle, counters.as_mut_ptr()) };
+    if ret == 0 {
+        sysinfo_debug!("GetProcessIoCounters call failed on process {}", p.pid());
+    } else {
+        let counters = unsafe { counters.assume_init() };
+        p.old_read_bytes = p.read_bytes;
+        p.old_written_bytes = p.written_bytes;
+        p.read_bytes = counters.ReadTransferCount;
+        p.written_bytes = counters.WriteTransferCount;
+    }
 }
 
 pub fn update_memory(p: &mut Process) {
diff --git a/src/windows/system.rs b/src/windows/system.rs
index dfa13cc..9070f9e 100644
--- a/src/windows/system.rs
+++ b/src/windows/system.rs
@@ -23,7 +23,8 @@ use SystemExt;
 use User;
 
 use windows::process::{
-    compute_cpu_usage, get_handle, get_system_computation_time, update_proc_info, Process,
+    compute_cpu_usage, get_handle, get_system_computation_time, update_disk_usage, update_proc_info,
+    Process,
 };
 use windows::tools::*;
 
@@ -37,7 +38,6 @@ use winapi::um::minwinbase::STILL_ACTIVE;
 use winapi::um::processthreadsapi::GetExitCodeProcess;
 use winapi::um::sysinfoapi::{GetTickCount64, GlobalMemoryStatusEx, MEMORYSTATUSEX};
 use winapi::um::winnt::HANDLE;
-
 use rayon::prelude::*;
 
 /// Struct containing the system's information.
@@ -67,10 +67,7 @@ unsafe fn boot_time() -> u64 {
     match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
         Ok(n) => n.as_secs() - GetTickCount64() / 1000,
         Err(_e) => {
-            #[cfg(feature = "debug")]
-            {
-                println!("Failed to compute boot time: {:?}", _e);
-            }
+            sysinfo_debug!("Failed to compute boot time: {:?}", _e);
             0
         }
     }
@@ -183,6 +180,7 @@ impl SystemExt for System {
         } else if let Some(mut p) = Process::new_from_pid(pid) {
             let system_time = get_system_computation_time();
             compute_cpu_usage(&mut p, self.processors.len() as u64, system_time);
+            update_disk_usage(&mut p);
             self.process_list.insert(pid, p);
             true
         } else {
@@ -251,6 +249,7 @@ impl SystemExt for System {
                             proc_.memory = (pi.WorkingSetSize as u64) >> 10u64;
                             proc_.virtual_memory = (pi.VirtualSize as u64) >> 10u64;
                             compute_cpu_usage(proc_, nb_processors, system_time);
+                            update_disk_usage(proc_);
                             proc_.updated = true;
                             return None;
                         }
@@ -267,6 +266,7 @@ impl SystemExt for System {
                             name,
                         );
                         compute_cpu_usage(&mut p, nb_processors, system_time);
+                        update_disk_usage(&mut p);
                         Some(p)
                     })
                     .collect::<Vec<_>>();


Test patch:
diff --git a/tests/process.rs b/tests/process.rs
index fcabbe6..b5b2dff 100644
--- a/tests/process.rs
+++ b/tests/process.rs
@@ -43,20 +43,64 @@ fn test_get_cmd_line() {
         .spawn()
         .unwrap();
     let mut s = sysinfo::System::new();
+    assert!(s.get_processes().len() == 0);
     s.refresh_processes();
-    let process = s.get_process(p.id() as sysinfo::Pid).unwrap();
-    assert_eq!(process.cmd(), &["timeout", "/t", "3"]);
+    assert!(s.get_processes().len() > 0);
+    if let Some(process) = s.get_process(p.id() as sysinfo::Pid) {
+        assert_eq!(process.cmd(), &["timeout", "/t", "3"]);
+    } else {
+        // We're very likely on a "linux-like" shell so let's try some unix command...
+        unix_like_cmd();
+    }
 }
 
 #[test]
 #[cfg(not(windows))]
 fn test_get_cmd_line() {
+    unix_like_cmd();
+}
+
+fn unix_like_cmd() {
     let p = std::process::Command::new("sleep")
         .arg("3")
         .spawn()
         .unwrap();
     let mut s = sysinfo::System::new();
+    assert!(s.get_processes().len() == 0);
     s.refresh_processes();
+    assert!(s.get_processes().len() > 0);
     let process = s.get_process(p.id() as sysinfo::Pid).unwrap();
     assert_eq!(process.cmd(), &["sleep", "3"]);
 }
+
+#[test]
+fn test_process_disk_usage() {
+    use std::fs;
+    use std::fs::File;
+    use std::io::prelude::*;
+    use sysinfo::{get_current_pid, ProcessExt, SystemExt};
+    {
+        let mut file = File::create("test.txt").unwrap();
+        file.write_all(b"This is a test file\nwith test data.\n")
+            .unwrap();
+    }
+    fs::remove_file("test.txt").ok();
+    let mut system = sysinfo::System::new();
+    assert!(system.get_processes().len() == 0);
+    system.refresh_processes();
+    assert!(system.get_processes().len() > 0);
+    let p = system
+        .get_process(get_current_pid().expect("Failed retrieving current pid."))
+        .expect("failed to get process");
+
+    assert!(
+        p.get_disk_usage().total_written_bytes > 0,
+        "found {} total written bytes...",
+        p.get_disk_usage().total_written_bytes
+    );
+    assert!(
+        p.get_disk_usage().written_bytes > 0,
+        "found {} written bytes...",
+        p.get_disk_usage().written_bytes
+    );
+}