GuillaumeGomez / sysinfo

Cross-platform library to fetch system information
MIT License
2.03k stars 304 forks source link

Add temperature support for M1 Macs #456

Closed mikemadden42 closed 1 year ago

mikemadden42 commented 3 years ago

Would it be possible to add temperature support for M1 Macs?

On an Intel Mac, I can use the following to find information about the temperature of system components:

use sysinfo::{System, SystemExt};

fn main() {
    let sys = System::new_all();

    for component in sys.get_components() {
        println!("{:?}", component);
    }
}
PECI CPU: 46.859375°C (max: 0°C)
CPU Proximity: 42.375°C (max: 0°C)
Battery: 30.09375°C (max: 0°C)

On M1 Macs, sys.get_components() appears empty.

$ rustc --version
rustc 1.51.0 (2fd73fabe 2021-03-23)

$ tail -2 Cargo.toml
[dependencies]
sysinfo = "0.16.5"
mikemadden42 commented 3 years ago

I wonder if the issue with sysinfo is related to this issue: https://github.com/iglance/iGlance/issues/225#issuecomment-813862371

GuillaumeGomez commented 3 years ago

I'd like to add this support but I don't have a mac with M1 processor, so it'll need someone else to implement it. :-/

nbigaouette commented 3 years ago

This Objective-C project seems to be able to read some temperature on the M1: https://github.com/freedomtan/sensors_cmdline/

I'll see if I can hack something...

EDIT: There is also this project (still in Objective-C): https://github.com/fermion-star/apple_sensors

GuillaumeGomez commented 3 years ago

Good luck!

nbigaouette commented 3 years ago

Good luck!

It's dangerous to go alone, I'll get all the luck I can!

:D

GuillaumeGomez commented 3 years ago

On any objective-C related source code, it's very useful indeed. :3

fdncred commented 2 years ago

just adding my voice here in wishing sensors worked on M1. :)

GuillaumeGomez commented 2 years ago

Same answer: I don't have a mac with M1 processors so I'll need someone to do it. :)

zannis commented 2 years ago

I could help with this one if you can provide some info on how to approach it, I'm pretty new to Macs internals :/

GuillaumeGomez commented 2 years ago

You have everything you need in this message. The difficulty being to find the C equivalent of the objective-C API so you can call it from rust.

macshome commented 2 years ago

Yeah, the issue is that the older code is using the SMC to get temperatures and M1 Macs don't have a SMC. They share the sensor layout with iOS devices for things like the CPU. This means you need to talk to IOKit to get the info.

Seeing as this has been sitting like this for a long time, and I've wanted to dabble in Rust, I might take a look at it.

P.S. To call Obj-C from just about anything that talks C you can use the objc_msgSend method. There are some Rust tools built on this as well like rust-objc.

GuillaumeGomez commented 2 years ago

It's in this file.

P.S. To call Obj-C from just about anything that talks C you can use the objc_msgSend method. There are some Rust tools built on this as well like rust-objc.

I removed all objective-C code from sysinfo and I'd prefer to keep it this way. :)

If needed, you can simply reimplement the objective-C function (I can help with that if you don't know how to do it) by looking at its source code.

macshome commented 2 years ago

I installed rust and read some docs last night to take a look at this in more depth. Seems like it's pretty straightforward to get the C working and then define those externals in the ffi class. I've got M1 and M1 Pro MacBooks to test with.

Work is busy, so I'm not blasting through it unfortunately.

GuillaumeGomez commented 2 years ago

It's fine, take your time. And thanks for working on this!

macshome commented 2 years ago

Going through this it seems we can get the temps without resorting to private SPI calls. As a question though, which temperature sensor are most interested in? I assume it's the closest analog to the CPU Proximity (heat spreader) "TC0P" that is being used in the existing code?

GuillaumeGomez commented 2 years ago

You can get all of them, no need to filter. :)

Currently it's not about filtering on mac but more like there isn't a generic way to get all of them (because they have IDs that change at each update for whatever reason...).

macshome commented 2 years ago

Well, a M1 Pro (10-core CPU) has 64 HIDEvent temp sensors it seems. Since the M1 Macs are all SoC based I suppose the differences in distance are minimal.

How would you like to consume the list from my code? Dictionary? Array? I assume you want the name and the temp value for each.

GuillaumeGomez commented 2 years ago

