Repository: GuillaumeGomez/sysinfo (language: rust)

Issue:
Process List incomplete on long names
**Describe the bug**
This is on 0.30.3. The issue is that the stat file on Linux is parsed under the assumption that it contains valid UTF-8. This is not necessarily the case for two reasons:
1. Process names don't need to be valid UTF-8 at all on Linux. They can contain invalid bytes.
2. Even if the process name was valid UTF-8, the assumption breaks because the process name is limited to 15 characters, which can cut a valid code point in half.

This causes the stat file to not be parseable, causing the entire process not to show up in the list. So even if I wasn't using the name of the process at all, I can't find it as part of the list.

**To Reproduce**
I named an executable `process-name-töööö`, which cuts one of the `ö` in half.

I have a fixed branch locally, but I want to discuss what the best solution is before doing a PR. My branch lossily converts the process name to a string. However this means that the [Unicode replacement character `�`](https://www.fileformat.info/info/unicode/char/fffd/index.htm) is now part of those process names. The character when encoded as UTF-8 is 3 bytes. This means that process names can now be longer than 15 bytes, or in other words, doing a name based comparison on the first 15 bytes, such as somewhat suggested in the documentation of the crate, is no longer easily possible. Maybe the solution instead is to just provide the name as an `OsStr` instead, keeping the bytes around as is. Rust recently made `OsStr` way more easily usable by allowing you to access its bytes directly.



Discussion / hints:
Thanks for the detailed description! Using `OsStr` is the best solution here I think.

I see that you can get a process/thread name with `pthread_getname_np` or by reading the proc `comm` file. Not sure if it's worth it though.

Fix patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index b1b768470..ac50b608d 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,10 +1,10 @@
 task:
-  name: rust 1.70 on freebsd 13
+  name: rust 1.74 on freebsd 13
   freebsd_instance:
     image: freebsd-13-1-release-amd64
   setup_script:
     - curl https://sh.rustup.rs -sSf --output rustup.sh
-    - sh rustup.sh -y --profile=minimal --default-toolchain=1.70
+    - sh rustup.sh -y --profile=minimal --default-toolchain=1.74
     - . $HOME/.cargo/env
     - rustup --version
     - rustup component add clippy
@@ -37,14 +37,14 @@ task:
     - FREEBSD_CI=1 cargo test --lib -j1 -- --ignored
 
 task:
-  name: rust 1.70 on mac m1
+  name: rust 1.74 on mac m1
   macos_instance:
     image: ghcr.io/cirruslabs/macos-monterey-base:latest
   setup_script:
     - brew update
     - brew install curl
     - curl https://sh.rustup.rs -sSf --output rustup.sh
-    - sh rustup.sh -y --profile=minimal --default-toolchain=1.70
+    - sh rustup.sh -y --profile=minimal --default-toolchain=1.74
     - source $HOME/.cargo/env
     - rustup --version
     - rustup component add clippy
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 04caa4878..7f6aa6f71 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -64,7 +64,7 @@ jobs:
           - { os: 'ubuntu-latest', target: 'x86_64-linux-android', cross: true }
           - { os: 'ubuntu-latest', target: 'i686-linux-android', cross: true }
         toolchain:
-          - "1.70.0"  # minimum supported rust version
+          - "1.74.0"  # minimum supported rust version
           - stable
           - nightly
     steps:
@@ -133,7 +133,7 @@ jobs:
           - macos-latest
           - windows-latest
         toolchain:
-          - "1.70.0"  # minimum supported rust version
+          - "1.74.0"  # minimum supported rust version
           - stable
           - nightly
     steps:
@@ -195,7 +195,7 @@ jobs:
     strategy:
       matrix:
         toolchain:
-          - "1.70.0" # minimum supported rust version
+          - "1.74.0" # minimum supported rust version
           - stable
     steps:
       - uses: actions/checkout@v4
diff --git a/Cargo.lock b/Cargo.lock
index bed8ce6af..086bdab0a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8,6 +8,17 @@ version = "2.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
 
+[[package]]
+name = "bstr"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+dependencies = [
+ "memchr",
+ "regex-automata",
+ "serde",
+]
+
 [[package]]
 name = "cfg-if"
 version = "1.0.0"
@@ -85,6 +96,12 @@ version = "0.4.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 
+[[package]]
+name = "memchr"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+
 [[package]]
 name = "ntapi"
 version = "0.4.1"
@@ -132,6 +149,12 @@ dependencies = [
  "crossbeam-utils",
 ]
 
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+
 [[package]]
 name = "rustix"
 version = "0.38.31"
@@ -197,9 +220,11 @@ dependencies = [
 name = "sysinfo"
 version = "0.30.7"
 dependencies = [
+ "bstr",
  "cfg-if",
  "core-foundation-sys",
  "libc",
+ "memchr",
  "ntapi",
  "rayon",
  "serde",
diff --git a/Cargo.toml b/Cargo.toml
index 0e89d4d88..dea4cbc25 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ description = "Library to get system information such as processes, CPUs, disks,
 repository = "https://github.com/GuillaumeGomez/sysinfo"
 license = "MIT"
 readme = "README.md"
-rust-version = "1.70"
+rust-version = "1.74"
 exclude = ["/test-unknown"]
 categories = ["filesystem", "os", "api-bindings"]
 edition = "2018"
@@ -42,6 +42,7 @@ rustdoc-args = ["--generate-link-to-definition"]
 
 [dependencies]
 cfg-if = "1.0"
+memchr = "2.7.1"
 rayon = { version = "^1.8", optional = true }
 serde = { version = "^1.0.190", optional = true }
 
@@ -94,3 +95,4 @@ tempfile = "3.9"
 
 [dev-dependencies]
 serde_json = "1.0" # Used in documentation tests.
+bstr = "1.9.0" # Used in example
diff --git a/README.md b/README.md
index 47cd0311f..eb7621486 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ You can still use `sysinfo` on non-supported OSes, it'll simply do nothing and a
 empty values. You can check in your program directly if an OS is supported by checking the
 [`IS_SUPPORTED_SYSTEM`] constant.
 
-The minimum-supported version of `rustc` is **1.70**.
+The minimum-supported version of `rustc` is **1.74**.
 
 ## Usage
 
@@ -68,7 +68,7 @@ println!("NB CPUs: {}", sys.cpus().len());
 
 // Display processes ID, name na disk usage:
 for (pid, process) in sys.processes() {
-    println!("[{pid}] {} {:?}", process.name(), process.disk_usage());
+    println!("[{pid}] {:?} {:?}", process.name(), process.disk_usage());
 }
 
 // We display all disks' information:
@@ -182,8 +182,8 @@ virtual systems.
 
 Apple has restrictions as to which APIs can be linked into binaries that are distributed through the app store.
 By default, `sysinfo` is not compatible with these restrictions. You can use the `apple-app-store`
-feature flag to disable the Apple prohibited features. This also enables the `apple-sandbox` feature. 
-In the case of applications using the sandbox outside of the app store, the `apple-sandbox` feature 
+feature flag to disable the Apple prohibited features. This also enables the `apple-sandbox` feature.
+In the case of applications using the sandbox outside of the app store, the `apple-sandbox` feature
 can be used alone to avoid causing policy violations at runtime.
 
 ### How it works
diff --git a/examples/simple.rs b/examples/simple.rs
index 34634150a..15ce7be45 100644
--- a/examples/simple.rs
+++ b/examples/simple.rs
@@ -4,6 +4,7 @@
 #![allow(unused_must_use, non_upper_case_globals)]
 #![allow(clippy::manual_range_contains)]
 
+use bstr::ByteSlice;
 use std::io::{self, BufRead, Write};
 use std::str::FromStr;
 use sysinfo::{Components, Disks, Networks, Pid, Signal, System, Users};
@@ -242,7 +243,7 @@ fn interpret_input(
                     &mut io::stdout(),
                     "{}:{} status={:?}",
                     pid,
-                    proc_.name(),
+                    proc_.name().as_encoded_bytes().as_bstr(),
                     proc_.status()
                 );
             }
@@ -289,8 +290,12 @@ fn interpret_input(
                 };
             } else {
                 let proc_name = tmp[1];
-                for proc_ in sys.processes_by_name(proc_name) {
-                    writeln!(&mut io::stdout(), "==== {} ====", proc_.name());
+                for proc_ in sys.processes_by_name(proc_name.as_ref()) {
+                    writeln!(
+                        &mut io::stdout(),
+                        "==== {} ====",
+                        proc_.name().as_encoded_bytes().as_bstr()
+                    );
                     writeln!(&mut io::stdout(), "{proc_:?}");
                 }
             }
diff --git a/src/common.rs b/src/common.rs
index 02e9ccf7b..7659129b7 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -414,7 +414,7 @@ impl System {
     ///
     /// let s = System::new_all();
     /// for (pid, process) in s.processes() {
-    ///     println!("{} {}", pid, process.name());
+    ///     println!("{} {:?}", pid, process.name());
     /// }
     /// ```
     pub fn processes(&self) -> &HashMap<Pid, Process> {
@@ -428,7 +428,7 @@ impl System {
     ///
     /// let s = System::new_all();
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     println!("{}", process.name());
+    ///     println!("{:?}", process.name());
     /// }
     /// ```
     pub fn process(&self, pid: Pid) -> Option<&Process> {
@@ -450,17 +450,18 @@ impl System {
     /// use sysinfo::System;
     ///
     /// let s = System::new_all();
-    /// for process in s.processes_by_name("htop") {
-    ///     println!("{} {}", process.pid(), process.name());
+    /// for process in s.processes_by_name("htop".as_ref()) {
+    ///     println!("{} {:?}", process.pid(), process.name());
     /// }
     /// ```
     pub fn processes_by_name<'a: 'b, 'b>(
         &'a self,
-        name: &'b str,
+        name: &'b OsStr,
     ) -> impl Iterator<Item = &'a Process> + 'b {
+        let finder = memchr::memmem::Finder::new(name.as_encoded_bytes());
         self.processes()
             .values()
-            .filter(move |val: &&Process| val.name().contains(name))
+            .filter(move |val: &&Process| finder.find(val.name().as_encoded_bytes()).is_some())
     }
 
     /// Returns an iterator of processes with exactly the given `name`.
@@ -478,13 +479,13 @@ impl System {
     /// use sysinfo::System;
     ///
     /// let s = System::new_all();
-    /// for process in s.processes_by_exact_name("htop") {
-    ///     println!("{} {}", process.pid(), process.name());
+    /// for process in s.processes_by_exact_name("htop".as_ref()) {
+    ///     println!("{} {:?}", process.pid(), process.name());
     /// }
     /// ```
     pub fn processes_by_exact_name<'a: 'b, 'b>(
         &'a self,
-        name: &'b str,
+        name: &'b OsStr,
     ) -> impl Iterator<Item = &'a Process> + 'b {
         self.processes()
             .values()
@@ -832,7 +833,7 @@ impl System {
 ///
 /// let s = System::new_all();
 /// if let Some(process) = s.process(Pid::from(1337)) {
-///     println!("{}", process.name());
+///     println!("{:?}", process.name());
 /// }
 /// ```
 pub struct Process {
@@ -900,10 +901,10 @@ impl Process {
     ///
     /// let s = System::new_all();
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     println!("{}", process.name());
+    ///     println!("{:?}", process.name());
     /// }
     /// ```
-    pub fn name(&self) -> &str {
+    pub fn name(&self) -> &OsStr {
         self.inner.name()
     }
 
diff --git a/src/debug.rs b/src/debug.rs
index 3926fe8dc..c9f502306 100644
--- a/src/debug.rs
+++ b/src/debug.rs
@@ -73,14 +73,7 @@ impl fmt::Debug for Process {
 
 impl fmt::Debug for Components {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Components {{ {} }}",
-            self.iter()
-                .map(|x| format!("{x:?}"))
-                .collect::<Vec<_>>()
-                .join(", ")
-        )
+        f.debug_list().entries(self.iter()).finish()
     }
 }
 
@@ -109,14 +102,7 @@ impl fmt::Debug for Component {
 
 impl fmt::Debug for Networks {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Networks {{ {} }}",
-            self.iter()
-                .map(|x| format!("{x:?}"))
-                .collect::<Vec<_>>()
-                .join(", ")
-        )
+        f.debug_list().entries(self.iter()).finish()
     }
 }
 
@@ -141,27 +127,13 @@ impl fmt::Debug for NetworkData {
 
 impl fmt::Debug for Disks {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Disks {{ {} }}",
-            self.iter()
-                .map(|x| format!("{x:?}"))
-                .collect::<Vec<_>>()
-                .join(", ")
-        )
+        f.debug_list().entries(self.iter()).finish()
     }
 }
 
 impl fmt::Debug for Users {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Users {{ {} }}",
-            self.iter()
-                .map(|x| format!("{x:?}"))
-                .collect::<Vec<_>>()
-                .join(", ")
-        )
+        f.debug_list().entries(self.iter()).finish()
     }
 }
 
diff --git a/src/unix/apple/app_store/process.rs b/src/unix/apple/app_store/process.rs
index 0d90111da..bac4798cd 100644
--- a/src/unix/apple/app_store/process.rs
+++ b/src/unix/apple/app_store/process.rs
@@ -1,5 +1,6 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
+use std::ffi::OsStr;
 use std::path::Path;
 
 use crate::{DiskUsage, Gid, Pid, ProcessStatus, Signal, Uid};
@@ -11,8 +12,8 @@ impl ProcessInner {
         None
     }
 
-    pub(crate) fn name(&self) -> &str {
-        ""
+    pub(crate) fn name(&self) -> &OsStr {
+        OsStr::new("")
     }
 
     pub(crate) fn cmd(&self) -> &[String] {
diff --git a/src/unix/apple/cpu.rs b/src/unix/apple/cpu.rs
index 5ccb0bf90..18c0e9925 100644
--- a/src/unix/apple/cpu.rs
+++ b/src/unix/apple/cpu.rs
@@ -186,7 +186,7 @@ pub(crate) unsafe fn get_cpu_frequency() -> u64 {
 
     #[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
     {
-        return 0;
+        0
     }
     #[cfg(not(any(target_os = "ios", feature = "apple-sandbox")))]
     {
diff --git a/src/unix/apple/disk.rs b/src/unix/apple/disk.rs
index e5025390d..048c85268 100644
--- a/src/unix/apple/disk.rs
+++ b/src/unix/apple/disk.rs
@@ -269,6 +269,7 @@ unsafe fn get_dict_value<T, F: FnOnce(*const c_void) -> Option<T>>(
 ) -> Option<T> {
     #[cfg(target_os = "macos")]
     let _defined;
+    #[allow(clippy::infallible_destructuring_match)]
     let key = match key {
         DictKey::Extern(val) => val,
         #[cfg(target_os = "macos")]
diff --git a/src/unix/apple/macos/process.rs b/src/unix/apple/macos/process.rs
index d246d5d7b..71c21b08a 100644
--- a/src/unix/apple/macos/process.rs
+++ b/src/unix/apple/macos/process.rs
@@ -1,6 +1,8 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
+use std::ffi::{OsStr, OsString};
 use std::mem::{self, MaybeUninit};
+use std::os::unix::ffi::{OsStrExt, OsStringExt};
 use std::path::{Path, PathBuf};
 
 use libc::{c_int, c_void, kill};
@@ -12,7 +14,7 @@ use crate::sys::system::Wrap;
 use crate::unix::utils::cstr_to_rust_with_size;
 
 pub(crate) struct ProcessInner {
-    pub(crate) name: String,
+    pub(crate) name: OsString,
     pub(crate) cmd: Vec<String>,
     pub(crate) exe: Option<PathBuf>,
     pid: Pid,
@@ -47,7 +49,7 @@ pub(crate) struct ProcessInner {
 impl ProcessInner {
     pub(crate) fn new_empty(pid: Pid) -> Self {
         Self {
-            name: String::new(),
+            name: OsString::new(),
             pid,
             parent: None,
             cmd: Vec::new(),
@@ -78,7 +80,7 @@ impl ProcessInner {
 
     pub(crate) fn new(pid: Pid, parent: Option<Pid>, start_time: u64, run_time: u64) -> Self {
         Self {
-            name: String::new(),
+            name: OsString::new(),
             pid,
             parent,
             cmd: Vec::new(),
@@ -112,7 +114,7 @@ impl ProcessInner {
         unsafe { Some(kill(self.pid.0, c_signal) == 0) }
     }
 
-    pub(crate) fn name(&self) -> &str {
+    pub(crate) fn name(&self) -> &OsStr {
         &self.name
     }
 
@@ -400,12 +402,11 @@ unsafe fn get_exe_and_name_backup(
     ) {
         x if x > 0 => {
             buffer.set_len(x as _);
-            let tmp = String::from_utf8_unchecked(buffer);
+            let tmp = OsString::from_vec(buffer);
             let exe = PathBuf::from(tmp);
             if process.name.is_empty() {
                 exe.file_name()
-                    .and_then(|x| x.to_str())
-                    .unwrap_or("")
+                    .unwrap_or_default()
                     .clone_into(&mut process.name);
             }
             if exe_needs_update {
@@ -540,14 +541,12 @@ unsafe fn get_process_infos(process: &mut ProcessInner, refresh_kind: ProcessRef
     let (exe, proc_args) = get_exe(proc_args);
     if process.name.is_empty() {
         exe.file_name()
-            .and_then(|x| x.to_str())
-            .unwrap_or("")
-            .to_owned()
+            .unwrap_or_default()
             .clone_into(&mut process.name);
     }
 
     if refresh_kind.exe().needs_update(|| process.exe.is_none()) {
-        process.exe = Some(exe);
+        process.exe = Some(exe.to_owned());
     }
 
     let environ_needs_update = refresh_kind
@@ -565,14 +564,10 @@ unsafe fn get_process_infos(process: &mut ProcessInner, refresh_kind: ProcessRef
     true
 }
 
-fn get_exe(data: &[u8]) -> (PathBuf, &[u8]) {
+fn get_exe(data: &[u8]) -> (&Path, &[u8]) {
     let pos = data.iter().position(|c| *c == 0).unwrap_or(data.len());
-    unsafe {
-        (
-            Path::new(std::str::from_utf8_unchecked(&data[..pos])).to_path_buf(),
-            &data[pos..],
-        )
-    }
+    let (exe, proc_args) = data.split_at(pos);
+    (Path::new(OsStr::from_bytes(exe)), proc_args)
 }
 
 fn get_arguments<'a>(
diff --git a/src/unix/freebsd/disk.rs b/src/unix/freebsd/disk.rs
index a42bd7ca1..aac1be0b3 100644
--- a/src/unix/freebsd/disk.rs
+++ b/src/unix/freebsd/disk.rs
@@ -6,7 +6,7 @@ use std::ffi::{OsStr, OsString};
 use std::os::unix::ffi::OsStringExt;
 use std::path::{Path, PathBuf};
 
-use super::utils::c_buf_to_str;
+use super::utils::c_buf_to_utf8_str;
 
 pub(crate) struct DiskInner {
     name: OsString,
@@ -134,7 +134,7 @@ pub unsafe fn get_all_list(container: &mut Vec<Disk>) {
             continue;
         }
 
-        let mount_point = match c_buf_to_str(&fs_info.f_mntonname) {
+        let mount_point = match c_buf_to_utf8_str(&fs_info.f_mntonname) {
             Some(m) => m,
             None => {
                 sysinfo_debug!("Cannot get disk mount point, ignoring it.");
diff --git a/src/unix/freebsd/network.rs b/src/unix/freebsd/network.rs
index 644eed90b..051bb8058 100644
--- a/src/unix/freebsd/network.rs
+++ b/src/unix/freebsd/network.rs
@@ -80,7 +80,7 @@ impl NetworksInner {
             if !utils::get_sys_value(&mib, &mut data) {
                 continue;
             }
-            if let Some(name) = utils::c_buf_to_string(&data.ifmd_name) {
+            if let Some(name) = utils::c_buf_to_utf8_string(&data.ifmd_name) {
                 let data = &data.ifmd_data;
                 match self.interfaces.entry(name) {
                     hash_map::Entry::Occupied(mut e) => {
diff --git a/src/unix/freebsd/process.rs b/src/unix/freebsd/process.rs
index 1c42cd6ba..bc9fa3a57 100644
--- a/src/unix/freebsd/process.rs
+++ b/src/unix/freebsd/process.rs
@@ -2,6 +2,7 @@
 
 use crate::{DiskUsage, Gid, Pid, Process, ProcessRefreshKind, ProcessStatus, Signal, Uid};
 
+use std::ffi::{OsStr, OsString};
 use std::fmt;
 use std::path::{Path, PathBuf};
 
@@ -41,7 +42,7 @@ impl fmt::Display for ProcessStatus {
 }
 
 pub(crate) struct ProcessInner {
-    pub(crate) name: String,
+    pub(crate) name: OsString,
     pub(crate) cmd: Vec<String>,
     pub(crate) exe: Option<PathBuf>,
     pub(crate) pid: Pid,
@@ -72,7 +73,7 @@ impl ProcessInner {
         unsafe { Some(libc::kill(self.pid.0, c_signal) == 0) }
     }
 
-    pub(crate) fn name(&self) -> &str {
+    pub(crate) fn name(&self) -> &OsStr {
         &self.name
     }
 
@@ -280,7 +281,7 @@ pub(crate) unsafe fn get_process_data(
             cwd: None,
             exe: None,
             // kvm_getargv isn't thread-safe so we get it in the main thread.
-            name: String::new(),
+            name: OsString::new(),
             // kvm_getargv isn't thread-safe so we get it in the main thread.
             cmd: Vec::new(),
             // kvm_getargv isn't thread-safe so we get it in the main thread.
diff --git a/src/unix/freebsd/system.rs b/src/unix/freebsd/system.rs
index 39ce92bc8..05eb9987e 100644
--- a/src/unix/freebsd/system.rs
+++ b/src/unix/freebsd/system.rs
@@ -14,7 +14,7 @@ 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,
+    self, boot_time, c_buf_to_os_string, from_cstr_array, get_sys_value, get_sys_value_by_name,
     get_system_info, init_mib,
 };
 
@@ -380,7 +380,7 @@ unsafe fn add_missing_proc_info(
             if !cmd.is_empty() {
                 // First, we try to retrieve the name from the command line.
                 let p = Path::new(&cmd[0]);
-                if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
+                if let Some(name) = p.file_name() {
                     name.clone_into(&mut proc_inner.name);
                 }
 
@@ -395,7 +395,7 @@ unsafe fn add_missing_proc_info(
             // 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();
+            proc_inner.name = c_buf_to_os_string(&kproc.ki_comm);
         }
         if refresh_kind
             .environ()
diff --git a/src/unix/freebsd/utils.rs b/src/unix/freebsd/utils.rs
index 4f7ba5e5a..555f32882 100644
--- a/src/unix/freebsd/utils.rs
+++ b/src/unix/freebsd/utils.rs
@@ -4,8 +4,9 @@ use crate::{Pid, Process};
 use libc::{c_char, c_int, timeval};
 use std::cell::UnsafeCell;
 use std::collections::HashMap;
-use std::ffi::CStr;
+use std::ffi::{CStr, OsStr, OsString};
 use std::mem;
+use std::os::unix::ffi::OsStrExt;
 use std::time::SystemTime;
 
 /// This struct is used to switch between the "old" and "new" every time you use "get_mut".
@@ -109,20 +110,37 @@ pub(crate) unsafe fn get_sys_value_array<T: Sized>(mib: &[c_int], value: &mut [T
     ) == 0
 }
 
-pub(crate) fn c_buf_to_str(buf: &[libc::c_char]) -> Option<&str> {
+pub(crate) fn c_buf_to_utf8_str(buf: &[libc::c_char]) -> Option<&str> {
     unsafe {
         let buf: &[u8] = std::slice::from_raw_parts(buf.as_ptr() as _, buf.len());
-        if let Some(pos) = buf.iter().position(|x| *x == 0) {
+        std::str::from_utf8(if let Some(pos) = buf.iter().position(|x| *x == 0) {
             // Shrink buffer to terminate the null bytes
-            std::str::from_utf8(&buf[..pos]).ok()
+            &buf[..pos]
         } else {
-            std::str::from_utf8(buf).ok()
-        }
+            buf
+        })
+        .ok()
+    }
+}
+
+pub(crate) fn c_buf_to_utf8_string(buf: &[libc::c_char]) -> Option<String> {
+    c_buf_to_utf8_str(buf).map(|s| s.to_owned())
+}
+
+pub(crate) fn c_buf_to_os_str(buf: &[libc::c_char]) -> &OsStr {
+    unsafe {
+        let buf: &[u8] = std::slice::from_raw_parts(buf.as_ptr() as _, buf.len());
+        OsStr::from_bytes(if let Some(pos) = buf.iter().position(|x| *x == 0) {
+            // Shrink buffer to terminate the null bytes
+            &buf[..pos]
+        } else {
+            buf
+        })
     }
 }
 
-pub(crate) fn c_buf_to_string(buf: &[libc::c_char]) -> Option<String> {
-    c_buf_to_str(buf).map(|s| s.to_owned())
+pub(crate) fn c_buf_to_os_string(buf: &[libc::c_char]) -> OsString {
+    c_buf_to_os_str(buf).to_owned()
 }
 
 pub(crate) unsafe fn get_sys_value_str(mib: &[c_int], buf: &mut [libc::c_char]) -> Option<String> {
@@ -138,7 +156,7 @@ pub(crate) unsafe fn get_sys_value_str(mib: &[c_int], buf: &mut [libc::c_char])
     {
         return None;
     }
-    c_buf_to_string(&buf[..len / mem::size_of::<libc::c_char>()])
+    c_buf_to_utf8_string(&buf[..len / mem::size_of::<libc::c_char>()])
 }
 
 pub(crate) unsafe fn get_sys_value_by_name<T: Sized>(name: &[u8], value: &mut T) -> bool {
@@ -180,7 +198,7 @@ pub(crate) fn get_sys_value_str_by_name(name: &[u8]) -> Option<String> {
             ) == 0
                 && size > 0
             {
-                c_buf_to_string(&buf)
+                c_buf_to_utf8_string(&buf)
             } else {
                 // getting the system value failed
                 None
@@ -224,7 +242,7 @@ pub(crate) fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option<St
                 // If command fails return default
                 default.map(|s| s.to_owned())
             } else {
-                c_buf_to_string(&buf)
+                c_buf_to_utf8_string(&buf)
             }
         }
     }
diff --git a/src/unix/linux/disk.rs b/src/unix/linux/disk.rs
index 8ead473a0..0df4d77bb 100644
--- a/src/unix/linux/disk.rs
+++ b/src/unix/linux/disk.rs
@@ -1,6 +1,6 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-use crate::sys::utils::{get_all_data, to_cpath};
+use crate::sys::utils::{get_all_utf8_data, to_cpath};
 use crate::{Disk, DiskKind};
 
 use libc::statvfs;
@@ -80,7 +80,7 @@ impl crate::DisksInner {
     pub(crate) fn refresh_list(&mut self) {
         get_all_list(
             &mut self.disks,
-            &get_all_data("/proc/mounts", 16_385).unwrap_or_default(),
+            &get_all_utf8_data("/proc/mounts", 16_385).unwrap_or_default(),
         )
     }
 
@@ -187,7 +187,7 @@ fn find_type_for_device_name(device_name: &OsStr) -> DiskKind {
         .join(trimmed)
         .join("queue/rotational");
     // Normally, this file only contains '0' or '1' but just in case, we get 8 bytes...
-    match get_all_data(path, 8)
+    match get_all_utf8_data(path, 8)
         .unwrap_or_default()
         .trim()
         .parse()
diff --git a/src/unix/linux/process.rs b/src/unix/linux/process.rs
index 408916502..d45f4b27b 100644
--- a/src/unix/linux/process.rs
+++ b/src/unix/linux/process.rs
@@ -2,18 +2,19 @@
 
 use std::cell::UnsafeCell;
 use std::collections::{HashMap, HashSet};
-use std::ffi::OsStr;
+use std::ffi::{OsStr, OsString};
 use std::fmt;
 use std::fs::{self, DirEntry, File};
 use std::io::Read;
+use std::os::unix::ffi::OsStrExt;
 use std::path::{Path, PathBuf};
-use std::str::FromStr;
+use std::str::{self, FromStr};
 
 use libc::{c_ulong, gid_t, kill, uid_t};
 
 use crate::sys::system::SystemInfo;
 use crate::sys::utils::{
-    get_all_data, get_all_data_from_file, realpath, FileCounter, PathHandler, PathPush,
+    get_all_data_from_file, get_all_utf8_data, realpath, FileCounter, PathHandler, PathPush,
 };
 use crate::{
     DiskUsage, Gid, Pid, Process, ProcessRefreshKind, ProcessStatus, Signal, ThreadKind, Uid,
@@ -62,7 +63,6 @@ impl fmt::Display for ProcessStatus {
 #[repr(usize)]
 enum ProcIndex {
     Pid = 0,
-    ShortExe,
     State,
     ParentPid,
     GroupId,
@@ -89,7 +89,7 @@ enum ProcIndex {
 }
 
 pub(crate) struct ProcessInner {
-    pub(crate) name: String,
+    pub(crate) name: OsString,
     pub(crate) cmd: Vec<String>,
     pub(crate) exe: Option<PathBuf>,
     pub(crate) pid: Pid,
@@ -126,7 +126,7 @@ pub(crate) struct ProcessInner {
 impl ProcessInner {
     pub(crate) fn new(pid: Pid, proc_path: PathBuf) -> Self {
         Self {
-            name: String::new(),
+            name: OsString::new(),
             pid,
             parent: None,
             cmd: Vec::new(),
@@ -166,7 +166,7 @@ impl ProcessInner {
         unsafe { Some(kill(self.pid.0, c_signal) == 0) }
     }
 
-    pub(crate) fn name(&self) -> &str {
+    pub(crate) fn name(&self) -> &OsStr {
         &self.name
     }
 
@@ -307,7 +307,7 @@ pub(crate) fn set_time(p: &mut ProcessInner, utime: u64, stime: u64) {
 }
 
 pub(crate) fn update_process_disk_activity(p: &mut ProcessInner, path: &mut PathHandler) {
-    let data = match get_all_data(path.join("io"), 16_384) {
+    let data = match get_all_utf8_data(path.join("io"), 16_384) {
         Ok(d) => d,
         Err(_) => return,
     };
@@ -352,13 +352,13 @@ unsafe impl<'a, T> Send for Wrap<'a, T> {}
 unsafe impl<'a, T> Sync for Wrap<'a, T> {}
 
 #[inline(always)]
-fn compute_start_time_without_boot_time(parts: &[&str], info: &SystemInfo) -> u64 {
+fn compute_start_time_without_boot_time(parts: &Parts<'_>, info: &SystemInfo) -> u64 {
     // To be noted that the start time is invalid here, it still needs to be converted into
     // "real" time.
-    u64::from_str(parts[ProcIndex::StartTime as usize]).unwrap_or(0) / info.clock_cycle
+    u64::from_str(parts.str_parts[ProcIndex::StartTime as usize]).unwrap_or(0) / info.clock_cycle
 }
 
-fn _get_stat_data(path: &Path, stat_file: &mut Option<FileCounter>) -> Result<String, ()> {
+fn _get_stat_data(path: &Path, stat_file: &mut Option<FileCounter>) -> Result<Vec<u8>, ()> {
     let mut file = File::open(path.join("stat")).map_err(|_| ())?;
     let data = get_all_data_from_file(&mut file, 1024).map_err(|_| ())?;
     *stat_file = FileCounter::new(file);
@@ -398,11 +398,11 @@ fn update_proc_info(
     p: &mut ProcessInner,
     refresh_kind: ProcessRefreshKind,
     proc_path: &mut PathHandler,
-    parts: &[&str],
+    str_parts: &[&str],
     uptime: u64,
     info: &SystemInfo,
 ) {
-    get_status(p, parts[ProcIndex::State as usize]);
+    get_status(p, str_parts[ProcIndex::State as usize]);
     refresh_user_group_ids(p, proc_path, refresh_kind);
 
     if refresh_kind.exe().needs_update(|| p.exe.is_none()) {
@@ -424,7 +424,7 @@ fn update_proc_info(
         p.root = realpath(proc_path.join("root"));
     }
 
-    update_time_and_memory(proc_path, p, parts, uptime, info, refresh_kind);
+    update_time_and_memory(proc_path, p, str_parts, uptime, info, refresh_kind);
     if refresh_kind.disk_usage() {
         update_process_disk_activity(p, proc_path);
     }
@@ -433,7 +433,7 @@ fn update_proc_info(
 fn retrieve_all_new_process_info(
     pid: Pid,
     parent_pid: Option<Pid>,
-    parts: &[&str],
+    parts: &Parts<'_>,
     path: &Path,
     info: &SystemInfo,
     refresh_kind: ProcessRefreshKind,
@@ -441,11 +441,11 @@ fn retrieve_all_new_process_info(
 ) -> Process {
     let mut p = ProcessInner::new(pid, path.to_owned());
     let mut proc_path = PathHandler::new(path);
-    let name = parts[ProcIndex::ShortExe as usize];
+    let name = parts.short_exe;
 
     p.parent = match parent_pid {
         Some(parent_pid) if parent_pid.0 != 0 => Some(parent_pid),
-        _ => match Pid::from_str(parts[ProcIndex::ParentPid as usize]) {
+        _ => match Pid::from_str(parts.str_parts[ProcIndex::ParentPid as usize]) {
             Ok(p) if p.0 != 0 => Some(p),
             _ => None,
         },
@@ -456,8 +456,8 @@ fn retrieve_all_new_process_info(
         .start_time_without_boot_time
         .saturating_add(info.boot_time);
 
-    p.name = name.into();
-    if c_ulong::from_str(parts[ProcIndex::Flags as usize])
+    p.name = OsStr::from_bytes(name).to_os_string();
+    if c_ulong::from_str(parts.str_parts[ProcIndex::Flags as usize])
         .map(|flags| flags & libc::PF_KTHREAD as c_ulong != 0)
         .unwrap_or(false)
     {
@@ -466,7 +466,14 @@ fn retrieve_all_new_process_info(
         p.thread_kind = Some(ThreadKind::Userland);
     }
 
-    update_proc_info(&mut p, refresh_kind, &mut proc_path, parts, uptime, info);
+    update_proc_info(
+        &mut p,
+        refresh_kind,
+        &mut proc_path,
+        &parts.str_parts,
+        uptime,
+        info,
+    );
 
     Process { inner: p }
 }
@@ -508,7 +515,14 @@ pub(crate) fn _get_process_data(
         if start_time_without_boot_time == entry.start_time_without_boot_time {
             let mut proc_path = PathHandler::new(path);
 
-            update_proc_info(entry, refresh_kind, &mut proc_path, &parts, uptime, info);
+            update_proc_info(
+                entry,
+                refresh_kind,
+                &mut proc_path,
+                &parts.str_parts,
+                uptime,
+                info,
+            );
 
             refresh_user_group_ids(entry, &mut proc_path, refresh_kind);
             return Ok((None, pid));
@@ -545,14 +559,14 @@ pub(crate) fn _get_process_data(
     Ok((None, pid))
 }
 
-fn old_get_memory(entry: &mut ProcessInner, parts: &[&str], info: &SystemInfo) {
+fn old_get_memory(entry: &mut ProcessInner, str_parts: &[&str], info: &SystemInfo) {
     // rss
-    entry.memory = u64::from_str(parts[ProcIndex::ResidentSetSize as usize])
+    entry.memory = u64::from_str(str_parts[ProcIndex::ResidentSetSize as usize])
         .unwrap_or(0)
         .saturating_mul(info.page_size_b);
     // 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[ProcIndex::VirtualSize as usize]).unwrap_or(0);
+    entry.virtual_memory = u64::from_str(str_parts[ProcIndex::VirtualSize as usize]).unwrap_or(0);
 }
 
 fn slice_to_nb(s: &[u8]) -> u64 {
@@ -601,7 +615,7 @@ fn get_memory(path: &Path, entry: &mut ProcessInner, info: &SystemInfo) -> bool
 fn update_time_and_memory(
     path: &mut PathHandler,
     entry: &mut ProcessInner,
-    parts: &[&str],
+    str_parts: &[&str],
     uptime: u64,
     info: &SystemInfo,
     refresh_kind: ProcessRefreshKind,
@@ -611,13 +625,13 @@ fn update_time_and_memory(
         if refresh_kind.memory() {
             // Keeping this nested level for readability reasons.
             if !get_memory(path.join("statm"), entry, info) {
-                old_get_memory(entry, parts, info);
+                old_get_memory(entry, str_parts, info);
             }
         }
         set_time(
             entry,
-            u64::from_str(parts[ProcIndex::UserTime as usize]).unwrap_or(0),
-            u64::from_str(parts[ProcIndex::SystemTime as usize]).unwrap_or(0),
+            u64::from_str(str_parts[ProcIndex::UserTime as usize]).unwrap_or(0),
+            u64::from_str(str_parts[ProcIndex::SystemTime as usize]).unwrap_or(0),
         );
         entry.run_time = uptime.saturating_sub(entry.start_time_without_boot_time);
     }
@@ -776,7 +790,7 @@ fn copy_from_file(entry: &Path) -> Vec<String> {
                 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()) {
+                    match str::from_utf8(&data[..pos]).map(|s| s.trim()) {
                         Ok(s) if !s.is_empty() => out.push(s.to_string()),
                         _ => {}
                     }
@@ -794,7 +808,7 @@ fn copy_from_file(entry: &Path) -> Vec<String> {
 
 // Fetch tuples of real and effective UID and GID.
 fn get_uid_and_gid(file_path: &Path) -> Option<((uid_t, uid_t), (gid_t, gid_t))> {
-    let status_data = get_all_data(file_path, 16_385).ok()?;
+    let status_data = get_all_utf8_data(file_path, 16_385).ok()?;
 
     // We're only interested in the lines starting with Uid: and Gid:
     // here. From these lines, we're looking at the first and second entries to get
@@ -839,7 +853,12 @@ fn get_uid_and_gid(file_path: &Path) -> Option<((uid_t, uid_t), (gid_t, gid_t))>
     }
 }
 
-fn parse_stat_file(data: &str) -> Option<Vec<&str>> {
+struct Parts<'a> {
+    str_parts: Vec<&'a str>,
+    short_exe: &'a [u8],
+}
+
+fn parse_stat_file(data: &[u8]) -> Option<Parts<'_>> {
     // The stat file is "interesting" to parse, because spaces cannot
     // be used as delimiters. The second field stores the command name
     // surrounded by parentheses. Unfortunately, whitespace and
@@ -849,16 +868,15 @@ fn parse_stat_file(data: &str) -> Option<Vec<&str>> {
     // in the entire string. All other fields are delimited by
     // whitespace.
 
-    let mut parts = Vec::with_capacity(52);
-    let mut data_it = data.splitn(2, ' ');
-    parts.push(data_it.next()?);
-    let mut data_it = data_it.next()?.rsplitn(2, ')');
-    let data = data_it.next()?;
-    parts.push(data_it.next()?);
-    parts.extend(data.split_whitespace());
-    // Remove command name '('
-    if let Some(name) = parts[ProcIndex::ShortExe as usize].strip_prefix('(') {
-        parts[ProcIndex::ShortExe as usize] = name;
-    }
-    Some(parts)
+    let mut str_parts = Vec::with_capacity(51);
+    let mut data_it = data.splitn(2, |&b| b == b' ');
+    str_parts.push(str::from_utf8(data_it.next()?).ok()?);
+    let mut data_it = data_it.next()?.rsplitn(2, |&b| b == b')');
+    let data = str::from_utf8(data_it.next()?).ok()?;
+    let short_exe = data_it.next()?;
+    str_parts.extend(data.split_whitespace());
+    Some(Parts {
+        str_parts,
+        short_exe: short_exe.strip_prefix(&[b'(']).unwrap_or(short_exe),
+    })
 }
diff --git a/src/unix/linux/system.rs b/src/unix/linux/system.rs
index 9f01ad68a..4742eca67 100644
--- a/src/unix/linux/system.rs
+++ b/src/unix/linux/system.rs
@@ -2,7 +2,7 @@
 
 use crate::sys::cpu::{get_physical_core_count, CpusWrapper};
 use crate::sys::process::{_get_process_data, compute_cpu_usage, refresh_procs, unset_updated};
-use crate::sys::utils::{get_all_data, to_u64};
+use crate::sys::utils::{get_all_utf8_data, to_u64};
 use crate::{Cpu, CpuRefreshKind, LoadAvg, MemoryRefreshKind, Pid, Process, ProcessRefreshKind};
 
 use libc::{self, c_char, sysconf, _SC_CLK_TCK, _SC_HOST_NAME_MAX, _SC_PAGESIZE};
@@ -352,7 +352,7 @@ impl SystemInner {
     }
 
     pub(crate) fn uptime() -> u64 {
-        let content = get_all_data("/proc/uptime", 50).unwrap_or_default();
+        let content = get_all_utf8_data("/proc/uptime", 50).unwrap_or_default();
         content
             .split('.')
             .next()
@@ -508,7 +508,7 @@ impl SystemInner {
 }
 
 fn read_u64(filename: &str) -> Option<u64> {
-    get_all_data(filename, 16_635)
+    get_all_utf8_data(filename, 16_635)
         .ok()
         .and_then(|d| u64::from_str(d.trim()).ok())
 }
@@ -517,7 +517,7 @@ fn read_table<F>(filename: &str, colsep: char, mut f: F)
 where
     F: FnMut(&str, u64),
 {
-    if let Ok(content) = get_all_data(filename, 16_635) {
+    if let Ok(content) = get_all_utf8_data(filename, 16_635) {
         content
             .split('\n')
             .flat_map(|line| {
diff --git a/src/unix/linux/utils.rs b/src/unix/linux/utils.rs
index 9effcc476..9c3d07611 100644
--- a/src/unix/linux/utils.rs
+++ b/src/unix/linux/utils.rs
@@ -7,16 +7,23 @@ use std::sync::atomic::Ordering;
 
 use crate::sys::system::remaining_files;
 
-pub(crate) fn get_all_data_from_file(file: &mut File, size: usize) -> io::Result<String> {
+pub(crate) fn get_all_data_from_file(file: &mut File, size: usize) -> io::Result<Vec<u8>> {
+    let mut buf = Vec::with_capacity(size);
+    file.rewind()?;
+    file.read_to_end(&mut buf)?;
+    Ok(buf)
+}
+
+pub(crate) fn get_all_utf8_data_from_file(file: &mut File, size: usize) -> io::Result<String> {
     let mut buf = String::with_capacity(size);
     file.rewind()?;
     file.read_to_string(&mut buf)?;
     Ok(buf)
 }
 
-pub(crate) fn get_all_data<P: AsRef<Path>>(file_path: P, size: usize) -> io::Result<String> {
+pub(crate) fn get_all_utf8_data<P: AsRef<Path>>(file_path: P, size: usize) -> io::Result<String> {
     let mut file = File::open(file_path.as_ref())?;
-    get_all_data_from_file(&mut file, size)
+    get_all_utf8_data_from_file(&mut file, size)
 }
 
 #[allow(clippy::useless_conversion)]
diff --git a/src/unknown/process.rs b/src/unknown/process.rs
index 0bb48554b..bca71e07f 100644
--- a/src/unknown/process.rs
+++ b/src/unknown/process.rs
@@ -2,6 +2,7 @@
 
 use crate::{DiskUsage, Gid, Pid, ProcessStatus, Signal, Uid};
 
+use std::ffi::OsStr;
 use std::fmt;
 use std::path::Path;
 
@@ -21,8 +22,8 @@ impl ProcessInner {
         None
     }
 
-    pub(crate) fn name(&self) -> &str {
-        ""
+    pub(crate) fn name(&self) -> &OsStr {
+        OsStr::new("")
     }
 
     pub(crate) fn cmd(&self) -> &[String] {
diff --git a/src/windows/groups.rs b/src/windows/groups.rs
index c2bbcad77..fda6e8c83 100644
--- a/src/windows/groups.rs
+++ b/src/windows/groups.rs
@@ -1,6 +1,6 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-use crate::sys::utils::to_str;
+use crate::sys::utils::to_utf8_str;
 use crate::{
     common::{Gid, GroupInner},
     windows::sid::Sid,
@@ -95,7 +95,7 @@ pub(crate) fn get_groups(groups: &mut Vec<Group>) {
                             // Get the account name from the SID (because it's usually
                             // a better name), but fall back to the name we were given
                             // if this fails.
-                            let name = to_str(entry.grpi0_name);
+                            let name = to_utf8_str(entry.grpi0_name);
                             groups.push(Group {
                                 inner: GroupInner::new(Gid(0), name),
                             });
diff --git a/src/windows/process.rs b/src/windows/process.rs
index 4e88f7846..e84f84c7a 100644
--- a/src/windows/process.rs
+++ b/src/windows/process.rs
@@ -4,7 +4,7 @@ use crate::sys::system::is_proc_running;
 use crate::windows::Sid;
 use crate::{DiskUsage, Gid, Pid, ProcessRefreshKind, ProcessStatus, Signal, Uid};
 
-use std::ffi::OsString;
+use std::ffi::{OsStr, OsString};
 use std::fmt;
 #[cfg(feature = "debug")]
 use std::io;
@@ -198,7 +198,7 @@ unsafe impl Send for HandleWrapper {}
 unsafe impl Sync for HandleWrapper {}
 
 pub(crate) struct ProcessInner {
-    name: String,
+    name: OsString,
     cmd: Vec<String>,
     exe: Option<PathBuf>,
     pid: Pid,
@@ -269,7 +269,7 @@ unsafe fn display_ntstatus_error(ntstatus: windows::core::HRESULT) {
 
 // Take a look at https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/query.htm
 // for explanations.
-unsafe fn get_process_name(pid: Pid) -> Option<String> {
+unsafe fn get_process_name(pid: Pid) -> Option<OsString> {
     let mut info = SYSTEM_PROCESS_ID_INFORMATION {
         ProcessId: pid.0 as _,
         ImageName: MaybeUninit::zeroed().assume_init(),
@@ -337,9 +337,7 @@ unsafe fn get_process_name(pid: Pid) -> Option<String> {
         info.ImageName.Length as usize / std::mem::size_of::<u16>(),
     );
     let os_str = OsString::from_wide(s);
-    let name = Path::new(&os_str)
-        .file_name()
-        .map(|s| s.to_string_lossy().to_string());
+    let name = Path::new(&os_str).file_name().map(|s| s.to_os_string());
     let _err = LocalFree(HLOCAL(info.ImageName.Buffer.cast()));
     name
 }
@@ -424,7 +422,7 @@ impl ProcessInner {
         parent: Option<Pid>,
         memory: u64,
         virtual_memory: u64,
-        name: String,
+        name: OsString,
         now: u64,
         refresh_kind: ProcessRefreshKind,
     ) -> Self {
@@ -548,7 +546,7 @@ impl ProcessInner {
         }
     }
 
-    pub(crate) fn name(&self) -> &str {
+    pub(crate) fn name(&self) -> &OsStr {
         &self.name
     }
 
diff --git a/src/windows/sid.rs b/src/windows/sid.rs
index 5ed502912..85c5b4b45 100644
--- a/src/windows/sid.rs
+++ b/src/windows/sid.rs
@@ -9,7 +9,7 @@ use windows::Win32::Security::{
     CopySid, GetLengthSid, IsValidSid, LookupAccountSidW, SidTypeUnknown,
 };
 
-use crate::sys::utils::to_str;
+use crate::sys::utils::to_utf8_str;
 
 #[doc = include_str!("../../md_doc/sid.md")]
 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -102,7 +102,7 @@ impl Sid {
                 return None;
             }
 
-            Some(to_str(PWSTR::from_raw(name.as_mut_ptr())))
+            Some(to_utf8_str(PWSTR::from_raw(name.as_mut_ptr())))
         }
     }
 }
@@ -115,7 +115,7 @@ impl Display for Sid {
                 sysinfo_debug!("ConvertSidToStringSidW failed: {:?}", _err);
                 return None;
             }
-            let result = to_str(string_sid);
+            let result = to_utf8_str(string_sid);
             let _err = LocalFree(HLOCAL(string_sid.0 as _));
             Some(result)
         }
diff --git a/src/windows/system.rs b/src/windows/system.rs
index ff631ee58..74d8eca75 100644
--- a/src/windows/system.rs
+++ b/src/windows/system.rs
@@ -12,7 +12,9 @@ use crate::utils::into_iter;
 
 use std::cell::UnsafeCell;
 use std::collections::HashMap;
+use std::ffi::OsString;
 use std::mem::{size_of, zeroed};
+use std::os::windows::ffi::OsStringExt;
 use std::ptr;
 use std::time::SystemTime;
 
@@ -427,14 +429,14 @@ impl SystemInner {
         if Self::is_windows_eleven() {
             return get_reg_string_value(
                 HKEY_LOCAL_MACHINE,
-                "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+                r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
                 "ProductName",
             )
             .map(|product_name| product_name.replace("Windows 10 ", "Windows 11 "));
         }
         get_reg_string_value(
             HKEY_LOCAL_MACHINE,
-            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+            r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
             "ProductName",
         )
     }
@@ -446,7 +448,7 @@ impl SystemInner {
     pub(crate) fn kernel_version() -> Option<String> {
         get_reg_string_value(
             HKEY_LOCAL_MACHINE,
-            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+            r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
             "CurrentBuildNumber",
         )
     }
@@ -454,7 +456,7 @@ impl SystemInner {
     pub(crate) fn os_version() -> Option<String> {
         let build_number = get_reg_string_value(
             HKEY_LOCAL_MACHINE,
-            "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+            r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
             "CurrentBuildNumber",
         )
         .unwrap_or_default();
@@ -464,7 +466,7 @@ impl SystemInner {
             u32::from_le_bytes(
                 get_reg_value_u32(
                     HKEY_LOCAL_MACHINE,
-                    "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+                    r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
                     "CurrentMajorVersionNumber",
                 )
                 .unwrap_or_default(),
@@ -535,7 +537,7 @@ fn refresh_existing_process(
 
 #[allow(clippy::size_of_in_element_count)]
 //^ needed for "name.Length as usize / std::mem::size_of::<u16>()"
-pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id: Pid) -> String {
+pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id: Pid) -> OsString {
     let name = &process.ImageName;
     if name.Buffer.is_null() {
         match process_id.0 {
@@ -543,6 +545,7 @@ pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id:
             4 => "System".to_owned(),
             _ => format!("<no name> Process {process_id}"),
         }
+        .into()
     } else {
         unsafe {
             let slice = std::slice::from_raw_parts(
@@ -551,7 +554,7 @@ pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id:
                 name.Length as usize / std::mem::size_of::<u16>(),
             );
 
-            String::from_utf16_lossy(slice)
+            OsString::from_wide(slice)
         }
     }
 }
diff --git a/src/windows/users.rs b/src/windows/users.rs
index 9403de26d..f86644c49 100644
--- a/src/windows/users.rs
+++ b/src/windows/users.rs
@@ -1,6 +1,6 @@
 // Take a look at the license at the top of the repository in the LICENSE file.
 
-use crate::sys::utils::to_str;
+use crate::sys::utils::to_utf8_str;
 use crate::{
     common::{Gid, Uid},
     windows::sid::Sid,
@@ -139,7 +139,7 @@ unsafe fn get_groups_for_user(username: PCWSTR) -> Vec<Group> {
         if !buf.0.is_null() {
             let entries = std::slice::from_raw_parts(buf.0, nb_entries as _);
             groups.extend(entries.iter().map(|entry| Group {
-                inner: GroupInner::new(Gid(0), to_str(entry.lgrui0_name)),
+                inner: GroupInner::new(Gid(0), to_utf8_str(entry.lgrui0_name)),
             }));
         }
     } else {
@@ -190,7 +190,7 @@ pub(crate) fn get_users(users: &mut Vec<User>) {
                             // if this fails.
                             let name = sid
                                 .account_name()
-                                .unwrap_or_else(|| to_str(entry.usri0_name));
+                                .unwrap_or_else(|| to_utf8_str(entry.usri0_name));
                             users.push(User {
                                 inner: UserInner::new(
                                     Uid(sid),
diff --git a/src/windows/utils.rs b/src/windows/utils.rs
index 56deb796d..1510f08a1 100644
--- a/src/windows/utils.rs
+++ b/src/windows/utils.rs
@@ -23,7 +23,7 @@ pub(crate) fn get_now() -> u64 {
         .unwrap_or(0)
 }
 
-pub(crate) unsafe fn to_str(p: PWSTR) -> String {
+pub(crate) unsafe fn to_utf8_str(p: PWSTR) -> String {
     if p.is_null() {
         return String::new();
     }


Test patch:
diff --git a/tests/process.rs b/tests/process.rs
index 8f04a17f9..54d16d508 100644
--- a/tests/process.rs
+++ b/tests/process.rs
@@ -404,7 +404,10 @@ fn test_refresh_tasks() {
             .map(|task| task.name() == task_name)
             .unwrap_or(false)))
         .unwrap_or(false));
-    assert!(s.processes_by_exact_name(task_name).next().is_some());
+    assert!(s
+        .processes_by_exact_name(task_name.as_ref())
+        .next()
+        .is_some());
 
     // Let's give some time to the system to clean up...
     std::thread::sleep(std::time::Duration::from_secs(2));
@@ -420,7 +423,10 @@ fn test_refresh_tasks() {
             .map(|task| task.name() == task_name)
             .unwrap_or(false)))
         .unwrap_or(false));
-    assert!(s.processes_by_exact_name(task_name).next().is_none());
+    assert!(s
+        .processes_by_exact_name(task_name.as_ref())
+        .next()
+        .is_none());
 }
 
 // Checks that `refresh_process` is NOT removing dead processes.
@@ -564,14 +570,14 @@ fn test_process_iterator_lifetimes() {
     {
         let name = String::from("");
         // errors before PR #904: name does not live long enough
-        process = s.processes_by_name(&name).next();
+        process = s.processes_by_name(name.as_ref()).next();
     }
     process.unwrap();
 
     let process: Option<&sysinfo::Process>;
     {
         // worked fine before and after: &'static str lives longer than System, error couldn't appear
-        process = s.processes_by_name("").next();
+        process = s.processes_by_name("".as_ref()).next();
     }
     process.unwrap();
 }