1. Beginning: Death Scene
I recently purchased a Mac Mini 6 (Apple M6 chip) and plan to use it to evaluate some small and large models. Therefore, a tool that can intuitively and conveniently observe system performance is needed. In comprehensive comparison, open source projects eul Very suitable – it is small and exquisite (the App is only about 5MB), and resides natively in the menu bar.
But after installing it with brew, the result is shocking:CPU temperature and GPU related information are all displayed N/A. This information is extremely critical for scenarios where large model testing is to be run.

Figure 1.1 CPU temperature and GPU related information are all displayed N/A
So I found the source code of eul. on GitHub,master The latest commit of the branch is b5c3bd0the submission information reads Create Localizable.strings (#238). The date column clearly displays:5 years ago。

Figure 1.2 eul project source code update status
upper right corner 10k Star It seems to be showing its past glory, just lying quietly on GitHub. In the Issues section, someone asked two years ago: “Can M1 be saved?” No one answered.
Is it still salvageable on the M6? I don’t have much experience in macOS application development, but in today’s AI era, maybe I can give it a try.
2. Question: Research site
After getting the source code from 5 years ago, I found that the temperature reading implementation of eul originated from the Intel era. SMCKit. SMCKit’s README clearly states that it is “for Intel based Macs”, and its built-in sensor list is all Intel model keys (TC0P/TG0P/TM0S etc.), obviously there is nothing that can be done about Apple Silicon.
Use Debug to build, the assertion is triggered directly, and the App crashes when it starts:
eul/SMC.swift:390: Assertion failed: SMCParamStruct size is != 80
Obviously, some of the original data structures are no longer suitable for the current M6 operating environment.
After searching for some information, I found something even more sad:Apple has never disclosed the API and key naming convention of AppleSMC. Do I have to make a blind guess based on my limited macOS development experience?
It’s time to use the tools of this era:Workbuddy
3. Resurrection, eul: write the probe first, then perform the operation
Since there is no official documentation support, Workbuddy first wrote a Swift probe (smc_probe.swift), talking directly to the underlying driver. Probes do two things:
- read
#KEYGet the total number of local keys, enumerate them one by one, and filter out allT*Prefix the temperature key and decode the current value. - Traverse
IOAcceleratorservice, printPerformanceStatisticsAll statistical items.
The most dramatic scene happened: the probe used to troubleshoot the problem collapsed first.
When the probe reads data, the underlying IOKit The call directly threw an error:IOKit call error: 0x-1ffffd39. To find out why, have the probe print out the size of the data structure it sends to the driver. It was found that the size became 88 bytes。
This exactly explains the assertion error reported when eul crashed during startup:SMCParamStruct size is != 80obviously when faced with the 88-byte driver data packet, the bottom layer of the program directly refused to communicate.
This means,The probe used to troubleshoot the problem, I first perfectly reproduced the eul fault. This directly changes root cause positioning from “blind man feeling the elephant” to “accurate actual measurement”.
After the probe ran through, I got the first-hand data of M6:
- Total number of SMC keys:2066
- Tx temperature key quantity:135
- Temperature type:135 / 135 both
flttype, nonesp78
Workbuddy followed suit and developed a “four-knife” repair plan.
The first cut: closing the ABI cracks (88 bytes vs 80 bytes)
Structure SMCParamStruct It comes from Apple’s early open source project, and the driver side is fixed to parse at 80 bytes. But a field in the structure IOByteCountbecomes 64 bits (8 bytes) in arm64 user mode. The extra 4 bytes plus alignment padding make the structure 88 bytes.
The Debug build crashes directly, and the Release build call fails silently.
- repair: Instead of relying on platform definitions, Workbuddy recommends directly
dataSizeThe type is fixed toUInt32. This not only ensures the stability of the binary layout, but also removes three redundant type conversions.
The second knife: adaptive data type (sp78 vs flt)
The original code writes the reading logic to the Intel era. sp78(2-byte fixed point). And the M6 is all flt(4 bytes IEEE 754 floating point). according to sp78 If you go to read it, you can only read garbage values.
- repair: Changed from “hypothesis type” to “query type”. Check first
keyInformationGet the real type, and then select the decoder according to the actual type. Workbuddy conveniently complements the originalfromSP78Bug of missing decimal places and addedfromFLTThe decoder covers T2 and Apple Silicon chips in one fell swoop.
The third cut: Abandon static lists and embrace dynamic discovery
The original code only traverses the hard-coded 28 Intel keys. On the M6, none of these keys exist, resulting in an empty sensor list.
- repair: New
allTemperatureSensors()function, directly enumerate 2066 keys, filter out 135 candidates according to “T prefix + temperature type”, and then eliminate uninitialized hot zone placeholders (such asTz11is always 0.0 °C) and is retained last 126 active sensors。 - Correction episode: When aggregating temperature, the first version of the code is prefixed by “display name” matching. However, the display name has been rewritten (for example, to “CPU P-Core”), resulting in a mismatch. Workbuddy reminds:Identifier (key code) and display name must be strictly separated. Press instead
Tp0、Tg0After the code prefixes match, the temperature finally comes out.
The fourth knife: GPU recognition moves away from the virtual to the real
The original code requires that the GPU must have a PCI identifier (device-id / IOPCIMatch). But on Apple Silicon, the integrated GPU does not have this thing at all, causing the GPU list to be directly discarded.
- repair: let
device-idChanged from “required” to “optional”, using model name if missing (e.g.Apple M6)reveal all the details. The temperature of the GPU has also been changed from “must have registry field” to “registry first, SMCTg0xreveal all the details”.
4. The run has started, but this is not the end.
After fine-tuning of compilation and build parameters (adjusted SDKROOT etc.), the App finally ran successfully.

Figure 4.1 CPU temperature and GPU related information are all displayed normally.
On this M6, it successfully read:
- CPU temperature: 32 °C
- GPU temperature: 31 °C
- GPU usage: 10%
But it wasn’t just a victory of a few numbers. The code comparison before and after the repair is essentially to bring the underlying acquisition architecture of eul back from the “dead end for Intel models” to the right track of “facing the underlying protocols”.
Overall architecture evolution after repair

Figure 4.2 Overall architecture evolution
As can be seen from the above figure, the data flow direction of the system has not changed, it is still bottom-up (hardware->interface layer->acquisition layer->status layer->presentation layer).The real change occurs in those lowest arrows.
In the past, the underlying interface layer (SMCKit / IOHelper) and the hardware were “hardwired”, and the key names, types, and identifiers were all hard-coded; now, they have become a common pipeline with “translators” and “filters”.
Temperature Reading Link: From “Dead End” to “Adaptive”

Figure 4.3 Temperature reading link
The left side is the original path: press the 28 Intel hard-coded key names to search -> cannot be found -> the list is empty -> N/A is displayed.
The right side is the repaired path: enumerate all keys -> filter flt/sp78 Type->Plausibility filtering (remove Tz11 This type of invalid hot zone is always 0) -> Cluster according to Key Code prefix to get the maximum value -> Output the real temperature.
Architecture level changes: Completely decouples “display name” and “identifier”. The original code press "CPU_0_DIE" Check the sensor. Once the hardware name is changed, the code will crash. Now the program only recognizes the bottom layer Tp0、TPD etc. code prefix, used for display layer friendlyName Ready to package into a beautiful “CPU P-Core”. This is the resilience that “protocol orientation” brings.
GPU reading link: moving away from the virtual to the real

Figure 4.4 GPU read link
The repair of the GPU link is similar. The original code is in system_profiler and IOAccelerator Find the PCI identifier (device-id / IOPCIMatch), discard the entire record if it cannot be found.
After the repair, the identifier became a “dual-track system”: if there is PCI, use PCI, and if there is no PCI (such as M6 integrated GPU), use the model name (such as Apple M6) A complete match. At the same time, the GPU temperature is no longer forced to be taken from the registry, but falls back to SMC. Tg0x Sensor cluster.
Modification scale and compatibility regression
This “surgery” seems to have affected the bottom layer, but in fact the incision is very small and no muscles or bones are damaged:
| document | Change amount | core mission |
|---|---|---|
SMC.swift |
+81 / -10 | Repair ABI cracks, adaptive decoding, dynamic sensor discovery |
SmcControl.swift |
+51 / -12 | Sensor filtering, aggregation of maximum temperatures by prefix |
GPU.swift |
+23 / -8 | Remove the strong dependence on PCI and reveal the compatible model names |
GpuStore.swift |
+11 / -3 | Reconstruct GPU statistical matching logic |
project.pbxproj |
+1 / -1 | Fix SwiftPM SDKROOT Script error |
What’s more importantcompatibility. Through regression verification, the logic of the Intel model was not only not destroyed, but also repaired sp78 Decimal precision becomes better. Name the sensor (e.g. CPU_0_DIE) is still the first priority on Intel machines, and the rollback branch will not be triggered at all. This is a qualified open source refactoring:Don’t destroy other people’s jobs, just pick up your own broken legs.
5. Review: When AI crosses the “threshold”, what will developers have left?
Looking back on this fix, Workbuddy plays a much more role than just a “code completion tool.” In the process, it profoundly demonstrates a new paradigm for solving underlying system problems in the AI era.
1. AI fills the obscure “black box gap”
If it is a traditional development model, faced with “unpublished API” and “error 0x-1ffffd39”, engineers who lack OS development experience will most likely be stuck in consulting countless outdated StackOverflow posts. However, Workbuddy can quickly understand the underlying logic of the crash log and “reversely” measure the structure size by generating probe code, thereby deducing the 88-byte ABI crack.AI has turned the black box into a gray box, and changed trial and error from “blind men touching the elephant” to “following the picture to find out what is going on.”
2. From “how do I write” to “how do I design”
In this project, the core difficulty lies not in writing Swift syntax, but in architectural decisions such as “how to aggregate sensors more reasonably” and “separating identifiers from display names”. Workbuddy is responsible for generating 80% of the boilerplate code, adapting and fixing drafts; while humans are responsible for verifying, testing, and rewriting the 20% of critical logic.The AI is responsible for pushing forward, and the human is responsible for pulling the reins. This collaborative model greatly raises the upper limit of personal abilities.
3. AI cannot take the “last step” for you
AI can write fromFLT decoder, but it can’t handle Xcode’s signing certificate for you; it can pciMatch It is optional, but it cannot take on the open source maintenance responsibility for you after the code is merged into the main trunk.The threshold for “writing code” is getting lower, but the threshold for “taking responsibility for results” will always remain.
6. Ending: The code that stopped 5 years ago also has the right to run on the new M6
10k Stars, 380 Forks, 35 Contributors, 48 Releases. Once representing an active project, these figures were transformed into a digital monument.
But AI doesn’t make old code new, it just helps us transcend time. What really revived this project was not a certain model, but “someone is willing to compile it again.”
Open source is not free labor. AI can lower the barrier to maintenance, but it cannot replace responsibility, testing, and community.
In the next five years, maybe it will stop updating again. But at least now, it is running on this Mac Mini 6, reading a CPU temperature of 32 °C, and continues to toss the large model for evaluation.
Friends who like this article are welcome to follow, like, and collect with one click. Your support is the biggest motivation for writing articles.