Compare commits

..

43 Commits
v0.94 ... v0.97

Author SHA1 Message Date
Dr-Noob
8f2f3d3a16 [v0.97] Merge bugfix3 branch to support Rocket Lake processors 2021-04-09 20:06:05 +02:00
Dr-Noob
6dd041bf9f [v0.97] Fix macOS compilation issue as noted by #73 2021-04-09 18:32:34 +02:00
Dr-Noob
b45c09efff [v0.97] Add raw option to help. Disable raw option in ARM 2021-04-09 15:58:33 +02:00
Dr-Noob
ec5f80adc1 [v0.97] Fix compilation in macOS 2021-04-09 15:49:21 +02:00
Dr-Noob
ecca042d86 [v0.97] Manually merge bugfix branch with latest fixes 2021-04-09 15:37:17 +02:00
Dr-Noob
c718d83868 [v0.96] Add Rocket Lake uarch detection as suggested by #68 2021-04-09 11:26:34 +02:00
Dr-Noob
32b035f1a2 [v0.96] Fix typo as noticed by #70 2021-04-09 10:50:10 +02:00
Dr-Noob
8bb65e0cc0 [v0.96] Fix compilation issue in Windows 2021-04-09 09:25:48 +02:00
Dr-Noob
e8d2898ae3 [v0.96] Remove cache sizes check 2021-04-08 13:18:35 +02:00
Dr-Noob
b699fdc3f2 [v0.96] Update README and fix typo 2021-04-08 11:13:21 +02:00
Dr-Noob
812ee0acc6 [v0.96] Add PREFIX to Makefile and uninstall target, as requested by many users 2021-04-08 09:36:21 +02:00
Dr-Noob
7916e8cbb4 [v0.96] Replace "Simplistic" by "Simple" in description 2021-04-07 20:06:24 +02:00
Dr-Noob
41dbb22a20 [v0.96] Merge branch bugfix3 to include an ARM SoC fix 2021-04-07 19:55:58 +02:00
Dr-Noob
bb9fb17ec8 [v0.96] Tracking issue #54 2021-04-07 16:25:31 +02:00
Dr-Noob
b3ed3e9240 [v0.96] Add 32bit support in Makefile 2021-04-07 14:52:52 +02:00
Dr-Noob
044608f31f [v0.95] Add 0x8000001D sublevel query to --raw option 2021-04-07 14:35:45 +02:00
Dr-Noob
2879876500 [v0.96] Dont treat unknown unified cache as a bug, since there are some processors with eDRAM which supports this level, like #41 2021-04-07 11:17:43 +02:00
Dr-Noob
c7cc8be712 [v0.96] Use lower verbosity for some errors found in cpuid 2021-04-07 10:37:17 +02:00
Dr-Noob
654d2e27e1 [v0.96] Replace Makefile var names (use C names instead of C++) 2021-04-07 10:14:20 +02:00
Dr-Noob
09cbb8874b [v0.96] Fix previous mistake: use level 0xB to check if the level is supported or not, according to Intel docs 2021-04-06 20:37:49 +02:00
Dr-Noob
fe95ca3e10 [v0.96] Fix bug where ebx returned 0 in apic.c when CPU max level >= 0xB but CPU does not support x2apic 2021-04-06 16:56:26 +02:00
Dr-Noob
ec2ad4fef6 [v0.96] Do not consider missing frequency file as a bug 2021-04-06 16:35:12 +02:00
Dr-Noob
d56f7ffd14 [v0.96] Fix segfault when invalid cache size is found 2021-04-06 12:56:44 +02:00
Dr-Noob
4900c10eb3 [v0.96] Remove space between badges in README 2021-03-31 15:33:15 +02:00
Dr-Noob
9f0dc85bd8 [v0.96] Small modifications to README 2021-03-31 15:31:46 +02:00
Dr-Noob
36aeba0e73 [v0.96] Improve README with badges and ToC 2021-03-31 15:15:01 +02:00
Dr-Noob
ca7091bc5e [v0.96] Add short options. Improve --help flag. Update man page 2021-03-31 12:40:19 +02:00
Dr-Noob
8abbd8f69f [v0.96] Fix AMD ASCII art. Add third digit in frequency output 2021-03-31 11:03:54 +02:00
Dr-Noob
7420792ef5 [v0.95] Fetch topology extensions field in AMD processors 2021-03-30 10:39:27 +02:00
Dr-Noob
db32cccd91 [v0.95] Add --raw option 2021-03-15 21:49:47 +01:00
Dr-Noob
a8d8ac2e91 [v0.95] Temporarily disable cache sanity checks 2021-03-06 22:13:32 +01:00
Dr-Noob
2f61ebd35a [0.95] Update README table 2021-01-14 19:30:36 +01:00
Dr-Noob
e21ca95da8 [0.95][x86] Merge bugfix branch, which adds macOS support to cpufetch 2021-01-14 18:46:41 +01:00
Dr-Noob
3bac5cbfe2 [v0.95][x86] Print CPUID 0x1 EAX register with debug flag 2021-01-10 22:03:25 +01:00
Dr-Noob
04f0bfcbde [v0.94][x86] Add uarch detection for Intel families derived from Kaby Lake 2021-01-10 22:01:17 +01:00
Dr-Noob
697a921042 [X86] Avoid checking /sys directory in macOS to find frequency 2021-01-10 09:01:50 +01:00
Dr-Noob
3b624f3025 [X86] Fix bug where unknown hypervisor caused a segfault. This should solve issue #38 2021-01-05 17:37:26 +01:00
Dr-Noob
2494b56a49 [X86] Fix compilation error in MacOS. MacOS does not provide any method to bin threads to cores, so its pretty hard to get apic ids. This first approach is really dark and I hope I can improve it in the future 2021-01-05 17:35:57 +01:00
Dr-Noob
797c708f2d [v0.94][x86] Consider missing frequency file in x86_64 as a bug if no hypervisor is present. Took this idea from issue #37. Add if hypervisor is present to debug mode to prevent more confusions in the future 2020-12-29 00:09:01 +01:00
Dr-Noob
56a1da3428 [v0.94] Do not consider missing frequency file in x86_64 as a bug. Fix typos 2020-12-26 08:52:14 +01:00
Dr-Noob
1ef6daf943 Fix pictures links and add ARM pictures 2020-12-25 18:39:58 +01:00
Dr-Noob
cc20fff6ea Fix grammar and typos 2020-12-25 17:50:10 +01:00
Dr-Noob
c7d1165a94 Update documentation 2020-12-25 15:42:33 +01:00
26 changed files with 759 additions and 371 deletions

View File

