mirror of
https://github.com/Dr-Noob/cpufetch.git
synced 2026-03-25 07:50:40 +01:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad6c3c88ce | ||
|
|
e114bde128 | ||
|
|
7164409ca2 | ||
|
|
08f79bb914 | ||
|
|
b457c86100 | ||
|
|
e5d86289b5 | ||
|
|
c6c4d8b6fd | ||
|
|
c8fde107dd | ||
|
|
b076189b32 | ||
|
|
d43229359a | ||
|
|
ba047c76e3 | ||
|
|
942a86c04f | ||
|
|
ea338a68c8 | ||
|
|
d7b7e2b62d | ||
|
|
941bf35d03 | ||
|
|
131d860de6 |
12
Makefile
12
Makefile
@@ -1,19 +1,21 @@
|
|||||||
CXX=gcc
|
CXX=gcc
|
||||||
|
|
||||||
CXXFLAGS=-Wall -Wextra -Werror -fstack-protector-all -pedantic -Wno-unused -std=c99
|
CXXFLAGS=-Wall -Wextra -Werror -pedantic -fstack-protector-all -pedantic -std=c99
|
||||||
SANITY_FLAGS=-Wfloat-equal -Wshadow -Wpointer-arith -Wstrict-overflow=5 -Wformat=2
|
SANITY_FLAGS=-Wfloat-equal -Wshadow -Wpointer-arith -Wstrict-overflow=5 -Wformat=2
|
||||||
|
|
||||||
SRC_DIR=src/
|
SRC_DIR=src/
|
||||||
SOURCE=$(SRC_DIR)main.c $(SRC_DIR)standart.c $(SRC_DIR)extended.c $(SRC_DIR)cpuid.c $(SRC_DIR)printer.c $(SRC_DIR)args.c $(SRC_DIR)global.c
|
SOURCE=$(SRC_DIR)main.c $(SRC_DIR)cpuid.c $(SRC_DIR)apic.c $(SRC_DIR)cpuid_asm.c $(SRC_DIR)printer.c $(SRC_DIR)args.c $(SRC_DIR)global.c
|
||||||
HEADERS=$(SRC_DIR)standart.h $(SRC_DIR)extended.h $(SRC_DIR)cpuid.h $(SRC_DIR)printer.h $(SRC_DIR)ascii.h $(SRC_DIR)args.h $(SRC_DIR)global.h
|
HEADERS=$(SRC_DIR)cpuid.h $(SRC_DIR)apic.h $(SRC_DIR)cpuid_asm.h $(SRC_DIR)printer.h $(SRC_DIR)ascii.h $(SRC_DIR)args.h $(SRC_DIR)global.h
|
||||||
|
|
||||||
ifneq ($(OS),Windows_NT)
|
ifneq ($(OS),Windows_NT)
|
||||||
SOURCE += $(SRC_DIR)udev.c
|
SOURCE += $(SRC_DIR)udev.c
|
||||||
HEADERS += $(SRC_DIR)udev.h
|
HEADERS += $(SRC_DIR)udev.h
|
||||||
|
OUTPUT=cpufetch
|
||||||
|
else
|
||||||
|
SANITY_FLAGS += -Wno-pedantic-ms-format
|
||||||
|
OUTPUT=cpufetch.exe
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OUTPUT=cpufetch
|
|
||||||
|
|
||||||
all: $(OUTPUT)
|
all: $(OUTPUT)
|
||||||
|
|
||||||
debug: CXXFLAGS += -g -O0
|
debug: CXXFLAGS += -g -O0
|
||||||
|
|||||||
260
src/apic.c
Normal file
260
src/apic.c
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <sched.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "apic.h"
|
||||||
|
#include "cpuid_asm.h"
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bit_scan_reverse and create_mask code taken from:
|
||||||
|
* https://software.intel.com/content/www/us/en/develop/articles/intel-64-architecture-processor-topology-enumeration.html
|
||||||
|
*/
|
||||||
|
unsigned char bit_scan_reverse(uint32_t* index, uint64_t mask) {
|
||||||
|
for(uint64_t i = (8 * sizeof(uint64_t)); i > 0; i--) {
|
||||||
|
if((mask & (1LL << (i-1))) != 0) {
|
||||||
|
*index = (uint64_t) (i-1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (unsigned char) (mask != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t create_mask(uint32_t num_entries, uint32_t *mask_width) {
|
||||||
|
uint32_t i = 0;
|
||||||
|
uint64_t k = 0;
|
||||||
|
|
||||||
|
// NearestPo2(numEntries) is the nearest power of 2 integer that is not less than numEntries
|
||||||
|
// The most significant bit of (numEntries * 2 -1) matches the above definition
|
||||||
|
|
||||||
|
k = (uint64_t)(num_entries) * 2 -1;
|
||||||
|
|
||||||
|
if (bit_scan_reverse(&i, k) == 0) {
|
||||||
|
if (mask_width) *mask_width = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask_width) *mask_width = i;
|
||||||
|
if (i == 31) return (uint32_t ) -1;
|
||||||
|
|
||||||
|
return (1 << i) -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_apic_id(bool x2apic_id) {
|
||||||
|
uint32_t eax = 0;
|
||||||
|
uint32_t ebx = 0;
|
||||||
|
uint32_t ecx = 0;
|
||||||
|
uint32_t edx = 0;
|
||||||
|
|
||||||
|
if(x2apic_id) {
|
||||||
|
eax = 0x0000000B;
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
return edx;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
eax = 0x00000001;
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
return (ebx >> 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bind_to_cpu(int cpu_id) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE process = GetCurrentProcess();
|
||||||
|
DWORD_PTR processAffinityMask = 1 << cpu_id;
|
||||||
|
return SetProcessAffinityMask(process, processAffinityMask);
|
||||||
|
#else
|
||||||
|
cpu_set_t currentCPU;
|
||||||
|
CPU_ZERO(¤tCPU);
|
||||||
|
CPU_SET(cpu_id, ¤tCPU);
|
||||||
|
if (sched_setaffinity (0, sizeof(currentCPU), ¤tCPU) == -1) {
|
||||||
|
perror("sched_setaffinity");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fill_topo_masks_apic(struct topology** topo) {
|
||||||
|
uint32_t eax = 0x00000001;
|
||||||
|
uint32_t ebx = 0;
|
||||||
|
uint32_t ecx = 0;
|
||||||
|
uint32_t edx = 0;
|
||||||
|
uint32_t core_plus_smt_id_max_cnt;
|
||||||
|
uint32_t core_id_max_cnt;
|
||||||
|
uint32_t smt_id_per_core_max_cnt;
|
||||||
|
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
|
||||||
|
core_plus_smt_id_max_cnt = (ebx >> 16) & 0xFF;
|
||||||
|
|
||||||
|
eax = 0x00000004;
|
||||||
|
ecx = 0;
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
|
||||||
|
core_id_max_cnt = (eax >> 26) + 1;
|
||||||
|
smt_id_per_core_max_cnt = core_plus_smt_id_max_cnt / core_id_max_cnt;
|
||||||
|
|
||||||
|
(*topo)->apic->smt_mask = create_mask(smt_id_per_core_max_cnt, &((*topo)->apic->smt_mask_width));
|
||||||
|
(*topo)->apic->core_mask = create_mask(core_id_max_cnt,&((*topo)->apic->pkg_mask_shift));
|
||||||
|
(*topo)->apic->pkg_mask_shift += (*topo)->apic->smt_mask_width;
|
||||||
|
(*topo)->apic->core_mask <<= (*topo)->apic->smt_mask_width;
|
||||||
|
(*topo)->apic->pkg_mask = (-1) ^ ((*topo)->apic->core_mask | (*topo)->apic->smt_mask);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fill_topo_masks_x2apic(struct topology** topo) {
|
||||||
|
int32_t level_type;
|
||||||
|
int32_t level_shift;
|
||||||
|
|
||||||
|
int32_t coreplus_smt_mask = 0;
|
||||||
|
bool level2 = false;
|
||||||
|
bool level1 = false;
|
||||||
|
|
||||||
|
uint32_t eax = 0;
|
||||||
|
uint32_t ebx = 0;
|
||||||
|
uint32_t ecx = 0;
|
||||||
|
uint32_t edx = 0;
|
||||||
|
uint32_t i = 0;
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
eax = 0x0000000B;
|
||||||
|
ecx = i;
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
if(ebx == 0) break;
|
||||||
|
|
||||||
|
level_type = (ecx >> 8) & 0xFF;
|
||||||
|
level_shift = eax & 0xFFF;
|
||||||
|
|
||||||
|
switch(level_type) {
|
||||||
|
case 1: // SMT
|
||||||
|
(*topo)->apic->smt_mask = ~(0xFFFFFFFF << level_shift);
|
||||||
|
(*topo)->apic->smt_mask_width = level_shift;
|
||||||
|
(*topo)->smt_supported = ebx & 0xFFFF;
|
||||||
|
level1 = true;
|
||||||
|
break;
|
||||||
|
case 2: // Core
|
||||||
|
coreplus_smt_mask = ~(0xFFFFFFFF << level_shift);
|
||||||
|
(*topo)->apic->pkg_mask_shift = level_shift;
|
||||||
|
(*topo)->apic->pkg_mask = (-1) ^ coreplus_smt_mask;
|
||||||
|
level2 = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printErr("Found invalid level when querying topology: %d", level_type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++; // sublevel to query
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level1 && level2) {
|
||||||
|
(*topo)->apic->core_mask = coreplus_smt_mask ^ (*topo)->apic->smt_mask;
|
||||||
|
}
|
||||||
|
else if (!level2 && level1) {
|
||||||
|
(*topo)->apic->core_mask = 0;
|
||||||
|
(*topo)->apic->pkg_mask_shift = (*topo)->apic->smt_mask_width;
|
||||||
|
(*topo)->apic->pkg_mask = (-1) ^ (*topo)->apic->smt_mask;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printErr("SMT level was not found when querying topology");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool build_topo_from_apic(uint32_t* apic_pkg, uint32_t* apic_smt, struct topology** topo) {
|
||||||
|
uint32_t sockets[64];
|
||||||
|
uint32_t smt[64];
|
||||||
|
|
||||||
|
memset(sockets, 0, sizeof(uint32_t) * 64);
|
||||||
|
memset(smt, 0, sizeof(uint32_t) * 64);
|
||||||
|
|
||||||
|
for(int i=0; i < (*topo)->total_cores; i++) {
|
||||||
|
sockets[apic_pkg[i]] = 1;
|
||||||
|
smt[apic_smt[i]] = 1;
|
||||||
|
}
|
||||||
|
for(int i=0; i < 64; i++) {
|
||||||
|
if(sockets[i] != 0)
|
||||||
|
(*topo)->sockets++;
|
||||||
|
if(smt[i] != 0)
|
||||||
|
(*topo)->smt_available++;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*topo)->logical_cores = (*topo)->total_cores / (*topo)->sockets;
|
||||||
|
(*topo)->physical_cores = (*topo)->logical_cores / (*topo)->smt_available;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_topology_from_apic(uint32_t cpuid_max_levels, struct topology** topo) {
|
||||||
|
uint32_t apic_id;
|
||||||
|
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);
|
||||||
|
bool x2apic_id = cpuid_max_levels >= 0x0000000B;
|
||||||
|
|
||||||
|
if(x2apic_id) {
|
||||||
|
if(!fill_topo_masks_x2apic(topo))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(!fill_topo_masks_apic(topo))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i < (*topo)->total_cores; i++) {
|
||||||
|
if(!bind_to_cpu(i)) {
|
||||||
|
printErr("Failed binding to CPU %d", i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
apic_id = get_apic_id(x2apic_id);
|
||||||
|
|
||||||
|
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;
|
||||||
|
apic_smt[i] = apic_id & (*topo)->apic->smt_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEBUG
|
||||||
|
for(int i=0; i < (*topo)->total_cores; i++)
|
||||||
|
printf("[%2d] 0x%.8X\n", i, apic_pkg[i]);
|
||||||
|
printf("\n");
|
||||||
|
for(int i=0; i < (*topo)->total_cores; i++)
|
||||||
|
printf("[%2d] 0x%.8X\n", i, apic_core[i]);
|
||||||
|
printf("\n");
|
||||||
|
for(int i=0; i < (*topo)->total_cores; i++)
|
||||||
|
printf("[%2d] 0x%.8X\n", i, apic_smt[i]);*/
|
||||||
|
|
||||||
|
|
||||||
|
bool ret = build_topo_from_apic(apic_pkg, apic_smt, topo);
|
||||||
|
|
||||||
|
// Assumption: If we cant get smt_available, we assume it is equal to smt_supported...
|
||||||
|
if(!x2apic_id) (*topo)->smt_supported = (*topo)->smt_available;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by AMD
|
||||||
|
uint32_t is_smt_enabled(struct topology* topo) {
|
||||||
|
uint32_t id;
|
||||||
|
|
||||||
|
for(int i = 0; i < topo->total_cores; i++) {
|
||||||
|
if(!bind_to_cpu(i)) {
|
||||||
|
printErr("Failed binding to CPU %d", i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
id = get_apic_id(true) & 1; // get the last bit
|
||||||
|
if(id == 1) return 2; // We assume there isn't any AMD CPU with more than 2th per core
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
18
src/apic.h
Normal file
18
src/apic.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef __APIC__
|
||||||
|
#define __APIC__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "cpuid.h"
|
||||||
|
|
||||||
|
struct apic {
|
||||||
|
uint32_t pkg_mask;
|
||||||
|
uint32_t pkg_mask_shift;
|
||||||
|
uint32_t core_mask;
|
||||||
|
uint32_t smt_mask_width;
|
||||||
|
uint32_t smt_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool get_topology_from_apic(uint32_t cpuid_max_levels, struct topology** topo);
|
||||||
|
uint32_t is_smt_enabled(struct topology* topo);
|
||||||
|
|
||||||
|
#endif
|
||||||
148
src/args.c
148
src/args.c
@@ -1,44 +1,48 @@
|
|||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include "args.h"
|
#include "args.h"
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
#define ARG_STR_STYLE "style"
|
#define ARG_STR_STYLE "style"
|
||||||
|
#define ARG_STR_COLOR "color"
|
||||||
#define ARG_STR_HELP "help"
|
#define ARG_STR_HELP "help"
|
||||||
#define ARG_STR_LEVELS "levels"
|
#define ARG_STR_LEVELS "levels"
|
||||||
|
#define ARG_STR_VERBOSE "verbose"
|
||||||
#define ARG_STR_VERSION "version"
|
#define ARG_STR_VERSION "version"
|
||||||
|
|
||||||
#define ARG_CHAR_STYLE 's'
|
#define ARG_CHAR_STYLE 's'
|
||||||
|
#define ARG_CHAR_COLOR 'c'
|
||||||
#define ARG_CHAR_HELP 'h'
|
#define ARG_CHAR_HELP 'h'
|
||||||
#define ARG_CHAR_LEVELS 'l'
|
#define ARG_CHAR_LEVELS 'l'
|
||||||
|
#define ARG_CHAR_VERBOSE 'v'
|
||||||
#define ARG_CHAR_VERSION 'v'
|
#define ARG_CHAR_VERSION 'v'
|
||||||
#define STYLE_STR_1 "default"
|
|
||||||
#define STYLE_STR_2 "dark"
|
#define STYLE_STR_1 "fancy"
|
||||||
#define STYLE_STR_3 "none"
|
#define STYLE_STR_2 "retro"
|
||||||
|
#define STYLE_STR_3 "legacy"
|
||||||
|
|
||||||
struct args_struct {
|
struct args_struct {
|
||||||
bool levels_flag;
|
bool levels_flag;
|
||||||
bool help_flag;
|
bool help_flag;
|
||||||
|
bool verbose_flag;
|
||||||
bool version_flag;
|
bool version_flag;
|
||||||
STYLE style;
|
STYLE style;
|
||||||
|
struct colors* colors;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* SYTLES_STR_LIST[STYLES_COUNT] = { STYLE_STR_1, STYLE_STR_2, STYLE_STR_3 };
|
static const char* SYTLES_STR_LIST[STYLES_COUNT] = { STYLE_STR_1, STYLE_STR_2, STYLE_STR_3 };
|
||||||
static struct args_struct args;
|
static struct args_struct args;
|
||||||
|
|
||||||
STYLE parse_style(char* style) {
|
|
||||||
int i = 0;
|
|
||||||
while(i != STYLES_COUNT && strcmp(SYTLES_STR_LIST[i],style) != 0)
|
|
||||||
i++;
|
|
||||||
|
|
||||||
if(i == STYLES_COUNT)
|
|
||||||
return STYLE_INVALID;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
STYLE get_style() {
|
STYLE get_style() {
|
||||||
return args.style;
|
return args.style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct colors* get_colors() {
|
||||||
|
return args.colors;
|
||||||
|
}
|
||||||
|
|
||||||
bool show_help() {
|
bool show_help() {
|
||||||
return args.help_flag;
|
return args.help_flag;
|
||||||
}
|
}
|
||||||
@@ -52,81 +56,173 @@ bool show_levels() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool verbose_enabled() {
|
bool verbose_enabled() {
|
||||||
|
return args.verbose_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
STYLE parse_style(char* style) {
|
||||||
|
int i = 0;
|
||||||
|
while(i != STYLES_COUNT && strcmp(SYTLES_STR_LIST[i],style) != 0)
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if(i == STYLES_COUNT)
|
||||||
|
return STYLE_INVALID;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_colors_struct(struct colors* cs) {
|
||||||
|
free(cs->c1);
|
||||||
|
free(cs->c2);
|
||||||
|
free(cs->c3);
|
||||||
|
free(cs->c4);
|
||||||
|
free(cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_color(char* optarg, struct colors** cs) {
|
||||||
|
*cs = malloc(sizeof(struct colors));
|
||||||
|
(*cs)->c1 = malloc(sizeof(struct color));
|
||||||
|
(*cs)->c2 = malloc(sizeof(struct color));
|
||||||
|
(*cs)->c3 = malloc(sizeof(struct color));
|
||||||
|
(*cs)->c4 = malloc(sizeof(struct color));
|
||||||
|
struct color** c1 = &((*cs)->c1);
|
||||||
|
struct color** c2 = &((*cs)->c2);
|
||||||
|
struct color** c3 = &((*cs)->c3);
|
||||||
|
struct color** c4 = &((*cs)->c4);
|
||||||
|
int32_t ret;
|
||||||
|
|
||||||
|
ret = sscanf(optarg, "%d,%d,%d:%d,%d,%d:%d,%d,%d:%d,%d,%d",
|
||||||
|
&(*c1)->R, &(*c1)->G, &(*c1)->B,
|
||||||
|
&(*c2)->R, &(*c2)->G, &(*c2)->B,
|
||||||
|
&(*c3)->R, &(*c3)->G, &(*c3)->B,
|
||||||
|
&(*c4)->R, &(*c4)->G, &(*c4)->B);
|
||||||
|
|
||||||
|
if(ret != 12) {
|
||||||
|
printErr("Expected to read 12 values for color but read %d", ret);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Refactor c1->R c2->R ... to c[i]->R
|
||||||
|
if((*c1)->R < 0 || (*c1)->R > 255) {
|
||||||
|
printErr("Red in color 1 is invalid. Must be in range (0, 255)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if((*c1)->G < 0 || (*c1)->G > 255) {
|
||||||
|
printErr("Green in color 1 is invalid. Must be in range (0, 255)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if((*c1)->B < 0 || (*c1)->B > 255) {
|
||||||
|
printErr("Blue in color 1 is invalid. Must be in range (0, 255)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if((*c2)->R < 0 || (*c2)->R > 255) {
|
||||||
|
printErr("Red in color 2 is invalid. Must be in range (0, 255)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if((*c2)->G < 0 || (*c2)->G > 255) {
|
||||||
|
printErr("Green in color 2 is invalid. Must be in range (0, 255)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if((*c2)->B < 0 || (*c2)->B > 255) {
|
||||||
|
printErr("Blue in color 2 is invalid. Must be in range (0, 255)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parse_args(int argc, char* argv[]) {
|
bool parse_args(int argc, char* argv[]) {
|
||||||
int c;
|
int c;
|
||||||
int digit_optind = 0;
|
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
opterr = 0;
|
opterr = 0;
|
||||||
|
|
||||||
|
bool color_flag = false;
|
||||||
args.levels_flag = false;
|
args.levels_flag = false;
|
||||||
|
args.verbose_flag = false;
|
||||||
args.help_flag = false;
|
args.help_flag = false;
|
||||||
args.style = STYLE_EMPTY;
|
args.style = STYLE_EMPTY;
|
||||||
|
args.colors = NULL;
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{ARG_STR_STYLE, required_argument, 0, ARG_CHAR_STYLE },
|
{ARG_STR_STYLE, required_argument, 0, ARG_CHAR_STYLE },
|
||||||
|
{ARG_STR_COLOR, required_argument, 0, ARG_CHAR_COLOR },
|
||||||
{ARG_STR_HELP, no_argument, 0, ARG_CHAR_HELP },
|
{ARG_STR_HELP, no_argument, 0, ARG_CHAR_HELP },
|
||||||
{ARG_STR_LEVELS, no_argument, 0, ARG_CHAR_LEVELS },
|
{ARG_STR_LEVELS, no_argument, 0, ARG_CHAR_LEVELS },
|
||||||
|
{ARG_STR_VERBOSE, no_argument, 0, ARG_CHAR_VERBOSE },
|
||||||
{ARG_STR_VERSION, no_argument, 0, ARG_CHAR_VERSION },
|
{ARG_STR_VERSION, no_argument, 0, ARG_CHAR_VERSION },
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv,"",long_options, &option_index);
|
c = getopt_long(argc, argv, "", long_options, &option_index);
|
||||||
|
|
||||||
while (c != -1) {
|
while (c != -1) {
|
||||||
if(c == ARG_CHAR_STYLE) {
|
if(c == ARG_CHAR_COLOR) {
|
||||||
|
if(color_flag) {
|
||||||
|
printErr("Color option specified more than once");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
color_flag = true;
|
||||||
|
if(!parse_color(optarg, &args.colors)) {
|
||||||
|
printErr("Color parsing failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(c == ARG_CHAR_STYLE) {
|
||||||
if(args.style != STYLE_EMPTY) {
|
if(args.style != STYLE_EMPTY) {
|
||||||
printf("ERROR: Style option specified more than once\n");
|
printErr("Style option specified more than once");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
args.style = parse_style(optarg);
|
args.style = parse_style(optarg);
|
||||||
if(args.style == STYLE_INVALID) {
|
if(args.style == STYLE_INVALID) {
|
||||||
printf("ERROR: Invalid style '%s'\n",optarg);
|
printErr("Invalid style '%s'",optarg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(c == ARG_CHAR_HELP) {
|
else if(c == ARG_CHAR_HELP) {
|
||||||
if(args.help_flag) {
|
if(args.help_flag) {
|
||||||
printf("ERROR: Help option specified more than once\n");
|
printErr("Help option specified more than once");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
args.help_flag = true;
|
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_LEVELS) {
|
else if(c == ARG_CHAR_LEVELS) {
|
||||||
if(args.levels_flag) {
|
if(args.levels_flag) {
|
||||||
printf("ERROR: Levels option specified more than once\n");
|
printErr("Levels option specified more than once");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
args.levels_flag = true;
|
args.levels_flag = true;
|
||||||
}
|
}
|
||||||
else if (c == ARG_CHAR_VERSION) {
|
else if (c == ARG_CHAR_VERSION) {
|
||||||
if(args.version_flag) {
|
if(args.version_flag) {
|
||||||
printf("ERROR: Version option specified more than once\n");
|
printErr("Version option specified more than once");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
args.version_flag = true;
|
args.version_flag = true;
|
||||||
}
|
}
|
||||||
else if(c == '?') {
|
else if(c == '?') {
|
||||||
printf("WARNING: Invalid options\n");
|
printWarn("Invalid options");
|
||||||
args.help_flag = true;
|
args.help_flag = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
printf("Bug at line number %d in file %s\n", __LINE__, __FILE__);
|
printBug("Bug at line number %d in file %s", __LINE__, __FILE__);
|
||||||
|
|
||||||
option_index = 0;
|
option_index = 0;
|
||||||
c = getopt_long(argc, argv,"",long_options, &option_index);
|
c = getopt_long(argc, argv,"",long_options, &option_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind < argc) {
|
if (optind < argc) {
|
||||||
printf("WARNING: Invalid options\n");
|
printWarn("Invalid options");
|
||||||
args.help_flag = true;
|
args.help_flag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((args.help_flag + args.version_flag + (args.style != STYLE_EMPTY)) > 1) {
|
if((args.help_flag + args.version_flag + color_flag) > 1) {
|
||||||
printf("WARNING: You should specify just one option\n");
|
printWarn("You should specify just one option");
|
||||||
args.help_flag = true;
|
args.help_flag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
19
src/args.h
19
src/args.h
@@ -2,13 +2,30 @@
|
|||||||
#define __ARGS__
|
#define __ARGS__
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct color {
|
||||||
|
int32_t R;
|
||||||
|
int32_t G;
|
||||||
|
int32_t B;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct colors {
|
||||||
|
struct color* c1;
|
||||||
|
struct color* c2;
|
||||||
|
struct color* c3;
|
||||||
|
struct color* c4;
|
||||||
|
};
|
||||||
|
|
||||||
#include "printer.h"
|
#include "printer.h"
|
||||||
|
|
||||||
bool parse_args(int argc, char* argv[]);
|
bool parse_args(int argc, char* argv[]);
|
||||||
STYLE get_style();
|
|
||||||
bool show_help();
|
bool show_help();
|
||||||
bool show_levels();
|
bool show_levels();
|
||||||
bool show_version();
|
bool show_version();
|
||||||
bool verbose_enabled();
|
bool verbose_enabled();
|
||||||
|
void free_colors_struct(struct colors* cs);
|
||||||
|
struct colors* get_colors();
|
||||||
|
STYLE get_style();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef __ASCII__
|
#ifndef __ASCII__
|
||||||
#define __ASCII__
|
#define __ASCII__
|
||||||
|
|
||||||
#define NUMBER_OF_LINES 20
|
#define NUMBER_OF_LINES 19
|
||||||
#define LINE_SIZE 62
|
#define LINE_SIZE 62
|
||||||
|
|
||||||
#define AMD_ASCII \
|
#define AMD_ASCII \
|
||||||
@@ -23,7 +23,6 @@
|
|||||||
\
|
\
|
||||||
\
|
\
|
||||||
\
|
\
|
||||||
\
|
|
||||||
"
|
"
|
||||||
|
|
||||||
#define INTEL_ASCII \
|
#define INTEL_ASCII \
|
||||||
@@ -45,7 +44,6 @@
|
|||||||
#### #### \
|
#### #### \
|
||||||
##### ########## \
|
##### ########## \
|
||||||
########## ################ \
|
########## ################ \
|
||||||
############################### \
|
############################### "
|
||||||
"
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
881
src/cpuid.c
881
src/cpuid.c
@@ -1,10 +1,875 @@
|
|||||||
#include "cpuid.h"
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include "udev.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
|
#include <stdio.h>
|
||||||
__asm volatile("cpuid"
|
#include <stdlib.h>
|
||||||
: "=a" (*eax),
|
#include <string.h>
|
||||||
"=b" (*ebx),
|
#include <assert.h>
|
||||||
"=c" (*ecx),
|
#include <stdbool.h>
|
||||||
"=d" (*edx)
|
|
||||||
: "0" (*eax), "2" (*ecx));
|
#include "cpuid.h"
|
||||||
|
#include "cpuid_asm.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "apic.h"
|
||||||
|
|
||||||
|
#define VENDOR_INTEL_STRING "GenuineIntel"
|
||||||
|
#define VENDOR_AMD_STRING "AuthenticAMD"
|
||||||
|
|
||||||
|
#define STRING_YES "Yes"
|
||||||
|
#define STRING_NO "No"
|
||||||
|
#define STRING_UNKNOWN "Unknown"
|
||||||
|
#define STRING_NONE "None"
|
||||||
|
#define STRING_MEGAHERZ "MHz"
|
||||||
|
#define STRING_GIGAHERZ "GHz"
|
||||||
|
#define STRING_KILOBYTES "KB"
|
||||||
|
#define STRING_MEGABYTES "MB"
|
||||||
|
|
||||||
|
#define CPU_NAME_MAX_LENGTH 64
|
||||||
|
|
||||||
|
#define MASK 0xFF
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cpuid reference: http://www.sandpile.org/x86/cpuid.htm
|
||||||
|
* cpuid amd: https://www.amd.com/system/files/TechDocs/25481.pdf
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct cpuInfo {
|
||||||
|
bool AVX;
|
||||||
|
bool AVX2;
|
||||||
|
bool AVX512;
|
||||||
|
bool SSE;
|
||||||
|
bool SSE2;
|
||||||
|
bool SSE3;
|
||||||
|
bool SSSE3;
|
||||||
|
bool SSE4a;
|
||||||
|
bool SSE4_1;
|
||||||
|
bool SSE4_2;
|
||||||
|
bool FMA3;
|
||||||
|
bool FMA4;
|
||||||
|
bool AES;
|
||||||
|
bool SHA;
|
||||||
|
|
||||||
|
VENDOR cpu_vendor;
|
||||||
|
|
||||||
|
char* cpu_name;
|
||||||
|
// Max cpuids levels
|
||||||
|
uint32_t maxLevels;
|
||||||
|
// Max cpuids extended levels
|
||||||
|
uint32_t maxExtendedLevels;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cache {
|
||||||
|
int32_t L1i;
|
||||||
|
int32_t L1d;
|
||||||
|
int32_t L2;
|
||||||
|
int32_t L3;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct frequency {
|
||||||
|
int64_t base;
|
||||||
|
int64_t max;
|
||||||
|
};
|
||||||
|
|
||||||
|
void init_cpu_info(struct cpuInfo* cpu) {
|
||||||
|
cpu->AVX = false;
|
||||||
|
cpu->AVX2 = false;
|
||||||
|
cpu->AVX512 = false;
|
||||||
|
cpu->SSE = false;
|
||||||
|
cpu->SSE2 = false;
|
||||||
|
cpu->SSE3 = false;
|
||||||
|
cpu->SSSE3 = false;
|
||||||
|
cpu->SSE4a = false;
|
||||||
|
cpu->SSE4_1 = false;
|
||||||
|
cpu->SSE4_2 = false;
|
||||||
|
cpu->FMA3 = false;
|
||||||
|
cpu->FMA4 = false;
|
||||||
|
cpu->AES = false;
|
||||||
|
cpu->SHA = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_topology_struct(struct topology** topo) {
|
||||||
|
(*topo)->total_cores = 0;
|
||||||
|
(*topo)->physical_cores = 0;
|
||||||
|
(*topo)->logical_cores = 0;
|
||||||
|
(*topo)->smt_available = 0;
|
||||||
|
(*topo)->smt_supported = 0;
|
||||||
|
(*topo)->sockets = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_cpu_vendor_internal(char* name, uint32_t ebx,uint32_t ecx,uint32_t edx) {
|
||||||
|
name[__COUNTER__] = ebx & MASK;
|
||||||
|
name[__COUNTER__] = (ebx>>8) & MASK;
|
||||||
|
name[__COUNTER__] = (ebx>>16) & MASK;
|
||||||
|
name[__COUNTER__] = (ebx>>24) & MASK;
|
||||||
|
|
||||||
|
name[__COUNTER__] = edx & MASK;
|
||||||
|
name[__COUNTER__] = (edx>>8) & MASK;
|
||||||
|
name[__COUNTER__] = (edx>>16) & MASK;
|
||||||
|
name[__COUNTER__] = (edx>>24) & MASK;
|
||||||
|
|
||||||
|
name[__COUNTER__] = ecx & MASK;
|
||||||
|
name[__COUNTER__] = (ecx>>8) & MASK;
|
||||||
|
name[__COUNTER__] = (ecx>>16) & MASK;
|
||||||
|
name[__COUNTER__] = (ecx>>24) & MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_cpu_name_internal() {
|
||||||
|
uint32_t eax = 0;
|
||||||
|
uint32_t ebx = 0;
|
||||||
|
uint32_t ecx = 0;
|
||||||
|
uint32_t edx = 0;
|
||||||
|
uint32_t c = 0;
|
||||||
|
|
||||||
|
char * name = malloc(sizeof(char) * CPU_NAME_MAX_LENGTH);
|
||||||
|
memset(name, 0, CPU_NAME_MAX_LENGTH);
|
||||||
|
|
||||||
|
for(int i=0; i < 3; i++) {
|
||||||
|
eax = 0x80000002 + i;
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
|
||||||
|
name[c++] = eax & MASK;
|
||||||
|
name[c++] = (eax>>8) & MASK;
|
||||||
|
name[c++] = (eax>>16) & MASK;
|
||||||
|
name[c++] = (eax>>24) & MASK;
|
||||||
|
name[c++] = ebx & MASK;
|
||||||
|
name[c++] = (ebx>>8) & MASK;
|
||||||
|
name[c++] = (ebx>>16) & MASK;
|
||||||
|
name[c++] = (ebx>>24) & MASK;
|
||||||
|
name[c++] = ecx & MASK;
|
||||||
|
name[c++] = (ecx>>8) & MASK;
|
||||||
|
name[c++] = (ecx>>16) & MASK;
|
||||||
|
name[c++] = (ecx>>24) & MASK;
|
||||||
|
name[c++] = edx & MASK;
|
||||||
|
name[c++] = (edx>>8) & MASK;
|
||||||
|
name[c++] = (edx>>16) & MASK;
|
||||||
|
name[c++] = (edx>>24) & MASK;
|
||||||
|
}
|
||||||
|
name[c] = '\0';
|
||||||
|
|
||||||
|
//Remove unused characters
|
||||||
|
char *str = name;
|
||||||
|
char *dest = name;
|
||||||
|
// Remove spaces before name
|
||||||
|
while (*str != '\0' && *str == ' ')str++;
|
||||||
|
// Remove spaces between the name and after it
|
||||||
|
while (*str != '\0') {
|
||||||
|
while (*str == ' ' && *(str + 1) == ' ') str++;
|
||||||
|
*dest++ = *str++;
|
||||||
|
}
|
||||||
|
*dest = '\0';
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cpuInfo* get_cpu_info() {
|
||||||
|
struct cpuInfo* cpu = malloc(sizeof(struct cpuInfo));
|
||||||
|
init_cpu_info(cpu);
|
||||||
|
uint32_t eax = 0;
|
||||||
|
uint32_t ebx = 0;
|
||||||
|
uint32_t ecx = 0;
|
||||||
|
uint32_t edx = 0;
|
||||||
|
|
||||||
|
//Get max cpuid level
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
cpu->maxLevels = eax;
|
||||||
|
|
||||||
|
//Fill vendor
|
||||||
|
char name[13];
|
||||||
|
memset(name,0,13);
|
||||||
|
get_cpu_vendor_internal(name, ebx, ecx, edx);
|
||||||
|
|
||||||
|
if(strcmp(VENDOR_INTEL_STRING,name) == 0)
|
||||||
|
cpu->cpu_vendor = VENDOR_INTEL;
|
||||||
|
else if (strcmp(VENDOR_AMD_STRING,name) == 0)
|
||||||
|
cpu->cpu_vendor = VENDOR_AMD;
|
||||||
|
else {
|
||||||
|
cpu->cpu_vendor = VENDOR_INVALID;
|
||||||
|
printErr("Unknown CPU vendor: %s", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get max extended level
|
||||||
|
eax = 0x80000000;
|
||||||
|
ebx = 0;
|
||||||
|
ecx = 0;
|
||||||
|
edx = 0;
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
cpu->maxExtendedLevels = eax;
|
||||||
|
|
||||||
|
//Fill instructions support
|
||||||
|
if (cpu->maxLevels >= 0x00000001){
|
||||||
|
eax = 0x00000001;
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
cpu->SSE = (edx & ((int)1 << 25)) != 0;
|
||||||
|
cpu->SSE2 = (edx & ((int)1 << 26)) != 0;
|
||||||
|
cpu->SSE3 = (ecx & ((int)1 << 0)) != 0;
|
||||||
|
|
||||||
|
cpu->SSSE3 = (ecx & ((int)1 << 9)) != 0;
|
||||||
|
cpu->SSE4_1 = (ecx & ((int)1 << 19)) != 0;
|
||||||
|
cpu->SSE4_2 = (ecx & ((int)1 << 20)) != 0;
|
||||||
|
|
||||||
|
cpu->AES = (ecx & ((int)1 << 25)) != 0;
|
||||||
|
|
||||||
|
cpu->AVX = (ecx & ((int)1 << 28)) != 0;
|
||||||
|
cpu->FMA3 = (ecx & ((int)1 << 12)) != 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printWarn("Can't read features information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000001, cpu->maxLevels);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpu->maxLevels >= 0x00000007){
|
||||||
|
eax = 0x00000007;
|
||||||
|
ecx = 0x00000000;
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
cpu->AVX2 = (ebx & ((int)1 << 5)) != 0;
|
||||||
|
cpu->SHA = (ebx & ((int)1 << 29)) != 0;
|
||||||
|
cpu->AVX512 = (((ebx & ((int)1 << 16)) != 0) ||
|
||||||
|
((ebx & ((int)1 << 28)) != 0) ||
|
||||||
|
((ebx & ((int)1 << 26)) != 0) ||
|
||||||
|
((ebx & ((int)1 << 27)) != 0) ||
|
||||||
|
((ebx & ((int)1 << 31)) != 0) ||
|
||||||
|
((ebx & ((int)1 << 30)) != 0) ||
|
||||||
|
((ebx & ((int)1 << 17)) != 0) ||
|
||||||
|
((ebx & ((int)1 << 21)) != 0));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printWarn("Can't read features information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000007, cpu->maxLevels);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpu->maxExtendedLevels >= 0x80000001){
|
||||||
|
eax = 0x80000001;
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
cpu->SSE4a = (ecx & ((int)1 << 6)) != 0;
|
||||||
|
cpu->FMA4 = (ecx & ((int)1 << 16)) != 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printWarn("Can't read features information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", 0x80000001, cpu->maxExtendedLevels);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpu->maxExtendedLevels >= 0x80000004){
|
||||||
|
cpu->cpu_name = get_str_cpu_name_internal();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cpu->cpu_name = malloc(sizeof(char)*8);
|
||||||
|
sprintf(cpu->cpu_name,"Unknown");
|
||||||
|
printWarn("Can't read cpu name from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", 0x80000004, cpu->maxExtendedLevels);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main reference: https://software.intel.com/content/www/us/en/develop/articles/intel-64-architecture-processor-topology-enumeration.html
|
||||||
|
// Very interesting resource: https://wiki.osdev.org/Detecting_CPU_Topology_(80x86)
|
||||||
|
struct topology* get_topology_info(struct cpuInfo* cpu) {
|
||||||
|
struct topology* topo = malloc(sizeof(struct topology));
|
||||||
|
topo->apic = malloc(sizeof(struct apic));
|
||||||
|
init_topology_struct(&topo);
|
||||||
|
uint32_t eax = 0;
|
||||||
|
uint32_t ebx = 0;
|
||||||
|
uint32_t ecx = 0;
|
||||||
|
uint32_t edx = 0;
|
||||||
|
|
||||||
|
// Ask the OS the total number of cores it sees
|
||||||
|
// If we have one socket, it will be same as the cpuid,
|
||||||
|
// but in dual socket it will not!
|
||||||
|
// TODO: Replace by apic?
|
||||||
|
#ifdef _WIN32
|
||||||
|
SYSTEM_INFO info;
|
||||||
|
GetSystemInfo(&info);
|
||||||
|
topo->total_cores = info.dwNumberOfProcessors;
|
||||||
|
#else
|
||||||
|
if((topo->total_cores = sysconf(_SC_NPROCESSORS_ONLN)) == -1) {
|
||||||
|
perror("sysconf");
|
||||||
|
topo->total_cores = topo->logical_cores; // fallback
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch(cpu->cpu_vendor) {
|
||||||
|
case VENDOR_INTEL:
|
||||||
|
if (cpu->maxLevels >= 0x00000004) {
|
||||||
|
get_topology_from_apic(cpu->maxLevels, &topo);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printErr("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 VENDOR_AMD:
|
||||||
|
if (cpu->maxExtendedLevels >= 0x80000008) {
|
||||||
|
eax = 0x80000008;
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
topo->logical_cores = (ecx & 0xFF) + 1;
|
||||||
|
|
||||||
|
if (cpu->maxExtendedLevels >= 0x8000001E) {
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
topo->physical_cores = 1;
|
||||||
|
topo->logical_cores = 1;
|
||||||
|
topo->smt_supported = 1;
|
||||||
|
}
|
||||||
|
if (cpu->maxLevels >= 0x0000000B) {
|
||||||
|
topo->smt_available = is_smt_enabled(topo);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printWarn("Can't read topology information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x0000000B, cpu->maxLevels);
|
||||||
|
topo->smt_available = 1;
|
||||||
|
}
|
||||||
|
topo->physical_cores = topo->logical_cores / topo->smt_available;
|
||||||
|
|
||||||
|
if(topo->smt_supported > 1)
|
||||||
|
topo->sockets = topo->total_cores / topo->smt_supported / topo->physical_cores; // Idea borrowed from lscpu
|
||||||
|
else
|
||||||
|
topo->sockets = topo->total_cores / topo->physical_cores;
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printBug("Cant get topology because VENDOR is empty");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return topo;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cache* get_cache_info(struct cpuInfo* cpu) {
|
||||||
|
struct cache* cach = malloc(sizeof(struct cache));
|
||||||
|
uint32_t eax = 0;
|
||||||
|
uint32_t ebx = 0;
|
||||||
|
uint32_t ecx = 0;
|
||||||
|
uint32_t edx = 0;
|
||||||
|
uint32_t level;
|
||||||
|
|
||||||
|
// We use standart 0x00000004 for Intel
|
||||||
|
// We use extended 0x8000001D for AMD
|
||||||
|
if(cpu->cpu_vendor == VENDOR_INTEL) {
|
||||||
|
level = 0x00000004;
|
||||||
|
if(cpu->maxLevels < level) {
|
||||||
|
printErr("Can't read cache information from cpuid (needed level is %d, max is %d)", level, cpu->maxLevels);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
level = 0x8000001D;
|
||||||
|
if(cpu->maxExtendedLevels < level) {
|
||||||
|
printErr("Can't read cache information from cpuid (needed extended level is %d, max is %d)", level, cpu->maxExtendedLevels);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We suppose there are 4 caches (at most)
|
||||||
|
for(int i=0; i < 4; i++) {
|
||||||
|
eax = level; // get cache info
|
||||||
|
ebx = 0;
|
||||||
|
ecx = i; // cache id
|
||||||
|
edx = 0;
|
||||||
|
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
|
||||||
|
int32_t cache_type = eax & 0x1F;
|
||||||
|
|
||||||
|
// If its 0, we tried fetching a non existing cache
|
||||||
|
if (cache_type > 0) {
|
||||||
|
int32_t cache_level = (eax >>= 5) & 0x7;
|
||||||
|
uint32_t cache_sets = ecx + 1;
|
||||||
|
uint32_t cache_coherency_line_size = (ebx & 0xFFF) + 1;
|
||||||
|
uint32_t cache_physical_line_partitions = ((ebx >>= 12) & 0x3FF) + 1;
|
||||||
|
uint32_t cache_ways_of_associativity = ((ebx >>= 10) & 0x3FF) + 1;
|
||||||
|
|
||||||
|
int32_t cache_total_size = cache_ways_of_associativity * cache_physical_line_partitions * cache_coherency_line_size * cache_sets;
|
||||||
|
|
||||||
|
switch (cache_type) {
|
||||||
|
case 1: // Data Cache (We assume this is L1d)
|
||||||
|
if(cache_level != 1) {
|
||||||
|
printBug("Found data cache at level %d (expected 1)", cache_level);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cach->L1d = cache_total_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // Instruction Cache (We assume this is L1i)
|
||||||
|
if(cache_level != 1) {
|
||||||
|
printBug("Found instruction cache at level %d (expected 1)", cache_level);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cach->L1i = cache_total_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: // Unified Cache (This may be L2 or L3)
|
||||||
|
if(cache_level == 2) cach->L2 = cache_total_size;
|
||||||
|
else if(cache_level == 3) cach->L3 = cache_total_size;
|
||||||
|
else {
|
||||||
|
printBug("Found unified cache at level %d (expected == 2 or 3)", cache_level);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // Unknown Type Cache
|
||||||
|
printBug("Unknown Type Cache found at ID %d", i);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(i == 2) cach->L2 = UNKNOWN;
|
||||||
|
else if(i == 3) cach->L3 = UNKNOWN;
|
||||||
|
else {
|
||||||
|
printBug("Could not find cache ID %d", i);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity checks. If we read values greater than this, they can't be valid ones
|
||||||
|
// The values were chosen by me
|
||||||
|
if(cach->L1i > 64 * 1024) {
|
||||||
|
printBug("Invalid L1i size: %dKB", cach->L1i/1024);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(cach->L1d > 64 * 1024) {
|
||||||
|
printBug("Invalid L1d size: %dKB", cach->L1d/1024);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(cach->L2 != UNKNOWN) {
|
||||||
|
if(cach->L3 != UNKNOWN && cach->L2 > 2 * 1048576) {
|
||||||
|
printBug("Invalid L2 size: %dMB", cach->L2/(1048576));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if(cach->L2 > 100 * 1048576) {
|
||||||
|
printBug("Invalid L2 size: %dMB", cach->L2/(1048576));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(cach->L3 != UNKNOWN && cach->L3 > 100 * 1048576) {
|
||||||
|
printBug("Invalid L3 size: %dMB", cach->L3/(1048576));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(cach->L2 == UNKNOWN) {
|
||||||
|
printBug("Could not find L2 cache");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cach;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct frequency* get_frequency_info(struct cpuInfo* cpu) {
|
||||||
|
struct frequency* freq = malloc(sizeof(struct frequency));
|
||||||
|
|
||||||
|
if(cpu->maxLevels < 0x16) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
printErr("Can't read frequency information from cpuid (needed level is %d, max is %d)", 0x16, cpu->maxLevels);
|
||||||
|
freq->base = UNKNOWN;
|
||||||
|
freq->max = UNKNOWN;
|
||||||
|
#else
|
||||||
|
printWarn("Can't read frequency information from cpuid (needed level is %d, max is %d). Using udev", 0x16, cpu->maxLevels);
|
||||||
|
freq->base = UNKNOWN;
|
||||||
|
freq->max = get_max_freq_from_file();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint32_t eax = 0x16;
|
||||||
|
uint32_t ebx = 0;
|
||||||
|
uint32_t ecx = 0;
|
||||||
|
uint32_t edx = 0;
|
||||||
|
|
||||||
|
cpuid(&eax, &ebx, &ecx, &edx);
|
||||||
|
|
||||||
|
freq->base = eax;
|
||||||
|
freq->max = ebx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_nsockets(struct topology* topo) {
|
||||||
|
return topo->sockets;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_freq(struct frequency* freq) {
|
||||||
|
return freq->max;
|
||||||
|
}
|
||||||
|
|
||||||
|
VENDOR get_cpu_vendor(struct cpuInfo* cpu) {
|
||||||
|
return cpu->cpu_vendor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug_cpu_info(struct cpuInfo* cpu) {
|
||||||
|
printf("AVX=%s\n", cpu->AVX ? "true" : "false");
|
||||||
|
printf("AVX2=%s\n", cpu->AVX2 ? "true" : "false");
|
||||||
|
printf("AVX512=%s\n\n", cpu->AVX512 ? "true" : "false");
|
||||||
|
|
||||||
|
printf("SSE=%s\n", cpu->SSE ? "true" : "false");
|
||||||
|
printf("SSE2=%s\n", cpu->SSE2 ? "true" : "false");
|
||||||
|
printf("SSE3=%s\n", cpu->SSE3 ? "true" : "false");
|
||||||
|
printf("SSSE3=%s\n", cpu->SSSE3 ? "true" : "false");
|
||||||
|
printf("SSE4a=%s\n", cpu->SSE4a ? "true" : "false");
|
||||||
|
printf("SSE4_1=%s\n", cpu->SSE4_1 ? "true" : "false");
|
||||||
|
printf("SSE4_2=%s\n\n", cpu->SSE4_2 ? "true" : "false");
|
||||||
|
|
||||||
|
printf("FMA3=%s\n", cpu->FMA3 ? "true" : "false");
|
||||||
|
printf("FMA4=%s\n\n", cpu->FMA4 ? "true" : "false");
|
||||||
|
|
||||||
|
printf("AES=%s\n", cpu->AES ? "true" : "false");
|
||||||
|
printf("SHA=%s\n", cpu->SHA ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug_cache(struct cache* cach) {
|
||||||
|
printf("L1i=%dB\n",cach->L1i);
|
||||||
|
printf("L1d=%dB\n",cach->L1d);
|
||||||
|
printf("L2=%dB\n",cach->L2);
|
||||||
|
printf("L3=%dB\n",cach->L3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug_frequency(struct frequency* freq) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
printf("maxf=%I64d Mhz\n",freq->max);
|
||||||
|
printf("basef=%I64d Mhz\n",freq->base);
|
||||||
|
#else
|
||||||
|
printf("maxf=%ld Mhz\n",freq->max);
|
||||||
|
printf("basef=%ld Mhz\n",freq->base);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** STRING FUNCTIONS ***/
|
||||||
|
|
||||||
|
char* get_str_peak_performance(struct cpuInfo* cpu, struct topology* topo, int64_t freq) {
|
||||||
|
/***
|
||||||
|
PP = PeakPerformance
|
||||||
|
SP = SinglePrecision
|
||||||
|
|
||||||
|
PP(SP) =
|
||||||
|
N_CORES *
|
||||||
|
FREQUENCY *
|
||||||
|
2(Two vector units) *
|
||||||
|
2(If cpu has fma) *
|
||||||
|
16(If AVX512), 8(If AVX), 4(If SSE) *
|
||||||
|
|
||||||
|
***/
|
||||||
|
|
||||||
|
//7 for GFLOP/s and 6 for digits,eg 412.14
|
||||||
|
uint32_t size = 7+6+1+1;
|
||||||
|
assert(strlen(STRING_UNKNOWN)+1 <= size);
|
||||||
|
char* string = malloc(sizeof(char)*size);
|
||||||
|
|
||||||
|
//First check we have consistent data
|
||||||
|
if(freq == UNKNOWN) {
|
||||||
|
snprintf(string,strlen(STRING_UNKNOWN)+1,STRING_UNKNOWN);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
double flops = topo->physical_cores*(freq*1000000);
|
||||||
|
|
||||||
|
// Intel USUALLY has two VPUs. I have never seen an AMD
|
||||||
|
// with two VPUs.
|
||||||
|
if(cpu->cpu_vendor == VENDOR_INTEL) flops = flops * 2;
|
||||||
|
|
||||||
|
if(cpu->FMA3 || cpu->FMA4)
|
||||||
|
flops = flops*2;
|
||||||
|
|
||||||
|
if(cpu->AVX512)
|
||||||
|
flops = flops*16;
|
||||||
|
else if(cpu->AVX || cpu->AVX2)
|
||||||
|
flops = flops*8;
|
||||||
|
else if(cpu->SSE)
|
||||||
|
flops = flops*4;
|
||||||
|
|
||||||
|
if(flops >= (double)1000000000000.0)
|
||||||
|
snprintf(string,size,"%.2f TFLOP/s",flops/1000000000000);
|
||||||
|
else if(flops >= 1000000000.0)
|
||||||
|
snprintf(string,size,"%.2f GFLOP/s",flops/1000000000);
|
||||||
|
else
|
||||||
|
snprintf(string,size,"%.2f MFLOP/s",flops/1000000);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Refactoring
|
||||||
|
char* get_str_topology(struct cpuInfo* cpu, struct topology* topo, bool dual_socket) {
|
||||||
|
char* string;
|
||||||
|
if(topo->smt_supported > 1) {
|
||||||
|
//3 for digits, 21 for ' cores (SMT disabled)' which is the longest possible output
|
||||||
|
uint32_t size = 3+21+1;
|
||||||
|
string = malloc(sizeof(char)*size);
|
||||||
|
if(dual_socket) {
|
||||||
|
if(topo->smt_available > 1)
|
||||||
|
snprintf(string, size, "%d cores (%d threads)",topo->physical_cores * topo->sockets, topo->logical_cores * topo->sockets);
|
||||||
|
else {
|
||||||
|
if(cpu->cpu_vendor == VENDOR_AMD)
|
||||||
|
snprintf(string, size, "%d cores (SMT disabled)",topo->physical_cores * topo->sockets);
|
||||||
|
else
|
||||||
|
snprintf(string, size, "%d cores (HT disabled)",topo->physical_cores * topo->sockets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(topo->smt_available > 1)
|
||||||
|
snprintf(string, size, "%d cores (%d threads)",topo->physical_cores,topo->logical_cores);
|
||||||
|
else {
|
||||||
|
if(cpu->cpu_vendor == VENDOR_AMD)
|
||||||
|
snprintf(string, size, "%d cores (SMT disabled)",topo->physical_cores);
|
||||||
|
else
|
||||||
|
snprintf(string, size, "%d cores (HT disabled)",topo->physical_cores);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uint32_t size = 3+7+1;
|
||||||
|
string = malloc(sizeof(char)*size);
|
||||||
|
if(dual_socket)
|
||||||
|
snprintf(string, size, "%d cores",topo->physical_cores * topo->sockets);
|
||||||
|
else
|
||||||
|
snprintf(string, size, "%d cores",topo->physical_cores);
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_sockets(struct topology* topo) {
|
||||||
|
char* string = malloc(sizeof(char) * 2);
|
||||||
|
int32_t sanity_ret = snprintf(string, 2, "%d", topo->sockets);
|
||||||
|
if(sanity_ret < 0) {
|
||||||
|
printBug("get_str_sockets: snprintf returned a negative value for input: '%d'", topo->sockets);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_cpu_name(struct cpuInfo* cpu) {
|
||||||
|
return cpu->cpu_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_avx(struct cpuInfo* cpu) {
|
||||||
|
//If all AVX are available, it will use up to 15
|
||||||
|
char* string = malloc(sizeof(char)*15+1);
|
||||||
|
if(!cpu->AVX)
|
||||||
|
snprintf(string,2+1,"No");
|
||||||
|
else if(!cpu->AVX2)
|
||||||
|
snprintf(string,3+1,"AVX");
|
||||||
|
else if(!cpu->AVX512)
|
||||||
|
snprintf(string,8+1,"AVX,AVX2");
|
||||||
|
else
|
||||||
|
snprintf(string,15+1,"AVX,AVX2,AVX512");
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_sse(struct cpuInfo* cpu) {
|
||||||
|
uint32_t last = 0;
|
||||||
|
uint32_t SSE_sl = 4;
|
||||||
|
uint32_t SSE2_sl = 5;
|
||||||
|
uint32_t SSE3_sl = 5;
|
||||||
|
uint32_t SSSE3_sl = 6;
|
||||||
|
uint32_t SSE4a_sl = 6;
|
||||||
|
uint32_t SSE4_1_sl = 7;
|
||||||
|
uint32_t SSE4_2_sl = 7;
|
||||||
|
char* string = malloc(sizeof(char)*SSE_sl+SSE2_sl+SSE3_sl+SSSE3_sl+SSE4a_sl+SSE4_1_sl+SSE4_2_sl+1);
|
||||||
|
|
||||||
|
if(cpu->SSE) {
|
||||||
|
snprintf(string+last,SSE_sl+1,"SSE,");
|
||||||
|
last+=SSE_sl;
|
||||||
|
}
|
||||||
|
if(cpu->SSE2) {
|
||||||
|
snprintf(string+last,SSE2_sl+1,"SSE2,");
|
||||||
|
last+=SSE2_sl;
|
||||||
|
}
|
||||||
|
if(cpu->SSE3) {
|
||||||
|
snprintf(string+last,SSE3_sl+1,"SSE3,");
|
||||||
|
last+=SSE3_sl;
|
||||||
|
}
|
||||||
|
if(cpu->SSSE3) {
|
||||||
|
snprintf(string+last,SSSE3_sl+1,"SSSE3,");
|
||||||
|
last+=SSSE3_sl;
|
||||||
|
}
|
||||||
|
if(cpu->SSE4a) {
|
||||||
|
snprintf(string+last,SSE4a_sl+1,"SSE4a,");
|
||||||
|
last+=SSE4a_sl;
|
||||||
|
}
|
||||||
|
if(cpu->SSE4_1) {
|
||||||
|
snprintf(string+last,SSE4_1_sl+1,"SSE4_1,");
|
||||||
|
last+=SSE4_1_sl;
|
||||||
|
}
|
||||||
|
if(cpu->SSE4_2) {
|
||||||
|
snprintf(string+last,SSE4_2_sl+1,"SSE4_2,");
|
||||||
|
last+=SSE4_2_sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Purge last comma
|
||||||
|
string[last-1] = '\0';
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_fma(struct cpuInfo* cpu) {
|
||||||
|
char* string = malloc(sizeof(char)*9+1);
|
||||||
|
if(!cpu->FMA3)
|
||||||
|
snprintf(string,2+1,"No");
|
||||||
|
else if(!cpu->FMA4)
|
||||||
|
snprintf(string,4+1,"FMA3");
|
||||||
|
else
|
||||||
|
snprintf(string,9+1,"FMA3,FMA4");
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_aes(struct cpuInfo* cpu) {
|
||||||
|
char* string = malloc(sizeof(char)*3+1);
|
||||||
|
if(cpu->AES)
|
||||||
|
snprintf(string,3+1,STRING_YES);
|
||||||
|
else
|
||||||
|
snprintf(string,2+1,STRING_NO);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_sha(struct cpuInfo* cpu) {
|
||||||
|
char* string = malloc(sizeof(char)*3+1);
|
||||||
|
if(cpu->SHA)
|
||||||
|
snprintf(string,3+1,STRING_YES);
|
||||||
|
else
|
||||||
|
snprintf(string,2+1,STRING_NO);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t get_value_as_smallest_unit(char ** str, uint32_t value) {
|
||||||
|
int32_t sanity_ret;
|
||||||
|
*str = malloc(sizeof(char)* 11); //8 for digits, 2 for units
|
||||||
|
|
||||||
|
if(value/1024 >= 1024)
|
||||||
|
sanity_ret = snprintf(*str, 10,"%.4g"STRING_MEGABYTES, (double)value/(1<<20));
|
||||||
|
else
|
||||||
|
sanity_ret = snprintf(*str, 10,"%.4g"STRING_KILOBYTES, (double)value/(1<<10));
|
||||||
|
|
||||||
|
return sanity_ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// String functions
|
||||||
|
char* get_str_cache_two(int32_t cache_size, uint32_t physical_cores) {
|
||||||
|
// 4 for digits, 2 for units, 2 for ' (', 3 digits, 2 for units and 7 for ' Total)'
|
||||||
|
uint32_t max_size = 4+2 + 2 + 4+2 + 7 + 1;
|
||||||
|
int32_t sanity_ret;
|
||||||
|
char* string = malloc(sizeof(char) * max_size);
|
||||||
|
char* tmp1;
|
||||||
|
char* tmp2;
|
||||||
|
int32_t tmp1_len = get_value_as_smallest_unit(&tmp1, cache_size);
|
||||||
|
int32_t tmp2_len = get_value_as_smallest_unit(&tmp2, cache_size * physical_cores);
|
||||||
|
|
||||||
|
if(tmp1_len < 0) {
|
||||||
|
printBug("get_value_as_smallest_unit: snprintf returned a negative value for input: %d\n", cache_size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(tmp2_len < 0) {
|
||||||
|
printBug("get_value_as_smallest_unit: snprintf returned a negative value for input: %d\n", cache_size * physical_cores);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t size = tmp1_len + 2 + tmp2_len + 7 + 1;
|
||||||
|
sanity_ret = snprintf(string, size, "%s (%s Total)", tmp1, tmp2);
|
||||||
|
|
||||||
|
if(sanity_ret < 0) {
|
||||||
|
printBug("get_str_cache_two: snprintf returned a negative value for input: '%s' and '%s'\n", tmp1, tmp2);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tmp1);
|
||||||
|
free(tmp2);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_cache_one(int32_t cache_size) {
|
||||||
|
// 4 for digits, 2 for units, 2 for ' (', 3 digits, 2 for units and 7 for ' Total)'
|
||||||
|
uint32_t max_size = 4+2 + 1;
|
||||||
|
int32_t sanity_ret;
|
||||||
|
char* string = malloc(sizeof(char) * max_size);
|
||||||
|
char* tmp;
|
||||||
|
int32_t tmp_len = get_value_as_smallest_unit(&tmp, cache_size);
|
||||||
|
|
||||||
|
if(tmp_len < 0) {
|
||||||
|
printBug("get_value_as_smallest_unit: snprintf returned a negative value for input: %d", cache_size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t size = tmp_len + 1;
|
||||||
|
sanity_ret = snprintf(string, size, "%s", tmp);
|
||||||
|
|
||||||
|
if(sanity_ret < 0) {
|
||||||
|
printBug("get_str_cache_one: snprintf returned a negative value for input: '%s'", tmp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
free(tmp);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_cache(int32_t cache_size, struct topology* topo, bool llc) {
|
||||||
|
if(topo->sockets == 1) {
|
||||||
|
if(llc)
|
||||||
|
return get_str_cache_one(cache_size);
|
||||||
|
else
|
||||||
|
return get_str_cache_two(cache_size, topo->physical_cores);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(llc)
|
||||||
|
return get_str_cache_two(cache_size, topo->sockets);
|
||||||
|
else
|
||||||
|
return get_str_cache_two(cache_size, topo->physical_cores * topo->sockets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_l1i(struct cache* cach, struct topology* topo) {
|
||||||
|
return get_str_cache(cach->L1i, topo, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_l1d(struct cache* cach, struct topology* topo) {
|
||||||
|
return get_str_cache(cach->L1d, topo, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_l2(struct cache* cach, struct topology* topo) {
|
||||||
|
assert(cach->L2 != UNKNOWN);
|
||||||
|
if(cach->L3 == UNKNOWN)
|
||||||
|
return get_str_cache(cach->L2, topo, true);
|
||||||
|
else
|
||||||
|
return get_str_cache(cach->L2, topo, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get_str_l3(struct cache* cach, struct topology* topo) {
|
||||||
|
if(cach->L3 == UNKNOWN)
|
||||||
|
return NULL;
|
||||||
|
return get_str_cache(cach->L3, topo, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
assert(strlen(STRING_UNKNOWN)+1 <= size);
|
||||||
|
char* string = malloc(sizeof(char)*size);
|
||||||
|
if(freq->max == UNKNOWN)
|
||||||
|
snprintf(string,strlen(STRING_UNKNOWN)+1,STRING_UNKNOWN);
|
||||||
|
else if(freq->max >= 1000)
|
||||||
|
snprintf(string,size,"%.2f"STRING_GIGAHERZ,(float)(freq->max)/1000);
|
||||||
|
else
|
||||||
|
snprintf(string,size,"%.2f"STRING_MEGAHERZ,(float)(freq->max));
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_levels(struct cpuInfo* cpu, char* cpu_name) {
|
||||||
|
printf("%s\n", cpu_name);
|
||||||
|
printf("- Max standart level: 0x%.8X\n", cpu->maxLevels);
|
||||||
|
printf("- Max extended level: 0x%.8X\n", cpu->maxExtendedLevels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_topo_struct(struct topology* topo) {
|
||||||
|
free(topo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_cache_struct(struct cache* cach) {
|
||||||
|
free(cach);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_freq_struct(struct frequency* freq) {
|
||||||
|
free(freq);
|
||||||
}
|
}
|
||||||
|
|||||||
62
src/cpuid.h
62
src/cpuid.h
@@ -3,6 +3,66 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
|
#define VENDOR_EMPTY 0
|
||||||
|
#define VENDOR_INTEL 1
|
||||||
|
#define VENDOR_AMD 2
|
||||||
|
#define VENDOR_INVALID 3
|
||||||
|
|
||||||
|
#define UNKNOWN -1
|
||||||
|
|
||||||
|
struct cpuInfo;
|
||||||
|
struct frequency;
|
||||||
|
struct cache;
|
||||||
|
|
||||||
|
struct topology {
|
||||||
|
int64_t total_cores;
|
||||||
|
uint32_t physical_cores;
|
||||||
|
uint32_t logical_cores;
|
||||||
|
uint32_t smt_available; // Number of SMT that is currently enabled
|
||||||
|
uint32_t smt_supported; // Number of SMT that CPU supports (equal to smt_available if SMT is enabled)
|
||||||
|
uint32_t sockets;
|
||||||
|
struct apic* apic;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int32_t VENDOR;
|
||||||
|
|
||||||
|
struct cpuInfo* get_cpu_info();
|
||||||
|
VENDOR get_cpu_vendor(struct cpuInfo* cpu);
|
||||||
|
uint32_t get_nsockets(struct topology* topo);
|
||||||
|
int64_t get_freq(struct frequency* freq);
|
||||||
|
struct cache* get_cache_info(struct cpuInfo* cpu);
|
||||||
|
struct frequency* get_frequency_info(struct cpuInfo* cpu);
|
||||||
|
struct topology* get_topology_info(struct cpuInfo* cpu);
|
||||||
|
|
||||||
|
char* get_str_cpu_name(struct cpuInfo* cpu);
|
||||||
|
char* get_str_ncores(struct cpuInfo* cpu);
|
||||||
|
char* get_str_avx(struct cpuInfo* cpu);
|
||||||
|
char* get_str_sse(struct cpuInfo* cpu);
|
||||||
|
char* get_str_fma(struct cpuInfo* cpu);
|
||||||
|
char* get_str_aes(struct cpuInfo* cpu);
|
||||||
|
char* get_str_sha(struct cpuInfo* cpu);
|
||||||
|
|
||||||
|
char* get_str_l1i(struct cache* cach, struct topology* topo);
|
||||||
|
char* get_str_l1d(struct cache* cach, struct topology* topo);
|
||||||
|
char* get_str_l2(struct cache* cach, struct topology* topo);
|
||||||
|
char* get_str_l3(struct cache* cach, struct topology* topo);
|
||||||
|
|
||||||
|
char* get_str_freq(struct frequency* freq);
|
||||||
|
|
||||||
|
char* get_str_sockets(struct topology* topo);
|
||||||
|
char* get_str_topology(struct cpuInfo* cpu, struct topology* topo, bool dual_socket);
|
||||||
|
|
||||||
|
char* get_str_peak_performance(struct cpuInfo* cpu, struct topology* topo, int64_t freq);
|
||||||
|
|
||||||
|
void print_levels(struct cpuInfo* cpu, char* cpu_name);
|
||||||
|
|
||||||
|
void free_cpuinfo_struct(struct cpuInfo* cpu);
|
||||||
|
void free_cache_struct(struct cache* cach);
|
||||||
|
void free_topo_struct(struct topology* topo);
|
||||||
|
void free_freq_struct(struct frequency* freq);
|
||||||
|
|
||||||
|
void debug_cpu_info(struct cpuInfo* cpu);
|
||||||
|
void debug_cache(struct cache* cach);
|
||||||
|
void debug_frequency(struct frequency* freq);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
10
src/cpuid_asm.c
Normal file
10
src/cpuid_asm.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include "cpuid_asm.h"
|
||||||
|
|
||||||
|
void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
|
||||||
|
__asm volatile("cpuid"
|
||||||
|
: "=a" (*eax),
|
||||||
|
"=b" (*ebx),
|
||||||
|
"=c" (*ecx),
|
||||||
|
"=d" (*edx)
|
||||||
|
: "0" (*eax), "2" (*ecx));
|
||||||
|
}
|
||||||
8
src/cpuid_asm.h
Normal file
8
src/cpuid_asm.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef __CPUID_ASM__
|
||||||
|
#define __CPUID_ASM__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "extended.h"
|
|
||||||
|
|
||||||
char* get_str_cpu_name() {
|
|
||||||
uint32_t eax = 0;
|
|
||||||
uint32_t ebx = 0;
|
|
||||||
uint32_t ecx = 0;
|
|
||||||
uint32_t edx = 0;
|
|
||||||
|
|
||||||
char *name = malloc(sizeof(char)*64);
|
|
||||||
memset(name, 0, 64);
|
|
||||||
|
|
||||||
//First, check we can use extended
|
|
||||||
eax = 0x80000000;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
if(eax < 0x80000001) {
|
|
||||||
char* none = malloc(sizeof(char)*64);
|
|
||||||
sprintf(none,"Unknown");
|
|
||||||
return none;
|
|
||||||
}
|
|
||||||
|
|
||||||
//We can, fetch name
|
|
||||||
eax = 0x80000002;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
|
|
||||||
name[__COUNTER__] = eax & MASK;
|
|
||||||
name[__COUNTER__] = (eax>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (eax>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (eax>>24) & MASK;
|
|
||||||
name[__COUNTER__] = ebx & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>24) & MASK;
|
|
||||||
name[__COUNTER__] = ecx & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>24) & MASK;
|
|
||||||
name[__COUNTER__] = edx & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>24) & MASK;
|
|
||||||
|
|
||||||
eax = 0x80000003;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
|
|
||||||
name[__COUNTER__] = eax & MASK;
|
|
||||||
name[__COUNTER__] = (eax>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (eax>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (eax>>24) & MASK;
|
|
||||||
name[__COUNTER__] = ebx & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>24) & MASK;
|
|
||||||
name[__COUNTER__] = ecx & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>24) & MASK;
|
|
||||||
name[__COUNTER__] = edx & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>24) & MASK;
|
|
||||||
|
|
||||||
eax = 0x80000004;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
|
|
||||||
name[__COUNTER__] = eax & MASK;
|
|
||||||
name[__COUNTER__] = (eax>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (eax>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (eax>>24) & MASK;
|
|
||||||
name[__COUNTER__] = ebx & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>24) & MASK;
|
|
||||||
name[__COUNTER__] = ecx & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>24) & MASK;
|
|
||||||
name[__COUNTER__] = edx & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>24) & MASK;
|
|
||||||
|
|
||||||
name[__COUNTER__] = '\0';
|
|
||||||
|
|
||||||
//Remove unused characters
|
|
||||||
char *str = name;
|
|
||||||
char *dest = name;
|
|
||||||
while (*str != '\0') {
|
|
||||||
while (*str == ' ' && *(str + 1) == ' ') str++;
|
|
||||||
*dest++ = *str++;
|
|
||||||
}
|
|
||||||
*dest = '\0';
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#ifndef __EXTENDED__
|
|
||||||
#define __EXTENDED__
|
|
||||||
|
|
||||||
#define MASK 0xFF
|
|
||||||
#include "cpuid.h"
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
char* get_str_cpu_name();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
93
src/main.c
93
src/main.c
@@ -3,37 +3,22 @@
|
|||||||
|
|
||||||
#include "args.h"
|
#include "args.h"
|
||||||
#include "printer.h"
|
#include "printer.h"
|
||||||
#include "standart.h"
|
#include "cpuid.h"
|
||||||
#include "extended.h"
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
/***
|
static const char* VERSION = "0.6";
|
||||||
SAMPLE OUTPUT
|
|
||||||
|
|
||||||
Name: Intel Core i7-4790K
|
|
||||||
Frequency: 4.0 GHz
|
|
||||||
NºCores: 4 cores(8 threads)
|
|
||||||
AXV: AVX,AVX2
|
|
||||||
SSE: SSE,SSE2,SSE4.1,SSE4.2
|
|
||||||
FMA: FMA3
|
|
||||||
AES: Yes
|
|
||||||
SHA: No
|
|
||||||
L1 Size: 32KB(Data)32KB(Instructions)
|
|
||||||
L2 Size: 512KB
|
|
||||||
L3 Size: 8MB
|
|
||||||
Peak FLOPS: 512 GFLOP/s(in simple precision)
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
static const char* VERSION = "0.410";
|
|
||||||
|
|
||||||
void print_help(char *argv[]) {
|
void print_help(char *argv[]) {
|
||||||
printf("Usage: %s [--version] [--help] [--style STYLE]\n\
|
printf("Usage: %s [--version] [--help] [--levels] [--style fancy|retro|legacy] [--color 'R,G,B:R,G,B:R,G,B:R,G,B']\n\
|
||||||
Options: \n\
|
Options: \n\
|
||||||
--style Set logo style color\n\
|
--color Set a custom color scheme. 4 colors must be specified in RGB with the format: R,G,B:R,G,B:...\n\
|
||||||
default: Default style color\n\
|
These colors correspond to the ASCII art color (2 colors) and for the text colors (next 2)\n\
|
||||||
dark: Dark style color\n\
|
Suggested color (Intel): --color 15,125,194:230,230,230:40,150,220:230,230,230\n\
|
||||||
none: Don't use colors\n\
|
Suggested color (AMD): --color 250,250,250:0,154,102:250,250,250:0,154,102\n\
|
||||||
|
--style Set the style of the ASCII art:\n\
|
||||||
|
* fancy \n\
|
||||||
|
* retro \n\
|
||||||
|
* legacy \n\
|
||||||
--help Prints this help and exit\n\
|
--help Prints this help and exit\n\
|
||||||
--levels Prints CPU model and cpuid levels (debug purposes)\n\
|
--levels Prints CPU model and cpuid levels (debug purposes)\n\
|
||||||
--version Prints cpufetch version and exit\n",
|
--version Prints cpufetch version and exit\n",
|
||||||
@@ -63,11 +48,10 @@ int main(int argc, char* argv[]) {
|
|||||||
struct cpuInfo* cpu = get_cpu_info();
|
struct cpuInfo* cpu = get_cpu_info();
|
||||||
if(cpu == NULL)
|
if(cpu == NULL)
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
char* cpuName = get_str_cpu_name();
|
|
||||||
|
|
||||||
if(show_levels()) {
|
if(show_levels()) {
|
||||||
print_version();
|
print_version();
|
||||||
print_levels(cpu, cpuName);
|
print_levels(cpu, get_str_cpu_name(cpu));
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,55 +67,8 @@ int main(int argc, char* argv[]) {
|
|||||||
if(topo == NULL)
|
if(topo == NULL)
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
struct ascii* art = set_ascii(get_cpu_vendor(cpu),get_style());
|
if(print_cpufetch(cpu, cach, freq, topo, get_style(), get_colors()))
|
||||||
if(art == NULL)
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
|
|
||||||
char* maxFrequency = get_str_freq(freq);
|
|
||||||
char* nCores = get_str_topology(topo);
|
|
||||||
char* avx = get_str_avx(cpu);
|
|
||||||
char* sse = get_str_sse(cpu);
|
|
||||||
char* fma = get_str_fma(cpu);
|
|
||||||
char* aes = get_str_aes(cpu);
|
|
||||||
char* sha = get_str_sha(cpu);
|
|
||||||
char* l1 = get_str_l1(cach);
|
|
||||||
char* l2 = get_str_l2(cach);
|
|
||||||
char* l3 = get_str_l3(cach);
|
|
||||||
char* pp = get_str_peak_performance(cpu,topo,get_freq(freq));
|
|
||||||
|
|
||||||
setAttribute(art,ATTRIBUTE_NAME,cpuName);
|
|
||||||
setAttribute(art,ATTRIBUTE_FREQUENCY,maxFrequency);
|
|
||||||
setAttribute(art,ATTRIBUTE_NCORES,nCores);
|
|
||||||
setAttribute(art,ATTRIBUTE_AVX,avx);
|
|
||||||
setAttribute(art,ATTRIBUTE_SSE,sse);
|
|
||||||
setAttribute(art,ATTRIBUTE_FMA,fma);
|
|
||||||
setAttribute(art,ATTRIBUTE_AES,aes);
|
|
||||||
setAttribute(art,ATTRIBUTE_SHA,sha);
|
|
||||||
setAttribute(art,ATTRIBUTE_L1,l1);
|
|
||||||
setAttribute(art,ATTRIBUTE_L2,l2);
|
|
||||||
setAttribute(art,ATTRIBUTE_L3,l3);
|
|
||||||
setAttribute(art,ATTRIBUTE_PEAK,pp);
|
|
||||||
|
|
||||||
print_ascii(art);
|
|
||||||
|
|
||||||
free(cpuName);
|
|
||||||
free(maxFrequency);
|
|
||||||
free(nCores);
|
|
||||||
free(avx);
|
|
||||||
free(sse);
|
|
||||||
free(fma);
|
|
||||||
free(aes);
|
|
||||||
free(sha);
|
|
||||||
free(l1);
|
|
||||||
free(l2);
|
|
||||||
free(l3);
|
|
||||||
free(pp);
|
|
||||||
|
|
||||||
free(cpu);
|
|
||||||
free(art);
|
|
||||||
free_cache_struct(cach);
|
|
||||||
free_topo_struct(topo);
|
|
||||||
free_freq_struct(freq);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
else
|
||||||
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|||||||
385
src/printer.c
385
src/printer.c
@@ -8,110 +8,201 @@
|
|||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
#define COL_NONE ""
|
#define COL_NONE ""
|
||||||
#define COL_INTEL_DEFAULT_1 "\x1b[36;1m"
|
#define COL_INTEL_FANCY_1 "\x1b[46;1m"
|
||||||
#define COL_INTEL_DEFAULT_2 "\x1b[37;1m"
|
#define COL_INTEL_FANCY_2 "\x1b[47;1m"
|
||||||
#define COL_INTEL_DARK_1 "\x1b[34;1m"
|
#define COL_INTEL_FANCY_3 "\x1b[36;1m"
|
||||||
#define COL_INTEL_DARK_2 "\x1b[30m"
|
#define COL_INTEL_FANCY_4 "\x1b[37;1m"
|
||||||
#define COL_AMD_DEFAULT_1 "\x1b[37;1m"
|
#define COL_INTEL_RETRO_1 "\x1b[36;1m"
|
||||||
#define COL_AMD_DEFAULT_2 "\x1b[31;1m"
|
#define COL_INTEL_RETRO_2 "\x1b[37;1m"
|
||||||
#define COL_AMD_DARK_1 "\x1b[30;1m"
|
#define COL_AMD_FANCY_1 "\x1b[47;1m"
|
||||||
#define COL_AMD_DARK_2 "\x1b[32;1m"
|
#define COL_AMD_FANCY_2 "\x1b[42;1m"
|
||||||
#define RESET "\x1b[0m"
|
#define COL_AMD_FANCY_3 "\x1b[37;1m"
|
||||||
|
#define COL_AMD_FANCY_4 "\x1b[32;1m"
|
||||||
|
#define COL_AMD_RETRO_1 "\x1b[37;1m"
|
||||||
|
#define COL_AMD_RETRO_2 "\x1b[32;1m"
|
||||||
|
#define RESET "\x1b[m"
|
||||||
|
|
||||||
#define TITLE_NAME "Name: "
|
#define TITLE_NAME "Name:"
|
||||||
#define TITLE_FREQUENCY "Frequency: "
|
#define TITLE_FREQUENCY "Frequency:"
|
||||||
#define TITLE_NCORES "N.Cores: "
|
#define TITLE_SOCKETS "Sockets:"
|
||||||
#define TITLE_AVX "AVX: "
|
#define TITLE_NCORES "Cores:"
|
||||||
#define TITLE_SSE "SSE: "
|
#define TITLE_NCORES_DUAL "Cores (Total):"
|
||||||
#define TITLE_FMA "FMA: "
|
#define TITLE_AVX "AVX:"
|
||||||
#define TITLE_AES "AES: "
|
#define TITLE_SSE "SSE:"
|
||||||
#define TITLE_SHA "SHA: "
|
#define TITLE_FMA "FMA:"
|
||||||
#define TITLE_L1 "L1 Size: "
|
#define TITLE_AES "AES:"
|
||||||
#define TITLE_L2 "L2 Size: "
|
#define TITLE_SHA "SHA:"
|
||||||
#define TITLE_L3 "L3 Size: "
|
#define TITLE_L1i "L1i Size:"
|
||||||
#define TITLE_PEAK "Peak FLOPS: "
|
#define TITLE_L1d "L1d Size:"
|
||||||
|
#define TITLE_L2 "L2 Size:"
|
||||||
|
#define TITLE_L3 "L3 Size:"
|
||||||
|
#define TITLE_PEAK "Peak Perf.:"
|
||||||
|
|
||||||
/*** CENTER TEXT ***/
|
#define MAX_ATTRIBUTE_COUNT 15
|
||||||
#define LINES_SPACE_UP 4
|
#define ATTRIBUTE_NAME 0
|
||||||
#define LINES_SPACE_DOWN 4
|
#define ATTRIBUTE_FREQUENCY 1
|
||||||
|
#define ATTRIBUTE_SOCKETS 2
|
||||||
|
#define ATTRIBUTE_NCORES 3
|
||||||
|
#define ATTRIBUTE_NCORES_DUAL 4
|
||||||
|
#define ATTRIBUTE_AVX 5
|
||||||
|
#define ATTRIBUTE_SSE 6
|
||||||
|
#define ATTRIBUTE_FMA 7
|
||||||
|
#define ATTRIBUTE_AES 8
|
||||||
|
#define ATTRIBUTE_SHA 9
|
||||||
|
#define ATTRIBUTE_L1i 10
|
||||||
|
#define ATTRIBUTE_L1d 11
|
||||||
|
#define ATTRIBUTE_L2 12
|
||||||
|
#define ATTRIBUTE_L3 13
|
||||||
|
#define ATTRIBUTE_PEAK 14
|
||||||
|
|
||||||
static const char* ATTRIBUTE_FIELDS [ATTRIBUTE_COUNT] = { TITLE_NAME, TITLE_FREQUENCY,
|
static const char* ATTRIBUTE_FIELDS [MAX_ATTRIBUTE_COUNT] = { TITLE_NAME, TITLE_FREQUENCY, TITLE_SOCKETS,
|
||||||
TITLE_NCORES, TITLE_AVX, TITLE_SSE,
|
TITLE_NCORES, TITLE_NCORES_DUAL,
|
||||||
|
TITLE_AVX, TITLE_SSE,
|
||||||
TITLE_FMA, TITLE_AES, TITLE_SHA,
|
TITLE_FMA, TITLE_AES, TITLE_SHA,
|
||||||
TITLE_L1, TITLE_L2, TITLE_L3,
|
TITLE_L1i, TITLE_L1d, TITLE_L2, TITLE_L3,
|
||||||
TITLE_PEAK };
|
TITLE_PEAK
|
||||||
|
};
|
||||||
|
|
||||||
static const int ATTRIBUTE_LIST[ATTRIBUTE_COUNT] = { ATTRIBUTE_NAME, ATTRIBUTE_FREQUENCY,
|
static const int ATTRIBUTE_LIST[MAX_ATTRIBUTE_COUNT] = { ATTRIBUTE_NAME, ATTRIBUTE_FREQUENCY, ATTRIBUTE_SOCKETS,
|
||||||
ATTRIBUTE_NCORES, ATTRIBUTE_AVX, ATTRIBUTE_SSE,
|
ATTRIBUTE_NCORES, ATTRIBUTE_NCORES_DUAL, ATTRIBUTE_AVX,
|
||||||
ATTRIBUTE_FMA, ATTRIBUTE_AES, ATTRIBUTE_SHA,
|
ATTRIBUTE_SSE, ATTRIBUTE_FMA, ATTRIBUTE_AES, ATTRIBUTE_SHA,
|
||||||
ATTRIBUTE_L1, ATTRIBUTE_L2, ATTRIBUTE_L3,
|
ATTRIBUTE_L1i, ATTRIBUTE_L1d, ATTRIBUTE_L2, ATTRIBUTE_L3,
|
||||||
ATTRIBUTE_PEAK };
|
ATTRIBUTE_PEAK };
|
||||||
|
|
||||||
struct ascii {
|
struct ascii {
|
||||||
char art[NUMBER_OF_LINES][LINE_SIZE];
|
char art[NUMBER_OF_LINES][LINE_SIZE];
|
||||||
char color1[10];
|
char color1_ascii[100];
|
||||||
char color2[10];
|
char color2_ascii[100];
|
||||||
char reset[10];
|
char color1_text[100];
|
||||||
char* atributes[ATTRIBUTE_COUNT];
|
char color2_text[100];
|
||||||
|
char ascii_chars[2];
|
||||||
|
char reset[100];
|
||||||
|
char* attributes[MAX_ATTRIBUTE_COUNT];
|
||||||
|
uint32_t n_attributes_set;
|
||||||
VENDOR vendor;
|
VENDOR vendor;
|
||||||
};
|
};
|
||||||
|
|
||||||
void setAttribute(struct ascii* art, int type, char* value) {
|
void setAttribute(struct ascii* art, int type, char* value) {
|
||||||
int i = 0;
|
art->attributes[type] = value;
|
||||||
while(i < ATTRIBUTE_COUNT && type != ATTRIBUTE_LIST[i])
|
art->n_attributes_set++;
|
||||||
i++;
|
|
||||||
if(i != ATTRIBUTE_COUNT)
|
|
||||||
art->atributes[i] = value;
|
|
||||||
else
|
|
||||||
printBug("Setting attribute failed because it was not found");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ascii* set_ascii(VENDOR cpuVendor, STYLE style) {
|
char* rgb_to_ansi(struct color* c, bool background, bool bold) {
|
||||||
/*** Check that number of lines of ascii art matches the number
|
char* str = malloc(sizeof(char) * 100);
|
||||||
of spaces plus the number of lines filled with text ***/
|
if(background) {
|
||||||
if(LINES_SPACE_UP+LINES_SPACE_DOWN+ATTRIBUTE_COUNT != NUMBER_OF_LINES) {
|
snprintf(str, 44, "\x1b[48;2;%.3d;%.3d;%.3dm", c->R, c->G, c->B);
|
||||||
printBug("Number of lines do not match (%d vs %d)",LINES_SPACE_UP+LINES_SPACE_DOWN+ATTRIBUTE_COUNT,NUMBER_OF_LINES);
|
}
|
||||||
return NULL;
|
else {
|
||||||
|
if(bold)
|
||||||
|
snprintf(str, 48, "\x1b[1m\x1b[38;2;%.3d;%.3d;%.3dm", c->R, c->G, c->B);
|
||||||
|
else
|
||||||
|
snprintf(str, 44, "\x1b[38;2;%.3d;%.3d;%.3dm", c->R, c->G, c->B);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *COL_DEFAULT_1, *COL_DEFAULT_2, *COL_DARK_1, *COL_DARK_2;
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ascii* set_ascii(VENDOR cpuVendor, STYLE style, struct colors* cs) {
|
||||||
|
// Sanity checks //
|
||||||
|
for(int i=0; i < MAX_ATTRIBUTE_COUNT; i++) {
|
||||||
|
if(ATTRIBUTE_FIELDS[i] == NULL) {
|
||||||
|
printBug("Attribute field at position %d is empty", i);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if(i > 0 && ATTRIBUTE_LIST[i] == 0) {
|
||||||
|
printBug("Attribute list at position %d is empty", i);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *COL_FANCY_1, *COL_FANCY_2, *COL_FANCY_3, *COL_FANCY_4, *COL_RETRO_1, *COL_RETRO_2, *COL_RETRO_3, *COL_RETRO_4;
|
||||||
struct ascii* art = malloc(sizeof(struct ascii));
|
struct ascii* art = malloc(sizeof(struct ascii));
|
||||||
|
art->n_attributes_set = 0;
|
||||||
art->vendor = cpuVendor;
|
art->vendor = cpuVendor;
|
||||||
|
for(int i=0; i < MAX_ATTRIBUTE_COUNT; i++)
|
||||||
|
art->attributes[i] = NULL;
|
||||||
strcpy(art->reset,RESET);
|
strcpy(art->reset,RESET);
|
||||||
|
|
||||||
if(cpuVendor == VENDOR_INTEL) {
|
if(cpuVendor == VENDOR_INTEL) {
|
||||||
COL_DEFAULT_1 = COL_INTEL_DEFAULT_1;
|
COL_FANCY_1 = COL_INTEL_FANCY_1;
|
||||||
COL_DEFAULT_2 = COL_INTEL_DEFAULT_2;
|
COL_FANCY_2 = COL_INTEL_FANCY_2;
|
||||||
COL_DARK_1 = COL_INTEL_DARK_1;
|
COL_FANCY_3 = COL_INTEL_FANCY_3;
|
||||||
COL_DARK_2 = COL_INTEL_DARK_2;
|
COL_FANCY_4 = COL_INTEL_FANCY_4;
|
||||||
|
COL_RETRO_1 = COL_INTEL_RETRO_1;
|
||||||
|
COL_RETRO_2 = COL_INTEL_RETRO_2;
|
||||||
|
COL_RETRO_3 = COL_INTEL_RETRO_1;
|
||||||
|
COL_RETRO_4 = COL_INTEL_RETRO_2;
|
||||||
|
art->ascii_chars[0] = '#';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
COL_DEFAULT_1 = COL_AMD_DEFAULT_1;
|
COL_FANCY_1 = COL_AMD_FANCY_1;
|
||||||
COL_DEFAULT_2 = COL_AMD_DEFAULT_2;
|
COL_FANCY_2 = COL_AMD_FANCY_2;
|
||||||
COL_DARK_1 = COL_AMD_DARK_1;
|
COL_FANCY_3 = COL_AMD_FANCY_3;
|
||||||
COL_DARK_2 = COL_AMD_DARK_2;
|
COL_FANCY_4 = COL_AMD_FANCY_4;
|
||||||
|
COL_RETRO_1 = COL_AMD_RETRO_1;
|
||||||
|
COL_RETRO_2 = COL_AMD_RETRO_2;
|
||||||
|
COL_RETRO_3 = COL_AMD_RETRO_1;
|
||||||
|
COL_RETRO_4 = COL_AMD_RETRO_2;
|
||||||
|
art->ascii_chars[0] = '@';
|
||||||
|
}
|
||||||
|
art->ascii_chars[1] = '#';
|
||||||
|
|
||||||
|
// If style is emtpy, set the default style
|
||||||
|
if(style == STYLE_EMPTY) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
style = STYLE_LEGACY;
|
||||||
|
#else
|
||||||
|
style = STYLE_FANCY;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(style) {
|
switch(style) {
|
||||||
case STYLE_NONE:
|
case STYLE_LEGACY:
|
||||||
strcpy(art->color1,COL_NONE);
|
strcpy(art->color1_ascii,COL_NONE);
|
||||||
strcpy(art->color2,COL_NONE);
|
strcpy(art->color2_ascii,COL_NONE);
|
||||||
break;
|
strcpy(art->color1_text,COL_NONE);
|
||||||
case STYLE_EMPTY:
|
strcpy(art->color2_text,COL_NONE);
|
||||||
#ifdef _WIN32
|
|
||||||
strcpy(art->color1,COL_NONE);
|
|
||||||
strcpy(art->color2,COL_NONE);
|
|
||||||
art->reset[0] = '\0';
|
art->reset[0] = '\0';
|
||||||
break;
|
break;
|
||||||
#endif
|
case STYLE_FANCY:
|
||||||
case STYLE_DEFAULT:
|
if(cs != NULL) {
|
||||||
strcpy(art->color1,COL_DEFAULT_1);
|
COL_FANCY_1 = rgb_to_ansi(cs->c1, true, true);
|
||||||
strcpy(art->color2,COL_DEFAULT_2);
|
COL_FANCY_2 = rgb_to_ansi(cs->c2, true, true);
|
||||||
|
COL_FANCY_3 = rgb_to_ansi(cs->c3, false, true);
|
||||||
|
COL_FANCY_4 = rgb_to_ansi(cs->c4, false, true);
|
||||||
|
}
|
||||||
|
art->ascii_chars[0] = ' ';
|
||||||
|
art->ascii_chars[1] = ' ';
|
||||||
|
strcpy(art->color1_ascii,COL_FANCY_1);
|
||||||
|
strcpy(art->color2_ascii,COL_FANCY_2);
|
||||||
|
strcpy(art->color1_text,COL_FANCY_3);
|
||||||
|
strcpy(art->color2_text,COL_FANCY_4);
|
||||||
|
if(cs != NULL) {
|
||||||
|
free(COL_FANCY_1);
|
||||||
|
free(COL_FANCY_2);
|
||||||
|
free(COL_FANCY_3);
|
||||||
|
free(COL_FANCY_4);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case STYLE_DARK:
|
case STYLE_RETRO:
|
||||||
strcpy(art->color1,COL_DARK_1);
|
if(cs != NULL) {
|
||||||
strcpy(art->color2,COL_DARK_2);
|
COL_RETRO_1 = rgb_to_ansi(cs->c1, false, true);
|
||||||
|
COL_RETRO_2 = rgb_to_ansi(cs->c2, false, true);
|
||||||
|
COL_RETRO_3 = rgb_to_ansi(cs->c3, false, true);
|
||||||
|
COL_RETRO_4 = rgb_to_ansi(cs->c4, false, true);
|
||||||
|
}
|
||||||
|
strcpy(art->color1_ascii,COL_RETRO_1);
|
||||||
|
strcpy(art->color2_ascii,COL_RETRO_2);
|
||||||
|
strcpy(art->color1_text,COL_RETRO_3);
|
||||||
|
strcpy(art->color2_text,COL_RETRO_4);
|
||||||
|
if(cs != NULL) {
|
||||||
|
free(COL_RETRO_1);
|
||||||
|
free(COL_RETRO_2);
|
||||||
|
free(COL_RETRO_3);
|
||||||
|
free(COL_RETRO_4);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
case STYLE_INVALID:
|
||||||
default:
|
default:
|
||||||
printBug("Found invalid style (%d)",style);
|
printBug("Found invalid style (%d)",style);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -126,62 +217,168 @@ struct ascii* set_ascii(VENDOR cpuVendor, STYLE style) {
|
|||||||
return art;
|
return art;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_ascii_intel(struct ascii* art) {
|
uint32_t get_next_attribute(struct ascii* art, uint32_t last_attr) {
|
||||||
|
last_attr++;
|
||||||
|
while(art->attributes[last_attr] == NULL) last_attr++;
|
||||||
|
return last_attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_ascii_intel(struct ascii* art, uint32_t la) {
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
|
int attr_to_print = -1;
|
||||||
|
uint32_t space_right;
|
||||||
|
uint32_t space_up = (NUMBER_OF_LINES - art->n_attributes_set)/2;
|
||||||
|
uint32_t space_down = NUMBER_OF_LINES - art->n_attributes_set - space_up;
|
||||||
|
|
||||||
for(int n=0;n<NUMBER_OF_LINES;n++) {
|
for(uint32_t n=0;n<NUMBER_OF_LINES;n++) {
|
||||||
|
|
||||||
/*** PRINT ASCII-ART ***/
|
|
||||||
for(int i=0;i<LINE_SIZE;i++) {
|
for(int i=0;i<LINE_SIZE;i++) {
|
||||||
if(flag) {
|
if(flag) {
|
||||||
if(art->art[n][i] == ' ') {
|
if(art->art[n][i] == ' ') {
|
||||||
flag = false;
|
flag = false;
|
||||||
printf("%c",art->art[n][i]);
|
printf("%s%c%s", art->color2_ascii, art->ascii_chars[1], art->reset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
printf("%s%c%s", art->color1, art->art[n][i], art->reset);
|
printf("%s%c%s", art->color1_ascii, art->ascii_chars[0], art->reset);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(art->art[n][i] != ' ') {
|
if(art->art[n][i] != ' ' && art->art[n][i] != '\0') {
|
||||||
flag = true;
|
flag = true;
|
||||||
printf("%s%c%s", art->color2, art->art[n][i], art->reset);
|
printf("%c",' ');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
printf("%c",art->art[n][i]);
|
printf("%c",' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** PRINT ATTRIBUTE ***/
|
if(n > space_up-1 && n < NUMBER_OF_LINES-space_down) {
|
||||||
if(n>LINES_SPACE_UP-1 && n<NUMBER_OF_LINES-LINES_SPACE_DOWN)
|
attr_to_print = get_next_attribute(art, attr_to_print);
|
||||||
printf("%s%s%s%s%s\n",art->color1,ATTRIBUTE_FIELDS[n-LINES_SPACE_UP],art->color2,art->atributes[n-LINES_SPACE_UP],art->reset);
|
space_right = 1 + (la - strlen(ATTRIBUTE_FIELDS[attr_to_print]));
|
||||||
|
printf("%s%s%s%*s%s%s%s\n",art->color1_text, ATTRIBUTE_FIELDS[attr_to_print], art->reset, space_right, "", art->color2_text, art->attributes[attr_to_print], art->reset);
|
||||||
|
}
|
||||||
else printf("\n");
|
else printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_ascii_amd(struct ascii* art) {
|
void print_ascii_amd(struct ascii* art, uint32_t la) {
|
||||||
|
int attr_to_print = -1;
|
||||||
|
uint32_t space_right;
|
||||||
|
uint32_t space_up = (NUMBER_OF_LINES - art->n_attributes_set)/2;
|
||||||
|
uint32_t space_down = NUMBER_OF_LINES - art->n_attributes_set - space_up;
|
||||||
|
|
||||||
for(int n=0;n<NUMBER_OF_LINES;n++) {
|
for(uint32_t n=0;n<NUMBER_OF_LINES;n++) {
|
||||||
/*** PRINT ASCII-ART ***/
|
|
||||||
for(int i=0;i<LINE_SIZE;i++) {
|
for(int i=0;i<LINE_SIZE;i++) {
|
||||||
if(art->art[n][i] == '@')
|
if(art->art[n][i] == '@')
|
||||||
printf("%s%c%s", art->color1, art->art[n][i], art->reset);
|
printf("%s%c%s", art->color1_ascii, art->ascii_chars[0], art->reset);
|
||||||
else if(art->art[n][i] == '#')
|
else if(art->art[n][i] == '#')
|
||||||
printf("%s%c%s", art->color2, art->art[n][i], art->reset);
|
printf("%s%c%s", art->color2_ascii, art->ascii_chars[1], art->reset);
|
||||||
else
|
else
|
||||||
printf("%c",art->art[n][i]);
|
printf("%c",art->art[n][i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** PRINT ATTRIBUTE ***/
|
if(n > space_up-1 && n < NUMBER_OF_LINES-space_down) {
|
||||||
if(n>LINES_SPACE_UP-1 && n<NUMBER_OF_LINES-LINES_SPACE_DOWN)
|
attr_to_print = get_next_attribute(art, attr_to_print);
|
||||||
printf("%s%s%s%s%s\n",art->color1,ATTRIBUTE_FIELDS[n-LINES_SPACE_UP],art->color2,art->atributes[n-LINES_SPACE_UP], art->reset);
|
space_right = 1 + (la - strlen(ATTRIBUTE_FIELDS[attr_to_print]));
|
||||||
|
printf("%s%s%s%*s%s%s%s\n",art->color1_text, ATTRIBUTE_FIELDS[attr_to_print], art->reset, space_right, "", art->color2_text, art->attributes[attr_to_print], art->reset);
|
||||||
|
}
|
||||||
else printf("\n");
|
else printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t longest_attribute_length(struct ascii* art) {
|
||||||
|
uint32_t max = 0;
|
||||||
|
uint64_t len = 0;
|
||||||
|
|
||||||
|
for(int i=0; i < MAX_ATTRIBUTE_COUNT; i++) {
|
||||||
|
if(art->attributes[i] != NULL) {
|
||||||
|
len = strlen(ATTRIBUTE_FIELDS[i]);
|
||||||
|
if(len > max) max = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
void print_ascii(struct ascii* art) {
|
void print_ascii(struct ascii* art) {
|
||||||
|
uint32_t longest_attribute = longest_attribute_length(art);
|
||||||
if(art->vendor == VENDOR_INTEL)
|
if(art->vendor == VENDOR_INTEL)
|
||||||
print_ascii_intel(art);
|
print_ascii_intel(art, longest_attribute);
|
||||||
else
|
else
|
||||||
print_ascii_amd(art);
|
print_ascii_amd(art, longest_attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool print_cpufetch(struct cpuInfo* cpu, struct cache* cach, struct frequency* freq, struct topology* topo, STYLE s, struct colors* cs) {
|
||||||
|
struct ascii* art = set_ascii(get_cpu_vendor(cpu), s, cs);
|
||||||
|
if(art == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char* cpu_name = get_str_cpu_name(cpu);
|
||||||
|
char* sockets = get_str_sockets(topo);
|
||||||
|
char* max_frequency = get_str_freq(freq);
|
||||||
|
char* n_cores = get_str_topology(cpu, topo, false);
|
||||||
|
char* n_cores_dual = get_str_topology(cpu, topo, true);
|
||||||
|
char* avx = get_str_avx(cpu);
|
||||||
|
char* sse = get_str_sse(cpu);
|
||||||
|
char* fma = get_str_fma(cpu);
|
||||||
|
char* aes = get_str_aes(cpu);
|
||||||
|
char* sha = get_str_sha(cpu);
|
||||||
|
char* l1i = get_str_l1i(cach, topo);
|
||||||
|
char* l1d = get_str_l1d(cach, topo);
|
||||||
|
char* l2 = get_str_l2(cach, topo);
|
||||||
|
char* l3 = get_str_l3(cach, topo);
|
||||||
|
char* pp = get_str_peak_performance(cpu,topo,get_freq(freq));
|
||||||
|
|
||||||
|
setAttribute(art,ATTRIBUTE_NAME,cpu_name);
|
||||||
|
setAttribute(art,ATTRIBUTE_FREQUENCY,max_frequency);
|
||||||
|
setAttribute(art,ATTRIBUTE_NCORES,n_cores);
|
||||||
|
setAttribute(art,ATTRIBUTE_AVX,avx);
|
||||||
|
setAttribute(art,ATTRIBUTE_SSE,sse);
|
||||||
|
setAttribute(art,ATTRIBUTE_FMA,fma);
|
||||||
|
setAttribute(art,ATTRIBUTE_AES,aes);
|
||||||
|
setAttribute(art,ATTRIBUTE_SHA,sha);
|
||||||
|
setAttribute(art,ATTRIBUTE_L1i,l1i);
|
||||||
|
setAttribute(art,ATTRIBUTE_L1d,l1d);
|
||||||
|
setAttribute(art,ATTRIBUTE_L2,l2);
|
||||||
|
setAttribute(art,ATTRIBUTE_PEAK,pp);
|
||||||
|
|
||||||
|
uint32_t socket_num = get_nsockets(topo);
|
||||||
|
if (socket_num > 1) {
|
||||||
|
setAttribute(art, ATTRIBUTE_SOCKETS, sockets);
|
||||||
|
setAttribute(art, ATTRIBUTE_NCORES_DUAL, n_cores_dual);
|
||||||
|
}
|
||||||
|
if(l3 != NULL) {
|
||||||
|
setAttribute(art,ATTRIBUTE_L3,l3);
|
||||||
|
}
|
||||||
|
if(art->n_attributes_set > NUMBER_OF_LINES) {
|
||||||
|
printBug("The number of attributes set is bigger than the max that can be displayed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_ascii(art);
|
||||||
|
|
||||||
|
free(cpu_name);
|
||||||
|
free(max_frequency);
|
||||||
|
free(sockets);
|
||||||
|
free(n_cores);
|
||||||
|
free(n_cores_dual);
|
||||||
|
free(avx);
|
||||||
|
free(sse);
|
||||||
|
free(fma);
|
||||||
|
free(aes);
|
||||||
|
free(sha);
|
||||||
|
free(l1i);
|
||||||
|
free(l1d);
|
||||||
|
free(l2);
|
||||||
|
free(l3);
|
||||||
|
free(pp);
|
||||||
|
|
||||||
|
free(cpu);
|
||||||
|
free(art);
|
||||||
|
if(cs != NULL) free_colors_struct(cs);
|
||||||
|
free_cache_struct(cach);
|
||||||
|
free_topo_struct(topo);
|
||||||
|
free_freq_struct(freq);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,19 @@
|
|||||||
#ifndef __PRINTER__
|
#ifndef __PRINTER__
|
||||||
#define __PRINTER__
|
#define __PRINTER__
|
||||||
|
|
||||||
#include "standart.h"
|
|
||||||
#include "ascii.h"
|
|
||||||
|
|
||||||
#define ATTRIBUTE_COUNT 12
|
|
||||||
#define ATTRIBUTE_NAME 0
|
|
||||||
#define ATTRIBUTE_FREQUENCY 1
|
|
||||||
#define ATTRIBUTE_NCORES 2
|
|
||||||
#define ATTRIBUTE_AVX 3
|
|
||||||
#define ATTRIBUTE_SSE 4
|
|
||||||
#define ATTRIBUTE_FMA 5
|
|
||||||
#define ATTRIBUTE_AES 6
|
|
||||||
#define ATTRIBUTE_SHA 7
|
|
||||||
#define ATTRIBUTE_L1 8
|
|
||||||
#define ATTRIBUTE_L2 9
|
|
||||||
#define ATTRIBUTE_L3 10
|
|
||||||
#define ATTRIBUTE_PEAK 11
|
|
||||||
|
|
||||||
typedef int STYLE;
|
typedef int STYLE;
|
||||||
|
|
||||||
|
#include "args.h"
|
||||||
|
#include "cpuid.h"
|
||||||
|
|
||||||
#define STYLES_COUNT 3
|
#define STYLES_COUNT 3
|
||||||
|
|
||||||
#define STYLE_EMPTY -2
|
#define STYLE_INVALID -2
|
||||||
#define STYLE_INVALID -1
|
#define STYLE_EMPTY -1
|
||||||
#define STYLE_DEFAULT 0
|
#define STYLE_FANCY 0
|
||||||
#define STYLE_DARK 1
|
#define STYLE_RETRO 1
|
||||||
#define STYLE_NONE 2
|
#define STYLE_LEGACY 2
|
||||||
|
|
||||||
struct ascii;
|
bool print_cpufetch(struct cpuInfo* cpu, struct cache* cach, struct frequency* freq, struct topology* topo, STYLE s, struct colors* cs);
|
||||||
|
|
||||||
static const int STYLES_CODE_LIST [STYLES_COUNT] = {STYLE_DEFAULT, STYLE_DARK};
|
|
||||||
struct ascii* set_ascii(VENDOR cpuVendor, STYLE style);
|
|
||||||
void print_ascii(struct ascii* art);
|
|
||||||
void setAttribute(struct ascii* art, int type, char* value);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
736
src/standart.c
736
src/standart.c
@@ -1,736 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
|
||||||
#include "udev.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "standart.h"
|
|
||||||
#include "cpuid.h"
|
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
#define VENDOR_INTEL_STRING "GenuineIntel"
|
|
||||||
#define VENDOR_AMD_STRING "AuthenticAMD"
|
|
||||||
|
|
||||||
#define STRING_YES "Yes"
|
|
||||||
#define STRING_NO "No"
|
|
||||||
#define STRING_UNKNOWN "Unknown"
|
|
||||||
#define STRING_NONE "None"
|
|
||||||
#define STRING_MEGAHERZ "MHz"
|
|
||||||
#define STRING_GIGAHERZ "GHz"
|
|
||||||
#define STRING_KILOBYTES "KB"
|
|
||||||
#define STRING_MEGABYTES "MB"
|
|
||||||
|
|
||||||
#define MASK 0xFF
|
|
||||||
|
|
||||||
/*
|
|
||||||
* cpuid reference: http://www.sandpile.org/x86/cpuid.htm
|
|
||||||
* cpuid amd: https://www.amd.com/system/files/TechDocs/25481.pdf
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct cpuInfo {
|
|
||||||
bool AVX;
|
|
||||||
bool AVX2;
|
|
||||||
bool AVX512;
|
|
||||||
bool SSE;
|
|
||||||
bool SSE2;
|
|
||||||
bool SSE3;
|
|
||||||
bool SSSE3;
|
|
||||||
bool SSE4a;
|
|
||||||
bool SSE4_1;
|
|
||||||
bool SSE4_2;
|
|
||||||
bool FMA3;
|
|
||||||
bool FMA4;
|
|
||||||
bool AES;
|
|
||||||
bool SHA;
|
|
||||||
|
|
||||||
VENDOR cpu_vendor;
|
|
||||||
|
|
||||||
// Max cpuids levels
|
|
||||||
uint32_t maxLevels;
|
|
||||||
// Max cpuids extended levels
|
|
||||||
uint32_t maxExtendedLevels;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cache {
|
|
||||||
int32_t L1i;
|
|
||||||
int32_t L1d;
|
|
||||||
int32_t L2;
|
|
||||||
int32_t L3;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct frequency {
|
|
||||||
int64_t base;
|
|
||||||
int64_t max;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct topology {
|
|
||||||
int64_t total_cores;
|
|
||||||
uint32_t physical_cores;
|
|
||||||
uint32_t logical_cores;
|
|
||||||
uint32_t smt;
|
|
||||||
uint32_t sockets;
|
|
||||||
bool ht;
|
|
||||||
};
|
|
||||||
|
|
||||||
void init_cpu_info(struct cpuInfo* cpu) {
|
|
||||||
cpu->AVX = false;
|
|
||||||
cpu->AVX2 = false;
|
|
||||||
cpu->AVX512 = false;
|
|
||||||
cpu->SSE = false;
|
|
||||||
cpu->SSE2 = false;
|
|
||||||
cpu->SSE3 = false;
|
|
||||||
cpu->SSSE3 = false;
|
|
||||||
cpu->SSE4a = false;
|
|
||||||
cpu->SSE4_1 = false;
|
|
||||||
cpu->SSE4_2 = false;
|
|
||||||
cpu->FMA3 = false;
|
|
||||||
cpu->FMA4 = false;
|
|
||||||
cpu->AES = false;
|
|
||||||
cpu->SHA = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_cpu_vendor_internal(char* name, uint32_t ebx,uint32_t ecx,uint32_t edx) {
|
|
||||||
name[__COUNTER__] = ebx & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (ebx>>24) & MASK;
|
|
||||||
|
|
||||||
name[__COUNTER__] = edx & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (edx>>24) & MASK;
|
|
||||||
|
|
||||||
name[__COUNTER__] = ecx & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>8) & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>16) & MASK;
|
|
||||||
name[__COUNTER__] = (ecx>>24) & MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cpuInfo* get_cpu_info() {
|
|
||||||
struct cpuInfo* cpu = malloc(sizeof(struct cpuInfo));
|
|
||||||
init_cpu_info(cpu);
|
|
||||||
uint32_t eax = 0;
|
|
||||||
uint32_t ebx = 0;
|
|
||||||
uint32_t ecx = 0;
|
|
||||||
uint32_t edx = 0;
|
|
||||||
|
|
||||||
//Get max cpuid level
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
cpu->maxLevels = eax;
|
|
||||||
|
|
||||||
//Fill vendor
|
|
||||||
char name[13];
|
|
||||||
memset(name,0,13);
|
|
||||||
get_cpu_vendor_internal(name, ebx, ecx, edx);
|
|
||||||
|
|
||||||
if(strcmp(VENDOR_INTEL_STRING,name) == 0)
|
|
||||||
cpu->cpu_vendor = VENDOR_INTEL;
|
|
||||||
else if (strcmp(VENDOR_AMD_STRING,name) == 0)
|
|
||||||
cpu->cpu_vendor = VENDOR_AMD;
|
|
||||||
else {
|
|
||||||
cpu->cpu_vendor = VENDOR_INVALID;
|
|
||||||
printErr("Unknown CPU vendor: %s", name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Get max extended level
|
|
||||||
eax = 0x80000000;
|
|
||||||
ebx = 0;
|
|
||||||
ecx = 0;
|
|
||||||
edx = 0;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
cpu->maxExtendedLevels = eax;
|
|
||||||
|
|
||||||
//Fill instructions support
|
|
||||||
if (cpu->maxLevels >= 0x00000001){
|
|
||||||
eax = 0x00000001;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
cpu->SSE = (edx & ((int)1 << 25)) != 0;
|
|
||||||
cpu->SSE2 = (edx & ((int)1 << 26)) != 0;
|
|
||||||
cpu->SSE3 = (ecx & ((int)1 << 0)) != 0;
|
|
||||||
|
|
||||||
cpu->SSSE3 = (ecx & ((int)1 << 9)) != 0;
|
|
||||||
cpu->SSE4_1 = (ecx & ((int)1 << 19)) != 0;
|
|
||||||
cpu->SSE4_2 = (ecx & ((int)1 << 20)) != 0;
|
|
||||||
|
|
||||||
cpu->AES = (ecx & ((int)1 << 25)) != 0;
|
|
||||||
|
|
||||||
cpu->AVX = (ecx & ((int)1 << 28)) != 0;
|
|
||||||
cpu->FMA3 = (ecx & ((int)1 << 12)) != 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printWarn("Can't read features information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000001, cpu->maxLevels);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cpu->maxLevels >= 0x00000007){
|
|
||||||
eax = 0x00000007;
|
|
||||||
ecx = 0x00000000;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
cpu->AVX2 = (ebx & ((int)1 << 5)) != 0;
|
|
||||||
cpu->SHA = (ebx & ((int)1 << 29)) != 0;
|
|
||||||
cpu->AVX512 = (((ebx & ((int)1 << 16)) != 0) ||
|
|
||||||
((ebx & ((int)1 << 28)) != 0) ||
|
|
||||||
((ebx & ((int)1 << 26)) != 0) ||
|
|
||||||
((ebx & ((int)1 << 27)) != 0) ||
|
|
||||||
((ebx & ((int)1 << 31)) != 0) ||
|
|
||||||
((ebx & ((int)1 << 30)) != 0) ||
|
|
||||||
((ebx & ((int)1 << 17)) != 0) ||
|
|
||||||
((ebx & ((int)1 << 21)) != 0));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printWarn("Can't read features information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000007, cpu->maxLevels);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cpu->maxExtendedLevels >= 0x80000001){
|
|
||||||
eax = 0x80000001;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
cpu->SSE4a = (ecx & ((int)1 << 6)) != 0;
|
|
||||||
cpu->FMA4 = (ecx & ((int)1 << 16)) != 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printWarn("Can't read features information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", 0x80000001, cpu->maxExtendedLevels);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cpu;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct topology* get_topology_info(struct cpuInfo* cpu) {
|
|
||||||
struct topology* topo = malloc(sizeof(struct topology));
|
|
||||||
uint32_t eax = 0;
|
|
||||||
uint32_t ebx = 0;
|
|
||||||
uint32_t ecx = 0;
|
|
||||||
uint32_t edx = 0;
|
|
||||||
int32_t type;
|
|
||||||
|
|
||||||
if (cpu->maxLevels >= 0x00000001) {
|
|
||||||
eax = 0x00000001;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
topo->ht = edx & (1 << 28);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printWarn("Can't read HT information from cpuid (needed level is 0x%.8X, max is 0x%.8X). Assuming HT is disabled", 0x00000001, cpu->maxLevels);
|
|
||||||
topo->ht = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(cpu->cpu_vendor) {
|
|
||||||
case VENDOR_INTEL:
|
|
||||||
if (cpu->maxLevels >= 0x0000000B) {
|
|
||||||
//TODO: This idea only works with no NUMA systems
|
|
||||||
eax = 0x0000000B;
|
|
||||||
ecx = 0x00000000;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
type = (ecx >> 8) & 0xFF;
|
|
||||||
if (type != 1) {
|
|
||||||
printBug("Unexpected type in cpuid 0x0000000B (expected 1, got %d)", type);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
topo->smt = ebx & 0xFFFF;
|
|
||||||
|
|
||||||
eax = 0x0000000B;
|
|
||||||
ecx = 0x00000001;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
type = (ecx >> 8) & 0xFF;
|
|
||||||
if (type < 2) {
|
|
||||||
printBug("Unexpected type in cpuid 0x0000000B (expected < 2, got %d)", type);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
topo->logical_cores = ebx & 0xFFFF;
|
|
||||||
topo->physical_cores = topo->logical_cores / topo->smt;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printWarn("Can't read topology information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x0000000B, cpu->maxLevels);
|
|
||||||
topo->physical_cores = 1;
|
|
||||||
topo->logical_cores = 1;
|
|
||||||
topo->smt = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case VENDOR_AMD:
|
|
||||||
if (cpu->maxExtendedLevels >= 0x80000008) {
|
|
||||||
eax = 0x80000008;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
topo->logical_cores = (ecx & 0xFF) + 1;
|
|
||||||
|
|
||||||
if (cpu->maxExtendedLevels >= 0x8000001E) {
|
|
||||||
eax = 0x8000001E;
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
topo->smt = ((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->maxLevels);
|
|
||||||
topo->smt = 1;
|
|
||||||
}
|
|
||||||
topo->physical_cores = topo->logical_cores / topo->smt;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printWarn("Can't read topology information from cpuid (needed extended level is 0x%.8X, max is 0x%.8X)", 0x80000008, cpu->maxLevels);
|
|
||||||
topo->physical_cores = 1;
|
|
||||||
topo->logical_cores = 1;
|
|
||||||
topo->smt = 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printBug("Cant get topology because VENDOR is empty");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask the OS the total number of cores it sees
|
|
||||||
// If we have one socket, it will be same as the cpuid,
|
|
||||||
// but in dual socket it will not!
|
|
||||||
#ifdef _WIN32
|
|
||||||
SYSTEM_INFO info;
|
|
||||||
GetSystemInfo(&info);
|
|
||||||
topo->total_cores = info.dwNumberOfProcessors;
|
|
||||||
#else
|
|
||||||
if((topo->total_cores = sysconf(_SC_NPROCESSORS_ONLN)) == -1) {
|
|
||||||
perror("sysconf");
|
|
||||||
topo->total_cores = topo->logical_cores; // fallback
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
topo->sockets = topo->total_cores / topo->smt / topo->physical_cores; // Idea borrowed from lscpu
|
|
||||||
|
|
||||||
return topo;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cache* get_cache_info(struct cpuInfo* cpu) {
|
|
||||||
struct cache* cach = malloc(sizeof(struct cache));
|
|
||||||
uint32_t eax = 0;
|
|
||||||
uint32_t ebx = 0;
|
|
||||||
uint32_t ecx = 0;
|
|
||||||
uint32_t edx = 0;
|
|
||||||
uint32_t level;
|
|
||||||
|
|
||||||
// We use standart 0x00000004 for Intel
|
|
||||||
// We use extended 0x8000001D for AMD
|
|
||||||
if(cpu->cpu_vendor == VENDOR_INTEL) {
|
|
||||||
level = 0x00000004;
|
|
||||||
if(cpu->maxLevels < level) {
|
|
||||||
printErr("Can't read cache information from cpuid (needed level is %d, max is %d)", level, cpu->maxLevels);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
level = 0x8000001D;
|
|
||||||
if(cpu->maxExtendedLevels < level) {
|
|
||||||
printErr("Can't read cache information from cpuid (needed extended level is %d, max is %d)", level, cpu->maxExtendedLevels);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We suppose there are 4 caches (at most)
|
|
||||||
for(int i=0; i < 4; i++) {
|
|
||||||
eax = level; // get cache info
|
|
||||||
ebx = 0;
|
|
||||||
ecx = i; // cache id
|
|
||||||
edx = 0;
|
|
||||||
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
|
|
||||||
int32_t cache_type = eax & 0x1F;
|
|
||||||
|
|
||||||
// If its 0, we tried fetching a non existing cache
|
|
||||||
if (cache_type > 0) {
|
|
||||||
int32_t cache_level = (eax >>= 5) & 0x7;
|
|
||||||
int32_t cache_is_self_initializing = (eax >>= 3) & 0x1; // does not need SW initialization
|
|
||||||
int32_t cache_is_fully_associative = (eax >>= 1) & 0x1;
|
|
||||||
uint32_t cache_sets = ecx + 1;
|
|
||||||
uint32_t cache_coherency_line_size = (ebx & 0xFFF) + 1;
|
|
||||||
uint32_t cache_physical_line_partitions = ((ebx >>= 12) & 0x3FF) + 1;
|
|
||||||
uint32_t cache_ways_of_associativity = ((ebx >>= 10) & 0x3FF) + 1;
|
|
||||||
|
|
||||||
int32_t cache_total_size = cache_ways_of_associativity * cache_physical_line_partitions * cache_coherency_line_size * cache_sets;
|
|
||||||
|
|
||||||
switch (cache_type) {
|
|
||||||
case 1: // Data Cache (We assume this is L1d)
|
|
||||||
if(cache_level != 1) {
|
|
||||||
printBug("Found data cache at level %d (expected 1)", cache_level);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
cach->L1d = cache_total_size;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // Instruction Cache (We assume this is L1i)
|
|
||||||
if(cache_level != 1) {
|
|
||||||
printBug("Found instruction cache at level %d (expected 1)", cache_level);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
cach->L1i = cache_total_size;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: // Unified Cache (This may be L2 or L3)
|
|
||||||
if(cache_level == 2) cach->L2 = cache_total_size;
|
|
||||||
else if(cache_level == 3) cach->L3 = cache_total_size;
|
|
||||||
else {
|
|
||||||
printBug("Found unified cache at level %d (expected == 2 or 3)", cache_level);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // Unknown Type Cache
|
|
||||||
printBug("Unknown Type Cache found at ID %d", i);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(i == 2) cach->L2 = UNKNOWN;
|
|
||||||
else if(i == 3) cach->L3 = UNKNOWN;
|
|
||||||
else {
|
|
||||||
printBug("Could not find cache ID %d", i);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity checks. If we read values greater than this, they can't be valid ones
|
|
||||||
// The values were chosen by me
|
|
||||||
if(cach->L1i > 64 * 1024) {
|
|
||||||
printBug("Invalid L1i size: %dKB\n", cach->L1i/1024);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if(cach->L1d > 64 * 1024) {
|
|
||||||
printBug("Invalid L1d size: %dKB\n", cach->L1d/1024);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if(cach->L2 != UNKNOWN && cach->L2 > 2 * 1048576) {
|
|
||||||
printBug("Invalid L2 size: %dMB\n", cach->L2/(1048576));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if(cach->L3 != UNKNOWN && cach->L3 > 100 * 1048576) {
|
|
||||||
printBug("Invalid L3 size: %dMB\n", cach->L3/(1048576));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cach;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct frequency* get_frequency_info(struct cpuInfo* cpu) {
|
|
||||||
struct frequency* freq = malloc(sizeof(struct frequency));
|
|
||||||
|
|
||||||
if(cpu->maxLevels < 0x16) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
printErr("Can't read frequency information from cpuid (needed level is %d, max is %d)", 0x16, cpu->maxLevels);
|
|
||||||
freq->base = UNKNOWN;
|
|
||||||
freq->max = UNKNOWN;
|
|
||||||
#else
|
|
||||||
printWarn("Can't read frequency information from cpuid (needed level is %d, max is %d). Using udev", 0x16, cpu->maxLevels);
|
|
||||||
freq->base = UNKNOWN;
|
|
||||||
freq->max = get_max_freq_from_file();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
uint32_t eax = 0x16;
|
|
||||||
uint32_t ebx = 0;
|
|
||||||
uint32_t ecx = 0;
|
|
||||||
uint32_t edx = 0;
|
|
||||||
|
|
||||||
cpuid(&eax, &ebx, &ecx, &edx);
|
|
||||||
|
|
||||||
freq->base = eax;
|
|
||||||
freq->max = ebx;
|
|
||||||
}
|
|
||||||
|
|
||||||
return freq;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t get_freq(struct frequency* freq) {
|
|
||||||
return freq->max;
|
|
||||||
}
|
|
||||||
|
|
||||||
VENDOR get_cpu_vendor(struct cpuInfo* cpu) {
|
|
||||||
return cpu->cpu_vendor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void debug_cpu_info(struct cpuInfo* cpu) {
|
|
||||||
printf("AVX=%s\n", cpu->AVX ? "true" : "false");
|
|
||||||
printf("AVX2=%s\n", cpu->AVX2 ? "true" : "false");
|
|
||||||
printf("AVX512=%s\n\n", cpu->AVX512 ? "true" : "false");
|
|
||||||
|
|
||||||
printf("SSE=%s\n", cpu->SSE ? "true" : "false");
|
|
||||||
printf("SSE2=%s\n", cpu->SSE2 ? "true" : "false");
|
|
||||||
printf("SSE3=%s\n", cpu->SSE3 ? "true" : "false");
|
|
||||||
printf("SSSE3=%s\n", cpu->SSSE3 ? "true" : "false");
|
|
||||||
printf("SSE4a=%s\n", cpu->SSE4a ? "true" : "false");
|
|
||||||
printf("SSE4_1=%s\n", cpu->SSE4_1 ? "true" : "false");
|
|
||||||
printf("SSE4_2=%s\n\n", cpu->SSE4_2 ? "true" : "false");
|
|
||||||
|
|
||||||
printf("FMA3=%s\n", cpu->FMA3 ? "true" : "false");
|
|
||||||
printf("FMA4=%s\n\n", cpu->FMA4 ? "true" : "false");
|
|
||||||
|
|
||||||
printf("AES=%s\n", cpu->AES ? "true" : "false");
|
|
||||||
printf("SHA=%s\n", cpu->SHA ? "true" : "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
void debug_cache(struct cache* cach) {
|
|
||||||
printf("L1i=%dB\n",cach->L1i);
|
|
||||||
printf("L1d=%dB\n",cach->L1d);
|
|
||||||
printf("L2=%dB\n",cach->L2);
|
|
||||||
printf("L3=%dB\n",cach->L3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void debug_frequency(struct frequency* freq) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
printf("maxf=%I64d Mhz\n",freq->max);
|
|
||||||
printf("basef=%I64d Mhz\n",freq->base);
|
|
||||||
#else
|
|
||||||
printf("maxf=%ld Mhz\n",freq->max);
|
|
||||||
printf("basef=%ld Mhz\n",freq->base);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** STRING FUNCTIONS ***/
|
|
||||||
|
|
||||||
char* get_str_peak_performance(struct cpuInfo* cpu, struct topology* topo, int64_t freq) {
|
|
||||||
/***
|
|
||||||
PP = PeakPerformance
|
|
||||||
SP = SinglePrecision
|
|
||||||
|
|
||||||
PP(SP) =
|
|
||||||
N_CORES *
|
|
||||||
FREQUENCY *
|
|
||||||
2(Two vector units) *
|
|
||||||
2(If cpu has fma) *
|
|
||||||
16(If AVX512), 8(If AVX), 4(If SSE) *
|
|
||||||
|
|
||||||
***/
|
|
||||||
|
|
||||||
//7 for GFLOP/s and 6 for digits,eg 412.14
|
|
||||||
uint32_t size = 7+6+1+1;
|
|
||||||
assert(strlen(STRING_UNKNOWN)+1 <= size);
|
|
||||||
char* string = malloc(sizeof(char)*size);
|
|
||||||
|
|
||||||
//First check we have consistent data
|
|
||||||
if(freq == UNKNOWN) {
|
|
||||||
snprintf(string,strlen(STRING_UNKNOWN)+1,STRING_UNKNOWN);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
double flops = topo->physical_cores*(freq*1000000);
|
|
||||||
|
|
||||||
// Intel USUALLY has two VPUs. I have never seen an AMD
|
|
||||||
// with two VPUs.
|
|
||||||
if(cpu->cpu_vendor == VENDOR_INTEL) flops = flops * 2;
|
|
||||||
|
|
||||||
if(cpu->FMA3 || cpu->FMA4)
|
|
||||||
flops = flops*2;
|
|
||||||
|
|
||||||
if(cpu->AVX512)
|
|
||||||
flops = flops*16;
|
|
||||||
else if(cpu->AVX || cpu->AVX2)
|
|
||||||
flops = flops*8;
|
|
||||||
else if(cpu->SSE)
|
|
||||||
flops = flops*4;
|
|
||||||
|
|
||||||
if(flops >= (double)1000000000000.0)
|
|
||||||
snprintf(string,size,"%.2f TFLOP/s",flops/1000000000000);
|
|
||||||
else if(flops >= 1000000000.0)
|
|
||||||
snprintf(string,size,"%.2f GFLOP/s",flops/1000000000);
|
|
||||||
else
|
|
||||||
snprintf(string,size,"%.2f MFLOP/s",flops/1000000);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* get_str_topology(struct topology* topo) {
|
|
||||||
char* string;
|
|
||||||
if(topo->smt > 1) {
|
|
||||||
//3 for digits, 8 for ' cores (', 3 for digits, 9 for ' threads)'
|
|
||||||
uint32_t size = 3+8+3+9+1;
|
|
||||||
string = malloc(sizeof(char)*size);
|
|
||||||
snprintf(string, size, "%d cores (%d threads)",topo->physical_cores,topo->logical_cores);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
uint32_t size = 3+7+1;
|
|
||||||
string = malloc(sizeof(char)*size);
|
|
||||||
snprintf(string, size, "%d cores",topo->physical_cores);
|
|
||||||
}
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* get_str_avx(struct cpuInfo* cpu) {
|
|
||||||
//If all AVX are available, it will use up to 15
|
|
||||||
char* string = malloc(sizeof(char)*15+1);
|
|
||||||
if(!cpu->AVX)
|
|
||||||
snprintf(string,2+1,"No");
|
|
||||||
else if(!cpu->AVX2)
|
|
||||||
snprintf(string,3+1,"AVX");
|
|
||||||
else if(!cpu->AVX512)
|
|
||||||
snprintf(string,8+1,"AVX,AVX2");
|
|
||||||
else
|
|
||||||
snprintf(string,15+1,"AVX,AVX2,AVX512");
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* get_str_sse(struct cpuInfo* cpu) {
|
|
||||||
uint32_t last = 0;
|
|
||||||
uint32_t SSE_sl = 4;
|
|
||||||
uint32_t SSE2_sl = 5;
|
|
||||||
uint32_t SSE3_sl = 5;
|
|
||||||
uint32_t SSSE3_sl = 6;
|
|
||||||
uint32_t SSE4a_sl = 6;
|
|
||||||
uint32_t SSE4_1_sl = 7;
|
|
||||||
uint32_t SSE4_2_sl = 7;
|
|
||||||
char* string = malloc(sizeof(char)*SSE_sl+SSE2_sl+SSE3_sl+SSSE3_sl+SSE4a_sl+SSE4_1_sl+SSE4_2_sl+1);
|
|
||||||
|
|
||||||
if(cpu->SSE) {
|
|
||||||
snprintf(string+last,SSE_sl+1,"SSE,");
|
|
||||||
last+=SSE_sl;
|
|
||||||
}
|
|
||||||
if(cpu->SSE2) {
|
|
||||||
snprintf(string+last,SSE2_sl+1,"SSE2,");
|
|
||||||
last+=SSE2_sl;
|
|
||||||
}
|
|
||||||
if(cpu->SSE3) {
|
|
||||||
snprintf(string+last,SSE3_sl+1,"SSE3,");
|
|
||||||
last+=SSE3_sl;
|
|
||||||
}
|
|
||||||
if(cpu->SSSE3) {
|
|
||||||
snprintf(string+last,SSSE3_sl+1,"SSSE3,");
|
|
||||||
last+=SSSE3_sl;
|
|
||||||
}
|
|
||||||
if(cpu->SSE4a) {
|
|
||||||
snprintf(string+last,SSE4a_sl+1,"SSE4a,");
|
|
||||||
last+=SSE4a_sl;
|
|
||||||
}
|
|
||||||
if(cpu->SSE4_1) {
|
|
||||||
snprintf(string+last,SSE4_1_sl+1,"SSE4_1,");
|
|
||||||
last+=SSE4_1_sl;
|
|
||||||
}
|
|
||||||
if(cpu->SSE4_2) {
|
|
||||||
snprintf(string+last,SSE4_2_sl+1,"SSE4_2,");
|
|
||||||
last+=SSE4_2_sl;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Purge last comma
|
|
||||||
string[last-1] = '\0';
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* get_str_fma(struct cpuInfo* cpu) {
|
|
||||||
char* string = malloc(sizeof(char)*9+1);
|
|
||||||
if(!cpu->FMA3)
|
|
||||||
snprintf(string,2+1,"No");
|
|
||||||
else if(!cpu->FMA4)
|
|
||||||
snprintf(string,4+1,"FMA3");
|
|
||||||
else
|
|
||||||
snprintf(string,9+1,"FMA3,FMA4");
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* get_str_aes(struct cpuInfo* cpu) {
|
|
||||||
char* string = malloc(sizeof(char)*3+1);
|
|
||||||
if(cpu->AES)
|
|
||||||
snprintf(string,3+1,STRING_YES);
|
|
||||||
else
|
|
||||||
snprintf(string,2+1,STRING_NO);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* get_str_sha(struct cpuInfo* cpu) {
|
|
||||||
char* string = malloc(sizeof(char)*3+1);
|
|
||||||
if(cpu->SHA)
|
|
||||||
snprintf(string,3+1,STRING_YES);
|
|
||||||
else
|
|
||||||
snprintf(string,2+1,STRING_NO);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// String functions
|
|
||||||
char* get_str_l1(struct cache* cach) {
|
|
||||||
// 2*2 for digits, 4 for two 'KB' and 6 for '(D)' and '(I)'
|
|
||||||
uint32_t size = (2*2+4+6+1);
|
|
||||||
int32_t sanity_ret;
|
|
||||||
char* string = malloc(sizeof(char)*size);
|
|
||||||
sanity_ret = snprintf(string,size,"%d"STRING_KILOBYTES"(D)%d"STRING_KILOBYTES"(I)",cach->L1d/1024,cach->L1i/1024);
|
|
||||||
assert(sanity_ret > 0);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* get_str_l2(struct cache* cach) {
|
|
||||||
if(cach->L2 == UNKNOWN) {
|
|
||||||
char* string = malloc(sizeof(char) * 5);
|
|
||||||
snprintf(string, 5, STRING_NONE);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int32_t sanity_ret;
|
|
||||||
char* string;
|
|
||||||
if(cach->L2/1024 >= 1024) {
|
|
||||||
//1 for digit, 2 for 'MB'
|
|
||||||
uint32_t size = (1+2+1);
|
|
||||||
string = malloc(sizeof(char)*size);
|
|
||||||
sanity_ret = snprintf(string,size,"%d"STRING_MEGABYTES,cach->L2/(1048576));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//4 for digits, 2 for 'KB'
|
|
||||||
uint32_t size = (4+2+1);
|
|
||||||
string = malloc(sizeof(char)*size);
|
|
||||||
sanity_ret = snprintf(string,size,"%d"STRING_KILOBYTES,cach->L2/1024);
|
|
||||||
}
|
|
||||||
assert(sanity_ret > 0);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* get_str_l3(struct cache* cach) {
|
|
||||||
if(cach->L3 == UNKNOWN) {
|
|
||||||
char* string = malloc(sizeof(char) * 5);
|
|
||||||
snprintf(string, 5, STRING_NONE);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int32_t sanity_ret;
|
|
||||||
char* string;
|
|
||||||
if(cach->L3/1024 >= 1024) {
|
|
||||||
//1 for digit, 2 for 'MB'
|
|
||||||
uint32_t size = (1+2+1);
|
|
||||||
string = malloc(sizeof(char)*size);
|
|
||||||
sanity_ret = snprintf(string,size,"%d"STRING_MEGABYTES,cach->L3/(1048576));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//4 for digits, 2 for 'KB'
|
|
||||||
uint32_t size = (4+2+1);
|
|
||||||
string = malloc(sizeof(char)*size);
|
|
||||||
sanity_ret = snprintf(string,size,"%d"STRING_KILOBYTES,cach->L3/1024);
|
|
||||||
}
|
|
||||||
assert(sanity_ret > 0);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
assert(strlen(STRING_UNKNOWN)+1 <= size);
|
|
||||||
char* string = malloc(sizeof(char)*size);
|
|
||||||
if(freq->max == UNKNOWN)
|
|
||||||
snprintf(string,strlen(STRING_UNKNOWN)+1,STRING_UNKNOWN);
|
|
||||||
else if(freq->max >= 1000)
|
|
||||||
snprintf(string,size,"%.2f"STRING_GIGAHERZ,(float)(freq->max)/1000);
|
|
||||||
else
|
|
||||||
snprintf(string,size,"%.2f"STRING_MEGAHERZ,(float)(freq->max));
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_levels(struct cpuInfo* cpu, char* cpu_name) {
|
|
||||||
printf("%s\n", cpu_name);
|
|
||||||
printf("- Max standart level: 0x%.8X\n", cpu->maxLevels);
|
|
||||||
printf("- Max extended level: 0x%.8X\n", cpu->maxExtendedLevels);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_topo_struct(struct topology* topo) {
|
|
||||||
free(topo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_cache_struct(struct cache* cach) {
|
|
||||||
free(cach);
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_freq_struct(struct frequency* freq) {
|
|
||||||
free(freq);
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#ifndef __01h__
|
|
||||||
#define __01h__
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define VENDOR_EMPTY 0
|
|
||||||
#define VENDOR_INTEL 1
|
|
||||||
#define VENDOR_AMD 2
|
|
||||||
#define VENDOR_INVALID 3
|
|
||||||
|
|
||||||
#define UNKNOWN -1
|
|
||||||
|
|
||||||
struct cpuInfo;
|
|
||||||
struct frequency;
|
|
||||||
struct cache;
|
|
||||||
struct topology;
|
|
||||||
|
|
||||||
typedef int32_t VENDOR;
|
|
||||||
|
|
||||||
struct cpuInfo* get_cpu_info();
|
|
||||||
VENDOR get_cpu_vendor(struct cpuInfo* cpu);
|
|
||||||
int64_t get_freq(struct frequency* freq);
|
|
||||||
struct cache* get_cache_info(struct cpuInfo* cpu);
|
|
||||||
struct frequency* get_frequency_info(struct cpuInfo* cpu);
|
|
||||||
struct topology* get_topology_info(struct cpuInfo* cpu);
|
|
||||||
|
|
||||||
char* get_str_ncores(struct cpuInfo* cpu);
|
|
||||||
char* get_str_avx(struct cpuInfo* cpu);
|
|
||||||
char* get_str_sse(struct cpuInfo* cpu);
|
|
||||||
char* get_str_fma(struct cpuInfo* cpu);
|
|
||||||
char* get_str_aes(struct cpuInfo* cpu);
|
|
||||||
char* get_str_sha(struct cpuInfo* cpu);
|
|
||||||
|
|
||||||
char* get_str_l1(struct cache* cach);
|
|
||||||
char* get_str_l2(struct cache* cach);
|
|
||||||
char* get_str_l3(struct cache* cach);
|
|
||||||
|
|
||||||
char* get_str_freq(struct frequency* freq);
|
|
||||||
|
|
||||||
char* get_str_topology(struct topology* topo);
|
|
||||||
|
|
||||||
char* get_str_peak_performance(struct cpuInfo* cpu, struct topology* topo, int64_t freq);
|
|
||||||
|
|
||||||
void print_levels(struct cpuInfo* cpu, char* cpu_name);
|
|
||||||
|
|
||||||
void free_cpuinfo_struct(struct cpuInfo* cpu);
|
|
||||||
void free_cache_struct(struct cache* cach);
|
|
||||||
void free_topo_struct(struct topology* topo);
|
|
||||||
void free_freq_struct(struct frequency* freq);
|
|
||||||
|
|
||||||
void debug_cpu_info(struct cpuInfo* cpu);
|
|
||||||
void debug_cache(struct cache* cach);
|
|
||||||
void debug_frequency(struct frequency* freq);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "standart.h"
|
#include "cpuid.h"
|
||||||
|
|
||||||
#define _PATH_SYS_SYSTEM "/sys/devices/system"
|
#define _PATH_SYS_SYSTEM "/sys/devices/system"
|
||||||
#define _PATH_SYS_CPU _PATH_SYS_SYSTEM"/cpu"
|
#define _PATH_SYS_CPU _PATH_SYS_SYSTEM"/cpu"
|
||||||
|
|||||||
Reference in New Issue
Block a user