Repository: GuillaumeGomez/sysinfo (Rust)

Issue:
 The brand name of the M1 CPU is not displayed correctly on Linux (Asahi).
**Describe the bug**

The premise is that M1 CPUs are divided into two categories: high-performance cores and low-power cores, each with a distinct name. These names are as follows:

- `high-performance core`: Firestorm-M1
- `low-power core`: Icestorm-M1

When examining the system using the "neofetch" command, it successfully retrieves information about two CPUs.

![image](https://github.com/GuillaumeGomez/sysinfo/assets/82384920/10c884ac-ad35-425e-bdeb-8f3197a72bbf)

The problem lies with the syslog, where only the brand name "Icestorm-M1" is displayed for all CPUs.
This is inconsistent with the expected result, as normally, "Firestorm-M1" should also be included in the CPU information.

Since neofetch is able to retrieve the brand names of the two cores, I believe it is not impossible to obtain this information in syslog as well.

How can i go about obtaining the brand names of both cores in syslog?

#### Environment
- `OS`: Arch Linux (Asahi Linux)
- `CPU`: M1
- `Hardware`: Macbook Air M1
- `uname -a`
```
Linux Slime-MBA 6.4.0-asahi-10-1-edge-ARCH #2 SMP PREEMPT_DYNAMIC Sat, 02 Sep 2023 14:11:37 +0000 aarch64 GNU/Linux
```
- Cargo.toml
```toml
[dependencies]
sysinfo = "0.29.10"
```
- `paru -Q rust`
```
rust 1:1.72.0-1
```

**To Reproduce**
```rs
println!("CPU: {:?}", sys.cpus());
// output
CPU: [Cpu { name: "cpu0", CPU usage: 0.0, frequency: 1332, vendor ID: "Apple", brand: "Icestorm-M1" }, Cpu { name: "cpu1", CPU usage: 0.0, frequency: 1332, vendor ID: "Apple", brand: "Icestorm-M1" }, Cpu { name: "cpu2", CPU usage: 0.0, frequency: 1332, vendor ID: "Apple", brand: "Icestorm-M1" }, Cpu { name: "cpu3", CPU usage: 0.0, frequency: 1332, vendor ID: "Apple", brand: "Icestorm-M1" }, Cpu { name: "cpu4", CPU usage: 0.0, frequency: 1056, vendor ID: "Apple", brand: "Icestorm-M1" }, Cpu { name: "cpu5", CPU usage: 0.0, frequency: 1056, vendor ID: "Apple", brand: "Icestorm-M1" }, Cpu { name: "cpu6", CPU usage: 0.0, frequency: 1056, vendor ID: "Apple", brand: "Icestorm-M1" }, Cpu { name: "cpu7", CPU usage: 0.0, frequency: 1056, vendor ID: "Apple", brand: "Icestorm-M1" }]
```


Discussion: I get this information from `/proc/cpuinfo` as you can see [here](https://github.com/GuillaumeGomez/sysinfo/blob/7cdab25d052fd3f2eeeef6a2d990aa1662c47998/src/unix/linux/cpu.rs#L720C20-L720C33).

What does this file look like for you?
I execute cat /proc/cpuinfo
```bash
processor	: 0
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint
CPU implementer	: 0x61
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0x022
CPU revision	: 1

processor	: 1
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint
CPU implementer	: 0x61
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0x022
CPU revision	: 1

processor	: 2
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint
CPU implementer	: 0x61
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0x022
CPU revision	: 1

processor	: 3
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint
CPU implementer	: 0x61
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0x022
CPU revision	: 1

processor	: 4
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint
CPU implementer	: 0x61
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0x023
CPU revision	: 1

processor	: 5
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint
CPU implementer	: 0x61
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0x023
CPU revision	: 1

processor	: 6
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint
CPU implementer	: 0x61
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0x023
CPU revision	: 1

processor	: 7
BogoMIPS	: 48.00
Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 asimddp sha512 asimdfhm dit uscat ilrcpc flagm ssbs sb paca pacg dcpodp flagm2 frint
CPU implementer	: 0x61
CPU architecture: 8
CPU variant	: 0x1
CPU part	: 0x023
CPU revision	: 1

```
Comparison shows that the CPU parts are separated in the Icestorm and Firestorm cores.
Yep, so seems like the bug is on our side. Gonna send a fix.
Can you test again to confirm it fixed the issue please?



Fix (patch):
diff --git a/src/unix/apple/cpu.rs b/src/unix/apple/cpu.rs
index 30df4547c..8185eddf5 100644
--- a/src/unix/apple/cpu.rs
+++ b/src/unix/apple/cpu.rs
@@ -323,11 +323,6 @@ pub(crate) fn init_cpus(
         }
         (percentage, cpus.len())
     });