@@ -1,8 +1,10 @@
CXX=gcc
CC=gcc
CXXFLAGS=-Wall -Wextra -Werror -pedantic -fstack-protector-all -pedantic -std=c99
CFLAGS=-Wall -Wextra -Werror -pedantic -fstack-protector-all -pedantic -std=c99
SANITY_FLAGS=-Wfloat-equal -Wshadow -Wpointer-arith
PREFIX ?= /usr
SRC_COMMON=src/common/
COMMON_SRC = $(SRC_COMMON)main.c $(SRC_COMMON)cpu.c $(SRC_COMMON)udev.c $(SRC_COMMON)printer.c $(SRC_COMMON)args.c $(SRC_COMMON)global.c
@@ -10,16 +12,16 @@ COMMON_HDR = $(SRC_COMMON)ascii.h $(SRC_COMMON)cpu.h $(SRC_COMMON)udev.h $(SRC_C
ifneq ($(OS),Windows_NT)
arch := $(shell uname -m)
ifeq ($(arch), x86_64)
ifeq ($(arch), $(filter $(arch), x86_64 i686))
SRC_DIR=src/x86/
SOURCE += $(COMMON_SRC) $(SRC_DIR)cpuid.c $(SRC_DIR)apic.c $(SRC_DIR)cpuid_asm.c $(SRC_DIR)uarch.c
HEADERS += $(COMMON_HDR) $(SRC_DIR)cpuid.h $(SRC_DIR)apic.h $(SRC_DIR)cpuid_asm.h $(SRC_DIR)uarch.h
CXXFLAGS += -DARCH_X86
CFLAGS += -DARCH_X86
else
SRC_DIR=src/arm/
SOURCE += $(COMMON_SRC) $(SRC_DIR)midr.c $(SRC_DIR)uarch.c $(SRC_DIR)soc.c $(SRC_DIR)udev.c
HEADERS += $(COMMON_HDR) $(SRC_DIR)midr.h $(SRC_DIR)uarch.h $(SRC_DIR)soc.h $(SRC_DIR)udev.c $(SRC_DIR)socs.h
CXXFLAGS += -DARCH_ARM -Wno-unused-parameter
CFLAGS += -DARCH_ARM -Wno-unused-parameter
endif
OUTPUT=cpufetch
@@ -28,21 +30,21 @@ else
SRC_DIR=src/x86/
SOURCE += $(COMMON_SRC) $(SRC_DIR)cpuid.c $(SRC_DIR)apic.c $(SRC_DIR)cpuid_asm.c $(SRC_DIR)uarch.c
HEADERS += $(COMMON_HDR) $(SRC_DIR)cpuid.h $(SRC_DIR)apic.h $(SRC_DIR)cpuid_asm.h $(SRC_DIR)uarch.h
CXXFLAGS += -DARCH_X86
CFLAGS += -DARCH_X86
SANITY_FLAGS += -Wno-pedantic-ms-format
OUTPUT=cpufetch.exe
endif
all: $(OUTPUT)
debug: CXXFLAGS += -g -O0
debug: CFLAGS += -g -O0
debug: $(OUTPUT)
release: CXXFLAGS += -static -O3
release: CFLAGS += -static -O3
release: $(OUTPUT)
$(OUTPUT): Makefile $(SOURCE) $(HEADERS)
$(CXX) $(CXXFLAGS) $(SANITY_FLAGS) $(SOURCE) -o $(OUTPUT)
$(CC) $(CFLAGS) $(SANITY_FLAGS) $(SOURCE) -o $(OUTPUT)
run: $(OUTPUT)
./$(OUTPUT)
@@ -51,6 +53,11 @@ clean:
@rm $(OUTPUT)
install: $(OUTPUT)
install -Dm755 "cpufetch" "/usr/bin/cpufetch"
install -Dm644 "LICENSE" "/usr/share/licenses/cpufetch-git/LICENSE"
install -Dm644 "cpufetch.8" "/usr/share/man/man8/cpufetch.8.gz"
install -Dm755 "cpufetch" "$(PREFIX)/bin/cpufetch"
install -Dm644 "LICENSE" "$(PREFIX)/share/licenses/cpufetch-git/LICENSE"
install -Dm644 "cpufetch.8" "$(PREFIX)/share/man/man8/cpufetch.8.gz"
uninstall:
rm -f "$(PREFIX)/bin/cpufetch"
rm -f "$(PREFIX)/share/licenses/cpufetch-git/LICENSE"
rm -f "$(PREFIX)/share/man/man8/cpufetch.8.gz"

120
README.md
View File

@@ -1,41 +1,56 @@
# cpufetch
<p align="center"><img width=50% src="./pictures/cpufetch.png"></p>
<div align="center">
![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/Dr-Noob/cpufetch?label=cpufetch)
![GitHub Repo stars](https://img.shields.io/github/stars/Dr-Noob/cpufetch?color=4CC61F)
![GitHub issues](https://img.shields.io/github/issues/Dr-Noob/cpufetch)
![Contributions welcome](https://img.shields.io/badge/contributions-welcome-orange.svg)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
<h4 align="center">Simple yet fancy CPU architecture fetching tool</h4>
&nbsp;
Simplistic yet fancy CPU architecture fetching tool
![cpu1](pictures/i9.png)
### Platforms
cpufetch currently supports x86_64 CPUs (both Intel and AMD) and ARM (experimental support)
</div>
| Platform | Intel | AMD | ARM | Notes |
|:---------:|:-------------------------:|:------------------------:|:------------------------:|:-----------------:|
| Linux | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | Prefered platform. <br> Experimental ARM support |
| Windows | :heavy_check_mark: | :heavy_check_mark: | :x: | Some information may be missing. <br> Colors will be used if supported |
| Android | :heavy_exclamation_mark: | :heavy_exclamation_mark: | :heavy_check_mark: | Experimental ARM support |
| macOS | :heavy_exclamation_mark: | :heavy_exclamation_mark: | :heavy_exclamation_mark: | Untested |
| Emoji | Meaning |
|:-----------------------:|:-------------:|
|:heavy_check_mark: | Supported |
|:x: | Not Supported |
|:heavy_exclamation_mark: | Unested |
# Table of contents
<!-- UPDATE with: doctoc --notitle README.md -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [1. Support](#1-support)
- [2. Installation](#2-installation)
- [2.1 Building from source](#21-building-from-source)
- [2.2 Linux](#22-linux)
- [2.2 Windows](#22-windows)
- [2.3 macOS](#23-macos)
- [2.4 Android](#24-android)
- [3. Examples](#3-examples)
- [3.1 x86_64 CPUs](#31-x86_64-cpus)
- [3.2 ARM CPUs](#32-arm-cpus)
- [4. Colors and style](#4-colors-and-style)
- [5. Implementation](#5-implementation)
- [6. Bugs or improvements](#6-bugs-or-improvements)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
### Usage and installation
#### Linux
There is a cpufetch package available in Arch Linux ([cpufetch-git](https://aur.archlinux.org/packages/cpufetch-git)).
# 1. Support
cpufetch supports x86, x86_64 (Intel and AMD) and ARM.
If you are in other distro, you can build `cpufetch` from source (see below)
| Platform | x86_64 | ARM | Notes |
|:---------:|:------------------------:|:-------------------:|:-----------------:|
| GNU/Linux | :heavy_check_mark: | :heavy_check_mark: | Best support |
| Windows | :heavy_check_mark: | :x: | Some information may be missing. <br> Colors will be used if supported |
| Android | :heavy_exclamation_mark: | :heavy_check_mark: | Some information may be missing. <br> Not tested under x86_64 |
| macOS | :heavy_check_mark: | :x: | Some information may be missing. <br> Apple M1 support may be added <br> in the future (see [#47](https://github.com/Dr-Noob/cpufetch/issues/47))|
#### Windows
In the [releases](https://github.com/Dr-Noob/cpufetch/releases) section you will find some cpufetch executables compiled for Windows. Just download and run it from Windows CMD.
# 2. Installation
## 2.1 Building from source
Just clone the repo and use `make` to compile it
#### Android
You need to build `cpufetch` from source (see below).
### Building from source
#### Linux and Windows
```
git clone https://github.com/Dr-Noob/cpufetch
cd cpufetch
@@ -43,43 +58,58 @@ make
./cpufetch
```
#### Android
I recommend using `termux` terminal emulator. Once you installed it, run the following commands:
The Makefile is designed to work on Linux, Windows and macOS.
```
pkg install -y git make clang
git clone https://github.com/Dr-Noob/cpufetch
cd cpufetch
make
./cpufetch
```
## 2.2 Linux
There is a cpufetch package available in Arch Linux ([cpufetch-git](https://aur.archlinux.org/packages/cpufetch-git)). If you are in another distribution, you can build `cpufetch` from source.
### Examples
## 2.2 Windows
In the [releases](https://github.com/Dr-Noob/cpufetch/releases) section you will find some cpufetch executables compiled for Windows. Just download and run it from Windows CMD. You can also build `cpufetch` from source.
## 2.3 macOS
You need to build `cpufetch` from source.
## 2.4 Android
1. Install `termux` app (terminal emulator)
2. Run `pkg install -y git make clang` inside termux.
3. Build from source normally:
- git clone https://github.com/Dr-Noob/cpufetch
- cd cpufetch
- make
- ./cpufetch
# 3. Examples
Here are more examples of how `cpufetch` looks on different CPUs.
## 3.1 x86_64 CPUs
![cpu2](pictures/epyc.png)
![cpu3](pictures/cascade_lake.png)
### Colors and style
## 3.2 ARM CPUs
![cpu4](pictures/exynos.png)
![cpu5](pictures/snapdragon.png)
# 4. Colors and style
By default, `cpufetch` will print the CPU art with the system colorscheme. However, you can always set a custom color scheme, either
specifying Intel or AMD, or specifying the colors in RGB format:
```
./cpufetch --color intel (default color for Intel)
./cpufetch --color amd (default color for AND)
./cpufetch --color amd (default color for AMD)
./cpufetch --color 239,90,45:210,200,200:100,200,45:0,200,200 (example)
```
In the case of setting the colors using RGB, 4 colors must be given in with the format: ``[R,G,B:R,G,B:R,G,B:R,G,B]``. These colors correspond to CPU art color (2 colors) and for the text colors (following 2). Thus, you can customize all the colors.
### Implementation
# 5. Implementation
`cpufetch` fetches all of the information using the `CPUID` x86 instruction. There are, however, some cases where the CPU does not support fetching some needed information. In this case, `cpufetch` will use `/sys/devices/system/cpu` in Linux as a fallback. If `cpufetch` is running on Windows and `CPUID` does not give all the data, `cpufetch` won't be able to show it. [I hope this can be fixed in the future](https://github.com/Dr-Noob/cpufetch/issues/30)
See [cpufetch programming documentation](https://github.com/Dr-Noob/cpufetch/blob/master/doc/README.md).
### Bugs or improvements
There are many open issues in github (see [issues](https://github.com/Dr-Noob/cpufetch/issues)). Feel free to open a new one report a issue or propose any improvement in `cpufetch`
# 6. Bugs or improvements
There are many open issues in github (see [issues](https://github.com/Dr-Noob/cpufetch/issues)). Feel free to open a new one report an issue or propose any improvement in `cpufetch`
### Testers
I would like to thank [Gonzalocl](https://github.com/Gonzalocl) and [OdnetninI](https://github.com/OdnetninI) for their help, running `cpufeth` in many different CPUs they have access to, which makes it easier to debug and check the correctness of `cpufetch`.
I would like to thank [Gonzalocl](https://github.com/Gonzalocl) and [OdnetninI](https://github.com/OdnetninI) for their help, running `cpufetch` in many different CPUs they have access to, which makes it easier to debug and check the correctness of `cpufetch`.

View File

@@ -1,57 +1,67 @@
.TH man 8 "1 Sep 2020" "0.7" "cpufetch man page"
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.2.
.TH CPUFETCH "1" "March 2021" "cpufetch v0.97 (x86_64 build)" "User Commands"
.SH NAME
cpufetch \- Simplistic yet fancy CPU architecture fetching tool
cpufetch \- manual page for cpufetch v0.97 (x86_64 build)
.SH SYNOPSIS
cpufetch [--version] [--help] [--levels] [--style fancy|retro|legacy] [--color intel|amd|'R,G,B:R,G,B:R,G,B:R,G,B']
.B cpufetch
[\fI\,OPTION\/\fR]...
.SH DESCRIPTION
cpufetch will print CPU information, for which will query CPUID instructions and udev directories on Linux as a fallback method. Some of this features are:
.IP \[bu] 2
Name
.IP \[bu]
Frequency
.IP \[bu]
Number of cores (Physical and Logical)
.IP \[bu]
Cache sizes
.IP \[bu]
Theoretical peak performance in floating point operations per second (FLOP/s)
Simple yet fancy CPU architecture fetching tool
.SH OPTIONS
.TP
\fB\-\-style\fR \f[I][intel|amd|R,G,B:R,G,B:R,G,B:R,G,B]\f[]
Set the color scheme. By default, cpufetch uses the system color scheme. This option lets the user use different colors to print the CPU art:
.IP \[bu]
\fB"intel"\fR: Use intel color scheme
.IP \[bu]
\fB"amd"\fR: Use amd color scheme
.IP \[bu]
\fBcustom\fR: If color do not match "intel" or "amd", a custom scheme can be specified: 4 colors must be given in RGB with the format: R,G,B:R,G,B:...
These colors correspond to CPU art color (2 colors) and for the text colors (following 2)
\fB\-c\fR, \fB\-\-color\fR
Set the color scheme (by default, cpufetch uses the system color scheme)
.TP
\fB\-\-style\fR \f[I]STYLE\f[]
Specify the style of ascii logo:
.IP \[bu]
\fB"fancy"\fR: Default style
.IP \[bu]
\fB"retro"\fR: Old cpufetch style
.IP \[bu]
\fB"legacy"\fR: Fallback style for terminals that does not support colors
\fB\-s\fR, \fB\-\-style\fR
Set the style of CPU art
.TP
\fB\-\-levels\fR
Prints CPUID levels and CPU name
\fB\-d\fR, \fB\-\-debug\fR
Prints CPU model and cpuid levels (debug purposes)
.TP
\fB\-\-verbose\fR
\fB\-v\fR, \fB\-\-verbose\fR
Prints extra information (if available) about how cpufetch tried fetching information
.TP
\fB\-\-help\fR
Prints help
\fB\-r\fR, \fB\-\-raw\fR
Prints raw cpuid data
.TP
\fB\-\-version\fR
Prints cpufetch version
.SH BUGS
Bugs should be posted on: https://github.com/Dr-Noob/cpufetch/issues
.SH NOTES
Peak performance information is NOT accurate. cpufetch computes peak performance using the max
frequency. However, to properly compute peak performance, you need to know the frequency of the
CPU running AVX code, which is not be fetched by cpufetch since it depends on each specific CPU.
.SH AUTHOR
\fB\-h\fR, \fB\-\-help\fR
Prints this help and exit
.TP
\fB\-V\fR, \fB\-\-version\fR
Prints cpufetch version and exit
.SH "COLORS:"
.TP
* "intel":
Use Intel default color scheme
.TP
* "amd":
Use AMD default color scheme
.TP
* "arm":
Use ARM default color scheme
.TP
* custom:
If color argument do not match "intel", "amd" or "arm", a custom scheme can be specified.
4 colors must be given in RGB with the format: R,G,B:R,G,B:...The first 2 colors are the CPU art color and the next 2 colors are the text colors
.SH "STYLES:"
.TP
* "fancy":
Default style
.TP
* "retro":
Old cpufetch style
.TP
* "legacy":
Fallback style for terminals that do not support colors
.SH "EXAMPLES:"
Run cpufetch with Intel color scheme:
\&./cpufetch \fB\-\-color\fR intel
Run cpufetch with a custom color scheme:
\&./cpufetch \fB\-\-color\fR 239,90,45:210,200,200:100,200,45:0,200,200
.SH "BUGS:"
Report bugs to https://github.com/Dr\-Noob/cpufetch/issues
.SH "NOTE:"
Peak performance information is NOT accurate. cpufetch computes peak performance using the max frequency. However, to properly compute peak performance, you need to know the frequency of the CPU running AVX code, which is not be fetched by cpufetch since it depends on each specific CPU. For peak performance measurement see: https://github.com/Dr\-Noob/peakperf
.SH "AUTHOR:"
Dr-Noob (https://github.com/Dr-Noob)

View File

@@ -1,2 +1,60 @@
### 2. Why ARM cpufetch works on Linux based systems?
TODO
CPUID instructions (present in x86 architectures) [[1](#references)] allow user level applications to obtain information about the CPU. In ARM architectures, there are many registers (MIDR [[2](#references)], CCSIDR [[3](#references)]) that provide information about the CPU too. However, those registers can only be read from privilege mode (PL1 or higher). Therefore, any user level tool which can actually read information about the running CPU must use the operating system to do so. cpufetch uses some Linux kernel features (see the remaining sections). Therefore, cpufetch in ARM processors is limited to Linux kernel based systems, such as __GNU/Linux__ and __Android__
### 3. How to get CPU microarchitecture?
__Involved code: [get_midr_from_cpuinfo (udev.c)](https://github.com/Dr-Noob/cpufetch/blob/master/src/arm/udev.c), [midr.c](https://github.com/Dr-Noob/cpufetch/blob/master/src/arm/midr.c)__
Microarchitecture information is acquired from the Main ID Register (MIDR) [[2](#references)]. Currently, cpufetch rebuilds this register using `/proc/cpuinfo` file. While this file does not contain the value of the register per se, it contains the following fields:
- `CPU implementer`,
- `CPU architecture`
- `CPU variant`
- `CPU part`
- `CPU revision`
The MIDR register can be built with this information. Another posible approach is to read MIDR directly from `/sys/devices/system/cpu/cpu*/regs/identification/midr_el1`
With the MIDR available, the approach is the same as the one used in x86_64 architectures. cpufetch has a file that acts like a database that tries to match the MIDR register with the specific CPU microarchitecture.
### 4. How to get CPU topology?
__Involved code: [get_ncores_from_cpuinfo (udev.c)](https://github.com/Dr-Noob/cpufetch/blob/master/src/arm/udev.c), [midr.c](https://github.com/Dr-Noob/cpufetch/blob/master/src/arm/midr.c)__
ARM provides a new interesting architecture feature: big.LITTLE architectures [[4](#references)]. An ARM CPU can be organized like a typical x86_64 CPU, where all cores share the same microarchitecture. However, ARM big.LITTLE architecture breaks this schema. In a big.LITTLE CPU, two or more CPUs microarchitecture live in the same chip.
This means that cpufetch can't just read which microarchitecture is the first core and assume that the rest of them shares the same microarchitecture. To get the CPU topology, cpufetch first reads the number of CPU cores. This can be obtained from `/sys/devices/system/cpu/present`
Then, for each core, cpufetch reads the MIDR and also the frequency (see section 5). Then, cpufetch assumes that two cores are different when their MIDR are different. This idea allows cpufetch to detect big.LITTLE architectures, and to know how many cores of each architecture the running CPU has.
### 5. How to get the frequency?
Frequency is read directly from `/sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_max_freq`
### 6. How to get system on chip model?
__Involved code: [soc.c](https://github.com/Dr-Noob/cpufetch/blob/master/src/arm/soc.c)__
System on chip (SoC) model is obtained using the same idea as the microarchitecture. First, SoC string is read. Then, the string has to be matched against a database-like function (__parse_soc_from_string__). The SoC string of the running CPU can be obtained using two different approaches:
- Using `/proc/cpuinfo`. This is the first thing to try. Linux kernel usually provides the string under the `Hardware` keyword. However, the Linux kernel may be unable to provide this information, or this string may not be found in the database-like function.
- Using Android properties: This only works on Android systems. Android properties can be read using `__system_property_get` function. cpufetch tries to read two properties:
- `ro.mediatek.platform`
- `ro.product.board`
If any string is returned, cpufetch tries to find a match in the database (using the same database as in the case of `/proc/cpuinfo`).
The expected strings have to be hardcoded. I found two ways of knowing which string should correspond to which SoC:
- Searching on the internet. Manufacturers __usually__ provide this information. For example, Qualcomm usually publishes the chip name along with other characteristics (under the `Part` or `Part number` keyword [[6](#references)]).
- "Hunting" for the strings. For example, finding smartphones with a given SoC and manually reading the `/proc/cpuinfo` or the `build.prop` file. A very good resource to do this is the SpecDevice webpage [[7](#references)]).
### 7. How to get cache size and topology?
ARM architecture supports reading the cache information via some registers (for example, the CCSIDR register [[3](#references)]). As mentioned earlier, user level applications are not able to read these registers directly. The remaining option is to ask the operating system for this information. However, at the moment, the __Linux kernel does not provide cache information__. Therefore, cpufetch does not print any cache information on ARM CPUs at the moment. There are, however, other approaches to be explored:
- Read the registers in kernel mode. This can be accomplished by running a kernel module [[4](#references)]. Unfortunately, running a custom kernel module is tricky, and sometimes impossible to do reasonably (for example, in Android devices). In any case, my decision is to run cpufetch on user level only.
- Hardcode the cache information for each SoC: Sometimes, manufacturers publish technical information about the chips, where cache topology and size are shown. This method is impractical, since this kind of information is very hard (or impossible) to find online, and the number of SoC is huge.
#### References
- [1] [cpufetch x86_64 documentation](https://github.com/Dr-Noob/cpufetch/blob/master/doc/DOCUMENTATION_X86.md)
- [2] [Main ID Register](https://developer.arm.com/documentation/ddi0433/c/system-control/register-descriptions/main-id-register)
- [3] [Cache size ID Register](https://developer.arm.com/documentation/100403/0200/register-descriptions/aarch32-system-registers/ccsidr--cache-size-id-register)
- [4] [How to get the size of the CPU cache in Linux](https://stackoverflow.com/a/63474811/9975463)
- [5] [ARM big.LITTLE](https://en.wikipedia.org/wiki/ARM_big.LITTLE)
- [6] [Snapdragon 855+ Mobile Platform](https://www.qualcomm.com/products/snapdragon-855-plus-mobile-platform)
- [7] [SpecDevice](http://specdevice.com/unmoderated.php)

View File

@@ -1,40 +1,38 @@
### 2. Why differences between Intel and AMD?
There are many different CPUID leaves (see [Useful documentation](#useful-documentation)). However, there are cases where a given leaf does the same thing in Intel and AMD, but the majority of them, they don't. For example, leaf 0x4 gives you the caches information, but in
AMD is a reserved (invalid) leaf! In the case of AMD, is more common to fetch information using extended levels than using the standard levels (the other way around with Intel).
There are many different CPUID leaves [[1](#references)]. In some cases, a given leaf does the same thing in Intel and AMD processors, but in the majority of them, they don't. For example, leaf 0x4 gives you the caches information, but in AMD is a reserved (invalid) leaf! In the case of AMD, is more common to fetch information using extended levels than using the standard levels (the other way around with Intel).
### 3. How to get the frequency?
__Involved code: get_frequency_info (cpuid.c)__
__Involved code: [get_frequency_info (cpuid.c)](https://github.com/Dr-Noob/cpufetch/blob/master/src/x86/cpuid.c)__
We use CPUID leaf 0x16.
CPUID leaf 0x16 is used.
If the CPU supports it, we are done. If not:
- Linux: cpufetch will try to obtain this information using `/sys` filesystem in Linux. I think that, in this case, Linux knows the frequency using MSRs, so at the user level you can't know it, but I will look at the kernel source code to check it.
- Windows: We are dead. cpufetch can't fetch CPU frequency. This means that peak performance can't be computed because the frequency is needed to compute it.
If the CPU does not support supports such level:
- Linux: cpufetch will try to obtain this information using `/sys` filesystem in Linux. I think that Linux knows the frequency using model specific registers (MSRs), which you can't read at the user level.
- Windows: cpufetch can't obtain CPU frequency. This means that peak performance can't be computed because the frequency is needed to compute it.
### 4. How to get cache sizes?
__Involved code: get_cache_info (cpuid.c)__
__Involved code: [get_cache_info (cpuid.c)](https://github.com/Dr-Noob/cpufetch/blob/master/src/x86/cpuid.c)__
- Intel: We use CPUID leaf 0x4
- AMD: We use extended CPUID leaf 0x1D
- Intel: CPUID leaf 0x4 is used (using __get_cache_info_general__). If the CPU does not support it, cpufetch can't get this information.
- AMD: Extended CPUID leaf 0x1D is used (using __get_cache_info_general__). If the CPU does not support this level, cpufetch uses a fallback method, which uses extended leaves 0x5 and 0x6. This fallback method uses __get_cache_info_amd_fallback__.
If CPU does not support it, we are dead: cpufetch can't get this information. If CPU does, we can fetch it, and 0x4 in Intel behaves the same way as 0x1D in AMD.
### 5. How to get CPU microarchitecture?
__Involved code: get_cpu_uarch (cpuid.c), get_uarch_from_cpuid (uarch.c)__
__Involved code: [get_cpu_uarch (cpuid.c)](https://github.com/Dr-Noob/cpufetch/blob/master/src/x86/cpuid.c), [get_uarch_from_cpuid (uarch.c)](https://github.com/Dr-Noob/cpufetch/blob/master/src/x86/uarch.c)__
We use CPUID leaf 0x1. From there, we get:
CPUID leaf 0x1 is used. From there, we get:
- Model
- Extended Model
- Family
- Extended Family
- Stepping
Knowing this information, we can distinguish any CPU microarchitecture. Inside __uarch.c__ there is a function that behaves as it had a database. It will search, for this information, what kind of microarchitecture the current CPU is. I got the data using and adapting the code form Todd Allen's cpuid program (Link 5 in [Useful documentation](#useful-documentation)). From the microarchitecture, we can obtain the manufacturing process (or technology, the size in nm of the transistors).
Knowing this information, we can distinguish any CPU microarchitecture. Inside __uarch.c__ there is a function that behaves like a database or a lookup table. The function of this database is to find a match between the information obtained from 0x1 and what kind of microarchitecture the current CPU is. I got the data using and adapting the code from Todd Allen's cpuid program [[5](#references)]. Knowing the microarchitecture, we can obtain the manufacturing process (or technology, the size in nm of the transistors).
### 6. How to get CPU topology?
__Involved code: cpuid.h, get_topology_info (cpuid.c), apic.c__
__Involved code: [cpuid.h](https://github.com/Dr-Noob/cpufetch/blob/master/src/x86/cpuid.h), [get_topology_info (cpuid.c)](https://github.com/Dr-Noob/cpufetch/blob/master/src/x86/cpuid.c), [apic.c](https://github.com/Dr-Noob/cpufetch/blob/master/src/x86/apic.c)__
cpufetch tries to support the most complex systems, so it supports multi socket CPUs and a detailed SMT (Intel HyperThreading) information. The CPU topology is stored like this:
cpufetch aims to support the most complex systems, so it supports multi-socket CPUs and detailed SMT (Intel HyperThreading) information. The CPU topology is stored in the following struct:
```
struct topology {
@@ -52,7 +50,7 @@ This structure needs a bit of explanation, to know what are we looking for:
- `logical_cores`: Number of logical cores. In a multi socket system, this field stores the number of logical cores for just one socket.
- `total_cores`: Total number of logical cores. In a multi socket system, this field stores the number of logical cores for the entire system.
- `sockets`: How many sockets the system has.
- `smt_supported`: Stores if SMT (or Intel HT) is supported in the CPU, storing the number of threads per core. So, if `smt_supported == 1`, it means that there is 1 thread per core, and SMT is not supported. If SMT is supported, then `smt_supported >= 1`. Note this field tells if CPU if supports it, but not if SMT is activated or not.
- `smt_supported`: Stores if SMT (or Intel HT) is supported in the CPU, storing the number of threads per core. So, if `smt_supported == 1`, it means that there is 1 thread per core, and SMT is not supported. If SMT is supported, then `smt_supported >= 1`. Note this field tells if the CPU if supports it, but not if SMT is activated or not.
- `smt_available`: The same idea as `smt_supported`, but it stores if SMT is available. If SMT is not supported, then `smt_available` is always `1`. The differentiation between supported and available lets cpufetch distinguish when a CPU has SMT capabilities, but are disabled (probably in the BIOS).
Let's give two CPU examples and the values that `struct topology` would have in these CPUs.
@@ -80,8 +78,8 @@ sockets = 1
Now that we know what data are we looking for, let's see how we get it:
- __Intel__: We use the methodology explained in the Intel webpage (Link 2 in [Useful documentation](#useful-documentation)). Here, Intel explains how to do it and also gives an example source code. I used it and modified it to fit cpufetch style. The core of this methodology is the usage of the APIC id, so the code is inside __apic.c__.
- __AMD__: Intel's algorithm using APIC does not work for AMD. To get the same information in AMD, I used the reference from OSdev(Link 3 in [Useful documentation](#useful-documentation)) and also ideas from lscpu(Link 4 in [Useful documentation](#useful-documentation)). This uses:
- __Intel__: The methodology used is explained in the Intel webpage [[2](#references)]. Intel explains how to do it and also gives an example source code. I used it and modified it to fit cpufetch style. The core of this methodology is the usage of the APIC id, so the code is inside __apic.c__.
- __AMD__: Intel's algorithm using APIC does not work for AMD. To get the same information in AMD, I used the reference from OSdev [[3](#references)] and also ideas from lscpu [[4](#references)]. This uses:
- CPUID extended leaf 0x8: Fill `logical_cores`
- CPUID extended leaf 0x1E: Fill `smt_supported`
- CPUID standard leaf 0x1 (APIC): Fill `smt_available`
@@ -97,25 +95,26 @@ Now that we know what data are we looking for, let's see how we get it:
```
### 7. How to get cache topology?
__Involved code: get_cache_topology_amd (cpuid.c), apic.c__
__Involved code: [get_cache_topology_amd (cpuid.c)](https://github.com/Dr-Noob/cpufetch/blob/master/src/x86/cpuid.c), [apic.c](https://github.com/Dr-Noob/cpufetch/blob/master/src/x86/apic.c)__
The topology is reduced to the idea of how many caches we have at a given level. It usually follows the rule of:
The topology of a cache gives us information about how many caches we have at a given level. It usually follows the rule of:
- L1: The same as the number of cores (one L1i and one L1d per core).
- L2: If L2 is the last level cache, one L2. If not, the same as the number of cores (one L2 per core).
- L3: One L3 cache per socket (shared among all cores).
These assumptions are generally (but not always) true. For example, for the AMD Zen generation, or the Intel Xeon Phi KNL. Thus, cpufetch does not assume the topology and tries to fetch it instead.
These assumptions are generally (but not always) true. For example, for the AMD Zen generation, or the Intel Xeon Phi KNL. Thus, cpufetch does not assume the topology but obtains it instead.
- __Intel__: We can use the methodology explained in the Intel webpage (Link 2 in [Useful documentation](#useful-documentation)) again (it also covers how to get cache topology using APIC id).
- __Intel__: The idea is similar to the mentioned in CPU topology [[2](#references)](it also covers how to get cache topology using APIC id).
- __AMD__: Again, we have to look another path for AMD. This time, the way to do it is easier and (I think) more solid and future proof. We just use extended CPUID leaf 0x1D. If the CPU does not support it, we can still guess the topology of the caches (as mentioned earlier). If it does, CPUID can give us how many cores shares a given level of cache. So, if we have the number of cores, we can guess how many caches are there for any given level (see __get_cache_topology_amd__).
- __AMD__: Again, we have to look for another path for AMD. This time, the way to do it is easier and (I think) more solid and future proof. The idea is to use extended CPUID leaf 0x1D. If the CPU does not support it, we can still guess the topology of the caches (as mentioned earlier). If it does, CPUID can give us how many cores shares a given level of cache. So, if we have the number of cores, we can guess how many caches are there for any given level (see __get_cache_topology_amd__).
#### Useful documentation
- Link 1: [sandpile CPUID webpage](https://www.sandpile.org/x86/cpuid.htm)
- Link 2: [CPU topology and cache topology: Intel](https://software.intel.com/content/www/us/en/develop/articles/intel-64-architecture-processor-topology-enumeration.html)
- Link 3: [CPU topology: AMD](https://wiki.osdev.org/Detecting_CPU_Topology_(80x86))
- Link 4: [lscpu](https://github.com/karelzak/util-linux/blob/master/sys-utils/lscpu.c)
- Link 5: [Todd Allen's cpuid](http://www.etallen.com/cpuid.html)
- Link 6: [AMD specific CPUID specification](https://www.amd.com/system/files/TechDocs/25481.pdf)
#### References
- [1] [sandpile CPUID webpage](https://www.sandpile.org/x86/cpuid.htm)
- [2] [CPU topology and cache topology: Intel](https://software.intel.com/content/www/us/en/develop/articles/intel-64-architecture-processor-topology-enumeration.html)
- [3] [CPU topology: AMD](https://wiki.osdev.org/Detecting_CPU_Topology_(80x86))
- [4] [lscpu](https://github.com/karelzak/util-linux/blob/master/sys-utils/lscpu.c)
- [5] [Todd Allen's cpuid](http://www.etallen.com/cpuid.html)
- [6] [AMD specific CPUID specification](https://www.amd.com/system/files/TechDocs/25481.pdf)
- [7] [Intel vs AMD CPU Architectural Differences: Chips and Chiplets](https://c.mi.com/thread-2585048-1-0.html)
In addition to all these resources, I found very interesting to search in the Linux kernel source code (for example, the directory `arch/x86/kernel/cpu/`), because sometetimes you can find ideas that cannot be found anywhere else!
In addition to all these resources, I found it very interesting to search in the Linux kernel source code (for example, the directory [`arch/x86/kernel/cpu/`](https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/cpu)), because sometimes you can find ideas that cannot be found anywhere else!

View File

@@ -1,12 +1,15 @@
# cpufetch programming documentation (v0.88)
This documentation explains how cpufetch works internally and all the design decisions I made. The intention of this documentation is to be useful for me in the future, for everyone interested in the project, and for anyone who is trying to obtain any specific information from the CPU. In this way, this can be used as a manual or a page which collects interesting material in this area.
# cpufetch programming documentation (v0.94)
This documentation explains how cpufetch works internally and all the design decisions I made. This document intends to be useful for me in the future, for everyone interested in the project, and for anyone who is trying to obtain any specific information from the CPU. In this way, this can be used as a manual or a page that collects interesting material in this area.
### 1. Basics
cpufetch works for __x86_64__ CPUs (Intel and AMD) and experimental support for __ARM__. Other kinds of x86_64 CPU are not supported (I don't think supporting other CPUs may pay off). Depending on the architecture, cpufetch choose certain files to be compiled. A summarized tree of the source code of cpufetch is shown below.
cpufetch works for __x86_64__ (Intel and AMD) and __ARM__ CPUs. However, cpufetch is expected to work better on x86_64, because the codebase is older and has been tested much more than the ARM version. Other kinds of x86_64 CPU are not supported (I don't think supporting other CPUs may pay off). Depending on the architecture, cpufetch choose certain files to be compiled. A summarized tree of the source code of cpufetch is shown below.
```
cpufetch/
├── DOCUMENTATION.md
├── doc
│   ├── DOCUMENTATION_ARM.md
│   ├── DOCUMENTATION_X86.md
│   └── README.md
├── Makefile
├── README.md
└── src/
@@ -22,17 +25,17 @@ cpufetch/
└── other files ...
```
Source code is divided in three directories:
Source code is divided into three directories:
- `common/`: Source code shared beteen x86 and ARM
- `common/`: Source code shared between x86 and ARM
- `arm/`: ARM dependant source code
- `x86/`: x86_64 dependant source code
##### 1.1 Basics (x86_64)
In x86, __cpufetch works using the CPUID instruction__. It is called directly using assembly (see `src/x86/cpuid_asm.c`). To understand how CPUID works you may have a look at the [Useful documentation](https://github.com/Dr-Noob/cpufetch/blob/master/DOCUMENTATION_x86.md#useful-documentation) section (more precisely, Link 1).
In x86, __cpufetch works using the CPUID instruction__. It is called directly using assembly (see `src/x86/cpuid_asm.c`). To understand how CPUID works, see [DOCUMENTATION_X86.md](https://github.com/Dr-Noob/cpufetch/blob/master/doc/DOCUMENTATION_X86.md).
At the beginning of execution, cpufetch needs to know the max standard CPUID level and max CPUID extended level supported in the running CPU. We also need to know if the x86 CPU is Intel or AMD because some fetching depends on it. This information will be stored in:
At the beginning of execution, cpufetch needs to know the max standard CPUID level and max CPUID extended level supported in the running CPU. We also need to know if the x86 CPU is Intel or AMD because sometimes, the way to obtain the information depends on the manufacturer. This information will be stored in:
```
struct cpuInfo {
@@ -44,13 +47,13 @@ struct cpuInfo {
};
```
To use any CPUID leaf, we always need to check that it is supported in the current CPU. cpufetch will always check if the leaf is supported in the current CPU, and will use a different workaround if the CPU is Intel or AMD, which leads us to the following section.
To use any CPUID leaf, cpufetch always needs to check that it is supported in the current CPU.
##### 1.2 Basics (ARM)
In ARM, __cpufetch works using the MIDR register and Linux filesystem__. MIDR (Main ID Register) is fetched from `/proc/cpuinfo`. It allows the detection of the microarchitecture of the cores. Furthermore, Linux filesystem `/sys/devices/system/cpu/` is used to fetch the number of cores, and other information. This is the main reason to explain __why `cpufetch` only works on Linux kernel based systems.__
In ARM, __cpufetch works using the MIDR register and Linux filesystem__. MIDR (Main ID Register) is read from `/proc/cpuinfo`. It allows the detection of the microarchitecture of the cores. Furthermore, Linux filesystem `/sys/devices/system/cpu/` is used to fetch the number of cores and other information. This is the main reason to explain __why `cpufetch` only works on Linux kernel based systems.__
##### 1.3 Documentation organization
The rest of the documentation is divided into x86 and ARM architectures, since each one need different implementations:
The rest of the documentation is divided into x86 and ARM architectures since each one needs different implementations:
- [DOCUMENTATION_X86.md](https://github.com/Dr-Noob/cpufetch/blob/master/doc/DOCUMENTATION_X86.md)
- [DOCUMENTATION_ARM.md](https://github.com/Dr-Noob/cpufetch/blob/master/doc/DOCUMENTATION_ARM.md)

BIN
pictures/cpufetch.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
pictures/exynos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
pictures/snapdragon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -52,7 +52,7 @@ struct frequency* get_frequency_info(uint32_t core) {
struct frequency* freq = malloc(sizeof(struct frequency));
freq->base = UNKNOWN_FREQ;
freq->max = get_max_freq_from_file(core);
freq->max = get_max_freq_from_file(core, false);
return freq;
}
@@ -186,7 +186,7 @@ struct cpuInfo* get_cpu_info() {
midr_array[i] = midr_array[0];
}
freq_array[i] = get_max_freq_from_file(i);
freq_array[i] = get_max_freq_from_file(i, false);
if(freq_array[i] == UNKNOWN_FREQ) {
printWarn("Unable to fetch max frequency for core %d. This is probably because the core is offline", i);
freq_array[i] = freq_array[0];
@@ -306,7 +306,7 @@ void print_debug(struct cpuInfo* cpu) {
for(int i=0; i < ncores; i++) {
printf("[Core %d] ", i);
long freq = get_max_freq_from_file(i);
long freq = get_max_freq_from_file(i, false);
uint32_t midr = get_midr_from_cpuinfo(i, &success);
if(!success) {
printWarn("Unable to fetch MIDR for core %d. This is probably because the core is offline", i);
@@ -317,7 +317,7 @@ void print_debug(struct cpuInfo* cpu) {
}
if(freq == UNKNOWN_FREQ) {
printWarn("Unable to fetch max frequency for core %d. This is probably because the core is offline", i);
printf("%ld MHz\n", get_max_freq_from_file(0));
printf("%ld MHz\n", get_max_freq_from_file(0, false));
}
else {
printf("%ld MHz\n", freq);

View File

@@ -436,12 +436,21 @@ bool match_special(char* soc_name, struct system_on_chip* soc) {
return true;
}
// Snapdragon 730 reported as "Qualcomm Technologies, Inc. SDMMAGPIE"
if((tmp = strstr(soc_name, "SDMMAGPIE")) != NULL) {
fill_soc(soc, "730", SOC_SNAPD_SM7150_AA, 8);
return true;
}
return false;
}
struct system_on_chip* parse_soc_from_string(struct system_on_chip* soc) {
char* raw_name = soc->raw_name;
if(match_special(raw_name, soc))
return soc;
if (match_qualcomm(raw_name, soc))
return soc;
@@ -454,10 +463,7 @@ struct system_on_chip* parse_soc_from_string(struct system_on_chip* soc) {
if(match_hisilicon(raw_name, soc))
return soc;
if(match_broadcom(raw_name, soc))
return soc;
match_special(raw_name, soc);
match_broadcom(raw_name, soc);
return soc;
}

View File

@@ -17,24 +17,36 @@ static const char *SYTLES_STR_LIST[] = {
[STYLE_INVALID] = NULL
};
enum {
ARG_CHAR_STYLE,
ARG_CHAR_COLOR,
ARG_CHAR_HELP,
ARG_CHAR_DEBUG,
ARG_CHAR_VERBOSE,
ARG_CHAR_VERSION
};
struct args_struct {
bool debug_flag;
bool help_flag;
bool raw_flag;
bool verbose_flag;
bool version_flag;
STYLE style;
struct colors* colors;
};
const char args_chr[] = {
/* [ARG_CHAR_STYLE] = */ 's',
/* [ARG_CHAR_COLOR] = */ 'c',
/* [ARG_CHAR_HELP] = */ 'h',
/* [ARG_CHAR_RAW] = */ 'r',
/* [ARG_CHAR_DEBUG] = */ 'd',
/* [ARG_CHAR_VERBOSE] = */ 'v',
/* [ARG_CHAR_VERSION] = */ 'V',
};
const char *args_str[] = {
/* [ARG_CHAR_STYLE] = */ "style",
/* [ARG_CHAR_COLOR] = */ "color",
/* [ARG_CHAR_HELP] = */ "help",
/* [ARG_CHAR_RAW] = */ "raw",
/* [ARG_CHAR_DEBUG] = */ "debug",
/* [ARG_CHAR_VERBOSE] = */ "verbose",
/* [ARG_CHAR_VERSION] = */ "version",
};
static struct args_struct args;
STYLE get_style() {
@@ -57,10 +69,23 @@ bool show_debug() {
return args.debug_flag;
}
bool show_raw() {
return args.raw_flag;
}
bool verbose_enabled() {
return args.verbose_flag;
}
int max_arg_str_length() {
int max_len = -1;
int len = sizeof(args_str) / sizeof(args_str[0]);
for(int i=0; i < len; i++) {
max_len = max(max_len, (int) strlen(args_str[i]));
}
return max_len;
}
STYLE parse_style(char* style) {
uint8_t i = 0;
uint8_t styles_count = sizeof(SYTLES_STR_LIST) / sizeof(SYTLES_STR_LIST[0]);
@@ -158,32 +183,56 @@ bool parse_color(char* optarg_str, struct colors** cs) {
return true;
}
char* build_short_options() {
const char *c = args_chr;
int len = sizeof(args_chr) / sizeof(args_chr[0]);
char* str = (char *) malloc(sizeof(char) * (len*2 + 1));
memset(str, 0, sizeof(char) * (len*2 + 1));
#ifdef ARCH_X86
sprintf(str, "%c:%c:%c%c%c%c%c",
c[ARG_STYLE], c[ARG_COLOR], c[ARG_HELP], c[ARG_RAW],
c[ARG_DEBUG], c[ARG_VERBOSE], c[ARG_VERSION]);
#else
sprintf(str, "%c:%c:%c%c%c%c",
c[ARG_STYLE], c[ARG_COLOR], c[ARG_HELP],
c[ARG_DEBUG], c[ARG_VERBOSE], c[ARG_VERSION]);
#endif
return str;
}
bool parse_args(int argc, char* argv[]) {
int c;
int opt;
int option_index = 0;
opterr = 0;
bool color_flag = false;
args.debug_flag = false;
args.raw_flag = false;
args.verbose_flag = false;
args.help_flag = false;
args.style = STYLE_EMPTY;
args.colors = NULL;
static struct option long_options[] = {
{"style", required_argument, 0, ARG_CHAR_STYLE },
{"color", required_argument, 0, ARG_CHAR_COLOR },
{"help", no_argument, 0, ARG_CHAR_HELP },
{"debug", no_argument, 0, ARG_CHAR_DEBUG },
{"verbose", no_argument, 0, ARG_CHAR_VERBOSE },
{"version", no_argument, 0, ARG_CHAR_VERSION },
const struct option long_options[] = {
{args_str[ARG_STYLE], required_argument, 0, args_chr[ARG_STYLE] },
{args_str[ARG_COLOR], required_argument, 0, args_chr[ARG_COLOR] },
{args_str[ARG_HELP], no_argument, 0, args_chr[ARG_HELP] },
#ifdef ARCH_X86
{args_str[ARG_RAW], no_argument, 0, args_chr[ARG_RAW] },
#endif
{args_str[ARG_DEBUG], no_argument, 0, args_chr[ARG_DEBUG] },
{args_str[ARG_VERBOSE], no_argument, 0, args_chr[ARG_VERBOSE] },
{args_str[ARG_VERSION], no_argument, 0, args_chr[ARG_VERSION] },
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, "", long_options, &option_index);
char* short_options = build_short_options();
opt = getopt_long(argc, argv, short_options, long_options, &option_index);
while (c != -1) {
if(c == ARG_CHAR_COLOR) {
while (!args.help_flag && !args.debug_flag && !args.version_flag && opt != -1) {
if(opt == args_chr[ARG_COLOR]) {
if(color_flag) {
printErr("Color option specified more than once");
return false;
@@ -194,7 +243,7 @@ bool parse_args(int argc, char* argv[]) {
return false;
}
}
else if(c == ARG_CHAR_STYLE) {
else if(opt == args_chr[ARG_STYLE]) {
if(args.style != STYLE_EMPTY) {
printErr("Style option specified more than once");
return false;
@@ -204,48 +253,33 @@ bool parse_args(int argc, char* argv[]) {
printErr("Invalid style '%s'",optarg);
return false;
}
}
else if(c == ARG_CHAR_HELP) {
if(args.help_flag) {
printErr("Help option specified more than once");
return false;
}
args.help_flag = true;
}
else if(c == ARG_CHAR_VERBOSE) {
if(args.verbose_flag) {
printErr("Verbose option specified more than once");
return false;
}
args.verbose_flag = true;
}
else if(c == ARG_CHAR_DEBUG) {
if(args.debug_flag) {
printErr("Debug option specified more than once");
return false;
}
args.debug_flag = true;
}
else if (c == ARG_CHAR_VERSION) {
if(args.version_flag) {
printErr("Version option specified more than once");
return false;
}
args.version_flag = true;
}
else if(c == '?') {
printWarn("Invalid options");
args.help_flag = true;
break;
}
else
printBug("Bug at line number %d in file %s", __LINE__, __FILE__);
option_index = 0;
c = getopt_long(argc, argv,"",long_options, &option_index);
else if(opt == args_chr[ARG_HELP]) {
args.help_flag = true;
}
else if(opt == args_chr[ARG_RAW]) {
args.raw_flag = true;
}
else if(opt == args_chr[ARG_VERBOSE]) {
args.verbose_flag = true;
}
else if(opt == args_chr[ARG_DEBUG]) {
args.debug_flag = true;
}
else if(opt == args_chr[ARG_VERSION]) {
args.version_flag = true;
}
else {
printWarn("Invalid options");
args.help_flag = true;
}
if (optind < argc) {
option_index = 0;
opt = getopt_long(argc, argv, short_options, long_options, &option_index);
}
if(optind < argc) {
printWarn("Invalid options");
args.help_flag = true;
}

View File

@@ -26,10 +26,25 @@ enum {
STYLE_INVALID
};
enum {
ARG_STYLE,
ARG_COLOR,
ARG_HELP,
ARG_RAW,
ARG_DEBUG,
ARG_VERBOSE,
ARG_VERSION
};
extern const char args_chr[];
extern const char *args_str[];
#include "printer.h"
int max_arg_str_length();
bool parse_args(int argc, char* argv[]);
bool show_help();
bool show_raw();
bool show_debug();
bool show_version();
bool verbose_enabled();

View File

@@ -12,10 +12,10 @@
\
\
@@@@ @@@ @@@ @@@@@@@@ ############ \
@@@@@@ @@@@@ @@@@ @@@ @@@@ ########## \
@@@ @@@ @@@@@@@@@@@@@ @@@ @@ # #### \
@@@ @@@ @@@ @@@ @@@ @@@ @@@ ### #### \
@@@@@@@@@@@@ @@@ @@@ @@@ @@@ #### ## ### \
@@@@@@ @@@@@ @@@@@ @@@ @@@ ########## \
@@@ @@@ @@@@@@@@@@@@@ @@@ @@ # ##### \
@@@ @@@ @@@ @@@ @@@ @@@ @@ ### ##### \
@@@@@@@@@@@@ @@@ @@@ @@@ @@@ ######### ### \
@@@ @@@ @@@ @@@ @@@@@@@@@ ######## ## \
\
\

View File

@@ -147,7 +147,7 @@ char* get_str_l3(struct cache* cach) {
char* get_str_freq(struct frequency* freq) {
//Max 3 digits and 3 for '(M/G)Hz' plus 1 for '\0'
uint32_t size = (4+3+1);
uint32_t size = (5+1+3+1);
assert(strlen(STRING_UNKNOWN)+1 <= size);
char* string = malloc(sizeof(char)*size);
memset(string, 0, sizeof(char)*size);
@@ -155,9 +155,9 @@ char* get_str_freq(struct frequency* freq) {
if(freq->max == UNKNOWN_FREQ || freq->max < 0)
snprintf(string,strlen(STRING_UNKNOWN)+1,STRING_UNKNOWN);
else if(freq->max >= 1000)
snprintf(string,size,"%.2f"STRING_GIGAHERZ,(float)(freq->max)/1000);
snprintf(string,size,"%.3f "STRING_GIGAHERZ,(float)(freq->max)/1000);
else
snprintf(string,size,"%d"STRING_MEGAHERZ,freq->max);
snprintf(string,size,"%d "STRING_MEGAHERZ,freq->max);
return string;
}

View File

@@ -119,6 +119,8 @@ struct cpuInfo {
uint32_t maxLevels;
// Max cpuids extended levels
uint32_t maxExtendedLevels;
// Topology Extensions (AMD only)
bool topology_extensions;
#elif ARCH_ARM
// Main ID register
uint32_t midr;

View File

@@ -64,3 +64,7 @@ void set_log_level(bool verbose) {
if(verbose) LOG_LEVEL = LOG_LEVEL_VERBOSE;
else LOG_LEVEL = LOG_LEVEL_NORMAL;
}
int max(int a, int b) {
return a > b ? a : b;
}

View File

@@ -7,5 +7,6 @@ void set_log_level(bool verbose);
void printWarn(const char *fmt, ...);
void printErr(const char *fmt, ...);
void printBug(const char *fmt, ...);
int max(int a, int b);
#endif

View File

@@ -1,5 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "args.h"
#include "printer.h"
@@ -13,41 +14,58 @@
#include "../arm/midr.h"
#endif
static const char* VERSION = "0.94";
static const char* VERSION = "0.97";
void print_help(char *argv[]) {
printf("Usage: %s [--version] [--help] [--debug] [--style \"fancy\"|\"retro\"|\"legacy\"] [--color \"intel\"|\"amd\"|'R,G,B:R,G,B:R,G,B:R,G,B']\n\n", argv[0]);
const char **t = args_str;
const char *c = args_chr;
int max_len = max_arg_str_length();
printf("Options: \n\
--color Set the color scheme. By default, cpufetch uses the system color scheme. This option \n\
lets the user use different colors to print the CPU art: \n\
* \"intel\": Use Intel default color scheme \n\
* \"amd\": Use AMD default color scheme \n\
* \"arm\": Use ARM default color scheme \n\
* custom: If color argument do not match \"Intel\", \"AMD\" or \"ARM\", a custom scheme can be specified: \n\
4 colors must be given in RGB with the format: R,G,B:R,G,B:... \n\
These colors correspond to CPU art color (2 colors) and for the text colors (following 2) \n\
For example: --color 239,90,45:210,200,200:100,200,45:0,200,200 \n\n\
--style Set the style of CPU art: \n\
* \"fancy\": Default style \n\
* \"retro\": Old cpufetch style \n\
* \"legacy\": Fallback style for terminals that does not support colors \n\n");
printf("Usage: %s [OPTION]...\n", argv[0]);
printf("Simple yet fancy CPU architecture fetching tool\n\n");
printf("Options: \n");
printf(" -%c, --%s %*s Set the color scheme (by default, cpufetch uses the system color scheme)\n", c[ARG_COLOR], t[ARG_COLOR], (int) (max_len-strlen(t[ARG_COLOR])), "");
printf(" -%c, --%s %*s Set the style of CPU art\n", c[ARG_STYLE], t[ARG_STYLE], (int) (max_len-strlen(t[ARG_STYLE])), "");
#ifdef ARCH_X86
printf(" --debug Prints CPU model and cpuid levels (debug purposes)\n\n");
printf(" -%c, --%s %*s Prints CPU model and cpuid levels (debug purposes)\n", c[ARG_DEBUG], t[ARG_DEBUG], (int) (max_len-strlen(t[ARG_DEBUG])), "");
#elif ARCH_ARM
printf(" --debug Prints main ID register values for all cores (debug purposes)\n\n");
printf(" -%c, --%s %*s Prints main ID register values for all cores (debug purposes)\n", c[ARG_DEBUG], t[ARG_DEBUG], (int) (max_len-strlen(t[ARG_DEBUG])), "");
#endif
printf(" -%c, --%s %*s Prints extra information (if available) about how cpufetch tried fetching information\n", c[ARG_VERBOSE], t[ARG_VERBOSE], (int) (max_len-strlen(t[ARG_VERBOSE])), "");
#ifdef ARCH_X86
printf(" -%c, --%s %*s Prints raw cpuid data\n", c[ARG_RAW], t[ARG_RAW], (int) (max_len-strlen(t[ARG_RAW])), "");
#endif
printf(" -%c, --%s %*s Prints this help and exit\n", c[ARG_HELP], t[ARG_HELP], (int) (max_len-strlen(t[ARG_HELP])), "");
printf(" -%c, --%s %*s Prints cpufetch version and exit\n", c[ARG_VERSION], t[ARG_VERSION], (int) (max_len-strlen(t[ARG_VERSION])), "");
printf(" --verbose Prints extra information (if available) about how cpufetch tried fetching information\n\n\
--help Prints this help and exit\n\n\
--version Prints cpufetch version and exit\n\n\
\n\
NOTES: \n\
- Bugs or improvements should be submitted to: github.com/Dr-Noob/cpufetch/issues \n\
- Peak performance information is NOT accurate. cpufetch computes peak performance using the max \n\
frequency. However, to properly compute peak performance, you need to know the frequency of the \n\
CPU running AVX code, which is not be fetched by cpufetch since it depends on each specific CPU. \n");
printf("\nCOLORS: \n");
printf(" * \"intel\": Use Intel default color scheme \n");
printf(" * \"amd\": Use AMD default color scheme \n");
printf(" * \"arm\": Use ARM default color scheme \n");
printf(" * custom: If color argument do not match \"intel\", \"amd\" or \"arm\", a custom scheme can be specified.\n");
printf(" 4 colors must be given in RGB with the format: R,G,B:R,G,B:...\n");
printf(" The first 2 colors are the CPU art color and the next 2 colors are the text colors\n");
printf("\nSTYLES: \n");
printf(" * \"fancy\": Default style\n");
printf(" * \"retro\": Old cpufetch style\n");
printf(" * \"legacy\": Fallback style for terminals that do not support colors\n");
printf("\nEXAMPLES: \n");
printf(" Run cpufetch with Intel color scheme:\n");
printf(" ./cpufetch --color intel\n");
printf(" Run cpufetch with a custom color scheme:\n");
printf(" ./cpufetch --color 239,90,45:210,200,200:100,200,45:0,200,200\n");
printf("\nBUGS: \n");
printf(" Report bugs to https://github.com/Dr-Noob/cpufetch/issues\n");
printf("\nNOTE: \n");
printf(" Peak performance information is NOT accurate. cpufetch computes peak performance using the max\n");
printf(" frequency. However, to properly compute peak performance, you need to know the frequency of the\n");
printf(" CPU running AVX code, which is not be fetched by cpufetch since it depends on each specific CPU.\n");
printf(" For peak performance measurement see: https://github.com/Dr-Noob/peakperf\n");
}
void print_version() {
@@ -59,7 +77,6 @@ int main(int argc, char* argv[]) {
return EXIT_FAILURE;
if(show_help()) {
print_version();
print_help(argv);
return EXIT_SUCCESS;
}
@@ -81,6 +98,17 @@ int main(int argc, char* argv[]) {
return EXIT_SUCCESS;
}
if(show_raw()) {
#ifdef ARCH_X86
print_version();
print_raw(cpu);
return EXIT_SUCCESS;
#else
printErr("raw option is valid only in x86_64");
return EXIT_FAILURE;
#endif
}
if(print_cpufetch(cpu, get_style(), get_colors()))
return EXIT_SUCCESS;
else

View File

@@ -28,16 +28,15 @@ char* read_file(char* path, int* len) {
return buf;
}
long get_freq_from_file(char* path) {
long get_freq_from_file(char* path, bool hv_present) {
int filelen;
char* buf;
if((buf = read_file(path, &filelen)) == NULL) {
#ifdef ARCH_X86
perror("open");
printBug("Could not open '%s'", path);
#elif ARCH_ARM
if(hv_present)
printWarn("Could not open '%s' (HV is present)", path);
else
printWarn("Could not open '%s'", path);
#endif
return UNKNOWN_FREQ;
}
@@ -64,14 +63,14 @@ long get_freq_from_file(char* path) {
return ret/1000;
}
long get_max_freq_from_file(uint32_t core) {
long get_max_freq_from_file(uint32_t core, bool hv_present) {
char path[_PATH_FREQUENCY_MAX_LEN];
sprintf(path, "%s%s/cpu%d%s%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, core, _PATH_FREQUENCY, _PATH_FREQUENCY_MAX);
return get_freq_from_file(path);
return get_freq_from_file(path, hv_present);
}
long get_min_freq_from_file(uint32_t core) {
long get_min_freq_from_file(uint32_t core, bool hv_present) {
char path[_PATH_FREQUENCY_MAX_LEN];
sprintf(path, "%s%s/cpu%d%s%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, core, _PATH_FREQUENCY, _PATH_FREQUENCY_MIN);
return get_freq_from_file(path);
return get_freq_from_file(path, hv_present);
}

View File

@@ -10,6 +10,8 @@
#include <fcntl.h>
#include <errno.h>
#include "cpu.h"
#define _PATH_SYS_SYSTEM "/sys/devices/system"
#define _PATH_SYS_CPU "/cpu"
#define _PATH_FREQUENCY "/cpufreq"
@@ -20,7 +22,7 @@
#define DEFAULT_FILE_SIZE 4096
char* read_file(char* path, int* len);
long get_max_freq_from_file(uint32_t core);
long get_min_freq_from_file(uint32_t core);
long get_max_freq_from_file(uint32_t core, bool hv_present);
long get_min_freq_from_file(uint32_t core, bool hv_present);
#endif

View File

@@ -1,14 +1,18 @@
#ifdef _WIN32
#include <windows.h>
#else
#define _GNU_SOURCE
#include <sched.h>
#define NOMINMAX
#include <windows.h>
#elif defined __linux__
#define _GNU_SOURCE
#include <sched.h>
#elif defined __APPLE__
#define UNUSED(x) (void)(x)
#endif
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include "apic.h"
#include "cpuid_asm.h"
@@ -66,6 +70,7 @@ uint32_t get_apic_id(bool x2apic_id) {
}
}
#ifndef __APPLE__
bool bind_to_cpu(int cpu_id) {
#ifdef _WIN32
HANDLE process = GetCurrentProcess();
@@ -82,6 +87,7 @@ bool bind_to_cpu(int cpu_id) {
return true;
#endif
}
#endif
bool fill_topo_masks_apic(struct topology* topo) {
uint32_t eax = 0x00000001;
@@ -254,14 +260,81 @@ void get_cache_topology_from_apic(struct topology* topo) {
}
}
bool apic_array_full(uint32_t* apic_ids, int n) {
for(int i=0; i < n; i++) {
if(apic_ids[i] == (uint32_t) -1) return false;
}
return true;
}
void add_apic_to_array(uint32_t apic, uint32_t* apic_ids, int n) {
int i=0;
int last=0;
bool found = false;
while(!found && i < n) {
if(apic_ids[i] == apic) found = true;
if(apic_ids[i] != (uint32_t) -1) last = i+1;
i++;
}
if(!found) {
apic_ids[last] = apic;
//printf("Added %d\n", apic);
}
}
bool fill_apic_ids(uint32_t* apic_ids, int n, bool x2apic_id) {
#ifdef __APPLE__
// macOS extremely dirty approach...
printf("cpufetch is computing APIC IDs, please wait...\n");
bool end = false;
uint32_t apic;
for(int i=0; i < n; i++) apic_ids[i] = (uint32_t) -1;
while(!end) {
apic = get_apic_id(x2apic_id);
add_apic_to_array(apic, apic_ids, n);
end = apic_array_full(apic_ids, n);
usleep(1000);
}
#else
for(int i=0; i < n; i++) {
if(!bind_to_cpu(i)) {
printErr("Failed binding to CPU %d", i);
return false;
}
apic_ids[i] = get_apic_id(x2apic_id);
}
#endif
return true;
}
bool get_topology_from_apic(struct cpuInfo* cpu, struct topology* topo) {
uint32_t apic_id;
uint32_t* apic_ids = malloc(sizeof(uint32_t) * topo->total_cores);
uint32_t* apic_pkg = malloc(sizeof(uint32_t) * topo->total_cores);
uint32_t* apic_core = malloc(sizeof(uint32_t) * topo->total_cores);
uint32_t* apic_smt = malloc(sizeof(uint32_t) * topo->total_cores);
uint32_t** cache_smt_id_apic = malloc(sizeof(uint32_t*) * topo->total_cores);
uint32_t** cache_id_apic = malloc(sizeof(uint32_t*) * topo->total_cores);
bool x2apic_id = cpu->maxLevels >= 0x0000000B;
bool x2apic_id;
if(cpu->maxLevels >= 0x0000000B) {
uint32_t eax = 0x0000000B;
uint32_t ebx = 0;
uint32_t ecx = 0;
uint32_t edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);
if(ebx == 0) x2apic_id = false;
else x2apic_id = true;
}
else {
x2apic_id = false;
}
for(int i=0; i < topo->total_cores; i++) {
cache_smt_id_apic[i] = malloc(sizeof(uint32_t) * (topo->cach->max_cache_level));
@@ -281,12 +354,11 @@ bool get_topology_from_apic(struct cpuInfo* cpu, struct topology* topo) {
get_cache_topology_from_apic(topo);
for(int i=0; i < topo->total_cores; i++) {
if(!bind_to_cpu(i)) {
printErr("Failed binding to CPU %d", i);
if(!fill_apic_ids(apic_ids, topo->total_cores, x2apic_id))
return false;
}
apic_id = get_apic_id(x2apic_id);
for(int i=0; i < topo->total_cores; i++) {
apic_id = apic_ids[i];
apic_pkg[i] = (apic_id & topo->apic->pkg_mask) >> topo->apic->pkg_mask_shift;
apic_core[i] = (apic_id & topo->apic->core_mask) >> topo->apic->smt_mask_width;
@@ -337,6 +409,10 @@ bool get_topology_from_apic(struct cpuInfo* cpu, struct topology* topo) {
}
uint32_t is_smt_enabled_amd(struct topology* topo) {
#ifdef __APPLE__
UNUSED(topo);
return 1;
#else
uint32_t id;
for(int i = 0; i < topo->total_cores; i++) {
@@ -349,4 +425,5 @@ uint32_t is_smt_enabled_amd(struct topology* topo) {
}
return 1;
#endif
}

View File

@@ -17,4 +17,8 @@ struct apic {
bool get_topology_from_apic(struct cpuInfo* cpu, struct topology* topo);
uint32_t is_smt_enabled_amd(struct topology* topo);
#ifndef __APPLE__
bool bind_to_cpu(int cpu_id);
#endif
#endif

View File

@@ -1,4 +1,5 @@
#ifdef _WIN32
#define NOMINMAX
#include <windows.h>
#else
#include "../common/udev.h"
@@ -27,7 +28,6 @@ static const char *hv_vendors_string[] = {
[HV_VENDOR_VMWARE] = "VMwareVMware",
[HV_VENDOR_XEN] = "XenVMMXenVMM",
[HV_VENDOR_PARALLELS] = "lrpepyh vr",
[HV_VENDOR_INVALID] = NULL
};
static char *hv_vendors_name[] = {
@@ -310,16 +310,26 @@ struct cpuInfo* get_cpu_info() {
printWarn("Can't read cpu name from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", 0x80000004, cpu->maxExtendedLevels);
}
cpu->topology_extensions = false;
if(cpu->cpu_vendor == CPU_VENDOR_AMD && cpu->maxExtendedLevels >= 0x80000001) {
eax = 0x80000001;
cpuid(&eax, &ebx, &ecx, &edx);
cpu->topology_extensions = (ecx >> 22) & 1;
}
cpu->arch = get_cpu_uarch(cpu);
cpu->freq = get_frequency_info(cpu);
cpu->cach = get_cache_info(cpu);
cpu->topo = get_topology_info(cpu, cpu->cach);
if(cpu->cach == NULL || cpu->topo == NULL) {
return NULL;
}
return cpu;
}
bool get_cache_topology_amd(struct cpuInfo* cpu, struct topology* topo) {
if(cpu->maxExtendedLevels >= 0x8000001D) {
if(cpu->maxExtendedLevels >= 0x8000001D && cpu->topology_extensions) {
uint32_t i, eax, ebx, ecx, edx, num_sharing_cache, cache_type, cache_level;
i = 0;
@@ -362,13 +372,12 @@ bool get_cache_topology_amd(struct cpuInfo* cpu, struct topology* topo) {
topo->cach->L3->num_caches = topo->logical_cores / num_sharing_cache;
}
else {
printBug("Found unified cache at level %d (expected == 2 or 3)", cache_level);
return false;
printWarn("Found unknown unified cache at level %d", cache_level);
}
break;
default: // Unknown Type Cache
printBug("Unknown Type Cache found at ID %d", i);
default: // Unknown cache type
printBug("Unknown cache type %d with level %d found at i=%d", cache_type, cache_level, i);
return false;
}
}
@@ -377,7 +386,7 @@ bool get_cache_topology_amd(struct cpuInfo* cpu, struct topology* topo) {
} while (cache_type > 0);
}
else {
printWarn("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X). Guessing cache sizes", 0x8000001D, cpu->maxExtendedLevels);
printWarn("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X and topology_extensions=%s). Guessing cache topology", 0x8000001D, cpu->maxExtendedLevels, cpu->topology_extensions ? "true" : "false");
topo->cach->L1i->num_caches = topo->physical_cores;
topo->cach->L1d->num_caches = topo->physical_cores;
@@ -425,7 +434,7 @@ struct topology* get_topology_info(struct cpuInfo* cpu, struct cache* cach) {
get_topology_from_apic(cpu, topo);
}
else {
printErr("Can't read topology information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000001, cpu->maxLevels);
printWarn("Can't read topology information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000001, cpu->maxLevels);
topo->physical_cores = 1;
topo->logical_cores = 1;
topo->smt_available = 1;
@@ -438,18 +447,18 @@ struct topology* get_topology_info(struct cpuInfo* cpu, struct cache* cach) {
cpuid(&eax, &ebx, &ecx, &edx);
topo->logical_cores = (ecx & 0xFF) + 1;
if (cpu->maxExtendedLevels >= 0x8000001E) {
if (cpu->maxExtendedLevels >= 0x8000001E && cpu->topology_extensions) {
eax = 0x8000001E;
cpuid(&eax, &ebx, &ecx, &edx);
topo->smt_supported = ((ebx >> 8) & 0x03) + 1;
}
else {
printWarn("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", 0x8000001E, cpu->maxExtendedLevels);
printWarn("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X and topology_extensions=%s)", 0x8000001E, cpu->maxExtendedLevels, cpu->topology_extensions ? "true" : "false");
topo->smt_supported = 1;
}
}
else {
printErr("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", 0x80000008, cpu->maxExtendedLevels);
printWarn("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", 0x80000008, cpu->maxExtendedLevels);
topo->physical_cores = 1;
topo->logical_cores = 1;
topo->smt_supported = 1;
@@ -571,13 +580,12 @@ struct cache* get_cache_info_general(struct cache* cach, uint32_t level) {
cach->L3->exists = true;
}
else {
printBug("Found unified cache at level %d (expected == 2 or 3)", cache_level);
return NULL;
printWarn("Found unknown unified cache at level %d (size is %d bytes)", cache_level, cache_total_size);
}
break;
default: // Unknown Type Cache
printBug("Unknown Type Cache found at ID %d", i);
default: // Unknown cache type
printBug("Unknown cache type %d with level %d found at i=%d", cache_type, cache_level, i);
return NULL;
}
}
@@ -594,13 +602,13 @@ struct cache* get_cache_info(struct cpuInfo* cpu) {
uint32_t level;
// We use standart 0x00000004 for Intel
// We use standard 0x00000004 for Intel
// We use extended 0x8000001D for AMD
// or 0x80000005/6 for old AMD
if(cpu->cpu_vendor == CPU_VENDOR_INTEL) {
level = 0x00000004;
if(cpu->maxLevels < level) {
printErr("Can't read cache information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", level, cpu->maxLevels);
printWarn("Can't read cache information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", level, cpu->maxLevels);
return NULL;
}
else {
@@ -609,11 +617,11 @@ struct cache* get_cache_info(struct cpuInfo* cpu) {
}
else {
level = 0x8000001D;
if(cpu->maxExtendedLevels < level) {
printWarn("Can't read cache information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", level, cpu->maxExtendedLevels);
if(cpu->maxExtendedLevels < level || !cpu->topology_extensions) {
printWarn("Can't read cache information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X and topology_extensions=%s)", level, cpu->maxExtendedLevels, cpu->topology_extensions ? "true" : "false");
level = 0x80000006;
if(cpu->maxExtendedLevels < level) {
printErr("Can't read cache information from cpuid using old method (needed extended level is 0x%.8X, max is 0x%.8X)", level, cpu->maxExtendedLevels);
printWarn("Can't read cache information from cpuid using old method (needed extended level is 0x%.8X, max is 0x%.8X)", level, cpu->maxExtendedLevels);
return NULL;
}
printWarn("Fallback to old method using 0x%.8X and 0x%.8X", level-1, level);
@@ -624,35 +632,6 @@ struct cache* get_cache_info(struct cpuInfo* cpu) {
}
}
// Sanity checks. If we read values greater than this, they can't be valid ones
// The values were chosen by me
if(cach->L1i->size > 64 * 1024) {
printBug("Invalid L1i size: %dKB", cach->L1i->size/1024);
return NULL;
}
if(cach->L1d->size > 64 * 1024) {
printBug("Invalid L1d size: %dKB", cach->L1d->size/1024);
return NULL;
}
if(cach->L2->exists) {
if(cach->L3->exists && cach->L2->size > 2 * 1048576) {
printBug("Invalid L2 size: %dMB", cach->L2->size/(1048576));
return NULL;
}
else if(cach->L2->size > 100 * 1048576) {
printBug("Invalid L2 size: %dMB", cach->L2->size/(1048576));
return NULL;
}
}
if(cach->L3->exists && cach->L3->size > 100 * 1048576) {
printBug("Invalid L3 size: %dMB", cach->L3->size/(1048576));
return NULL;
}
if(!cach->L2->exists) {
printBug("Could not find L2 cache");
return NULL;
}
return cach;
}
@@ -660,14 +639,24 @@ struct frequency* get_frequency_info(struct cpuInfo* cpu) {
struct frequency* freq = malloc(sizeof(struct frequency));
if(cpu->maxLevels < 0x00000016) {
#ifdef _WIN32
printErr("Can't read frequency information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000016, cpu->maxLevels);
#if defined (_WIN32) || defined (__APPLE__)
printWarn("Can't read frequency information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000016, cpu->maxLevels);
freq->base = UNKNOWN_FREQ;
freq->max = UNKNOWN_FREQ;
#else
printWarn("Can't read frequency information from cpuid (needed level is 0x%.8X, max is 0x%.8X). Using udev", 0x00000016, cpu->maxLevels);
freq->base = UNKNOWN_FREQ;
freq->max = get_max_freq_from_file(0);
freq->max = get_max_freq_from_file(0, cpu->hv->present);
if(freq->max == 0) {
if(cpu->hv->present) {
printWarn("Read max CPU frequency and got 0 MHz");
}
else {
printBug("Read max CPU frequency and got 0 MHz");
}
freq->max = UNKNOWN_FREQ;
}
#endif
}
else {
@@ -682,11 +671,21 @@ struct frequency* get_frequency_info(struct cpuInfo* cpu) {
freq->max = ebx;
if(freq->base == 0) {
if(cpu->hv->present) {
printWarn("Read base CPU frequency and got 0 MHz");
}
else {
printBug("Read base CPU frequency and got 0 MHz");
}
freq->base = UNKNOWN_FREQ;
}
if(freq->max == 0) {
if(cpu->hv->present) {
printWarn("Read max CPU frequency and got 0 MHz");
}
else {
printBug("Read max CPU frequency and got 0 MHz");
}
freq->max = UNKNOWN_FREQ;
}
}
@@ -866,16 +865,111 @@ char* get_str_fma(struct cpuInfo* cpu) {
return string;
}
/*** DEBUG ***/
void print_debug(struct cpuInfo* cpu) {
uint32_t eax = 0x00000001;
uint32_t ebx = 0;
uint32_t ecx = 0;
uint32_t edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);
printf("%s\n", cpu->cpu_name);
printf("- Max standart level: 0x%.8X\n", cpu->maxLevels);
if(cpu->hv->present) {
printf("- Hypervisor: %s\n", cpu->hv->hv_name);
}
printf("- Max standard level: 0x%.8X\n", cpu->maxLevels);
printf("- Max extended level: 0x%.8X\n", cpu->maxExtendedLevels);
if(cpu->cpu_vendor == CPU_VENDOR_AMD) {
printf("- AMD topology extensions: %d\n", cpu->topology_extensions);
}
printf("- CPUID dump: 0x%.8X\n", eax);
free_cpuinfo_struct(cpu);
}
// TODO: Query HV and Xeon Phi levels
void print_raw(struct cpuInfo* cpu) {
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
printf("%s\n\n", cpu->cpu_name);
printf(" CPUID leaf sub EAX EBX ECX EDX \n");
printf("--------------------------------------------------------------\n");
for(int c=0; c < cpu->topo->total_cores; c++) {
#ifndef __APPLE__
if(!bind_to_cpu(c)) {
printErr("Failed binding to CPU %d", c);
return;
}
#endif
printf("CPU %d:\n", c);
for(uint32_t reg=0x00000000; reg <= cpu->maxLevels; reg++) {
if(reg == 0x00000004) {
for(uint32_t reg2=0x00000000; reg2 < cpu->cach->max_cache_level; reg2++) {
eax = reg;
ebx = 0;
ecx = reg2;
edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);
printf(" 0x%.8X 0x%.2X: 0x%.8X 0x%.8X 0x%.8X 0x%.8X\n", reg, reg2, eax, ebx, ecx, edx);
}
}
else if(reg == 0x0000000B) {
for(uint32_t reg2=0x00000000; reg2 < cpu->topo->smt_supported; reg2++) {
eax = reg;
ebx = 0;
ecx = reg2;
edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);
printf(" 0x%.8X 0x%.2X: 0x%.8X 0x%.8X 0x%.8X 0x%.8X\n", reg, reg2, eax, ebx, ecx, edx);
}
}
else {
eax = reg;
ebx = 0;
ecx = 0;
edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);
printf(" 0x%.8X 0x%.2X: 0x%.8X 0x%.8X 0x%.8X 0x%.8X\n", reg, 0x00, eax, ebx, ecx, edx);
}
}
for(uint32_t reg=0x80000000; reg <= cpu->maxExtendedLevels; reg++) {
if(reg == 0x8000001D) {
for(uint32_t reg2=0x00000000; reg2 < cpu->cach->max_cache_level; reg2++) {
eax = reg;
ebx = 0;
ecx = reg2;
edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);
printf(" 0x%.8X 0x%.2X: 0x%.8X 0x%.8X 0x%.8X 0x%.8X\n", reg, reg2, eax, ebx, ecx, edx);
}
}
else {
eax = reg;
ebx = 0;
ecx = 0;
edx = 0;
cpuid(&eax, &ebx, &ecx, &edx);
printf(" 0x%.8X 0x%.2X: 0x%.8X 0x%.8X 0x%.8X 0x%.8X\n", reg, 0x00, eax, ebx, ecx, edx);
}
}
}
}
void free_topo_struct(struct topology* topo) {
free(topo->apic->cache_select_mask);
free(topo->apic->cache_id_apic);

View File

@@ -15,6 +15,7 @@ char* get_str_topology(struct cpuInfo* cpu, struct topology* topo, bool dual_soc
char* get_str_peak_performance(struct cpuInfo* cpu, struct topology* topo, int64_t freq);
void print_debug(struct cpuInfo* cpu);
void print_raw(struct cpuInfo* cpu);
void free_topo_struct(struct topology* topo);

View File

@@ -11,6 +11,7 @@
* http://www.etallen.com/cpuid.html
* - This should be updated from time to time, to support newer CPUs. A good reference to look at:
* https://en.wikichip.org/
* http://instlatx64.atw.hu/
*/
// From Todd Allen:
@@ -63,6 +64,10 @@ enum {
UARCH_BROADWELL,
UARCH_AIRMONT,
UARCH_KABY_LAKE,
UARCH_COMET_LAKE,
UARCH_ROCKET_LAKE,
UARCH_AMBER_LAKE,
UARCH_WHISKEY_LAKE,
UARCH_SKYLAKE,
UARCH_CASCADE_LAKE,
UARCH_COOPER_LAKE,
@@ -123,7 +128,7 @@ void fill_uarch(struct uarch* arch, char* str, MICROARCH u, uint32_t process) {
arch->process= process;
}
// iNApired in Todd Allen's decode_uarch_intel
// Inspired in Todd Allen's decode_uarch_intel
struct uarch* get_uarch_from_cpuid_intel(uint32_t ef, uint32_t f, uint32_t em, uint32_t m, int s) {
struct uarch* arch = malloc(sizeof(struct uarch));
@@ -215,7 +220,10 @@ struct uarch* get_uarch_from_cpuid_intel(uint32_t ef, uint32_t f, uint32_t em, u
CHECK_UARCH(arch, 0, 6, 8, 10, NA, "Tremont", UARCH_TREMONT, 10) // no spec update; only geekbench.com example
CHECK_UARCH(arch, 0, 6, 8, 12, NA, "Willow Cove", UARCH_WILLOW_COVE, 10) // found only on en.wikichip.org
CHECK_UARCH(arch, 0, 6, 8, 13, NA, "Willow Cove", UARCH_WILLOW_COVE, 10) // LX*
CHECK_UARCH(arch, 0, 6, 8, 14, NA, "Kaby Lake", UARCH_KABY_LAKE, 14)
CHECK_UARCH(arch, 0, 6, 8, 14, 9, "Amber Lake", UARCH_AMBER_LAKE, 14) // wikichip
CHECK_UARCH(arch, 0, 6, 8, 14, 10, "Kaby Lake", UARCH_KABY_LAKE, 14) // wikichip
CHECK_UARCH(arch, 0, 6, 8, 14, 11, "Whiskey Lake", UARCH_WHISKEY_LAKE, 14) // wikichip
CHECK_UARCH(arch, 0, 6, 8, 14, 12, "Comet Lake", UARCH_COMET_LAKE, 14) // wikichip
CHECK_UARCH(arch, 0, 6, 9, 6, NA, "Tremont", UARCH_TREMONT, 10) // LX*
CHECK_UARCH(arch, 0, 6, 9, 12, NA, "Tremont", UARCH_TREMONT, 10) // LX*
CHECK_UARCH(arch, 0, 6, 9, 13, NA, "Sunny Cove", UARCH_SUNNY_COVE, 10) // LX*
@@ -224,8 +232,9 @@ struct uarch* get_uarch_from_cpuid_intel(uint32_t ef, uint32_t f, uint32_t em, u
CHECK_UARCH(arch, 0, 6, 9, 14, 11, "Coffee Lake", UARCH_COFFE_LAKE, 14)
CHECK_UARCH(arch, 0, 6, 9, 14, 12, "Coffee Lake", UARCH_COFFE_LAKE, 14)
CHECK_UARCH(arch, 0, 6, 9, 14, 13, "Coffee Lake", UARCH_COFFE_LAKE, 14)
CHECK_UARCH(arch, 0, 6, 10, 5, NA, "Kaby Lake", UARCH_KABY_LAKE, 14) // LX*
CHECK_UARCH(arch, 0, 6, 10, 6, NA, "Kaby Lake", UARCH_KABY_LAKE, 14) // no spec update; only iNAtlatx64 example
CHECK_UARCH(arch, 0, 6, 10, 5, NA, "Comet Lake", UARCH_COMET_LAKE, 14) // wikichip
CHECK_UARCH(arch, 0, 6, 10, 6, NA, "Comet Lake", UARCH_COMET_LAKE, 14) // instlatx64.atw.hu (i7-10710U)
CHECK_UARCH(arch, 0, 6, 10, 7, NA, "Rocket Lake", UARCH_ROCKET_LAKE, 14) // instlatx64.atw.hu (i7-11700K)
CHECK_UARCH(arch, 0, 11, 0, 0, NA, "Knights Ferry", UARCH_KNIGHTS_FERRY, 45) // found only on en.wikichip.org
CHECK_UARCH(arch, 0, 11, 0, 1, NA, "Knights Corner", UARCH_KNIGHTS_CORNER, 22)
CHECK_UARCH(arch, 0, 15, 0, 0, NA, "Willamette", UARCH_WILLAMETTE, 180)
@@ -352,7 +361,6 @@ struct uarch* get_uarch_from_cpuid(struct cpuInfo* cpu, uint32_t ef, uint32_t f,
bool vpus_are_AVX512(struct cpuInfo* cpu) {
return cpu->arch->uarch != UARCH_ICE_LAKE;
return cpu->arch->uarch != UARCH_ICE_LAKE;
}
bool is_knights_landing(struct cpuInfo* cpu) {
@@ -360,16 +368,18 @@ bool is_knights_landing(struct cpuInfo* cpu) {
}
int get_number_of_vpus(struct cpuInfo* cpu) {
if(cpu->cpu_vendor == CPU_VENDOR_AMD)
return 1;
switch(cpu->arch->uarch) {
// Intel
case UARCH_HASWELL:
case UARCH_BROADWELL:
case UARCH_SKYLAKE:
case UARCH_CASCADE_LAKE:
case UARCH_KABY_LAKE:
case UARCH_COMET_LAKE:
case UARCH_ROCKET_LAKE:
case UARCH_AMBER_LAKE:
case UARCH_WHISKEY_LAKE:
case UARCH_COFFE_LAKE:
case UARCH_PALM_COVE:
@@ -377,6 +387,10 @@ int get_number_of_vpus(struct cpuInfo* cpu) {
case UARCH_KNIGHTS_MILL:
case UARCH_ICE_LAKE:
// AMD
case UARCH_ZEN2:
case UARCH_ZEN3:
return 2;
default:
return 1;