Repository: GuillaumeGomez/sysinfo
Pull request #1139 (2023-11-13 20:52:56)

Extend `ProcessRefreshKind` to allow more specific updates
Fixes https://github.com/GuillaumeGomez/sysinfo/issues/1016.


Patch:
diff --git a/src/common.rs b/src/common.rs
index a676faa..23b1a98 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -1401,6 +1401,12 @@ pub struct ProcessRefreshKind {
     cpu: bool,
     disk_usage: bool,
     user: bool,
+    memory: bool,
+    cwd: bool,
+    root: bool,
+    environ: bool,
+    cmd: bool,
+    exe: bool,
 }
 
 impl ProcessRefreshKind {
@@ -1433,6 +1439,12 @@ impl ProcessRefreshKind {
             cpu: true,
             disk_usage: true,
             user: true,
+            memory: true,
+            cwd: true,
+            root: true,
+            environ: true,
+            cmd: true,
+            exe: true,
         }
     }
 
@@ -1443,14 +1455,13 @@ impl ProcessRefreshKind {
         with_disk_usage,
         without_disk_usage
     );
-    impl_get_set!(
-        ProcessRefreshKind,
-        user,
-        with_user,
-        without_user,
-        r#"This refresh is about `user_id` and `group_id`. Please note that it has an effect mostly
-on Windows as other platforms get this information alongside the Process information directly."#,
-    );
+    impl_get_set!(ProcessRefreshKind, user, with_user, without_user);
+    impl_get_set!(ProcessRefreshKind, memory, with_memory, without_memory);
+    impl_get_set!(ProcessRefreshKind, cwd, with_cwd, without_cwd);
+    impl_get_set!(ProcessRefreshKind, root, with_root, without_root);
+    impl_get_set!(ProcessRefreshKind, environ, with_environ, without_environ);
+    impl_get_set!(ProcessRefreshKind, cmd, with_cmd, without_cmd);
+    impl_get_set!(ProcessRefreshKind, exe, with_exe, without_exe);
 }
 
 /// Used to determine what you want to refresh specifically on the [`Cpu`] type.
diff --git a/src/lib.rs b/src/lib.rs
index 540980a..ca9e0ce 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -506,21 +506,6 @@ mod test {
         );
     }
 