-
-    // We didn't set them above to avoid cloning them unnecessarily.
-    global_cpu.brand = brand;
-    global_cpu.vendor_id = vendor_id;
-    global_cpu.frequency = frequency;
 }
 
 fn get_sysctl_str(s: &[u8]) -> String {
diff --git a/src/unix/freebsd/cpu.rs b/src/unix/freebsd/cpu.rs
index 9d136886c..3ddeda1d6 100644
--- a/src/unix/freebsd/cpu.rs
+++ b/src/unix/freebsd/cpu.rs
@@ -74,7 +74,6 @@ impl CpusWrapper {
                 self.cpus
                     .push(Cpu::new(format!("cpu {pos}"), vendor_id.clone(), frequency));
             }
-            self.global_cpu.vendor_id = vendor_id;
             self.got_cpu_frequency = refresh_kind.frequency();
         } else if refresh_kind.frequency() && !self.got_cpu_frequency {
             for (pos, proc_) in self.cpus.iter_mut().enumerate() {
@@ -87,9 +86,6 @@ impl CpusWrapper {
         if refresh_kind.cpu_usage() {
             self.get_cpu_usage();
         }
-        if refresh_kind.frequency() {
-            self.global_cpu.frequency = self.cpus.get(0).map(|cpu| cpu.frequency).unwrap_or(0);
-        }
     }
 
     fn get_cpu_usage(&mut self) {
diff --git a/src/unix/linux/cpu.rs b/src/unix/linux/cpu.rs
index e73ff2928..3575bf070 100644
--- a/src/unix/linux/cpu.rs
+++ b/src/unix/linux/cpu.rs
@@ -2,7 +2,7 @@
 
 #![allow(clippy::too_many_arguments)]
 
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet};
 use std::fs::File;
 use std::io::{BufRead, BufReader, Read};
 use std::time::Instant;
@@ -72,10 +72,10 @@ impl CpusWrapper {
             .unwrap_or(true);
 
         let first = self.cpus.is_empty();
-        let (vendor_id, brand) = if first {
+        let mut vendors_brands = if first {
             get_vendor_id_and_brand()
         } else {
-            (String::new(), String::new())
+            HashMap::new()
         };
 
         // If the last CPU usage update is too close (less than `MINIMUM_CPU_UPDATE_INTERVAL`),
@@ -127,6 +127,10 @@ impl CpusWrapper {
 
                         let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty());
                         if first {
+                            let (vendor_id, brand) = match vendors_brands.remove(&i) {
+                                Some((vendor_id, brand)) => (vendor_id, brand),
+                                None => (String::new(), String::new()),
+                            };
                             self.cpus.push(Cpu::new_with_values(
                                 to_str!(parts.next().unwrap_or(&[])),
                                 parts.next().map(to_u64).unwrap_or(0),
@@ -140,8 +144,8 @@ impl CpusWrapper {
                                 parts.next().map(to_u64).unwrap_or(0),
                                 parts.next().map(to_u64).unwrap_or(0),
                                 0,
-                                vendor_id.clone(),
-                                brand.clone(),
+                                vendor_id,
+                                brand,
                             ));
                         } else {
                             parts.next(); // we don't want the name again
@@ -188,22 +192,12 @@ impl CpusWrapper {
             }
 
             // `get_cpu_frequency` is very slow, so better run it in parallel.
-            self.global_cpu.frequency = iter_mut(&mut self.cpus)
+            iter_mut(&mut self.cpus)
                 .enumerate()
-                .map(|(pos, proc_)| {
-                    proc_.frequency = get_cpu_frequency(pos);
-                    proc_.frequency
-                })
-                .max()
-                .unwrap_or(0);
+                .for_each(|(pos, proc_)| proc_.frequency = get_cpu_frequency(pos));
 
             self.got_cpu_frequency = true;
         }
-
-        if first {
-            self.global_cpu.vendor_id = vendor_id;
-            self.global_cpu.brand = brand;
-        }
     }
 
     pub(crate) fn get_global_raw_times(&self) -> (u64, u64) {
@@ -715,13 +709,13 @@ fn get_arm_part(implementer: u32, part: u32) -> Option<&'static str> {
 }
 
 /// Returns the brand/vendor string for the first CPU (which should be the same for all CPUs).
-pub(crate) fn get_vendor_id_and_brand() -> (String, String) {
+pub(crate) fn get_vendor_id_and_brand() -> HashMap<usize, (String, String)> {
     let mut s = String::new();
     if File::open("/proc/cpuinfo")
         .and_then(|mut f| f.read_to_string(&mut s))
         .is_err()
     {
-        return (String::new(), String::new());
+        return HashMap::new();
     }
 
     fn get_value(s: &str) -> String {
@@ -740,40 +734,92 @@ pub(crate) fn get_vendor_id_and_brand() -> (String, String) {
             .unwrap_or_default()
     }
 
-    let mut vendor_id = None;
-    let mut brand = None;
-    let mut implementer = None;
-    let mut part = None;
-
-    for it in s.split('\n') {
-        if it.starts_with("vendor_id\t") {
-            vendor_id = Some(get_value(it));
-        } else if it.starts_with("model name\t") {
-            brand = Some(get_value(it));
-        } else if it.starts_with("CPU implementer\t") {
-            implementer = Some(get_hex_value(it));
-        } else if it.starts_with("CPU part\t") {
-            part = Some(get_hex_value(it));
-        } else {
-            continue;
+    #[inline]
+    fn is_new_processor(line: &str) -> bool {
+        line.starts_with("processor\t")
+            || line.starts_with("processor ")
+            || line.starts_with("processor:")
+    }
+
+    #[derive(Default)]
+    struct CpuInfo {
+        index: usize,
+        vendor_id: Option<String>,
+        brand: Option<String>,
+        implementer: Option<u32>,
+        part: Option<u32>,
+    }
+
+    impl CpuInfo {
+        fn has_all_info(&self) -> bool {
+            (self.brand.is_some() && self.vendor_id.is_some())
+                || (self.implementer.is_some() && self.part.is_some())
         }
-        if (brand.is_some() && vendor_id.is_some()) || (implementer.is_some() && part.is_some()) {
-            break;
+
+        fn convert(mut self) -> (usize, String, String) {
+            let (vendor_id, brand) = if let (Some(implementer), Some(part)) =
+                (self.implementer.take(), self.part.take())
+            {
+                let vendor_id = get_arm_implementer(implementer).map(String::from);
+                // It's possible to "model name" even with an ARM CPU, so just in case we can't retrieve
+                // the brand from "CPU part", we will then use the value from "model name".
+                //
+                // Example from raspberry pi 3B+:
+                //
+                // ```
+                // model name      : ARMv7 Processor rev 4 (v7l)
+                // CPU implementer : 0x41
+                // CPU part        : 0xd03
+                // ```
+                let brand = get_arm_part(implementer, part)
+                    .map(String::from)
+                    .or_else(|| self.brand.take());
+                (vendor_id, brand)
+            } else {
+                (self.vendor_id.take(), self.brand.take())
+            };
+            (
+                self.index,
+                vendor_id.unwrap_or_default(),
+                brand.unwrap_or_default(),
+            )
         }
     }
-    if let (Some(implementer), Some(part)) = (implementer, part) {
-        vendor_id = get_arm_implementer(implementer).map(String::from);
-        // It's possible to "model name" even with an ARM CPU, so just in case we can't retrieve
-        // the brand from "CPU part", we will then use the value from "model name".
-        //
-        // Example from raspberry pi 3B+:
-        //
-        // ```
-        // model name      : ARMv7 Processor rev 4 (v7l)
-        // CPU implementer : 0x41
-        // CPU part        : 0xd03
-        // ```
-        brand = get_arm_part(implementer, part).map(String::from).or(brand);
+
+    let mut cpus: HashMap<usize, (String, String)> = HashMap::new();
+    let mut lines = s.split('\n');
+    while let Some(line) = lines.next() {
+        if is_new_processor(line) {
+            let index = match line.split(':').nth(1).and_then(|i| i.parse::<usize>().ok()) {
+                Some(index) => index,
+                None => {
+                    sysinfo_debug!("Couldn't get processor ID from {line:?}, ignoring this core");
+                    continue;
+                }
+            };
+
+            let mut info = CpuInfo {
+                index,
+                ..Default::default()
+            };
+
+            #[allow(clippy::while_let_on_iterator)]
+            while let Some(line) = lines.next() {
+                if line.starts_with("vendor_id\t") {
+                    info.vendor_id = Some(get_value(line));
+                } else if line.starts_with("model name\t") {
+                    info.brand = Some(get_value(line));
+                } else if line.starts_with("CPU implementer\t") {
+                    info.implementer = Some(get_hex_value(line));
+                } else if line.starts_with("CPU part\t") {
+                    info.part = Some(get_hex_value(line));
+                } else if info.has_all_info() || is_new_processor(line) {
+                    break;
+                }
+            }
+            let (index, vendor_id, brand) = info.convert();
+            cpus.insert(index, (vendor_id, brand));
+        }
     }
-    (vendor_id.unwrap_or_default(), brand.unwrap_or_default())
+    cpus
 }
diff --git a/src/windows/cpu.rs b/src/windows/cpu.rs
index 3a998868e..f1c1dfed8 100644
--- a/src/windows/cpu.rs
+++ b/src/windows/cpu.rs
@@ -269,10 +269,7 @@ impl CpusWrapper {
 
     fn init_if_needed(&mut self, refresh_kind: CpuRefreshKind) {
         if self.cpus.is_empty() {
-            let (cpus, vendor_id, brand) = super::tools::init_cpus(refresh_kind);
-            self.cpus = cpus;
-            self.global.vendor_id = vendor_id;
-            self.global.brand = brand;
+            self.cpus = super::tools::init_cpus(refresh_kind);
             self.got_cpu_frequency = refresh_kind.frequency();
         }
     }
diff --git a/src/windows/tools.rs b/src/windows/tools.rs
index a0c334010..27b419769 100644
--- a/src/windows/tools.rs
+++ b/src/windows/tools.rs
@@ -17,7 +17,7 @@ impl KeyHandler {
     }
 }
 
-pub(crate) fn init_cpus(refresh_kind: CpuRefreshKind) -> (Vec<Cpu>, String, String) {
+pub(crate) fn init_cpus(refresh_kind: CpuRefreshKind) -> Vec<Cpu> {
     unsafe {
         let mut sys_info: SYSTEM_INFO = zeroed();
         GetSystemInfo(&mut sys_info);
@@ -37,7 +37,7 @@ pub(crate) fn init_cpus(refresh_kind: CpuRefreshKind) -> (Vec<Cpu>, String, Stri
                 *frequency,
             ));
         }
-        (ret, vendor_id, brand)
+        ret
     }
 }