The answer is here (so yes, a vector for the moment, maybe I'll switch to a hash map later on... But out of scope in here).

macshome commented 2 years ago

Here is a list of the sensors and temps on a 10 Core M1 Pro. Some of the names are easier than others to figure out. In your code you seem to be looking at 4 in particular. Is that still the case here or do you just want all of them? It's simple enough to filter or just return them all.

Sensor Name Temp C
gas gauge battery 23.2
NAND CH0 temp 24.0
PMU tcal 51.9
PMU tdev1 27.6
PMU tdev2 27.5
PMU tdev3 27.7
PMU tdev4 23.0
PMU tdev5 23.4
PMU tdev6 26.9
PMU tdev7 28.4
PMU tdev8 26.9
PMU tdie0 27.8
PMU tdie1 28.1
PMU tdie10 27.7
PMU tdie2 28.1
PMU tdie3 27.9
PMU tdie4 28.2
PMU tdie5 28.1
PMU tdie6 27.9
PMU tdie7 27.9
PMU tdie8 27.9
PMU tdie9 27.9
PMU TP0s 27.8
PMU TP0s 27.8
PMU TP1g 28.1
PMU TP1s 27.5
PMU TP2g 28.1
PMU TP2s 27.7
PMU TP3g 27.6
GuillaumeGomez commented 2 years ago

Like I said, I didn't grow the list because these IDs change between macs and I don't have a generic way to get them all.

In the list you provided, as you long as there is a "readable name" (meaning you'll need a table for this too unfortunately), you can keep all of them.

macshome commented 2 years ago

Ok. That actually makes the ARM sensors easier to get as I’m just asking for all the HID events with the usage page for the internal temperature sensors. The only processing I’ll do is to dedupe and toss the ones without readable names.

I’ll be back to my computers tomorrow and can start looking at how to get this working with the ffi.

macshome commented 2 years ago

OK, taking a look at how to get this working in Rust now. It seems like I add my function prototypes to this ffi.rs file and then implement them in this components.rs file?

macshome commented 2 years ago

I guess the first step is learning how to define and call non-public types and functions in C from Rust. :D

In my main.c I setup the non-public types like this...

#include <IOKit/hidsystem/IOHIDEventSystemClient.h>
#include <stdio.h>

typedef struct __IOHIDEvent *IOHIDEventRef;
typedef double IOHIDFloat;

IOHIDEventSystemClientRef IOHIDEventSystemClientCreate(CFAllocatorRef allocator);
IOHIDEventRef IOHIDServiceClientCopyEvent(IOHIDServiceClientRef, int64_t , int32_t, int64_t);
IOHIDFloat IOHIDEventGetFloatValue(IOHIDEventRef event, int32_t field);

#define kIOHIDEventTypeTemperature  15
#define IOHIDEventFieldBase(type)   (type << 16)

The main sticking point is the import of that specific hidsystem file. I think once I can do that I can declare my types and functions pretty easily. Would copying the header to the project and then using #[link] work?

Sorry to be such a pain on this, this is my first time touching Rust.

GuillaumeGomez commented 2 years ago

No problem, don't worry. We all have to learn somewhere when we begin and your efforts are really appreciated!

OK, taking a look at how to get this working in Rust now. It seems like I add my function prototypes to this ffi.rs file and then implement them in this components.rs file?

Yes and yes.

The main sticking point is the import of that specific hidsystem file. I think once I can do that I can declare my types and functions pretty easily. Would copying the header to the project and then using #[link] work?

It'd be much simpler to simply redeclare them in Rust. For example:

extern "C" {
    pub type IOHIDEventRef = c_void;
    pub type __IOHIDEvent = *mut IOHIDEventRef;

    pub fn IOHIDEventSystemClientCreate(allocator: CFAllocatorRef)-> IOHIDEventSystemClientRef;
}
macshome commented 2 years ago

OK. Been reading up on Rust and finally got my debugger working. Been at horse shows the last two weekends in a row, but hopefully I have some time now to get back to this.

GuillaumeGomez commented 2 years ago

Thanks! Again please remember: we're not in a hurry so take as much time as you want/need.

GuillaumeGomez commented 1 year ago

Fixed in #792.

macshome commented 1 year ago

Thanks for getting this done. I haven't had the time and have had some debates with work about allowing me to participate.

GuillaumeGomez commented 1 year ago

No problem. We were lucky that someone had a mac M1, time and motivation to do it. ;)