-    // We ensure that the `Process` cmd information is retrieved as expected.
-    #[test]
-    fn check_cmd_line() {
-        if !IS_SUPPORTED {
-            return;
-        }
-        let mut sys = System::new();
-        sys.refresh_processes_specifics(ProcessRefreshKind::new());
-
-        assert!(sys
-            .processes()
-            .iter()
-            .any(|(_, process)| !process.cmd().is_empty()));
-    }
-
     #[test]
     fn check_cpu_arch() {
         let s = System::new();
diff --git a/src/unix/apple/macos/process.rs b/src/unix/apple/macos/process.rs
index 7bd997e..f7fe1df 100644
--- a/src/unix/apple/macos/process.rs
+++ b/src/unix/apple/macos/process.rs
@@ -1,11 +1,8 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
 use std::mem::{self, MaybeUninit};
-use std::ops::Deref;
 use std::path::{Path, PathBuf};
 
-use std::borrow::Borrow;
-
 use libc::{c_int, c_void, kill};
 
 use crate::{DiskUsage, Gid, Pid, Process, ProcessRefreshKind, ProcessStatus, Signal, Uid};
@@ -48,22 +45,16 @@ pub(crate) struct ProcessInner {
 }
 
 impl ProcessInner {
-    pub(crate) fn new_empty(
-        pid: Pid,
-        exe: PathBuf,
-        name: String,
-        cwd: PathBuf,
-        root: PathBuf,
-    ) -> Self {
+    pub(crate) fn new_empty(pid: Pid) -> Self {
         Self {
-            name,
+            name: String::new(),
             pid,
             parent: None,
             cmd: Vec::new(),
             environ: Vec::new(),
-            exe,
-            cwd,
-            root,
+            exe: PathBuf::new(),
+            cwd: PathBuf::new(),
+            root: PathBuf::new(),
             memory: 0,
             virtual_memory: 0,
             cpu_usage: 0.,
@@ -85,14 +76,7 @@ impl ProcessInner {
         }
     }
 
-    pub(crate) fn new(
-        pid: Pid,
-        parent: Option<Pid>,
-        start_time: u64,
-        run_time: u64,
-        cwd: PathBuf,
-        root: PathBuf,
-    ) -> Self {
+    pub(crate) fn new(pid: Pid, parent: Option<Pid>, start_time: u64, run_time: u64) -> Self {
         Self {
             name: String::new(),
             pid,
@@ -100,8 +84,8 @@ impl ProcessInner {
             cmd: Vec::new(),
             environ: Vec::new(),
             exe: PathBuf::new(),
-            cwd,
-            root,
+            cwd: PathBuf::new(),
+            root: PathBuf::new(),
             memory: 0,
             virtual_memory: 0,
             cpu_usage: 0.,
@@ -349,65 +333,21 @@ unsafe fn get_bsd_info(pid: Pid) -> Option<libc::proc_bsdinfo> {
     }
 }
 
-unsafe fn convert_node_path_info(node: &libc::vnode_info_path) -> PathBuf {
-    if node.vip_vi.vi_stat.vst_dev == 0 {
-        return PathBuf::new();
-    }
-    cstr_to_rust_with_size(
-        node.vip_path.as_ptr() as _,
-        Some(node.vip_path.len() * node.vip_path[0].len()),
-    )
-    .map(PathBuf::from)
-    .unwrap_or_default()
-}
-
 unsafe fn create_new_process(
     pid: Pid,
     now: u64,
     refresh_kind: ProcessRefreshKind,
     info: Option<libc::proc_bsdinfo>,
 ) -> Result<Option<Process>, ()> {
-    let mut vnodepathinfo = mem::zeroed::<libc::proc_vnodepathinfo>();
-    let result = libc::proc_pidinfo(
-        pid.0,
-        libc::PROC_PIDVNODEPATHINFO,
-        0,
-        &mut vnodepathinfo as *mut _ as *mut _,
-        mem::size_of::<libc::proc_vnodepathinfo>() as _,
-    );
-    let (cwd, root) = if result > 0 {
-        (
-            convert_node_path_info(&vnodepathinfo.pvi_cdir),
-            convert_node_path_info(&vnodepathinfo.pvi_rdir),
-        )
-    } else {
-        (PathBuf::new(), PathBuf::new())
-    };
-
     let info = match info {
         Some(info) => info,
         None => {
-            let mut buffer: Vec<u8> = Vec::with_capacity(libc::PROC_PIDPATHINFO_MAXSIZE as _);
-            match libc::proc_pidpath(
-                pid.0,
-                buffer.as_mut_ptr() as *mut _,
-                libc::PROC_PIDPATHINFO_MAXSIZE as _,
-            ) {
-                x if x > 0 => {
-                    buffer.set_len(x as _);
-                    let tmp = String::from_utf8_unchecked(buffer);
-                    let exe = PathBuf::from(tmp);
-                    let name = exe
-                        .file_name()
-                        .and_then(|x| x.to_str())
-                        .unwrap_or("")
-                        .to_owned();
-                    return Ok(Some(Process {
-                        inner: ProcessInner::new_empty(pid, exe, name, cwd, root),
-                    }));
-                }
-                _ => {}
+            let mut p = ProcessInner::new_empty(pid);
+            if get_exe_and_name_backup(&mut p, refresh_kind) {
+                get_cwd_root(&mut p, refresh_kind);
+                return Ok(Some(Process { inner: p }));
             }
+            // If we can't even have the name, no point in keeping it.
             return Err(());
         }
     };
@@ -416,6 +356,107 @@ unsafe fn create_new_process(
         p => Some(Pid(p)),
     };
 
+    let start_time = info.pbi_start_tvsec;
+    let run_time = now.saturating_sub(start_time);
+
+    let mut p = ProcessInner::new(pid, parent, start_time, run_time);
+    if !get_process_infos(&mut p, refresh_kind) && !get_exe_and_name_backup(&mut p, refresh_kind) {
+        // If we can't even have the name, no point in keeping it.
+        return Err(());
+    }
+    get_cwd_root(&mut p, refresh_kind);
+
+    if refresh_kind.memory() {
+        let task_info = get_task_info(pid);
+        p.memory = task_info.pti_resident_size;
+        p.virtual_memory = task_info.pti_virtual_size;
+    }
+
+    p.user_id = Some(Uid(info.pbi_ruid));
+    p.effective_user_id = Some(Uid(info.pbi_uid));
+    p.group_id = Some(Gid(info.pbi_rgid));
+    p.effective_group_id = Some(Gid(info.pbi_gid));
+    p.process_status = ProcessStatus::from(info.pbi_status);
+    if refresh_kind.disk_usage() {
+        update_proc_disk_activity(&mut p);
+    }
+    Ok(Some(Process { inner: p }))
+}
+
+/// Less efficient way to retrieve `exe` and `name`.
+unsafe fn get_exe_and_name_backup(
+    process: &mut ProcessInner,
+    refresh_kind: ProcessRefreshKind,
+) -> bool {
+    if !refresh_kind.exe() && !process.name.is_empty() {
+        return false;
+    }
+    let mut buffer: Vec<u8> = Vec::with_capacity(libc::PROC_PIDPATHINFO_MAXSIZE as _);
+    match libc::proc_pidpath(
+        process.pid.0,
+        buffer.as_mut_ptr() as *mut _,
+        libc::PROC_PIDPATHINFO_MAXSIZE as _,
+    ) {
+        x if x > 0 => {
+            buffer.set_len(x as _);
+            let tmp = String::from_utf8_unchecked(buffer);
+            let exe = PathBuf::from(tmp);
+            if process.name.is_empty() {
+                process.name = exe
+                    .file_name()
+                    .and_then(|x| x.to_str())
+                    .unwrap_or("")
+                    .to_owned();
+            }
+            if refresh_kind.exe() {
+                process.exe = exe;
+            }
+            true
+        }
+        _ => false,
+    }
+}
+
+unsafe fn convert_node_path_info(node: &libc::vnode_info_path) -> Option<PathBuf> {
+    if node.vip_vi.vi_stat.vst_dev == 0 {
+        return None;
+    }
+    cstr_to_rust_with_size(
+        node.vip_path.as_ptr() as _,
+        Some(node.vip_path.len() * node.vip_path[0].len()),
+    )
+    .map(PathBuf::from)
+}
+
+unsafe fn get_cwd_root(process: &mut ProcessInner, refresh_kind: ProcessRefreshKind) {
+    if !refresh_kind.cwd() && !refresh_kind.root() {
+        return;
+    }
+    let mut vnodepathinfo = mem::zeroed::<libc::proc_vnodepathinfo>();
+    let result = libc::proc_pidinfo(
+        process.pid.0,
+        libc::PROC_PIDVNODEPATHINFO,
+        0,
+        &mut vnodepathinfo as *mut _ as *mut _,
+        mem::size_of::<libc::proc_vnodepathinfo>() as _,
+    );
+    if result < 1 {
+        sysinfo_debug!("Failed to retrieve cwd and root for {}", process.pid.0);
+        return;
+    }
+    if refresh_kind.cwd() {
+        if let Some(cwd) = convert_node_path_info(&vnodepathinfo.pvi_cdir) {
+            process.cwd = cwd;
+        }
+    }
+    if refresh_kind.root() {
+        if let Some(root) = convert_node_path_info(&vnodepathinfo.pvi_rdir) {
+            process.root = root;
+        }
+    }
+}
+
+unsafe fn get_process_infos(process: &mut ProcessInner, refresh_kind: ProcessRefreshKind) -> bool {
     /*
      * /---------------\ 0x00000000
      * | ::::::::::::: |
@@ -446,7 +487,7 @@ unsafe fn create_new_process(
      * :               :
      * \---------------/ 0xffffffff
      */
-    let mut mib = [libc::CTL_KERN, libc::KERN_PROCARGS2, pid.0 as _];
+    let mut mib: [libc::c_int; 3] = [libc::CTL_KERN, libc::KERN_PROCARGS2, process.pid.0 as _];
     let mut arg_max = 0;
     // First we retrieve the size we will need for our data (in `arg_max`).
     if libc::sysctl(
@@ -460,9 +501,9 @@ unsafe fn create_new_process(
     {
         sysinfo_debug!(
             "couldn't get arguments and environment size for PID {}",
-            pid.0
+            process.pid.0
         );
-        return Err(()); // not enough rights I assume?
+        return false; // not enough rights I assume?
     }
 
     let mut proc_args: Vec<u8> = Vec::with_capacity(arg_max as _);
@@ -475,62 +516,51 @@ unsafe fn create_new_process(
         0,
     ) == -1
     {
-        sysinfo_debug!("couldn't get arguments and environment for PID {}", pid.0);
-        return Err(()); // What changed since the previous call? Dark magic!
+        sysinfo_debug!(
+            "couldn't get arguments and environment for PID {}",
+            process.pid.0
+        );
+        return false; // What changed since the previous call? Dark magic!
     }
 
     proc_args.set_len(arg_max);
 
-    let start_time = info.pbi_start_tvsec;
-    let run_time = now.saturating_sub(start_time);
-
-    let mut p = if !proc_args.is_empty() {
-        // We copy the number of arguments (`argc`) to `n_args`.
-        let mut n_args: c_int = 0;
-        libc::memcpy(
-            &mut n_args as *mut _ as *mut _,
-            proc_args.as_slice().as_ptr() as *const _,
-            mem::size_of::<c_int>(),
-        );
+    if proc_args.is_empty() {
+        return false;
+    }
+    // We copy the number of arguments (`argc`) to `n_args`.
+    let mut n_args: c_int = 0;
+    libc::memcpy(
+        &mut n_args as *mut _ as *mut _,
+        proc_args.as_slice().as_ptr() as *const _,
+        mem::size_of::<c_int>(),
+    );
 
-        // We skip `argc`.
-        let proc_args = &proc_args[mem::size_of::<c_int>()..];
+    // We skip `argc`.
+    let proc_args = &proc_args[mem::size_of::<c_int>()..];
 
-        let (exe, proc_args) = get_exe(proc_args);
-        let name = exe
+    let (exe, proc_args) = get_exe(proc_args);
+    if process.name.is_empty() {
+        process.name = exe
             .file_name()
             .and_then(|x| x.to_str())
             .unwrap_or("")
             .to_owned();
+    }
 
-        let (cmd, proc_args) = get_arguments(proc_args, n_args);
-
-        let environ = get_environ(proc_args);
-        let mut p = ProcessInner::new(pid, parent, start_time, run_time, cwd, root);
-
-        p.exe = exe;
-        p.name = name;
-        p.cmd = parse_command_line(&cmd);
-        p.environ = environ;
-        p
-    } else {
-        ProcessInner::new(pid, parent, start_time, run_time, cwd, root)
-    };
-
-    let task_info = get_task_info(pid);
-
-    p.memory = task_info.pti_resident_size;
-    p.virtual_memory = task_info.pti_virtual_size;
+    if refresh_kind.exe() {
+        process.exe = exe;
+    }
 
-    p.user_id = Some(Uid(info.pbi_ruid));
-    p.effective_user_id = Some(Uid(info.pbi_uid));
-    p.group_id = Some(Gid(info.pbi_rgid));
-    p.effective_group_id = Some(Gid(info.pbi_gid));
-    p.process_status = ProcessStatus::from(info.pbi_status);
-    if refresh_kind.disk_usage() {
-        update_proc_disk_activity(&mut p);
+    if !refresh_kind.environ() && !refresh_kind.cmd() {
+        // Nothing else to be done!
+        return true;
     }
-    Ok(Some(Process { inner: p }))
+    let proc_args = get_arguments(&mut process.cmd, proc_args, n_args, refresh_kind.cmd());
+    if refresh_kind.environ() {
+        get_environ(&mut process.environ, proc_args);
+    }
+    true
 }
 
 fn get_exe(data: &[u8]) -> (PathBuf, &[u8]) {
@@ -543,20 +573,28 @@ fn get_exe(data: &[u8]) -> (PathBuf, &[u8]) {
     }
 }
 
-fn get_arguments(mut data: &[u8], mut n_args: c_int) -> (Vec<String>, &[u8]) {
+fn get_arguments<'a>(
+    cmd: &mut Vec<String>,
+    mut data: &'a [u8],
+    mut n_args: c_int,
+    refresh_cmd: bool,
+) -> &'a [u8] {
+    if refresh_cmd {
+        cmd.clear();
+    }
+
     if n_args < 1 {
-        return (Vec::new(), data);
+        return data;
     }
     while data.first() == Some(&0) {
         data = &data[1..];
     }
-    let mut cmd = Vec::with_capacity(n_args as _);
 
     unsafe {
         while n_args > 0 && !data.is_empty() {
             let pos = data.iter().position(|c| *c == 0).unwrap_or(data.len());
             let arg = std::str::from_utf8_unchecked(&data[..pos]);
-            if !arg.is_empty() {
+            if !arg.is_empty() && refresh_cmd {
                 cmd.push(arg.to_string());
             }
             data = &data[pos..];
@@ -565,21 +603,23 @@ fn get_arguments(mut data: &[u8], mut n_args: c_int) -> (Vec<String>, &[u8]) {
             }
             n_args -= 1;
         }
-        (cmd, data)
+        data
     }
 }
 
-fn get_environ(mut data: &[u8]) -> Vec<String> {
+fn get_environ(environ: &mut Vec<String>, mut data: &[u8]) {
+    environ.clear();
+
     while data.first() == Some(&0) {
         data = &data[1..];
     }
-    let mut environ = Vec::new();
+
     unsafe {
         while !data.is_empty() {
             let pos = data.iter().position(|c| *c == 0).unwrap_or(data.len());
             let arg = std::str::from_utf8_unchecked(&data[..pos]);
             if arg.is_empty() {
-                return environ;
+                return;
             }
             environ.push(arg.to_string());
             data = &data[pos..];
@@ -587,7 +627,6 @@ fn get_environ(mut data: &[u8]) -> Vec<String> {
                 data = &data[1..];
             }
         }
-        environ
     }
 }
 
@@ -602,15 +641,7 @@ pub(crate) fn update_process(
     unsafe {
         if let Some(ref mut p) = (*wrap.0.get()).get_mut(&pid) {
             let p = &mut p.inner;
-            if p.memory == 0 {
-                // We don't have access to this process' information.
-                return if check_if_pid_is_alive(pid, check_if_alive) {
-                    p.updated = true;
-                    Ok(None)
-                } else {
-                    Err(())
-                };
-            }
+
             if let Some(info) = get_bsd_info(pid) {
                 if info.pbi_start_tvsec != p.start_time {
                     // We don't it to be removed, just replaced.
@@ -619,7 +650,16 @@ pub(crate) fn update_process(
                     return create_new_process(pid, now, refresh_kind, Some(info));
                 }
             }
-            let task_info = get_task_info(pid);
+
+            if !get_process_infos(p, refresh_kind) {
+                get_exe_and_name_backup(p, refresh_kind);
+            }
+            get_cwd_root(p, refresh_kind);
+
+            if refresh_kind.disk_usage() {
+                update_proc_disk_activity(p);
+            }
+
             let mut thread_info = mem::zeroed::<libc::proc_threadinfo>();
             let (user_time, system_time, thread_status) = if libc::proc_pidinfo(
                 pid.0,
@@ -644,19 +684,22 @@ pub(crate) fn update_process(
             };
             p.status = thread_status;
 
-            if refresh_kind.cpu() {
-                compute_cpu_usage(p, task_info, system_time, user_time, time_interval);
-            }
+            if refresh_kind.cpu() || refresh_kind.memory() {
+                let task_info = get_task_info(pid);
 
-            p.memory = task_info.pti_resident_size;
-            p.virtual_memory = task_info.pti_virtual_size;
-            if refresh_kind.disk_usage() {
-                update_proc_disk_activity(p);
+                if refresh_kind.cpu() {
+                    compute_cpu_usage(p, task_info, system_time, user_time, time_interval);
+                }
+                if refresh_kind.memory() {
+                    p.memory = task_info.pti_resident_size;
+                    p.virtual_memory = task_info.pti_virtual_size;
+                }
             }
             p.updated = true;
-            return Ok(None);
+            Ok(None)
+        } else {
+            create_new_process(pid, now, refresh_kind, get_bsd_info(pid))
         }
-        create_new_process(pid, now, refresh_kind, get_bsd_info(pid))
     }
 }
 
@@ -703,23 +746,3 @@ pub(crate) fn get_proc_list() -> Option<Vec<Pid>> {
         }
     }
 }
-
-fn parse_command_line<T: Deref<Target = str> + Borrow<str>>(cmd: &[T]) -> Vec<String> {
-    let mut x = 0;
-    let mut command = Vec::with_capacity(cmd.len());
-    while x < cmd.len() {
-        let mut y = x;
-        if cmd[y].starts_with('\'') || cmd[y].starts_with('"') {
-            let c = if cmd[y].starts_with('\'') { '\'' } else { '"' };
-            while y < cmd.len() && !cmd[y].ends_with(c) {
-                y += 1;
-            }
-            command.push(cmd[x..y].join(" "));
-            x = y;
-        } else {
-            command.push(cmd[x].to_owned());
-        }
-        x += 1;
-    }
-    command
-}
diff --git a/src/unix/freebsd/process.rs b/src/unix/freebsd/process.rs
index d7fb7ca..42289dc 100644
--- a/src/unix/freebsd/process.rs
+++ b/src/unix/freebsd/process.rs
@@ -207,8 +207,15 @@ pub(crate) unsafe fn get_process_data(
     let status = ProcessStatus::from(kproc.ki_stat);
 
     // from FreeBSD source /src/usr.bin/top/machine.c
-    let virtual_memory = kproc.ki_size as _;
-    let memory = (kproc.ki_rssize as u64).saturating_mul(page_size as _);
+    let (virtual_memory, memory) = if refresh_kind.memory() {
+        (
+            kproc.ki_size as _,
+            (kproc.ki_rssize as u64).saturating_mul(page_size as _),
+        )
+    } else {
+        (0, 0)
+    };
+
     // FIXME: This is to get the "real" run time (in micro-seconds).
     // let run_time = (kproc.ki_runtime + 5_000) / 10_000;
 
@@ -223,8 +230,10 @@ pub(crate) unsafe fn get_process_data(
             proc_.cpu_usage = cpu_usage;
             proc_.parent = parent;
             proc_.status = status;
-            proc_.virtual_memory = virtual_memory;
-            proc_.memory = memory;
+            if refresh_kind.memory() {
+                proc_.virtual_memory = virtual_memory;
+                proc_.memory = memory;
+            }
             proc_.run_time = now.saturating_sub(proc_.start_time);
 
             if refresh_kind.disk_usage() {
@@ -239,18 +248,7 @@ pub(crate) unsafe fn get_process_data(
     }
 
     // This is a new process, we need to get more information!
-    let mut buffer = [0; libc::PATH_MAX as usize + 1];
-
-    let exe = get_sys_value_str(
-        &[
-            libc::CTL_KERN,
-            libc::KERN_PROC,
-            libc::KERN_PROC_PATHNAME,
-            kproc.ki_pid,
-        ],
-        &mut buffer,
-    )
-    .unwrap_or_default();
+
     // For some reason, it can return completely invalid path like `p\u{5}`. So we need to use
     // procstat to get around this problem.
     // let cwd = get_sys_value_str(
@@ -280,7 +278,7 @@ pub(crate) unsafe fn get_process_data(
             memory,
             // procstat_getfiles
             cwd: PathBuf::new(),
-            exe: exe.into(),
+            exe: PathBuf::new(),
             // kvm_getargv isn't thread-safe so we get it in the main thread.
             name: String::new(),
             // kvm_getargv isn't thread-safe so we get it in the main thread.
@@ -298,3 +296,24 @@ pub(crate) unsafe fn get_process_data(
         },
     }))
 }
+
+pub(crate) unsafe fn get_exe(exe: &mut PathBuf, pid: crate::Pid, refresh_kind: ProcessRefreshKind) {
+    if refresh_kind.exe() && exe.as_os_str().is_empty() {
+        let mut buffer = [0; libc::PATH_MAX as usize + 1];
+
+        *exe = get_sys_value_str(
+            &[
+                libc::CTL_KERN,
+                libc::KERN_PROC,
+                libc::KERN_PROC_PATHNAME,
+                pid.0,
+            ],
+            &mut buffer,
+        )
+        .map(PathBuf::from)
+        .unwrap_or_else(|| {
+            sysinfo_debug!("Failed to get `exe` for {}", pid.0);
+            PathBuf::new()
+        });
+    }
+}
diff --git a/src/unix/freebsd/system.rs b/src/unix/freebsd/system.rs
index 23f0fc7..373fb13 100644
--- a/src/unix/freebsd/system.rs
+++ b/src/unix/freebsd/system.rs
@@ -10,6 +10,7 @@ use std::path::{Path, PathBuf};
 use std::ptr::NonNull;
 
 use crate::sys::cpu::{physical_core_count, CpusWrapper};
+use crate::sys::process::get_exe;
 use crate::sys::utils::{
     self, boot_time, c_buf_to_string, from_cstr_array, get_sys_value, get_sys_value_by_name,
     get_system_info, init_mib,
@@ -116,13 +117,15 @@ impl SystemInner {
                 now,
                 refresh_kind,
             ) {
-                Ok(Some(proc_)) => {
-                    self.add_missing_proc_info(self.system_info.kd.as_ptr(), kproc, proc_);
-                    true
+                Ok(Some(process)) => {
+                    self.process_list.insert(process.inner.pid, process);
                 }
-                Ok(None) => true,
-                Err(_) => false,
+                Ok(None) => {}
+                Err(_) => return false,
             }
+            let process = self.process_list.get_mut(&Pid(kproc.ki_pid)).unwrap();
+            add_missing_proc_info(&mut self.system_info, kproc, process, refresh_kind);
+            true
         }
     }
 
@@ -248,27 +251,33 @@ impl SystemInner {
 
 impl SystemInner {
     unsafe fn refresh_procs(&mut self, refresh_kind: ProcessRefreshKind) {
-        let kd = self.system_info.kd.as_ptr();
-        let procs = {
-            let mut count = 0;
-            let procs = libc::kvm_getprocs(kd, libc::KERN_PROC_PROC, 0, &mut count);
-            if count < 1 {
-                sysinfo_debug!("kvm_getprocs returned nothing...");
-                return;
-            }
+        let mut count = 0;
+        let kvm_procs = libc::kvm_getprocs(
+            self.system_info.kd.as_ptr(),
+            libc::KERN_PROC_PROC,
+            0,
+            &mut count,
+        );
+        if count < 1 {
+            sysinfo_debug!("kvm_getprocs returned nothing...");
+            return;
+        }
+
+        let new_processes = {
             #[cfg(feature = "multithread")]
             use rayon::iter::{ParallelIterator, ParallelIterator as IterTrait};
             #[cfg(not(feature = "multithread"))]
             use std::iter::Iterator as IterTrait;
 
+            let kvm_procs: &mut [utils::KInfoProc] =
+                std::slice::from_raw_parts_mut(kvm_procs as _, count as _);
+
             let fscale = self.system_info.fscale;
             let page_size = self.system_info.page_size as isize;
             let now = super::utils::get_now();
             let proc_list = utils::WrapMap(UnsafeCell::new(&mut self.process_list));
-            let procs: &mut [utils::KInfoProc] =
-                std::slice::from_raw_parts_mut(procs as _, count as _);
 
-            IterTrait::filter_map(crate::utils::into_iter(procs), |kproc| {
+            IterTrait::filter_map(crate::utils::into_iter(kvm_procs), |kproc| {
                 super::process::get_process_data(
                     kproc,
                     &proc_list,
@@ -277,8 +286,7 @@ impl SystemInner {
                     now,
                     refresh_kind,
                 )
-                .ok()
-                .and_then(|p| p.map(|p| (kproc, p)))
+                .ok()?
             })
             .collect::<Vec<_>>()
         };
@@ -287,42 +295,55 @@ impl SystemInner {
         self.process_list
             .retain(|_, v| std::mem::replace(&mut v.inner.updated, false));
 
-        for (kproc, proc_) in procs {
-            self.add_missing_proc_info(kd, kproc, proc_);
+        for process in new_processes {
+            self.process_list.insert(process.inner.pid, process);
+        }
+        let kvm_procs: &mut [utils::KInfoProc] =
+            std::slice::from_raw_parts_mut(kvm_procs as _, count as _);
+
+        for kproc in kvm_procs {
+            if let Some(process) = self.process_list.get_mut(&Pid(kproc.ki_pid)) {
+                add_missing_proc_info(&mut self.system_info, kproc, process, refresh_kind);
+            }
         }
     }
+}
 
-    unsafe fn add_missing_proc_info(
-        &mut self,
-        kd: *mut libc::kvm_t,
-        kproc: &libc::kinfo_proc,
-        mut proc_: Process,
-    ) {
-        {
-            let proc_inner = &mut proc_.inner;
-            proc_inner.cmd = from_cstr_array(libc::kvm_getargv(kd, kproc, 0) as _);
-            self.system_info.get_proc_missing_info(kproc, proc_inner);
-            if !proc_inner.cmd.is_empty() {
+unsafe fn add_missing_proc_info(
+    system_info: &mut SystemInfo,
+    kproc: &libc::kinfo_proc,
+    proc_: &mut Process,
+    refresh_kind: ProcessRefreshKind,
+) {
+    {
+        let kd = system_info.kd.as_ptr();
+        let proc_inner = &mut proc_.inner;
+        if refresh_kind.cmd() || proc_inner.name.is_empty() {
+            let cmd = from_cstr_array(libc::kvm_getargv(kd, kproc, 0) as _);
+
+            if !cmd.is_empty() {
                 // First, we try to retrieve the name from the command line.
-                let p = Path::new(&proc_inner.cmd[0]);
+                let p = Path::new(&cmd[0]);
                 if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
                     proc_inner.name = name.to_owned();
                 }
-                if proc_inner.root.as_os_str().is_empty() {
-                    if let Some(parent) = p.parent() {
-                        proc_inner.root = parent.to_path_buf();
-                    }
-                }
             }
-            if proc_inner.name.is_empty() {
-                // The name can be cut short because the `ki_comm` field size is limited,
-                // which is why we prefer to get the name from the command line as much as
-                // possible.
-                proc_inner.name = c_buf_to_string(&kproc.ki_comm).unwrap_or_default();
+
+            if refresh_kind.cmd() {
+                proc_inner.cmd = cmd;
             }
+        }
+        get_exe(&mut proc_inner.exe, proc_inner.pid, refresh_kind);
+        system_info.get_proc_missing_info(kproc, proc_inner, refresh_kind);
+        if proc_inner.name.is_empty() {
+            // The name can be cut short because the `ki_comm` field size is limited,
+            // which is why we prefer to get the name from the command line as much as
+            // possible.
+            proc_inner.name = c_buf_to_string(&kproc.ki_comm).unwrap_or_default();
+        }
+        if refresh_kind.environ() {
             proc_inner.environ = from_cstr_array(libc::kvm_getenvv(kd, kproc, 0) as _);
         }
-        self.process_list.insert(proc_.inner.pid, proc_);
     }
 }
 
@@ -565,34 +586,49 @@ impl SystemInfo {
     }
 
     #[allow(clippy::collapsible_if)] // I keep as is for readability reasons.
-    unsafe fn get_proc_missing_info(&mut self, kproc: &libc::kinfo_proc, proc_: &mut ProcessInner) {
-        if self.procstat.is_null() {
-            self.procstat = libc::procstat_open_sysctl();
+    unsafe fn get_proc_missing_info(
+        &mut self,
+        kproc: &libc::kinfo_proc,
+        proc_: &mut ProcessInner,
+        refresh_kind: ProcessRefreshKind,
+    ) {
+        let mut done = 0;
+        if refresh_kind.cwd() {
+            done += 1;
         }
-        if self.procstat.is_null() {
+        if refresh_kind.root() {
+            done += 1;
+        }
+        if done == 0 {
             return;
         }
+        if self.procstat.is_null() {
+            self.procstat = libc::procstat_open_sysctl();
+            if self.procstat.is_null() {
+                sysinfo_debug!("procstat_open_sysctl failed");
+                return;
+            }
+        }
         let head = libc::procstat_getfiles(self.procstat, kproc as *const _ as usize as *mut _, 0);
         if head.is_null() {
             return;
         }
         let mut entry = (*head).stqh_first;
-        let mut done = 0;
-        while !entry.is_null() && done < 2 {
+        while !entry.is_null() && done > 0 {
             {
                 let tmp = &*entry;
                 if tmp.fs_uflags & libc::PS_FST_UFLAG_CDIR != 0 {
-                    if !tmp.fs_path.is_null() {
+                    if refresh_kind.cwd() && !tmp.fs_path.is_null() {
                         if let Ok(p) = CStr::from_ptr(tmp.fs_path).to_str() {
                             proc_.cwd = PathBuf::from(p);
-                            done += 1;
+                            done -= 1;
                         }
                     }
                 } else if tmp.fs_uflags & libc::PS_FST_UFLAG_RDIR != 0 {
-                    if !tmp.fs_path.is_null() {
+                    if refresh_kind.root() && !tmp.fs_path.is_null() {
                         if let Ok(p) = CStr::from_ptr(tmp.fs_path).to_str() {
                             proc_.root = PathBuf::from(p);
-                            done += 1;
+                            done -= 1;
                         }
                     }
                 }
diff --git a/src/unix/linux/process.rs b/src/unix/linux/process.rs
index 1b13b20..f7977d0 100644
--- a/src/unix/linux/process.rs
+++ b/src/unix/linux/process.rs
@@ -92,11 +92,11 @@ pub(crate) struct ProcessInner {
 impl ProcessInner {
     pub(crate) fn new(pid: Pid) -> Self {
         Self {
-            name: String::with_capacity(20),
+            name: String::new(),
             pid,
             parent: None,
-            cmd: Vec::with_capacity(2),
-            environ: Vec::with_capacity(10),
+            cmd: Vec::new(),
+            environ: Vec::new(),
             exe: PathBuf::new(),
             cwd: PathBuf::new(),
             root: PathBuf::new(),
@@ -277,7 +277,7 @@ pub(crate) fn set_time(p: &mut ProcessInner, utime: u64, stime: u64) {
     p.updated = true;
 }
 
-pub(crate) fn update_process_disk_activity(p: &mut ProcessInner, path: &Path) {
+pub(crate) fn update_process_disk_activity(p: &mut ProcessInner, path: &mut PathHandler) {
     let data = match get_all_data(path.join("io"), 16_384) {
         Ok(d) => d,
         Err(_) => return,
@@ -345,7 +345,7 @@ fn get_status(p: &mut ProcessInner, part: &str) {
         .unwrap_or_else(|| ProcessStatus::Unknown(0));
 }
 
-fn refresh_user_group_ids<P: PathPush>(p: &mut ProcessInner, path: &mut P) {
+fn refresh_user_group_ids(p: &mut ProcessInner, path: &mut PathHandler) {
     if let Some(((user_id, effective_user_id), (group_id, effective_group_id))) =
         get_uid_and_gid(path.join("status"))
     {
@@ -356,6 +356,63 @@ fn refresh_user_group_ids<P: PathPush>(p: &mut ProcessInner, path: &mut P) {
     }
 }
 
+#[allow(clippy::too_many_arguments)]
+fn update_proc_info(
+    p: &mut ProcessInner,
+    refresh_kind: ProcessRefreshKind,
+    proc_path: &mut PathHandler,
+    parts: &[&str],
+    memory: u64,
+    virtual_memory: u64,
+    uptime: u64,
+    info: &SystemInfo,
+) {
+    get_status(p, parts[2]);
+
+    if refresh_kind.user() && p.user_id.is_none() {
+        refresh_user_group_ids(p, proc_path);
+    }
+
+    if refresh_kind.exe() && p.exe == Path::new("") {
+        match proc_path.join("exe").read_link() {
+            Ok(exe_path) => p.exe = exe_path,
+            Err(_error) => {
+                sysinfo_debug!("Failed to retrieve exe for {}: {_error:?}", p.pid().0);
+                // Do not use cmd[0] because it is not the same thing.
+                // See https://github.com/GuillaumeGomez/sysinfo/issues/697.
+                p.exe = PathBuf::new();
+            }
+        }
+    }
+
+    if refresh_kind.cmd() && p.cmd.is_empty() {
+        p.cmd = copy_from_file(proc_path.join("cmdline"));
+    }
+    if refresh_kind.environ() {
+        p.environ = copy_from_file(proc_path.join("environ"));
+    }
+    if refresh_kind.cwd() {
+        p.cwd = realpath(proc_path.join("cwd"));
+    }
+    if refresh_kind.root() {
+        p.root = realpath(proc_path.join("root"));
+    }
+
+    update_time_and_memory(
+        proc_path,
+        p,
+        parts,
+        memory,
+        virtual_memory,
+        uptime,
+        info,
+        refresh_kind,
+    );
+    if refresh_kind.disk_usage() {
+        update_process_disk_activity(p, proc_path);
+    }
+}
+
 fn retrieve_all_new_process_info(
     pid: Pid,
     proc_list: &ProcessInner,
@@ -366,7 +423,7 @@ fn retrieve_all_new_process_info(
     uptime: u64,
 ) -> Process {
     let mut p = ProcessInner::new(pid);
-    let mut tmp = PathHandler::new(path);
+    let mut proc_path = PathHandler::new(path);
     let name = parts[1];
 
     p.parent = if proc_list.pid.0 != 0 {
@@ -383,43 +440,19 @@ fn retrieve_all_new_process_info(
         .start_time_without_boot_time
         .saturating_add(info.boot_time);
 
-    get_status(&mut p, parts[2]);
-
-    if refresh_kind.user() {
-        refresh_user_group_ids(&mut p, &mut tmp);
-    }
-
     p.name = name.into();
 
-    match tmp.join("exe").read_link() {
-        Ok(exe_path) => {
-            p.exe = exe_path;
-        }
-        Err(_) => {
-            // Do not use cmd[0] because it is not the same thing.
-            // See https://github.com/GuillaumeGomez/sysinfo/issues/697.
-            p.exe = PathBuf::new()
-        }
-    }
-
-    p.cmd = copy_from_file(tmp.join("cmdline"));
-    p.environ = copy_from_file(tmp.join("environ"));
-    p.cwd = realpath(tmp.join("cwd"));
-    p.root = realpath(tmp.join("root"));
-
-    update_time_and_memory(
-        path,
+    update_proc_info(
         &mut p,
+        refresh_kind,
+        &mut proc_path,
         parts,
         proc_list.memory,
         proc_list.virtual_memory,
         uptime,
         info,
-        refresh_kind,
     );
-    if refresh_kind.disk_usage() {
-        update_process_disk_activity(&mut p, path);
-    }
+
     Process { inner: p }
 }
 
@@ -441,9 +474,6 @@ pub(crate) fn _get_process_data(
         _ => return Err(()),
     };
 
-    let parent_memory = proc_list.memory;
-    let parent_virtual_memory = proc_list.virtual_memory;
-
     let data;
     let parts = if let Some(ref mut entry) = proc_list.tasks.get_mut(&pid) {
         let entry = &mut entry.inner;
@@ -470,22 +500,21 @@ pub(crate) fn _get_process_data(
         // If the start time differs, then it means it's not the same process anymore and that we
         // need to get all its information, hence why we check it here.
         if start_time_without_boot_time == entry.start_time_without_boot_time {
-            get_status(entry, parts[2]);
-            update_time_and_memory(
-                path,
+            let mut proc_path = PathHandler::new(path);
+
+            update_proc_info(
                 entry,
+                refresh_kind,
+                &mut proc_path,
                 &parts,
-                parent_memory,
-                parent_virtual_memory,
+                proc_list.memory,
+                proc_list.virtual_memory,
                 uptime,
                 info,
-                refresh_kind,
             );
-            if refresh_kind.disk_usage() {
-                update_process_disk_activity(entry, path);
-            }
+
             if refresh_kind.user() && entry.user_id.is_none() {
-                refresh_user_group_ids(entry, &mut PathBuf::from(path));
+                refresh_user_group_ids(entry, &mut proc_path);
             }
             return Ok((None, pid));
         }
@@ -515,7 +544,7 @@ pub(crate) fn _get_process_data(
 
 #[allow(clippy::too_many_arguments)]
 fn update_time_and_memory(
-    path: &Path,
+    path: &mut PathHandler,
     entry: &mut ProcessInner,
     parts: &[&str],
     parent_memory: u64,
@@ -525,18 +554,20 @@ fn update_time_and_memory(
     refresh_kind: ProcessRefreshKind,
 ) {
     {
-        // rss
-        entry.memory = u64::from_str(parts[23])
-            .unwrap_or(0)
-            .saturating_mul(info.page_size_b);
-        if entry.memory >= parent_memory {
-            entry.memory -= parent_memory;
-        }
-        // vsz correspond to the Virtual memory size in bytes.
-        // see: https://man7.org/linux/man-pages/man5/proc.5.html
-        entry.virtual_memory = u64::from_str(parts[22]).unwrap_or(0);
-        if entry.virtual_memory >= parent_virtual_memory {
-            entry.virtual_memory -= parent_virtual_memory;
+        if refresh_kind.memory() {
+            // rss
+            entry.memory = u64::from_str(parts[23])
+                .unwrap_or(0)
+                .saturating_mul(info.page_size_b);
+            if entry.memory >= parent_memory {
+                entry.memory -= parent_memory;
+            }
+            // vsz correspond to the Virtual memory size in bytes.
+            // see: https://man7.org/linux/man-pages/man5/proc.5.html
+            entry.virtual_memory = u64::from_str(parts[22]).unwrap_or(0);
+            if entry.virtual_memory >= parent_virtual_memory {
+                entry.virtual_memory -= parent_virtual_memory;
+            }
         }
         set_time(
             entry,
@@ -547,7 +578,7 @@ fn update_time_and_memory(
     }
     refresh_procs(
         entry,
-        &path.join("task"),
+        path.join("task"),
         entry.pid,
         uptime,
         info,
@@ -633,19 +664,14 @@ fn copy_from_file(entry: &Path) -> Vec<String> {
                 sysinfo_debug!("Failed to read file in `copy_from_file`: {:?}", _e);
                 Vec::new()
             } else {
-                let mut out = Vec::with_capacity(20);
-                let mut start = 0;
-                for (pos, x) in data.iter().enumerate() {
-                    if *x == 0 {
-                        if pos - start >= 1 {
-                            if let Ok(s) =
-                                std::str::from_utf8(&data[start..pos]).map(|x| x.trim().to_owned())
-                            {
-                                out.push(s);
-                            }
-                        }
-                        start = pos + 1; // to keeping prevent '\0'
+                let mut out = Vec::with_capacity(10);
+                let mut data = data.as_slice();
+                while let Some(pos) = data.iter().position(|c| *c == 0) {
+                    match std::str::from_utf8(&data[..pos]).map(|s| s.trim()) {
+                        Ok(s) if !s.is_empty() => out.push(s.to_string()),
+                        _ => {}
                     }
+                    data = &data[pos + 1..];
                 }
                 out
             }
diff --git a/src/windows/process.rs b/src/windows/process.rs
index f2f8b14..9ea4f8e 100644
--- a/src/windows/process.rs
+++ b/src/windows/process.rs
@@ -357,15 +357,10 @@ impl ProcessInner {
             let info = info.assume_init();
 
             let name = get_process_name(pid).unwrap_or_default();
-            let exe = get_exe(&process_handler);
-            let mut root = exe.clone();
-            root.pop();
-            let (cmd, environ, cwd) = match get_process_params(&process_handler) {
-                Ok(args) => args,
-                Err(_e) => {
-                    sysinfo_debug!("Failed to get process parameters: {}", _e);
-                    (Vec::new(), Vec::new(), PathBuf::new())
-                }
+            let exe = if refresh_kind.exe() {
+                get_exe(&process_handler)
+            } else {
+                PathBuf::new()
             };
             let (start_time, run_time) = get_start_and_run_time(*process_handler, now);
             let parent = if info.InheritedFromUniqueProcessId != 0 {
@@ -374,17 +369,17 @@ impl ProcessInner {
                 None
             };
             let user_id = get_process_user_id(&process_handler, refresh_kind);
-            Some(Self {
+            let mut p = Self {
                 handle: Some(Arc::new(process_handler)),
                 name,
                 pid,
                 parent,
                 user_id,
-                cmd,
-                environ,
+                cmd: Vec::new(),
+                environ: Vec::new(),
                 exe,
-                cwd,
-                root,
+                cwd: PathBuf::new(),
+                root: PathBuf::new(),
                 status: ProcessStatus::Run,
                 memory: 0,
                 virtual_memory: 0,
@@ -397,7 +392,9 @@ impl ProcessInner {
                 old_written_bytes: 0,
                 read_bytes: 0,
                 written_bytes: 0,
-            })
+            };
+            get_process_params(&mut p, refresh_kind);
+            Some(p)
         }
     }
 
@@ -412,29 +409,24 @@ impl ProcessInner {
     ) -> Self {
         if let Some(handle) = get_process_handler(pid) {
             unsafe {
-                let exe = get_exe(&handle);
-                let mut root = exe.clone();
-                root.pop();
-                let (cmd, environ, cwd) = match get_process_params(&handle) {
-                    Ok(args) => args,
-                    Err(_e) => {
-                        sysinfo_debug!("Failed to get process parameters: {}", _e);
-                        (Vec::new(), Vec::new(), PathBuf::new())
-                    }
+                let exe = if refresh_kind.exe() {
+                    get_exe(&handle)
+                } else {
+                    PathBuf::new()
                 };
                 let (start_time, run_time) = get_start_and_run_time(*handle, now);
                 let user_id = get_process_user_id(&handle, refresh_kind);
-                Self {
+                let mut p = Self {
                     handle: Some(Arc::new(handle)),
                     name,
                     pid,
                     user_id,
                     parent,
-                    cmd,
-                    environ,
+                    cmd: Vec::new(),
+                    environ: Vec::new(),
                     exe,
-                    cwd,
-                    root,
+                    cwd: PathBuf::new(),
+                    root: PathBuf::new(),
                     status: ProcessStatus::Run,
                     memory,
                     virtual_memory,
@@ -447,9 +439,17 @@ impl ProcessInner {
                     old_written_bytes: 0,
                     read_bytes: 0,
                     written_bytes: 0,
-                }
+                };
+
+                get_process_params(&mut p, refresh_kind);
+                p
             }
         } else {
+            let exe = if refresh_kind.exe() {
+                get_executable_path(pid)
+            } else {
+                PathBuf::new()
+            };
             Self {
                 handle: None,
                 name,
@@ -458,7 +458,7 @@ impl ProcessInner {
                 parent,
                 cmd: Vec::new(),
                 environ: Vec::new(),
-                exe: get_executable_path(pid),
+                exe,
                 cwd: PathBuf::new(),
                 root: PathBuf::new(),
                 status: ProcessStatus::Run,
@@ -489,7 +489,20 @@ impl ProcessInner {
         if refresh_kind.disk_usage() {
             update_disk_usage(self);
         }
-        update_memory(self);
+        if refresh_kind.memory() {
+            update_memory(self);
+        }
+        unsafe {
+            get_process_params(self, refresh_kind);
+        }
+        if refresh_kind.exe() {
+            unsafe {
+                self.exe = match self.handle.as_ref() {
+                    Some(handle) => get_exe(handle),
+                    None => get_executable_path(self.pid),
+                };
+            }
+        }
         self.run_time = now.saturating_sub(self.start_time());
         self.updated = true;
     }
@@ -639,6 +652,24 @@ unsafe fn get_process_times(handle: HANDLE) -> u64 {
     super::utils::filetime_to_u64(fstart)
 }
 
+// On Windows, the root folder is always the current drive. So we get it from its `cwd`.
+fn update_root(refresh_kind: ProcessRefreshKind, cwd: &Path, root: &mut PathBuf) {
+    if !refresh_kind.root() {
+        return;
+    }
+    if cwd.as_os_str().is_empty() || !cwd.has_root() {
+        *root = PathBuf::new();
+        return;
+    }
+    let mut ancestors = cwd.ancestors().peekable();
+    while let Some(path) = ancestors.next() {
+        if ancestors.peek().is_none() {
+            *root = path.into();
+            break;
+        }
+    }
+}
+
 #[inline]
 fn compute_start(process_times: u64) -> u64 {
     // 11_644_473_600 is the number of seconds between the Windows epoch (1601-01-01) and
@@ -664,13 +695,13 @@ pub(crate) fn get_start_time(handle: HANDLE) -> u64 {
 }
 
 unsafe fn ph_query_process_variable_size(
-    process_handle: &HandleWrapper,
+    process_handle: HANDLE,
     process_information_class: PROCESSINFOCLASS,
 ) -> Option<Vec<u16>> {
     let mut return_length = MaybeUninit::<u32>::uninit();
 
     let mut status = NtQueryInformationProcess(
-        **process_handle,
+        process_handle,
         process_information_class as _,
         null_mut(),
         0,
@@ -693,7 +724,7 @@ unsafe fn ph_query_process_variable_size(
     let buf_len = (return_length as usize) / 2;
     let mut buffer: Vec<u16> = Vec::with_capacity(buf_len + 1);
     status = NtQueryInformationProcess(
-        **process_handle,
+        process_handle,
         process_information_class as _,
         buffer.as_mut_ptr() as *mut _,
         return_length,
@@ -727,13 +758,10 @@ unsafe fn get_cmdline_from_buffer(buffer: PCWSTR) -> Vec<String> {
     res
 }
 
-unsafe fn get_region_size(
-    handle: &HandleWrapper,
-    ptr: *const c_void,
-) -> Result<usize, &'static str> {
+unsafe fn get_region_size(handle: HANDLE, ptr: *const c_void) -> Result<usize, &'static str> {
     let mut meminfo = MaybeUninit::<MEMORY_BASIC_INFORMATION>::uninit();
     if VirtualQueryEx(
-        **handle,
+        handle,
         Some(ptr),
         meminfo.as_mut_ptr().cast(),
         size_of::<MEMORY_BASIC_INFORMATION>(),
@@ -746,7 +774,7 @@ unsafe fn get_region_size(
 }
 
 unsafe fn get_process_data(
-    handle: &HandleWrapper,
+    handle: HANDLE,
     ptr: *const c_void,
     size: usize,
 ) -> Result<Vec<u16>, &'static str> {
@@ -754,7 +782,7 @@ unsafe fn get_process_data(
     let mut bytes_read = 0;
 
     if ReadProcessMemory(
-        **handle,
+        handle,
         ptr,
         buffer.as_mut_ptr().cast(),
         size,
@@ -777,25 +805,25 @@ unsafe fn get_process_data(
 }
 
 trait RtlUserProcessParameters {
-    fn get_cmdline(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str>;
-    fn get_cwd(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str>;
-    fn get_environ(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str>;
+    fn get_cmdline(&self, handle: HANDLE) -> Result<Vec<u16>, &'static str>;
+    fn get_cwd(&self, handle: HANDLE) -> Result<Vec<u16>, &'static str>;
+    fn get_environ(&self, handle: HANDLE) -> Result<Vec<u16>, &'static str>;
 }
 
 macro_rules! impl_RtlUserProcessParameters {
     ($t:ty) => {
         impl RtlUserProcessParameters for $t {
-            fn get_cmdline(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str> {
+            fn get_cmdline(&self, handle: HANDLE) -> Result<Vec<u16>, &'static str> {
                 let ptr = self.CommandLine.Buffer;
                 let size = self.CommandLine.Length;
                 unsafe { get_process_data(handle, ptr as _, size as _) }
             }
-            fn get_cwd(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str> {
+            fn get_cwd(&self, handle: HANDLE) -> Result<Vec<u16>, &'static str> {
                 let ptr = self.CurrentDirectory.DosPath.Buffer;
                 let size = self.CurrentDirectory.DosPath.Length;
                 unsafe { get_process_data(handle, ptr as _, size as _) }
             }
-            fn get_environ(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str> {
+            fn get_environ(&self, handle: HANDLE) -> Result<Vec<u16>, &'static str> {
                 let ptr = self.Environment;
                 unsafe {
                     let size = get_region_size(handle, ptr as _)?;
@@ -809,17 +837,24 @@ macro_rules! impl_RtlUserProcessParameters {
 impl_RtlUserProcessParameters!(RTL_USER_PROCESS_PARAMETERS32);
 impl_RtlUserProcessParameters!(RTL_USER_PROCESS_PARAMETERS);
 
-unsafe fn get_process_params(
-    handle: &HandleWrapper,
-) -> Result<(Vec<String>, Vec<String>, PathBuf), &'static str> {
+unsafe fn get_process_params(process: &mut ProcessInner, refresh_kind: ProcessRefreshKind) {
     if !cfg!(target_pointer_width = "64") {
-        return Err("Non 64 bit targets are not supported");
+        sysinfo_debug!("Non 64 bit targets are not supported");
+        return;
+    }
+    if !(refresh_kind.cmd() || refresh_kind.environ() || refresh_kind.cwd() || refresh_kind.root())
+    {
+        return;
     }
+    let handle = match process.handle.as_ref().map(|handle| handle.0) {
+        Some(h) => h,
+        None => return,
+    };
 
     // First check if target process is running in wow64 compatibility emulator
     let mut pwow32info = MaybeUninit::<*const c_void>::uninit();
     if NtQueryInformationProcess(
-        **handle,
+        handle,
         ProcessWow64Information,
         pwow32info.as_mut_ptr().cast(),
         size_of::<*const c_void>() as u32,
@@ -827,7 +862,8 @@ unsafe fn get_process_params(
     )
     .is_err()
     {
-        return Err("Unable to check WOW64 information about the process");
+        sysinfo_debug!("Unable to check WOW64 information about the process");
+        return;
     }
     let pwow32info = pwow32info.assume_init();
 
@@ -836,7 +872,7 @@ unsafe fn get_process_params(
 
         let mut pbasicinfo = MaybeUninit::<PROCESS_BASIC_INFORMATION>::uninit();
         if NtQueryInformationProcess(
-            **handle,
+            handle,
             ProcessBasicInformation,
             pbasicinfo.as_mut_ptr().cast(),
             size_of::<PROCESS_BASIC_INFORMATION>() as u32,
@@ -844,13 +880,14 @@ unsafe fn get_process_params(
         )
         .is_err()
         {
-            return Err("Unable to get basic process information");
+            sysinfo_debug!("Unable to get basic process information");
+            return;
         }
         let pinfo = pbasicinfo.assume_init();
 
         let mut peb = MaybeUninit::<PEB>::uninit();
         if ReadProcessMemory(
-            **handle,
+            handle,
             pinfo.PebBaseAddress.cast(),
             peb.as_mut_ptr().cast(),
             size_of::<PEB>(),
@@ -858,14 +895,15 @@ unsafe fn get_process_params(
         )
         .is_err()
         {
-            return Err("Unable to read process PEB");
+            sysinfo_debug!("Unable to read process PEB");
+            return;
         }
 
         let peb = peb.assume_init();
 
         let mut proc_params = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS>::uninit();
         if ReadProcessMemory(
-            **handle,
+            handle,
             peb.ProcessParameters.cast(),
             proc_params.as_mut_ptr().cast(),
             size_of::<RTL_USER_PROCESS_PARAMETERS>(),
@@ -873,21 +911,26 @@ unsafe fn get_process_params(
         )
         .is_err()
         {
-            return Err("Unable to read process parameters");
+            sysinfo_debug!("Unable to read process parameters");
+            return;
         }
 
         let proc_params = proc_params.assume_init();
-        return Ok((
-            get_cmd_line(&proc_params, handle),
-            get_proc_env(&proc_params, handle),
-            get_cwd(&proc_params, handle),
-        ));
+        get_cmd_line(&proc_params, handle, refresh_kind, &mut process.cmd);
+        get_proc_env(&proc_params, handle, refresh_kind, &mut process.environ);
+        get_cwd_and_root(
+            &proc_params,
+            handle,
+            refresh_kind,
+            &mut process.cwd,
+            &mut process.root,
+        );
     }
     // target is a 32 bit process in wow64 mode
 
     let mut peb32 = MaybeUninit::<PEB32>::uninit();
     if ReadProcessMemory(
-        **handle,
+        handle,
         pwow32info,
         peb32.as_mut_ptr().cast(),
         size_of::<PEB32>(),
@@ -895,13 +938,14 @@ unsafe fn get_process_params(
     )
     .is_err()
     {
-        return Err("Unable to read PEB32");
+        sysinfo_debug!("Unable to read PEB32");
+        return;
     }
     let peb32 = peb32.assume_init();
 
     let mut proc_params = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS32>::uninit();
     if ReadProcessMemory(
-        **handle,
+        handle,
         peb32.ProcessParameters as *mut _,
         proc_params.as_mut_ptr().cast(),
         size_of::<RTL_USER_PROCESS_PARAMETERS32>(),
@@ -909,22 +953,43 @@ unsafe fn get_process_params(
     )
     .is_err()
     {
-        return Err("Unable to read 32 bit process parameters");
+        sysinfo_debug!("Unable to read 32 bit process parameters");
+        return;
     }
     let proc_params = proc_params.assume_init();
-    Ok((
-        get_cmd_line(&proc_params, handle),
-        get_proc_env(&proc_params, handle),
-        get_cwd(&proc_params, handle),
-    ))
+    get_cmd_line(&proc_params, handle, refresh_kind, &mut process.cmd);
+    get_proc_env(&proc_params, handle, refresh_kind, &mut process.environ);
+    get_cwd_and_root(
+        &proc_params,
+        handle,
+        refresh_kind,
+        &mut process.cwd,
+        &mut process.root,
+    );
 }
 
-fn get_cwd<T: RtlUserProcessParameters>(params: &T, handle: &HandleWrapper) -> PathBuf {
+fn get_cwd_and_root<T: RtlUserProcessParameters>(
+    params: &T,
+    handle: HANDLE,
+    refresh_kind: ProcessRefreshKind,
+    cwd: &mut PathBuf,
+    root: &mut PathBuf,
+) {
+    if !refresh_kind.cwd() && !refresh_kind.root() {
+        return;
+    }
     match params.get_cwd(handle) {
-        Ok(buffer) => unsafe { PathBuf::from(null_terminated_wchar_to_string(buffer.as_slice())) },
+        Ok(buffer) => unsafe {
+            let tmp_cwd = PathBuf::from(null_terminated_wchar_to_string(buffer.as_slice()));
+            // Should always be called after we refreshed `cwd`.
+            update_root(refresh_kind, &tmp_cwd, root);
+            if refresh_kind.cwd() {
+                *cwd = tmp_cwd;
+            }
+        },
         Err(_e) => {
-            sysinfo_debug!("get_cwd failed to get data: {}", _e);
-            PathBuf::new()
+            sysinfo_debug!("get_cwd_and_root failed to get data: {:?}", _e);
+            *cwd = PathBuf::new();
         }
     }
 }
@@ -938,10 +1003,7 @@ unsafe fn null_terminated_wchar_to_string(slice: &[u16]) -> String {
     }
 }
 
-fn get_cmd_line_old<T: RtlUserProcessParameters>(
-    params: &T,
-    handle: &HandleWrapper,
-) -> Vec<String> {
+fn get_cmd_line_old<T: RtlUserProcessParameters>(params: &T, handle: HANDLE) -> Vec<String> {
     match params.get_cmdline(handle) {
         Ok(buffer) => unsafe { get_cmdline_from_buffer(PCWSTR::from_raw(buffer.as_ptr())) },
         Err(_e) => {
@@ -952,7 +1014,7 @@ fn get_cmd_line_old<T: RtlUserProcessParameters>(
 }
 
 #[allow(clippy::cast_ptr_alignment)]
-fn get_cmd_line_new(handle: &HandleWrapper) -> Vec<String> {
+fn get_cmd_line_new(handle: HANDLE) -> Vec<String> {
     unsafe {
         if let Some(buffer) = ph_query_process_variable_size(handle, ProcessCommandLineInformation)
         {
@@ -965,25 +1027,41 @@ fn get_cmd_line_new(handle: &HandleWrapper) -> Vec<String> {
     }
 }
 
-fn get_cmd_line<T: RtlUserProcessParameters>(params: &T, handle: &HandleWrapper) -> Vec<String> {
+fn get_cmd_line<T: RtlUserProcessParameters>(
+    params: &T,
+    handle: HANDLE,
+    refresh_kind: ProcessRefreshKind,
+    cmd_line: &mut Vec<String>,
+) {
+    if !refresh_kind.cmd() {
+        return;
+    }
     if *WINDOWS_8_1_OR_NEWER {
-        get_cmd_line_new(handle)
+        *cmd_line = get_cmd_line_new(handle);
     } else {
-        get_cmd_line_old(params, handle)
+        *cmd_line = get_cmd_line_old(params, handle);
     }
 }
 
-fn get_proc_env<T: RtlUserProcessParameters>(params: &T, handle: &HandleWrapper) -> Vec<String> {
+fn get_proc_env<T: RtlUserProcessParameters>(
+    params: &T,
+    handle: HANDLE,
+    refresh_kind: ProcessRefreshKind,
+    environ: &mut Vec<String>,
+) {
+    if !refresh_kind.environ() {
+        return;
+    }
     match params.get_environ(handle) {
         Ok(buffer) => {
             let equals = "=".encode_utf16().next().unwrap();
             let raw_env = buffer;
-            let mut result = Vec::new();
+            environ.clear();
             let mut begin = 0;
             while let Some(offset) = raw_env[begin..].iter().position(|&c| c == 0) {
                 let end = begin + offset;
                 if raw_env[begin..end].iter().any(|&c| c == equals) {
-                    result.push(
+                    environ.push(
                         OsString::from_wide(&raw_env[begin..end])
                             .to_string_lossy()
                             .into_owned(),
@@ -993,11 +1071,10 @@ fn get_proc_env<T: RtlUserProcessParameters>(params: &T, handle: &HandleWrapper)
                     break;
                 }
             }
-            result
         }
         Err(_e) => {
             sysinfo_debug!("get_proc_env failed to get data: {}", _e);
-            Vec::new()
+            *environ = Vec::new();
         }
     }
 }
diff --git a/src/windows/system.rs b/src/windows/system.rs
index 3a7c656..685e752 100644
--- a/src/windows/system.rs
+++ b/src/windows/system.rs
@@ -296,8 +296,10 @@ impl SystemInner {
                         .map(|start| start == proc_.start_time())
                         .unwrap_or(true)
                     {
-                        proc_.memory = pi.WorkingSetSize as _;
-                        proc_.virtual_memory = pi.VirtualSize as _;
+                        if refresh_kind.memory() {
+                            proc_.memory = pi.WorkingSetSize as _;
+                            proc_.virtual_memory = pi.VirtualSize as _;
+                        }
                         proc_.update(refresh_kind, nb_cpus, now);
                         return None;
                     }
@@ -305,6 +307,11 @@ impl SystemInner {
                     sysinfo_debug!("owner changed for PID {}", proc_.pid());
                 }
                 let name = get_process_name(&pi, pid);
+                let (memory, virtual_memory) = if refresh_kind.memory() {
+                    (pi.WorkingSetSize as _, pi.VirtualSize as _)
+                } else {
+                    (0, 0)
+                };
                 let mut p = ProcessInner::new_full(
                     pid,
                     if pi.InheritedFromUniqueProcessId as usize != 0 {
@@ -312,13 +319,13 @@ impl SystemInner {
                     } else {
                         None
                     },
-                    pi.WorkingSetSize as _,
-                    pi.VirtualSize as _,
+                    memory,
+                    virtual_memory,
                     name,
                     now,
                     refresh_kind,
                 );
-                p.update(refresh_kind, nb_cpus, now);
+                p.update(refresh_kind.without_memory(), nb_cpus, now);
                 Some(Process { inner: p })
             })
             .collect::<Vec<_>>();


Test patch:
diff --git a/tests/process.rs b/tests/process.rs
index 47d95ac..32c12f8 100644
--- a/tests/process.rs
+++ b/tests/process.rs
@@ -632,22 +632,106 @@ fn test_process_creds() {
     }));
 }
 
-// Regression test for <https://github.com/GuillaumeGomez/sysinfo/issues/1084>
+// This test ensures that only the requested information is retrieved.
 #[test]
-fn test_process_memory_refresh() {
+fn test_process_specific_refresh() {
+    use std::path::Path;
+    use sysinfo::{DiskUsage, ProcessRefreshKind};
+
     if !sysinfo::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
         return;
     }
 
-    // Ensure the process memory is available on the first refresh.
-    let mut s = System::new();
+    fn check_empty(s: &System, pid: Pid) {
+        let p = s.process(pid).unwrap();
+
+        // Name should never be empty.
+        assert!(!p.name().is_empty());
+        if cfg!(target_os = "windows") {
+            assert_eq!(p.user_id(), None);
+        }
+        assert_eq!(p.environ().len(), 0);
+        assert_eq!(p.cmd().len(), 0);
+        assert_eq!(p.exe(), Path::new(""));
+        assert_eq!(p.cwd(), Path::new(""));
+        assert_eq!(p.root(), Path::new(""));
+        assert_eq!(p.memory(), 0);
+        assert_eq!(p.virtual_memory(), 0);
+        // These two won't be checked, too much lazyness in testing them...
+        assert_eq!(p.disk_usage(), DiskUsage::default());
+        assert_eq!(p.cpu_usage(), 0.);
+    }
 
-    // Refresh our own process
+    let mut s = System::new();
     let pid = Pid::from_u32(std::process::id());
-    s.refresh_process_specifics(pid, sysinfo::ProcessRefreshKind::new());
 
-    let proc = s.process(pid).unwrap();
-    // Check that the memory values re not empty.
-    assert!(proc.memory() > 0);
-    assert!(proc.virtual_memory() > 0);
+    macro_rules! update_specific_and_check {
+        (memory) => {
+            s.refresh_process_specifics(pid, ProcessRefreshKind::new());
+            {
+                let p = s.process(pid).unwrap();
+                assert_eq!(p.memory(), 0, "failed 0 check for memory");
+                assert_eq!(p.virtual_memory(), 0, "failed 0 check for virtual memory");
+            }
+            s.refresh_process_specifics(pid, ProcessRefreshKind::new().with_memory());
+            {
+                let p = s.process(pid).unwrap();
+                assert_ne!(p.memory(), 0, "failed non-0 check for memory");
+                assert_ne!(p.virtual_memory(), 0, "failed non-0 check for virtual memory");
+            }
+            // And now we check that re-refreshing nothing won't remove the
+            // information.
+            s.refresh_process_specifics(pid, ProcessRefreshKind::new());
+            {
+                let p = s.process(pid).unwrap();
+                assert_ne!(p.memory(), 0, "failed non-0 check (number 2) for memory");
+                assert_ne!(p.virtual_memory(), 0, "failed non-0 check(number 2) for virtual memory");
+            }
+        };
+        ($name:ident, $method:ident, $($extra:tt)+) => {
+            s.refresh_process_specifics(pid, ProcessRefreshKind::new());
+            {
+                let p = s.process(pid).unwrap();
+                assert_eq!(
+                    p.$name()$($extra)+,
+                    concat!("failed 0 check check for ", stringify!($name)),
+                );
+            }
+            s.refresh_process_specifics(pid, ProcessRefreshKind::new().$method());
+            {
+                let p = s.process(pid).unwrap();
+                assert_ne!(
+                    p.$name()$($extra)+,
+                    concat!("failed non-0 check check for ", stringify!($name)),);
+            }
+            // And now we check that re-refreshing nothing won't remove the
+            // information.
+            s.refresh_process_specifics(pid, ProcessRefreshKind::new());
+            {
+                let p = s.process(pid).unwrap();
+                assert_ne!(
+                    p.$name()$($extra)+,
+                    concat!("failed non-0 check (number 2) check for ", stringify!($name)),);
+            }
+        }
+    }
+
+    s.refresh_process_specifics(pid, ProcessRefreshKind::new());
+    check_empty(&s, pid);
+
+    s.refresh_process_specifics(pid, ProcessRefreshKind::new());
+    check_empty(&s, pid);
+
+    update_specific_and_check!(memory);
+    update_specific_and_check!(environ, with_environ, .len(), 0);
+    update_specific_and_check!(cmd, with_cmd, .len(), 0);
+    if !cfg!(any(
+        target_os = "macos",
+        target_os = "ios",
+        feature = "apple-sandbox",
+    )) {
+        update_specific_and_check!(root, with_root, , Path::new(""));
+    }
+    update_specific_and_check!(exe, with_exe, , Path::new(""));
+    update_specific_and_check!(cwd, with_cwd, , Path::new(""));
 }