Compare commits

..

22 Commits

Author SHA1 Message Date
Dr-Noob
e5e317ea92 [v1.05][X86] Print core or cores depending on the number of CPU cores 2024-07-19 08:47:38 +01:00
Dr-Noob
41a194948d [v1.05][X86] Small fix in newline 2024-07-17 08:52:24 +01:00
Dr-Noob
5dc2234e97 [v1.05][X86] Assume logical and physical cores from total_cores 2024-07-17 08:50:11 +01:00
Dr-Noob
21dddc63ff [v1.05][X86] Try fetching topology from udev even when cpuid is not able to do so 2024-07-12 08:32:27 +01:00
Dr-Noob
8fca4cb250 [v1.05][X86] Fix frequency fetching from udev and measurement in hybrid architectures 2024-07-11 22:27:42 +01:00
Dr-Noob
7c947bdf64 [v1.05] Use UNKNOWN_DATA instead of -1 in frequency measurement 2024-07-11 22:10:00 +01:00
Dr-Noob
1ed3a0f2bf [v1.05] Adapt frequency measurement iterations depending on CPU speed
This is achieved by running a first measurement which gets a taste of
CPU speed, then estimate a reasonable value for the iterations of the
real measurement and then running the actual measurement. This is very
helpful to reduce the runtime of the measurement, especially for slow
CPUs
2024-07-11 21:55:01 +01:00
Dr-Noob
0fe6fc3f4d [v1.05][ARM] Fix mistake in Makefile 2024-07-11 08:17:26 +01:00
Dr-Noob
96c784026b [v1.05] Fix formatting issues in Makefile 2024-07-09 08:47:34 +01:00
Dr-Noob
59cd2dd128 [v1.05][X86] Add support for Hygon CPUs (#244) 2024-07-09 08:34:44 +01:00
Dr-Noob
da1981b97c [v1.05] Check read return value in frequency measurement 2024-07-09 08:32:24 +01:00
Dr-Noob
8506c91e00 [v1.05] Add --measure-max-freq (available on x86 and ARM) 2024-07-08 09:07:57 +01:00
Dr-Noob
ece28cbdee [v1.05] Small fix in help message 2024-07-08 08:31:00 +01:00
Dr-Noob
7b46c78249 [v1.05] Replace printf with proper printErr 2024-07-08 08:28:48 +01:00
Dr-Noob
e0095c303d [v1.05] Print a tilde in case the freq was measured (indicating that this value is an approximation) 2024-07-08 08:23:56 +01:00
Dr-Noob
65378aaed9 [v1.05] Move sysctl from ARM-specific to common (#251) 2024-07-07 12:43:03 +01:00
Dr-Noob
946729dd06 [v1.05] Compile measure freq only in Linux (avoids compiler warning in non-linux builds) 2024-07-06 11:28:46 +01:00
Dr-Noob
9212f19de1 [v1.05] Add support for frequency measurement (both x86 and ARM) (branch measure-freq #220) 2024-07-05 08:44:34 +01:00
Dr-Noob
b019256515 [v1.05] Continue merging measure-freq #220
- [v1.05][X86] Show SSE if AVX/FMA is not supported
- [v1.05][X86] Do not stop if cach is NULL and check for non-NULL cache in get_topology_info functions
- [v1.05][X86] Fix bug where the number of cpus were not set if NULL was returned inside the loop. Ensure topo is not NULL in get_peak_performance. Fallback to UNKNOWN_DATA when we have no information about topology
2024-07-05 08:37:54 +01:00
Dr-Noob
d4cadbd807 [v1.05] Move bind_to_cpu from x86-specific to global (merging measure-freq #220) 2024-07-05 08:32:11 +01:00
Dr-Noob
4f081ef1a2 [v1.05] Add newline to fix dummy warning with clang 2024-07-03 09:09:37 +02:00
Dr-Noob
1b746bc67d [v1.05][ARM] Add support to detect SoC from PCI (#245) with initial support for NVIDIA Tegra 2024-07-03 08:01:53 +01:00
24 changed files with 525 additions and 154 deletions

View File

@@ -13,12 +13,18 @@ COMMON_HDR = $(SRC_COMMON)ascii.h $(SRC_COMMON)cpu.h $(SRC_COMMON)udev.h $(SRC_C
ifneq ($(OS),Windows_NT)
GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"
arch := $(shell uname -m)
os := $(shell uname -s)
ifeq ($(os), Linux)
COMMON_SRC += $(SRC_COMMON)freq.c
COMMON_HDR += $(SRC_COMMON)freq.h
endif
ifeq ($(arch), $(filter $(arch), x86_64 amd64 i386 i486 i586 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 $(SRC_DIR)freq/freq.h
os := $(shell uname -s)
ifeq ($(os), Linux)
SOURCE += $(SRC_DIR)freq/freq.c freq_nov.o freq_avx.o freq_avx512.o
HEADERS += $(SRC_DIR)freq/freq.h
@@ -36,10 +42,9 @@ ifneq ($(OS),Windows_NT)
HEADERS += $(COMMON_HDR) $(SRC_DIR)midr.h $(SRC_DIR)uarch.h $(SRC_COMMON)soc.h $(SRC_DIR)soc.h $(SRC_COMMON)pci.h $(SRC_DIR)udev.c $(SRC_DIR)socs.h
CFLAGS += -DARCH_ARM -Wno-unused-parameter -std=c99 -fstack-protector-all
os := $(shell uname -s)
ifeq ($(os), Darwin)
SOURCE += $(SRC_DIR)sysctl.c
HEADERS += $(SRC_DIR)sysctl.h
SOURCE += $(SRC_COMMON)sysctl.c
HEADERS += $(SRC_COMMON)sysctl.h
endif
else ifeq ($(arch), $(filter $(arch), riscv64 riscv32))
SRC_DIR=src/riscv/

View File

@@ -8,12 +8,14 @@
#ifdef __linux__
#include <sys/auxv.h>
#include <asm/hwcap.h>
#include "../common/freq.h"
#elif defined __APPLE__ || __MACH__
#include "sysctl.h"
#include "../common/sysctl.h"
#endif
#include "../common/global.h"
#include "../common/soc.h"
#include "../common/args.h"
#include "udev.h"
#include "midr.h"
#include "uarch.h"
@@ -39,8 +41,17 @@ struct cache* get_cache_info(struct cpuInfo* cpu) {
struct frequency* get_frequency_info(uint32_t core) {
struct frequency* freq = emalloc(sizeof(struct frequency));
freq->measured = false;
freq->base = UNKNOWN_DATA;
freq->max = get_max_freq_from_file(core);
#ifdef __linux__
if (freq->max == UNKNOWN_DATA || measure_max_frequency_flag()) {
if (freq->max == UNKNOWN_DATA)
printWarn("Unable to find max frequency from udev, measuring CPU frequency");
freq->max = measure_max_frequency(core);
freq->measured = true;
}
#endif
return freq;
}

View File

@@ -11,7 +11,7 @@
#include "../common/pci.h"
#if defined(__APPLE__) || defined(__MACH__)
#include "sysctl.h"
#include "../common/sysctl.h"
#endif
#define NA -1

View File

@@ -28,6 +28,7 @@ struct args_struct {
bool help_flag;
bool raw_flag;
bool accurate_pp;
bool measure_max_frequency_flag;
bool full_cpu_name_flag;
bool logo_long;
bool logo_short;
@@ -50,6 +51,7 @@ const char args_chr[] = {
/* [ARG_LOGO_INTEL_NEW] = */ 3,
/* [ARG_LOGO_INTEL_OLD] = */ 4,
/* [ARG_ACCURATE_PP] = */ 5,
/* [ARG_MEASURE_MAX_FREQ] = */ 6,
/* [ARG_DEBUG] = */ 'd',
/* [ARG_VERBOSE] = */ 'v',
/* [ARG_VERSION] = */ 'V',
@@ -66,6 +68,7 @@ const char *args_str[] = {
/* [ARG_LOGO_INTEL_NEW] = */ "logo-intel-new",
/* [ARG_LOGO_INTEL_OLD] = */ "logo-intel-old",
/* [ARG_ACCURATE_PP] = */ "accurate-pp",
/* [ARG_MEASURE_MAX_FREQ] = */ "measure-max-freq",
/* [ARG_DEBUG] = */ "debug",
/* [ARG_VERBOSE] = */ "verbose",
/* [ARG_VERSION] = */ "version",
@@ -101,6 +104,10 @@ bool accurate_pp(void) {
return args.accurate_pp;
}
bool measure_max_frequency_flag(void) {
return args.measure_max_frequency_flag;
}
bool show_full_cpu_name(void) {
return args.full_cpu_name_flag;
}
@@ -222,12 +229,20 @@ char* build_short_options(void) {
memset(str, 0, sizeof(char) * (len*2 + 1));
#ifdef ARCH_X86
sprintf(str, "%c:%c:%c%c%c%c%c%c%c%c%c%c%c",
sprintf(str, "%c:%c:%c%c%c%c%c%c%c%c%c%c%c%c",
c[ARG_STYLE], c[ARG_COLOR], c[ARG_HELP],
c[ARG_RAW], c[ARG_FULLCPUNAME],
c[ARG_LOGO_SHORT], c[ARG_LOGO_LONG],
c[ARG_LOGO_INTEL_NEW], c[ARG_LOGO_INTEL_OLD],
c[ARG_ACCURATE_PP], c[ARG_DEBUG], c[ARG_VERBOSE],
c[ARG_ACCURATE_PP], c[ARG_MEASURE_MAX_FREQ],
c[ARG_DEBUG], c[ARG_VERBOSE],
c[ARG_VERSION]);
#elif ARCH_ARM
sprintf(str, "%c:%c:%c%c%c%c%c%c%c",
c[ARG_STYLE], c[ARG_COLOR], c[ARG_HELP],
c[ARG_LOGO_SHORT], c[ARG_LOGO_LONG],
c[ARG_MEASURE_MAX_FREQ],
c[ARG_DEBUG], c[ARG_VERBOSE],
c[ARG_VERSION]);
#else
sprintf(str, "%c:%c:%c%c%c%c%c%c",
@@ -270,8 +285,11 @@ bool parse_args(int argc, char* argv[]) {
{args_str[ARG_LOGO_INTEL_NEW], no_argument, 0, args_chr[ARG_LOGO_INTEL_NEW] },
{args_str[ARG_LOGO_INTEL_OLD], no_argument, 0, args_chr[ARG_LOGO_INTEL_OLD] },
{args_str[ARG_ACCURATE_PP], no_argument, 0, args_chr[ARG_ACCURATE_PP] },
{args_str[ARG_MEASURE_MAX_FREQ], no_argument, 0, args_chr[ARG_MEASURE_MAX_FREQ] },
{args_str[ARG_FULLCPUNAME], no_argument, 0, args_chr[ARG_FULLCPUNAME] },
{args_str[ARG_RAW], no_argument, 0, args_chr[ARG_RAW] },
#elif ARCH_ARM
{args_str[ARG_MEASURE_MAX_FREQ], no_argument, 0, args_chr[ARG_MEASURE_MAX_FREQ] },
#endif
{args_str[ARG_LOGO_SHORT], no_argument, 0, args_chr[ARG_LOGO_SHORT] },
{args_str[ARG_LOGO_LONG], no_argument, 0, args_chr[ARG_LOGO_LONG] },
@@ -313,6 +331,9 @@ bool parse_args(int argc, char* argv[]) {
else if(opt == args_chr[ARG_ACCURATE_PP]) {
args.accurate_pp = true;
}
else if(opt == args_chr[ARG_MEASURE_MAX_FREQ]) {
args.measure_max_frequency_flag = true;
}
else if(opt == args_chr[ARG_FULLCPUNAME]) {
args.full_cpu_name_flag = true;
}

View File

@@ -29,6 +29,7 @@ enum {
ARG_LOGO_INTEL_NEW,
ARG_LOGO_INTEL_OLD,
ARG_ACCURATE_PP,
ARG_MEASURE_MAX_FREQ,
ARG_DEBUG,
ARG_VERBOSE,
ARG_VERSION
@@ -43,6 +44,7 @@ int max_arg_str_length(void);
bool parse_args(int argc, char* argv[]);
bool show_help(void);
bool accurate_pp(void);
bool measure_max_frequency_flag(void);
bool show_full_cpu_name(void);
bool show_logo_long(void);
bool show_logo_short(void);

View File

@@ -105,6 +105,19 @@ $C1 MMM :MMM NMM dMMK dMMX MMN \
$C1 MMM :MMM NMM dMMMoo OMM0....:Nx. MMN \
$C1 MMM :WWW XWW lONMM 'xXMMMMNOc MMN "
#define ASCII_HYGON \
"$C1 \
$C1 \
$C1 \
$C1 ## ## ## ## ###### ###### ## # \
$C1 ##....## ## ## ## ## ## #### # \
$C1 ######## ## ## ##. ## ## # #### \
$C1 ## ## ## *######. ###### # ## \
$C1 \
$C1 \
$C1 \
$C1 "
#define ASCII_SNAPD \
" $C1@@$C2######## \
$C1@@@@@$C2########### \
@@ -538,6 +551,7 @@ typedef struct ascii_logo asciiL;
asciiL logo_amd = { ASCII_AMD, 39, 15, false, {C_FG_WHITE, C_FG_GREEN}, {C_FG_WHITE, C_FG_GREEN} };
asciiL logo_intel = { ASCII_INTEL, 48, 14, false, {C_FG_CYAN}, {C_FG_CYAN, C_FG_WHITE} };
asciiL logo_intel_new = { ASCII_INTEL_NEW, 51, 9, false, {C_FG_CYAN}, {C_FG_CYAN, C_FG_WHITE} };
asciiL logo_hygon = { ASCII_HYGON, 51, 11, false, {C_FG_RED}, {C_FG_RED, C_FG_WHITE} };
asciiL logo_snapd = { ASCII_SNAPD, 39, 16, false, {C_FG_RED, C_FG_WHITE}, {C_FG_RED, C_FG_WHITE} };
asciiL logo_mtk = { ASCII_MTK, 59, 5, false, {C_FG_BLUE, C_FG_YELLOW}, {C_FG_BLUE, C_FG_YELLOW} };
asciiL logo_exynos = { ASCII_EXYNOS, 22, 13, true, {C_BG_BLUE, C_FG_WHITE}, {C_FG_BLUE, C_FG_WHITE} };

View File

@@ -145,17 +145,25 @@ 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 = (5+1+3+1);
uint32_t size = (1+5+1+3+1);
assert(strlen(STRING_UNKNOWN)+1 <= size);
char* string = emalloc(sizeof(char)*size);
memset(string, 0, sizeof(char)*size);
char* string = ecalloc(size, sizeof(char));
if(freq->max == UNKNOWN_DATA || freq->max < 0)
if(freq->max == UNKNOWN_DATA || freq->max < 0) {
snprintf(string,strlen(STRING_UNKNOWN)+1,STRING_UNKNOWN);
else if(freq->max >= 1000)
}
else if(freq->max >= 1000) {
if (freq->measured)
snprintf(string,size,"~%.3f "STRING_GIGAHERZ,(float)(freq->max)/1000);
else
snprintf(string,size,"%.3f "STRING_GIGAHERZ,(float)(freq->max)/1000);
}
else {
if (freq->measured)
snprintf(string,size,"~%d "STRING_MEGAHERZ,freq->max);
else
snprintf(string,size,"%d "STRING_MEGAHERZ,freq->max);
}
return string;
}

View File

@@ -8,6 +8,7 @@ enum {
// ARCH_X86
CPU_VENDOR_INTEL,
CPU_VENDOR_AMD,
CPU_VENDOR_HYGON,
// ARCH_ARM
CPU_VENDOR_ARM,
CPU_VENDOR_APPLE,
@@ -57,6 +58,8 @@ typedef int32_t VENDOR;
struct frequency {
int32_t base;
int32_t max;
// Indicates if max frequency was measured
bool measured;
};
struct hypervisor {

195
src/common/freq.c Normal file
View File

@@ -0,0 +1,195 @@
#ifdef __linux__
#define _GNU_SOURCE
#include <time.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
#include "global.h"
#include "cpu.h"
static long
perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags) {
int ret;
ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
group_fd, flags);
return ret;
}
#define INSERT_ASM_ONCE __asm volatile("nop");
#define INSERT_ASM_10_TIMES \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
INSERT_ASM_ONCE \
#define INSERT_ASM_100_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES \
INSERT_ASM_10_TIMES
#define INSERT_ASM_1000_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
INSERT_ASM_100_TIMES \
void nop_function(uint64_t iters) {
for (uint64_t i = 0; i < iters; i++) {
INSERT_ASM_1000_TIMES
INSERT_ASM_1000_TIMES
INSERT_ASM_1000_TIMES
INSERT_ASM_1000_TIMES
}
}
// Run the nop_function with the number of iterations specified and
// measure both the time and number of cycles
int measure_freq_iters(uint64_t iters, uint32_t core, double* freq) {
clockid_t clock = CLOCK_PROCESS_CPUTIME_ID;
struct timespec start, end;
struct perf_event_attr pe;
uint64_t cycles;
int fd;
int pid = 0;
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.type = PERF_TYPE_HARDWARE;
pe.size = sizeof(struct perf_event_attr);
pe.config = PERF_COUNT_HW_CPU_CYCLES;
pe.disabled = 1;
pe.exclude_kernel = 1;
pe.exclude_hv = 1;
fd = perf_event_open(&pe, pid, core, -1, 0);
if (fd == -1) {
perror("perf_event_open");
if (errno == EPERM || errno == EACCES) {
printErr("You may not have permission to collect stats.\n"\
"Consider tweaking /proc/sys/kernel/perf_event_paranoid or running as root");
}
return -1;
}
if (clock_gettime(clock, &start) == -1) {
perror("clock_gettime");
return -1;
}
if(ioctl(fd, PERF_EVENT_IOC_RESET, 0) == -1) {
perror("ioctl");
return -1;
}
if(ioctl(fd, PERF_EVENT_IOC_ENABLE, 0) == -1) {
perror("ioctl");
return -1;
}
nop_function(iters);
ssize_t ret = read(fd, &cycles, sizeof(uint64_t));
if (ret == -1) {
perror("read");
return -1;
}
if (ret != sizeof(uint64_t)) {
printErr("Read returned %d, expected %d", ret, sizeof(uint64_t));
return -1;
}
if(ioctl(fd, PERF_EVENT_IOC_DISABLE, 0) == -1) {
perror("ioctl");
return -1;
}
if (clock_gettime(clock, &end) == -1) {
perror("clock_gettime");
return -1;
}
uint64_t nsecs = (end.tv_sec*1e9 + end.tv_nsec) - (start.tv_sec*1e9 + start.tv_nsec);
uint64_t usecs = nsecs/1000;
*freq = cycles/((double)usecs);
return 0;
}
// Return a good number of iterations to run the nop_function in
// order to get a precise measurement of the frequency without taking
// too much time.
uint64_t get_num_iters_from_freq(double frequency) {
// Truncate to reduce variability
uint64_t freq_trunc = ((uint64_t) frequency / 100) * 100;
uint64_t osp_per_iter = 4 * 1000;
return freq_trunc * 1e7 * 1/osp_per_iter;
}
// Differences between x86 measure_frequency and this measure_max_frequency:
// - measure_frequency employs all cores simultaneously whereas
// measure_max_frequency only employs 1.
// - measure_frequency runs the computation and checks /proc/cpuinfo whereas
// measure_max_frequency does not rely on /proc/cpuinfo and simply
// counts cpu cycles to measure frequency.
// - measure_frequency uses actual computation while measuring the frequency
// whereas measure_max_frequency uses nop instructions. This makes the former
// x86 dependant whereas the latter is architecture independant.
int64_t measure_max_frequency(uint32_t core) {
if (!bind_to_cpu(core)) {
printErr("Failed binding the process to CPU %d", core);
return UNKNOWN_DATA;
}
// First, get very rough estimation of clock cycle to
// compute a reasonable value for the iterations
double estimation_freq, frequency;
uint64_t iters = 100000;
if (measure_freq_iters(iters, core, &estimation_freq) == -1)
return UNKNOWN_DATA;
if (estimation_freq <= 0.0) {
printErr("First frequency measurement yielded an invalid value: %f", estimation_freq);
return UNKNOWN_DATA;
}
iters = get_num_iters_from_freq(estimation_freq);
printWarn("Running frequency measurement with %ld iterations on core %d...", iters, core);
// Now perform actual measurement
const char* frequency_banner = "cpufetch is measuring the max frequency...";
printf("%s", frequency_banner);
fflush(stdout);
if (measure_freq_iters(iters, core, &frequency) == -1)
return UNKNOWN_DATA;
// Clean screen once measurement is finished
printf("\r%*c\r", (int) strlen(frequency_banner), ' ');
// Discard last digit in the frequency, which should help providing
// more reliable and predictable values.
return (((int) frequency + 5)/10) * 10;
}
#endif // #ifdef __linux__

6
src/common/freq.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef __COMMON_FREQ__
#define __COMMON_FREQ__
int64_t measure_max_frequency(uint32_t core);
#endif

View File

@@ -1,3 +1,14 @@
#ifdef _WIN32
#define NOMINMAX
#include <windows.h>
#elif defined __linux__
#define _GNU_SOURCE
#include <sched.h>
#elif defined __FreeBSD__
#include <sys/param.h>
#include <sys/cpuset.h>
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -199,6 +210,34 @@ void* erealloc(void *ptr, size_t size) {
return newptr;
}
#ifndef __APPLE__
bool bind_to_cpu(int cpu_id) {
#ifdef _WIN32
HANDLE process = GetCurrentProcess();
DWORD_PTR processAffinityMask = 1 << cpu_id;
return SetProcessAffinityMask(process, processAffinityMask);
#elif defined __linux__
cpu_set_t currentCPU;
CPU_ZERO(&currentCPU);
CPU_SET(cpu_id, &currentCPU);
if (sched_setaffinity (0, sizeof(currentCPU), &currentCPU) == -1) {
printWarn("sched_setaffinity: %s", strerror(errno));
return false;
}
return true;
#elif defined __FreeBSD__
cpuset_t currentCPU;
CPU_ZERO(&currentCPU);
CPU_SET(cpu_id, &currentCPU);
if(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset_t), &currentCPU) == -1) {
printWarn("cpuset_setaffinity: %s", strerror(errno));
return false;
}
return true;
#endif
}
#endif
void print_version(FILE *restrict stream) {
#ifdef GIT_FULL_VERSION
fprintf(stream, "cpufetch %s (%s %s)\n", GIT_FULL_VERSION, OS_STR, ARCH_STR);

View File

@@ -19,6 +19,9 @@ char *strremove(char *str, const char *sub);
void* emalloc(size_t size);
void* ecalloc(size_t nmemb, size_t size);
void* erealloc(void *ptr, size_t size);
#ifndef __APPLE__
bool bind_to_cpu(int cpu_id);
#endif
void print_version(FILE *restrict stream);
#endif

View File

@@ -30,11 +30,17 @@ void print_help(char *argv[]) {
#ifdef ARCH_X86
#ifdef __linux__
printf(" --%s %*s Compute the peak performance accurately (measure the CPU frequency instead of using the maximum)\n", t[ARG_ACCURATE_PP], (int) (max_len-strlen(t[ARG_ACCURATE_PP])), "");
#endif
printf(" --%s %*s Measure the max CPU frequency instead of reading it\n", t[ARG_MEASURE_MAX_FREQ], (int) (max_len-strlen(t[ARG_MEASURE_MAX_FREQ])), "");
#endif // __linux__
printf(" --%s %*s Show the old Intel logo\n", t[ARG_LOGO_INTEL_OLD], (int) (max_len-strlen(t[ARG_LOGO_INTEL_OLD])), "");
printf(" --%s %*s Show the new Intel logo\n", t[ARG_LOGO_INTEL_NEW], (int) (max_len-strlen(t[ARG_LOGO_INTEL_NEW])), "");
printf(" -%c, --%s %*s Show the full CPU name (do not abbreviate it)\n", c[ARG_FULLCPUNAME], t[ARG_FULLCPUNAME], (int) (max_len-strlen(t[ARG_FULLCPUNAME])), "");
printf(" -%c, --%s %*s Print raw cpuid data (debug purposes)\n", c[ARG_RAW], t[ARG_RAW], (int) (max_len-strlen(t[ARG_RAW])), "");
#endif // ARCH_X86
#ifdef ARCH_ARM
#ifdef __linux__
printf(" --%s %*s Measure the max CPU frequency instead of reading it\n", t[ARG_MEASURE_MAX_FREQ], (int) (max_len-strlen(t[ARG_MEASURE_MAX_FREQ])), "");
#endif
#endif
printf(" -%c, --%s %*s Print this help and exit\n", c[ARG_HELP], t[ARG_HELP], (int) (max_len-strlen(t[ARG_HELP])), "");
printf(" -%c, --%s %*s Print cpufetch version and exit\n", c[ARG_VERSION], t[ARG_VERSION], (int) (max_len-strlen(t[ARG_VERSION])), "");
@@ -45,7 +51,7 @@ void print_help(char *argv[]) {
printf(" * \"amd\": Use AMD color scheme \n");
printf(" * \"ibm\", Use IBM color scheme \n");
printf(" * \"arm\": Use ARM color scheme \n");
printf(" * \"rockchip\": Use ARM color scheme \n");
printf(" * \"rockchip\": Use Rockchip color scheme \n");
printf(" * \"sifive\": Use SiFive color scheme \n");
printf(" * custom: If the argument of --color does not match any of the previous strings, a custom scheme can be specified.\n");
printf(" 5 colors must be given in RGB with the format: R,G,B:R,G,B:...\n");
@@ -80,6 +86,11 @@ void print_help(char *argv[]) {
printf(" --accurate-pp option, which will measure the AVX frequency and show a more precise estimation\n");
printf(" (this option is only available in x86 architectures).\n");
printf(" To precisely measure peak performance, see: https://github.com/Dr-Noob/peakperf\n");
printf("\n");
printf(" Both --accurate-pp and --measure-max-freq measure the actual frequency of the CPU. However,\n");
printf(" they differ slightly. The former measures the max frequency while running vectorized SSE/AVX\n");
printf(" instructions and it is thus x86 only, whereas the latter simply measures the max clock cycle\n");
printf(" and is architecture independent.\n");
}
int main(int argc, char* argv[]) {

View File

@@ -61,6 +61,7 @@ enum {
ATTRIBUTE_NCORES,
ATTRIBUTE_NCORES_DUAL,
#ifdef ARCH_X86
ATTRIBUTE_SSE,
ATTRIBUTE_AVX,
ATTRIBUTE_FMA,
#elif ARCH_PPC
@@ -96,6 +97,7 @@ static const char* ATTRIBUTE_FIELDS [] = {
"Cores:",
"Cores (Total):",
#ifdef ARCH_X86
"SSE:",
"AVX:",
"FMA:",
#elif ARCH_PPC
@@ -131,6 +133,7 @@ static const char* ATTRIBUTE_FIELDS_SHORT [] = {
"Cores:",
"Cores (Total):",
#ifdef ARCH_X86
"SSE:",
"AVX:",
"FMA:",
#elif ARCH_PPC
@@ -357,6 +360,9 @@ void choose_ascii_art(struct ascii* art, struct color** cs, struct terminal* ter
else if(art->vendor == CPU_VENDOR_AMD) {
art->art = choose_ascii_art_aux(&logo_amd_l, &logo_amd, term, lf);
}
else if(art->vendor == CPU_VENDOR_HYGON) {
art->art = &logo_hygon;
}
else {
art->art = &logo_unknown;
}
@@ -591,6 +597,7 @@ bool print_cpufetch_x86(struct cpuInfo* cpu, STYLE s, struct color** cs, struct
for(int i = 0; i < cpu->num_cpus; ptr = ptr->next_cpu, i++) {
char* max_frequency = get_str_freq(ptr->freq);
char* avx = get_str_avx(ptr);
char* sse = get_str_sse(ptr);
char* fma = get_str_fma(ptr);
char* cpu_num = emalloc(sizeof(char) * 9);
@@ -625,8 +632,17 @@ bool print_cpufetch_x86(struct cpuInfo* cpu, STYLE s, struct color** cs, struct
setAttribute(art, ATTRIBUTE_NCORES, n_cores);
}
}
// Show the most modern vector instructions.
// If AVX is supported show it, otherwise show SSE
if (strcmp(avx, "No") == 0) {
setAttribute(art, ATTRIBUTE_SSE, sse);
}
else {
setAttribute(art, ATTRIBUTE_AVX, avx);
setAttribute(art, ATTRIBUTE_FMA, fma);
}
if(l1i != NULL) setAttribute(art, ATTRIBUTE_L1i, l1i);
if(l1d != NULL) setAttribute(art, ATTRIBUTE_L1d, l1d);
if(l2 != NULL) setAttribute(art, ATTRIBUTE_L2, l2);

View File

@@ -4,8 +4,8 @@
#include <string.h>
#include <errno.h>
#include "../common/global.h"
#include "../common/cpu.h"
#include "global.h"
#include "cpu.h"
uint32_t get_sys_info_by_name(char* name) {
size_t size = 0;

View File

@@ -146,6 +146,7 @@ struct uarch* get_cpu_uarch(struct cpuInfo* cpu) {
struct frequency* get_frequency_info(void) {
struct frequency* freq = emalloc(sizeof(struct frequency));
freq->measured = false;
freq->max = get_max_freq_from_file(0);
freq->base = get_min_freq_from_file(0);

View File

@@ -19,6 +19,7 @@
struct frequency* get_frequency_info(uint32_t core) {
struct frequency* freq = emalloc(sizeof(struct frequency));
freq->measured = false;
freq->base = UNKNOWN_DATA;
freq->max = get_max_freq_from_file(core);

View File

@@ -72,34 +72,6 @@ uint32_t get_apic_id(bool x2apic_id) {
}
}
#ifndef __APPLE__
bool bind_to_cpu(int cpu_id) {
#ifdef _WIN32
HANDLE process = GetCurrentProcess();
DWORD_PTR processAffinityMask = 1 << cpu_id;
return SetProcessAffinityMask(process, processAffinityMask);
#elif defined __linux__
cpu_set_t currentCPU;
CPU_ZERO(&currentCPU);
CPU_SET(cpu_id, &currentCPU);
if (sched_setaffinity (0, sizeof(currentCPU), &currentCPU) == -1) {
printWarn("sched_setaffinity: %s", strerror(errno));
return false;
}
return true;
#elif defined __FreeBSD__
cpuset_t currentCPU;
CPU_ZERO(&currentCPU);
CPU_SET(cpu_id, &currentCPU);
if(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset_t), &currentCPU) == -1) {
printWarn("cpuset_setaffinity: %s", strerror(errno));
return false;
}
return true;
#endif
}
#endif
#ifdef __linux__
int get_total_cores_module(int total_cores, int module) {
int total_modules = 2;
@@ -397,6 +369,11 @@ bool fill_apic_ids(uint32_t* apic_ids, int first_core, int n, bool x2apic_id) {
}
bool get_topology_from_apic(struct cpuInfo* cpu, struct topology* topo) {
if (topo->cach == NULL) {
printWarn("get_topology_from_apic: cach is NULL");
return false;
}
uint32_t apic_id;
uint32_t* apic_ids = emalloc(sizeof(uint32_t) * topo->total_cores_module);
uint32_t* apic_pkg = emalloc(sizeof(uint32_t) * topo->total_cores_module);

View File

@@ -17,10 +17,6 @@ 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
#ifdef __linux__
int get_total_cores_module(int total_cores, int module);
#endif

View File

@@ -6,6 +6,10 @@
#include <unistd.h>
#endif
#ifdef __linux__
#include "../common/freq.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -22,6 +26,7 @@
#define CPU_VENDOR_INTEL_STRING "GenuineIntel"
#define CPU_VENDOR_AMD_STRING "AuthenticAMD"
#define CPU_VENDOR_HYGON_STRING "HygonGenuine"
static const char *hv_vendors_string[] = {
[HV_VENDOR_KVM] = "KVMKVMKVM",
@@ -218,7 +223,7 @@ int64_t get_peak_performance(struct cpuInfo* cpu, bool accurate_pp) {
#endif
//First, check we have consistent data
if(freq == UNKNOWN_DATA || topo->logical_cores == UNKNOWN_DATA) {
if(freq == UNKNOWN_DATA || topo == NULL || topo->logical_cores == UNKNOWN_DATA) {
return -1;
}
@@ -451,7 +456,7 @@ struct cpuInfo* get_cpu_info(void) {
cpu->cach = NULL;
cpu->feat = NULL;
uint32_t modules = 1;
cpu->num_cpus = 1;
uint32_t eax = 0;
uint32_t ebx = 0;
uint32_t ecx = 0;
@@ -470,6 +475,8 @@ struct cpuInfo* get_cpu_info(void) {
cpu->cpu_vendor = CPU_VENDOR_INTEL;
else if (strcmp(CPU_VENDOR_AMD_STRING,name) == 0)
cpu->cpu_vendor = CPU_VENDOR_AMD;
else if (strcmp(CPU_VENDOR_HYGON_STRING,name) == 0)
cpu->cpu_vendor = CPU_VENDOR_HYGON;
else {
cpu->cpu_vendor = CPU_VENDOR_INVALID;
printErr("Unknown CPU vendor: %s", name);
@@ -507,12 +514,12 @@ struct cpuInfo* get_cpu_info(void) {
cpu->hybrid_flag = (edx >> 15) & 0x1;
}
if(cpu->hybrid_flag) modules = 2;
if(cpu->hybrid_flag) cpu->num_cpus = 2;
struct cpuInfo* ptr = cpu;
for(uint32_t i=0; i < modules; i++) {
for(uint32_t i=0; i < cpu->num_cpus; i++) {
int32_t first_core;
set_cpu_module(i, modules, &first_core);
set_cpu_module(i, cpu->num_cpus, &first_core);
if(i > 0) {
ptr->next_cpu = emalloc(sizeof(struct cpuInfo));
@@ -547,11 +554,7 @@ struct cpuInfo* get_cpu_info(void) {
cpu->cpu_name = infer_cpu_name_from_uarch(cpu->arch);
}
// If any field of the struct is NULL,
// return early, as next functions
// require non NULL fields in cach and topo
ptr->cach = get_cache_info(ptr);
if(ptr->cach == NULL) return cpu;
if(cpu->hybrid_flag) {
ptr->topo = get_topology_info(ptr, ptr->cach, i);
@@ -559,16 +562,23 @@ struct cpuInfo* get_cpu_info(void) {
else {
ptr->topo = get_topology_info(ptr, ptr->cach, -1);
}
if(cpu->topo == NULL) return cpu;
// If topo is NULL, return early, as get_peak_performance
// requries non-NULL topology.
if(ptr->topo == NULL) return cpu;
}
cpu->num_cpus = modules;
cpu->peak_performance = get_peak_performance(cpu, accurate_pp());
return cpu;
}
bool get_cache_topology_amd(struct cpuInfo* cpu, struct topology* topo) {
if (topo->cach == NULL) {
printWarn("get_cache_topology_amd: cach is NULL");
return false;
}
if(cpu->maxExtendedLevels >= 0x8000001D && cpu->topology_extensions) {
uint32_t i, eax, ebx, ecx, edx, num_sharing_cache, cache_type, cache_level;
@@ -644,10 +654,17 @@ bool get_cache_topology_amd(struct cpuInfo* cpu, struct topology* topo) {
#ifdef __linux__
void get_topology_from_udev(struct topology* topo) {
// TODO: To be improved in the future
topo->total_cores = get_ncores_from_cpuinfo();
// TODO: To be improved in the future
if (topo->total_cores == 1) {
// We can assume it's a single core CPU
topo->logical_cores = topo->total_cores;
topo->physical_cores = topo->total_cores;
}
else {
topo->logical_cores = UNKNOWN_DATA;
topo->physical_cores = UNKNOWN_DATA;
}
topo->smt_available = 1;
topo->smt_supported = 1;
topo->sockets = 1;
@@ -694,30 +711,29 @@ struct topology* get_topology_info(struct cpuInfo* cpu, struct cache* cach, int
switch(cpu->cpu_vendor) {
case CPU_VENDOR_INTEL:
bool toporet = false;
if (cpu->maxLevels >= 0x00000004) {
bool toporet = get_topology_from_apic(cpu, topo);
toporet = get_topology_from_apic(cpu, topo);
}
else {
printWarn("Can't read topology information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000004, cpu->maxLevels);
}
if(!toporet) {
#ifdef __linux__
printWarn("Failed to retrieve topology from APIC, using udev...\n");
printWarn("Failed to retrieve topology from APIC, using udev...");
get_topology_from_udev(topo);
#else
printErr("Failed to retrieve topology from APIC, assumming default values...\n");
if (cpu->maxLevels >= 0x00000004)
printErr("Failed to retrieve topology from APIC, assumming default values...");
topo->logical_cores = UNKNOWN_DATA;
topo->physical_cores = UNKNOWN_DATA;
topo->smt_available = 1;
topo->smt_supported = 1;
#endif
}
}
else {
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;
topo->smt_supported = 1;
}
break;
case CPU_VENDOR_AMD:
case CPU_VENDOR_HYGON:
if (cpu->maxExtendedLevels >= 0x80000008) {
eax = 0x80000008;
cpuid(&eax, &ebx, &ecx, &edx);
@@ -914,6 +930,7 @@ struct cache* get_cache_info(struct cpuInfo* cpu) {
struct frequency* get_frequency_info(struct cpuInfo* cpu) {
struct frequency* freq = emalloc(sizeof(struct frequency));
freq->measured = false;
if(cpu->maxLevels < 0x00000016) {
#if defined (_WIN32) || defined (__APPLE__)
@@ -923,7 +940,7 @@ struct frequency* get_frequency_info(struct cpuInfo* cpu) {
#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_DATA;
freq->max = get_max_freq_from_file(0);
freq->max = get_max_freq_from_file(cpu->first_core_id);
if(freq->max == 0) {
printWarn("Read max CPU frequency from udev and got 0 MHz");
@@ -950,7 +967,7 @@ struct frequency* get_frequency_info(struct cpuInfo* cpu) {
printWarn("Read max CPU frequency from CPUID and got 0 MHz");
#ifdef __linux__
printWarn("Using udev to detect frequency");
freq->max = get_max_freq_from_file(0);
freq->max = get_max_freq_from_file(cpu->first_core_id);
if(freq->max == 0) {
printWarn("Read max CPU frequency from udev and got 0 MHz");
@@ -962,6 +979,15 @@ struct frequency* get_frequency_info(struct cpuInfo* cpu) {
}
}
#ifdef __linux__
if (freq->max == UNKNOWN_DATA || measure_max_frequency_flag()) {
if (freq->max == UNKNOWN_DATA)
printWarn("All previous methods failed, measuring CPU frequency");
freq->max = measure_max_frequency(cpu->first_core_id);
freq->measured = true;
}
#endif
return freq;
}
@@ -983,24 +1009,33 @@ char* get_str_topology(struct cpuInfo* cpu, struct topology* topo, bool dual_soc
string = emalloc(sizeof(char) * (strlen(STRING_UNKNOWN) + 1));
strcpy(string, STRING_UNKNOWN);
}
else if(topo->smt_supported > 1) {
else {
char cores_str[6];
memset(cores_str, 0, sizeof(char) * 6);
if (topo->physical_cores * topo_sockets > 1)
strcpy(cores_str, "cores");
else
strcpy(cores_str, "core");
if(topo->smt_supported > 1) {
// 4 for digits, 21 for ' cores (SMT disabled)' which is the longest possible output
uint32_t max_size = 4+21+1;
string = emalloc(sizeof(char) * max_size);
if(topo->smt_available > 1)
snprintf(string, max_size, "%d cores (%d threads)", topo->physical_cores * topo_sockets, topo->logical_cores * topo_sockets);
snprintf(string, max_size, "%d %s (%d threads)", topo->physical_cores * topo_sockets, cores_str, topo->logical_cores * topo_sockets);
else {
if(cpu->cpu_vendor == CPU_VENDOR_AMD)
snprintf(string, max_size, "%d cores (SMT disabled)", topo->physical_cores * topo_sockets);
snprintf(string, max_size, "%d %s (SMT disabled)", topo->physical_cores * topo_sockets, cores_str);
else
snprintf(string, max_size, "%d cores (HT disabled)", topo->physical_cores * topo_sockets);
snprintf(string, max_size, "%d %s (HT disabled)", topo->physical_cores * topo_sockets, cores_str);
}
}
else {
uint32_t max_size = 4+7+1;
string = emalloc(sizeof(char) * max_size);
snprintf(string, max_size, "%d cores",topo->physical_cores * topo_sockets);
snprintf(string, max_size, "%d %s",topo->physical_cores * topo_sockets, cores_str);
}
}
return string;

View File

@@ -392,6 +392,25 @@ struct uarch* get_uarch_from_cpuid_amd(uint32_t ef, uint32_t f, uint32_t em, uin
return arch;
}
struct uarch* get_uarch_from_cpuid_hygon(uint32_t ef, uint32_t f, uint32_t em, uint32_t m, int s) {
struct uarch* arch = emalloc(sizeof(struct uarch));
// EF: Extended Family //
// F: Family //
// EM: Extended Model //
// M: Model //
// S: Stepping //
// ----------------------------------------------------------------------------- //
// EF F EM M S //
UARCH_START
// https://www.phoronix.com/news/Hygon-Dhyana-AMD-China-CPUs
CHECK_UARCH(arch, 9, 15, 0, 1, NA, "Zen", UARCH_ZEN, UNK) // https://github.com/Dr-Noob/cpufetch/issues/244
// CHECK_UARCH(arch, 9, 15, 0, 2, NA, "???", ?????????, UNK) // http://instlatx64.atw.hu/
UARCH_END
return arch;
}
struct uarch* get_uarch_from_cpuid(struct cpuInfo* cpu, uint32_t dump, uint32_t ef, uint32_t f, uint32_t em, uint32_t m, int s) {
if(cpu->cpu_vendor == CPU_VENDOR_INTEL) {
struct uarch* arch = emalloc(sizeof(struct uarch));
@@ -436,8 +455,16 @@ struct uarch* get_uarch_from_cpuid(struct cpuInfo* cpu, uint32_t dump, uint32_t
}
return get_uarch_from_cpuid_intel(ef, f, em, m, s);
}
else
else if(cpu->cpu_vendor == CPU_VENDOR_AMD) {
return get_uarch_from_cpuid_amd(ef, f, em, m, s);
}
else if(cpu->cpu_vendor == CPU_VENDOR_HYGON) {
return get_uarch_from_cpuid_hygon(ef, f, em, m, s);
}
else {
printBug("Invalid CPU vendor: %d", cpu->cpu_vendor);
return NULL;
}
}
// If we cannot get the CPU name from CPUID, try to infer it from uarch