mirror of
https://github.com/Dr-Noob/cpufetch.git
synced 2026-03-26 08:20:38 +01:00
364 lines
9.2 KiB
C
364 lines
9.2 KiB
C
#include "../common/global.h"
|
|
#include "udev.h"
|
|
#include "global.h"
|
|
#include "cpu.h"
|
|
|
|
#define _PATH_DEVTREE "/proc/device-tree/compatible"
|
|
|
|
// https://www.kernel.org/doc/html/latest/core-api/cpu_hotplug.html
|
|
int get_ncores_from_cpuinfo(void) {
|
|
// Examples:
|
|
// 0-271
|
|
// 0-7
|
|
// 0
|
|
|
|
int filelen;
|
|
char* buf;
|
|
if((buf = read_file(_PATH_CPUS_PRESENT, &filelen)) == NULL) {
|
|
printWarn("read_file: %s: %s\n", _PATH_CPUS_PRESENT, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
int ncores;
|
|
char* tmp1;
|
|
if((tmp1 = strstr(buf, "-")) == NULL) {
|
|
// file contains no - character, we assume that it contains 0,
|
|
// which means that the CPU contains only one core
|
|
return 1;
|
|
}
|
|
else {
|
|
tmp1++;
|
|
}
|
|
char* tmp2 = strstr(buf, "\n");
|
|
char ncores_str[filelen];
|
|
memset(ncores_str, 0, sizeof(char) * filelen);
|
|
memcpy(ncores_str, tmp1, tmp2-tmp1);
|
|
|
|
char* end;
|
|
errno = 0;
|
|
ncores = strtol(ncores_str, &end, 10) + 1;
|
|
if(errno != 0) {
|
|
printWarn("strtol: %s:\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
free(buf);
|
|
|
|
return ncores;
|
|
}
|
|
|
|
char* read_file(char* path, int* len) {
|
|
int fd = open(path, O_RDONLY);
|
|
|
|
if(fd == -1) {
|
|
return NULL;
|
|
}
|
|
|
|
//File exists, read it
|
|
int bytes_read = 0;
|
|
int offset = 0;
|
|
int block = 1024;
|
|
int buf_size = block * 4;
|
|
char* buf = emalloc(sizeof(char) * buf_size);
|
|
|
|
while ((bytes_read = read(fd, buf+offset, block)) > 0) {
|
|
offset += bytes_read;
|
|
if(offset + block > buf_size) {
|
|
buf = erealloc(buf, sizeof(char) * (buf_size + block));
|
|
buf_size += block;
|
|
}
|
|
}
|
|
buf[offset] = '\0';
|
|
|
|
if (close(fd) == -1) {
|
|
return NULL;
|
|
}
|
|
|
|
*len = offset;
|
|
return buf;
|
|
}
|
|
|
|
long get_freq_from_file(char* path) {
|
|
int filelen;
|
|
char* buf;
|
|
if((buf = read_file(path, &filelen)) == NULL) {
|
|
printWarn("Could not open '%s'", path);
|
|
return UNKNOWN_DATA;
|
|
}
|
|
|
|
char* end;
|
|
errno = 0;
|
|
long ret = strtol(buf, &end, 10);
|
|
if(errno != 0) {
|
|
printBug("strtol: %s", strerror(errno));
|
|
free(buf);
|
|
return UNKNOWN_DATA;
|
|
}
|
|
|
|
// We will be getting the frequency in KHz
|
|
// We consider it is an error if frequency is
|
|
// greater than 10 GHz or less than 100 MHz
|
|
if(ret > 10000 * 1000 || ret < 100 * 1000) {
|
|
printBug("Invalid data was read from file '%s': %ld\n", path, ret);
|
|
return UNKNOWN_DATA;
|
|
}
|
|
|
|
free(buf);
|
|
|
|
return ret/1000;
|
|
}
|
|
|
|
long get_cache_size_from_file(char* path) {
|
|
int filelen;
|
|
char* buf;
|
|
if((buf = read_file(path, &filelen)) == NULL) {
|
|
printWarn("Could not open '%s'", path);
|
|
return -1;
|
|
}
|
|
|
|
buf[filelen] = '\0'; // remove the K at the end
|
|
|
|
char* end;
|
|
errno = 0;
|
|
long ret = strtol(buf, &end, 10);
|
|
if(errno != 0) {
|
|
printBug("strtol: %s", strerror(errno));
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
|
|
free(buf);
|
|
|
|
return ret * 1024;
|
|
}
|
|
|
|
char* get_field_from_cpuinfo(char* CPUINFO_FIELD) {
|
|
int filelen;
|
|
char* buf;
|
|
if((buf = read_file(_PATH_CPUINFO, &filelen)) == NULL) {
|
|
printWarn("read_file: %s: %s:\n", _PATH_CPUINFO, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
char* tmp1 = strstr(buf, CPUINFO_FIELD);
|
|
if(tmp1 == NULL) return NULL;
|
|
tmp1 = tmp1 + strlen(CPUINFO_FIELD);
|
|
char* tmp2 = strstr(tmp1, "\n");
|
|
|
|
int strlen = (1 + (tmp2-tmp1));
|
|
char* hardware = ecalloc(strlen, sizeof(char));
|
|
strncpy(hardware, tmp1, tmp2-tmp1);
|
|
|
|
return hardware;
|
|
}
|
|
|
|
long get_max_freq_from_file(uint32_t core) {
|
|
char path[_PATH_FREQUENCY_MAX_LEN];
|
|
sprintf(path, "%s%s/cpu%d%s%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, core, _PATH_FREQUENCY, _PATH_FREQUENCY_MAX);
|
|
return get_freq_from_file(path);
|
|
}
|
|
|
|
long get_min_freq_from_file(uint32_t core) {
|
|
char path[_PATH_FREQUENCY_MAX_LEN];
|
|
sprintf(path, "%s%s/cpu%d%s%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, core, _PATH_FREQUENCY, _PATH_FREQUENCY_MIN);
|
|
return get_freq_from_file(path);
|
|
}
|
|
|
|
long get_l1i_cache_size(uint32_t core) {
|
|
char path[_PATH_CACHE_MAX_LEN];
|
|
sprintf(path, "%s%s/cpu%d%s%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, core, _PATH_CACHE_L1I, _PATH_CACHE_SIZE);
|
|
return get_cache_size_from_file(path);
|
|
}
|
|
|
|
long get_l1d_cache_size(uint32_t core) {
|
|
char path[_PATH_CACHE_MAX_LEN];
|
|
sprintf(path, "%s%s/cpu%d%s%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, core, _PATH_CACHE_L1D, _PATH_CACHE_SIZE);
|
|
return get_cache_size_from_file(path);
|
|
}
|
|
|
|
long get_l2_cache_size(uint32_t core) {
|
|
char path[_PATH_CACHE_MAX_LEN];
|
|
sprintf(path, "%s%s/cpu%d%s%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, core, _PATH_CACHE_L2, _PATH_CACHE_SIZE);
|
|
return get_cache_size_from_file(path);
|
|
}
|
|
|
|
long get_l3_cache_size(uint32_t core) {
|
|
char path[_PATH_CACHE_MAX_LEN];
|
|
sprintf(path, "%s%s/cpu%d%s%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, core, _PATH_CACHE_L3, _PATH_CACHE_SIZE);
|
|
return get_cache_size_from_file(path);
|
|
}
|
|
|
|
void add_shared_map(uint32_t** src, int src_idx, uint32_t** dst, int dst_idx, int n) {
|
|
for(int j=0; j < n; j++) {
|
|
dst[dst_idx][j] = src[src_idx][j];
|
|
}
|
|
}
|
|
|
|
bool maps_equal(uint32_t* map1, uint32_t* map2, int n) {
|
|
for(int i=0; i < n; i++) {
|
|
if(map1[i] != map2[i]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Generic function to count the number of distinct
|
|
// elements in the list of files passed in char** paths.
|
|
// An element can be potentially anything.
|
|
// We use this function to count:
|
|
// - The number of caches
|
|
// - The number of sockets
|
|
int get_num_elements_from_files(char** paths, int num_paths) {
|
|
int filelen;
|
|
char* buf;
|
|
char* tmpbuf;
|
|
|
|
// 1. Count the number of bitmasks per file
|
|
if((buf = read_file(paths[0], &filelen)) == NULL) {
|
|
printWarn("Could not open '%s'", paths[0]);
|
|
return -1;
|
|
}
|
|
int num_bitmasks = 1;
|
|
for(int i=0; buf[i]; i++) {
|
|
num_bitmasks += (buf[i] == ',');
|
|
}
|
|
|
|
// 2. Read map from every core
|
|
uint32_t** maps = emalloc(sizeof(uint32_t *) * num_paths);
|
|
for(int i=0; i < num_paths; i++) {
|
|
maps[i] = emalloc(sizeof(uint32_t) * num_bitmasks);
|
|
|
|
if((buf = read_file(paths[i], &filelen)) == NULL) {
|
|
printWarn("Could not open '%s'", paths[i]);
|
|
return -1;
|
|
}
|
|
|
|
for(int j=0; j < num_bitmasks; j++) {
|
|
char* end;
|
|
tmpbuf = emalloc(sizeof(char) * (strlen(buf) + 1));
|
|
memset(tmpbuf, 0, sizeof(char) * (strlen(buf) + 1));
|
|
char* commaend = strstr(buf, ",");
|
|
if(commaend == NULL) {
|
|
strcpy(tmpbuf, buf);
|
|
}
|
|
else {
|
|
strncpy(tmpbuf, buf, commaend-buf);
|
|
}
|
|
errno = 0;
|
|
long ret = strtol(tmpbuf, &end, 16);
|
|
if(errno != 0) {
|
|
printf("strtol: %s", strerror(errno));
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
|
|
maps[i][j] = (uint32_t) ret;
|
|
buf = commaend + 1;
|
|
free(tmpbuf);
|
|
}
|
|
}
|
|
|
|
// 2. Count number of different masks; this is the number of elements
|
|
int num_elements = 0;
|
|
bool found = false;
|
|
uint32_t** unique_maps = emalloc(sizeof(uint32_t *) * num_paths);
|
|
for(int i=0; i < num_paths; i++) {
|
|
unique_maps[i] = emalloc(sizeof(uint32_t) * num_bitmasks);
|
|
for(int j=0; j < num_bitmasks; j++) {
|
|
unique_maps[i][j] = 0;
|
|
}
|
|
}
|
|
|
|
for(int i=0; i < num_paths; i++) {
|
|
for(int j=0; j < num_paths && !found; j++) {
|
|
if(maps_equal(maps[i], unique_maps[j], num_bitmasks)) found = true;
|
|
}
|
|
if(!found) {
|
|
add_shared_map(maps, i, unique_maps, num_elements, num_bitmasks);
|
|
num_elements++;
|
|
}
|
|
found = false;
|
|
}
|
|
|
|
return num_elements;
|
|
}
|
|
|
|
int get_num_caches_from_files(char** paths, int num_paths) {
|
|
return get_num_elements_from_files(paths, num_paths);
|
|
}
|
|
|
|
int get_num_sockets_from_files(char** paths, int num_paths) {
|
|
return get_num_elements_from_files(paths, num_paths);
|
|
}
|
|
|
|
int get_num_caches_by_level(struct cpuInfo* cpu, uint32_t level) {
|
|
char** paths = emalloc(sizeof(char *) * cpu->topo->total_cores);
|
|
char* cache_path = NULL;
|
|
|
|
if(level == 0) cache_path = _PATH_CACHE_L1I;
|
|
else if(level == 1) cache_path = _PATH_CACHE_L1D;
|
|
else if(level == 2) cache_path = _PATH_CACHE_L2;
|
|
else if(level == 3) cache_path = _PATH_CACHE_L3;
|
|
else {
|
|
printBug("Found invalid cache level to inspect: %d\n", level);
|
|
return -1;
|
|
}
|
|
|
|
for(int i=0; i < cpu->topo->total_cores; i++) {
|
|
paths[i] = emalloc(sizeof(char) * _PATH_CACHE_MAX_LEN);
|
|
sprintf(paths[i], "%s%s/cpu%d%s%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, i, cache_path, _PATH_CACHE_SHARED_MAP);
|
|
}
|
|
|
|
int ret = get_num_caches_from_files(paths, cpu->topo->total_cores);
|
|
|
|
for(int i=0; i < cpu->topo->total_cores; i++)
|
|
free(paths[i]);
|
|
free(paths);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int get_num_sockets_package_cpus(struct topology* topo) {
|
|
// Get number of sockets using
|
|
// /sys/devices/system/cpu/cpu*/topology/package_cpus
|
|
|
|
char** paths = emalloc(sizeof(char *) * topo->total_cores);
|
|
|
|
for(int i=0; i < topo->total_cores; i++) {
|
|
paths[i] = emalloc(sizeof(char) * _PATH_PACKAGE_MAX_LEN);
|
|
sprintf(paths[i], "%s%s/cpu%d%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, i, _PATH_TOPO_PACKAGE_CPUS);
|
|
}
|
|
|
|
int ret = get_num_sockets_from_files(paths, topo->total_cores);
|
|
|
|
for(int i=0; i < topo->total_cores; i++)
|
|
free(paths[i]);
|
|
free(paths);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Inspired in is_devtree_compatible from lscpu
|
|
bool is_devtree_compatible(char* str) {
|
|
int filelen;
|
|
char* buf;
|
|
if((buf = read_file("/proc/device-tree/compatible", &filelen)) == NULL) {
|
|
return false;
|
|
}
|
|
|
|
char* tmp;
|
|
if((tmp = strstr(buf, str)) == NULL) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
char* get_devtree_compatible(int *filelen) {
|
|
char* buf;
|
|
|
|
if ((buf = read_file(_PATH_DEVTREE, filelen)) == NULL) {
|
|
printWarn("read_file: %s: %s", _PATH_DEVTREE, strerror(errno));
|
|
}
|
|
|
|
return buf;
|
|
}
|