Carga inicial al repo

This commit is contained in:
lastdragon
2026-09-07 16:32:06 -06:00
parent 11aa879582
commit 0ddde1f705
2 changed files with 911 additions and 1 deletions
+884
View File
@@ -0,0 +1,884 @@
/*
* cpustress.c - Estresador de CPU con monitor ncurses para Linux
*
* - Detecta automaticamente el numero de nucleos logicos.
* - Lanza un hilo por nucleo, fijado con afinidad (pthread_setaffinity_np).
* - Monitoriza uso (/proc/stat), temperatura (hwmon / thermal_zone),
* frecuencia (cpufreq) y consumo energetico (Intel RAPL / powercap).
* - Interfaz responsiva: se adapta al tamano de la terminal (consola o
* ventana grafica) y colapsa a una sola fila promedio si no caben todos
* los nucleos.
*
* Compilar: gcc -O2 -Wall -o cpustress cpustress.c -lncursesw -lpthread -lm
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#include <signal.h>
#include <dirent.h>
#include <getopt.h>
#include <locale.h>
#include <langinfo.h>
#include <pthread.h>
#include <sched.h>
#include <ncurses.h>
#define MAX_CPUS 512
#define MAX_SENSORS 256
#define MAX_RAPL 16
/* ------------------------------------------------------------------ */
/* Pares de color */
/* ------------------------------------------------------------------ */
#define CP_GREEN 1
#define CP_YELLOW 2
#define CP_RED 3
#define CP_HEADER 4
#define CP_DIM 5
#define CP_TITLE 6
#define CP_CYAN 7
/* ------------------------------------------------------------------ */
/* Estado global */
/* ------------------------------------------------------------------ */
static volatile sig_atomic_t g_run = 1;
static int g_ncpu = 0; /* nucleos logicos detectados */
static char g_model[256] = "CPU desconocida";
static int g_use_color = 1;
static int g_ascii = 0;
static const char *g_bar_full = "#";
static const char *g_bar_empty = ".";
static unsigned char g_sel[MAX_CPUS]; /* 1 = nucleo estresado */
static int g_nsel = 0;
static int g_threads_per_core = 1;
static double g_usage[MAX_CPUS];
static double g_temp[MAX_CPUS]; /* -1 = desconocida */
static double g_freq[MAX_CPUS]; /* MHz, -1 = desconocida */
static double g_usage_avg = 0.0;
static double g_pkg_temp = -1.0;
static double g_power_w = -1.0;
static double g_sink[MAX_CPUS]; /* evita que GCC elimine el bucle */
static int g_topo_core[MAX_CPUS];
/* ------------------------------------------------------------------ */
/* Utilidades de lectura de sysfs / procfs */
/* ------------------------------------------------------------------ */
static int read_line_file(const char *path, char *buf, size_t n)
{
FILE *f = fopen(path, "r");
if (!f) return -1;
if (!fgets(buf, (int)n, f)) { fclose(f); return -1; }
fclose(f);
buf[strcspn(buf, "\n")] = '\0';
return 0;
}
static int read_ll_file(const char *path, long long *out)
{
char buf[64];
if (read_line_file(path, buf, sizeof buf) != 0) return -1;
errno = 0;
long long v = strtoll(buf, NULL, 10);
if (errno) return -1;
*out = v;
return 0;
}
/* ------------------------------------------------------------------ */
/* Modelo de CPU */
/* ------------------------------------------------------------------ */
static void detect_model(void)
{
FILE *f = fopen("/proc/cpuinfo", "r");
if (!f) return;
char line[512];
while (fgets(line, sizeof line, f)) {
char *c = NULL;
if (!strncmp(line, "model name", 10) ||
!strncmp(line, "Model", 5) ||
!strncmp(line, "Processor", 9) ||
!strncmp(line, "cpu model", 9)) {
c = strchr(line, ':');
}
if (c) {
c++;
while (*c == ' ' || *c == '\t') c++;
c[strcspn(c, "\n")] = '\0';
if (*c) {
snprintf(g_model, sizeof g_model, "%s", c);
break;
}
}
}
fclose(f);
}
/* ------------------------------------------------------------------ */
/* Topologia: cpu logico -> core_id fisico */
/* ------------------------------------------------------------------ */
static void detect_topology(void)
{
char p[256], buf[64];
for (int i = 0; i < g_ncpu; i++) {
g_topo_core[i] = i;
snprintf(p, sizeof p,
"/sys/devices/system/cpu/cpu%d/topology/core_id", i);
if (read_line_file(p, buf, sizeof buf) == 0)
g_topo_core[i] = atoi(buf);
}
}
/* ------------------------------------------------------------------ */
/* Sensores de temperatura */
/* ------------------------------------------------------------------ */
typedef struct {
char path[600];
int core_id; /* -1 = paquete / global */
} sensor_t;
static sensor_t g_sensors[MAX_SENSORS];
static int g_nsensors = 0;
static int is_cpu_hwmon(const char *name)
{
static const char *ok[] = {
"coretemp", "k10temp", "k8temp", "zenpower", "cpu_thermal",
"soc_thermal", "cpu-thermal", "cpu_temp_thermal", "scpi_sensors",
"nct6", "it87", NULL
};
for (int i = 0; ok[i]; i++)
if (strstr(name, ok[i])) return 1;
return 0;
}
static void add_sensor(const char *path, int core_id)
{
if (g_nsensors >= MAX_SENSORS) return;
snprintf(g_sensors[g_nsensors].path, sizeof g_sensors[0].path, "%s", path);
g_sensors[g_nsensors].core_id = core_id;
g_nsensors++;
}
static void scan_hwmon(void)
{
DIR *d = opendir("/sys/class/hwmon");
if (!d) return;
struct dirent *e;
while ((e = readdir(d))) {
if (strncmp(e->d_name, "hwmon", 5)) continue;
char base[512], p[600], name[128];
snprintf(base, sizeof base, "/sys/class/hwmon/%s", e->d_name);
snprintf(p, sizeof p, "%s/name", base);
if (read_line_file(p, name, sizeof name) != 0) continue;
if (!is_cpu_hwmon(name)) continue;
DIR *hd = opendir(base);
if (!hd) continue;
struct dirent *he;
while ((he = readdir(hd))) {
int idx;
if (sscanf(he->d_name, "temp%d_input", &idx) != 1) continue;
char lpath[700], label[128];
int core_id = -1;
snprintf(lpath, sizeof lpath, "%s/temp%d_label", base, idx);
if (read_line_file(lpath, label, sizeof label) == 0) {
int n;
if (sscanf(label, "Core %d", &n) == 1) core_id = n;
else if (sscanf(label, "core %d", &n) == 1) core_id = n;
}
char ipath[700];
snprintf(ipath, sizeof ipath, "%s/temp%d_input", base, idx);
long long dummy;
if (read_ll_file(ipath, &dummy) == 0)
add_sensor(ipath, core_id);
}
closedir(hd);
}
closedir(d);
}
static void scan_thermal_zones(void)
{
DIR *d = opendir("/sys/class/thermal");
if (!d) return;
struct dirent *e;
while ((e = readdir(d))) {
if (strncmp(e->d_name, "thermal_zone", 12)) continue;
char p[600], type[128];
snprintf(p, sizeof p, "/sys/class/thermal/%s/type", e->d_name);
if (read_line_file(p, type, sizeof type) != 0) continue;
if (!strstr(type, "cpu") && !strstr(type, "x86_pkg_temp") &&
!strstr(type, "acpitz") && !strstr(type, "soc"))
continue;
snprintf(p, sizeof p, "/sys/class/thermal/%s/temp", e->d_name);
long long dummy;
if (read_ll_file(p, &dummy) == 0)
add_sensor(p, -1);
}
closedir(d);
}
static void init_sensors(void)
{
scan_hwmon();
if (g_nsensors == 0) scan_thermal_zones();
}
static void update_temps(void)
{
for (int i = 0; i < g_ncpu; i++) g_temp[i] = -1.0;
g_pkg_temp = -1.0;
for (int s = 0; s < g_nsensors; s++) {
long long raw;
if (read_ll_file(g_sensors[s].path, &raw) != 0) continue;
double c = (raw > 1000) ? raw / 1000.0 : (double)raw;
if (g_sensors[s].core_id >= 0) {
for (int i = 0; i < g_ncpu; i++)
if (g_topo_core[i] == g_sensors[s].core_id)
g_temp[i] = c;
} else {
if (c > g_pkg_temp) g_pkg_temp = c;
}
}
/* si no hay sensor por nucleo, usar la del paquete */
if (g_pkg_temp > 0.0)
for (int i = 0; i < g_ncpu; i++)
if (g_temp[i] < 0.0) g_temp[i] = g_pkg_temp;
if (g_pkg_temp < 0.0) {
double mx = -1.0;
for (int i = 0; i < g_ncpu; i++) if (g_temp[i] > mx) mx = g_temp[i];
g_pkg_temp = mx;
}
}
/* ------------------------------------------------------------------ */
/* Frecuencia */
/* ------------------------------------------------------------------ */
static int g_have_cpufreq = 0;
static void init_freq(void)
{
long long v;
if (read_ll_file("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq",
&v) == 0)
g_have_cpufreq = 1;
}
static void update_freq(void)
{
for (int i = 0; i < g_ncpu; i++) g_freq[i] = -1.0;
if (g_have_cpufreq) {
char p[256];
long long khz;
for (int i = 0; i < g_ncpu; i++) {
snprintf(p, sizeof p,
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
if (read_ll_file(p, &khz) == 0) g_freq[i] = khz / 1000.0;
}
return;
}
FILE *f = fopen("/proc/cpuinfo", "r");
if (!f) return;
char line[512];
int idx = -1;
while (fgets(line, sizeof line, f)) {
char *c = strchr(line, ':');
if (!c) continue;
if (!strncmp(line, "processor", 9)) idx = atoi(c + 1);
else if (!strncmp(line, "cpu MHz", 7) && idx >= 0 && idx < g_ncpu)
g_freq[idx] = atof(c + 1);
}
fclose(f);
}
/* ------------------------------------------------------------------ */
/* Consumo energetico (powercap / RAPL) */
/* ------------------------------------------------------------------ */
typedef struct {
char path[600];
long long last;
long long max;
} rapl_t;
static rapl_t g_rapl[MAX_RAPL];
static int g_nrapl = 0;
static struct timespec g_rapl_ts;
static void init_rapl(void)
{
DIR *d = opendir("/sys/class/powercap");
if (!d) return;
struct dirent *e;
while ((e = readdir(d)) && g_nrapl < MAX_RAPL) {
/* solo dominios de paquete de primer nivel: "intel-rapl:0" */
if (strncmp(e->d_name, "intel-rapl:", 11) &&
strncmp(e->d_name, "amd-rapl:", 9)) continue;
if (strchr(strchr(e->d_name, ':') + 1, ':')) continue; /* subdominio */
char ep[600], mp[600];
long long v, mx;
snprintf(ep, sizeof ep, "/sys/class/powercap/%s/energy_uj", e->d_name);
snprintf(mp, sizeof mp, "/sys/class/powercap/%s/max_energy_range_uj",
e->d_name);
if (read_ll_file(ep, &v) != 0) continue; /* sin permisos -> N/A */
if (read_ll_file(mp, &mx) != 0) mx = 0;
snprintf(g_rapl[g_nrapl].path, sizeof g_rapl[0].path, "%s", ep);
g_rapl[g_nrapl].last = v;
g_rapl[g_nrapl].max = mx;
g_nrapl++;
}
closedir(d);
clock_gettime(CLOCK_MONOTONIC, &g_rapl_ts);
}
static void update_power(void)
{
if (g_nrapl == 0) { g_power_w = -1.0; return; }
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
double dt = (now.tv_sec - g_rapl_ts.tv_sec) +
(now.tv_nsec - g_rapl_ts.tv_nsec) / 1e9;
if (dt <= 0.02) return;
g_rapl_ts = now;
double total_uj = 0.0;
for (int i = 0; i < g_nrapl; i++) {
long long v;
if (read_ll_file(g_rapl[i].path, &v) != 0) continue;
long long d = v - g_rapl[i].last;
if (d < 0) d = (g_rapl[i].max > 0) ? (g_rapl[i].max - g_rapl[i].last + v) : 0;
g_rapl[i].last = v;
total_uj += (double)d;
}
g_power_w = (total_uj / 1e6) / dt;
}
/* ------------------------------------------------------------------ */
/* Uso de CPU via /proc/stat */
/* ------------------------------------------------------------------ */
static unsigned long long g_idle_prev[MAX_CPUS + 1];
static unsigned long long g_tot_prev[MAX_CPUS + 1];
static void sample_stat(int first)
{
FILE *f = fopen("/proc/stat", "r");
if (!f) return;
char line[512];
while (fgets(line, sizeof line, f)) {
if (strncmp(line, "cpu", 3)) break;
int idx;
char *p = line + 3;
if (*p == ' ') idx = g_ncpu; /* linea agregada */
else {
idx = atoi(p);
if (idx < 0 || idx >= g_ncpu) continue;
}
while (*p && !isdigit((unsigned char)*p)) p++;
if (idx != g_ncpu) while (*p && isdigit((unsigned char)*p)) p++;
unsigned long long v[10] = {0};
int n = sscanf(p, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
&v[0], &v[1], &v[2], &v[3], &v[4],
&v[5], &v[6], &v[7], &v[8], &v[9]);
if (n < 4) continue;
unsigned long long total = 0;
for (int i = 0; i < n; i++) total += v[i];
unsigned long long idle = v[3] + v[4]; /* idle + iowait */
if (!first) {
unsigned long long dt = total - g_tot_prev[idx];
unsigned long long di = idle - g_idle_prev[idx];
double u = (dt > 0) ? 100.0 * (double)(dt - di) / (double)dt : 0.0;
if (u < 0.0) u = 0.0;
if (u > 100.0) u = 100.0;
if (idx == g_ncpu) g_usage_avg = u;
else g_usage[idx] = u;
}
g_tot_prev[idx] = total;
g_idle_prev[idx] = idle;
}
fclose(f);
}
/* ------------------------------------------------------------------ */
/* Hilos de estres */
/* ------------------------------------------------------------------ */
static void *stress_worker(void *arg)
{
long cpu = (long)arg;
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET((int)cpu, &set);
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &set);
double a = 1.0000001, b = 0.9999999, c = 1.0000003;
unsigned long long x = 0x9E3779B97F4A7C15ULL ^ (unsigned long long)cpu;
while (g_run) {
for (int i = 0; i < 20000; i++) {
/* Carga mixta: FPU (mul/div/sqrt) + ALU entera de 64 bits */
c = c * a + b;
c = sqrt(c * c + 1.0);
c = c / 1.0000007 + 1e-7;
x = x * 6364136223846793005ULL + 1442695040888963407ULL;
x ^= x >> 33;
x += (unsigned long long)(c);
if (c > 1e8) c = 1.0000003;
}
/* barrera: impide que el optimizador elimine el bucle */
g_sink[cpu] = c + (double)(x & 0xFFFF);
__asm__ __volatile__("" ::: "memory");
}
return NULL;
}
/* ------------------------------------------------------------------ */
/* Interfaz ncurses */
/* ------------------------------------------------------------------ */
static int color_for_usage(double u)
{
if (u < 50.0) return CP_GREEN;
if (u < 85.0) return CP_YELLOW;
return CP_RED;
}
static int color_for_temp(double t)
{
if (t < 0.0) return CP_DIM;
if (t < 60.0) return CP_GREEN;
if (t < 80.0) return CP_YELLOW;
return CP_RED;
}
static void draw_bar(int y, int x, int w, double pct, int cp)
{
if (w <= 0) return;
int fill = (int)((pct / 100.0) * w + 0.5);
if (fill < 0) fill = 0;
if (fill > w) fill = w;
mvaddch(y, x - 1, '[');
if (g_use_color) attron(COLOR_PAIR(cp) | A_BOLD);
for (int i = 0; i < fill; i++) mvaddstr(y, x + i, g_bar_full);
if (g_use_color) attroff(COLOR_PAIR(cp) | A_BOLD);
if (g_use_color) attron(COLOR_PAIR(CP_DIM));
for (int i = fill; i < w; i++) mvaddstr(y, x + i, g_bar_empty);
if (g_use_color) attroff(COLOR_PAIR(CP_DIM));
mvaddch(y, x + w, ']');
}
static void draw_row(int y, const char *label, int marked,
double usage, double temp, double freq,
int wbar, int show_temp, int show_freq)
{
int x = 1;
if (marked >= 0) {
int cp = marked ? CP_RED : CP_DIM;
if (g_use_color) attron(COLOR_PAIR(cp) | A_BOLD);
mvaddstr(y, x, marked ? (g_ascii ? "*" : "\xe2\x97\x8f")
: (g_ascii ? " " : "\xc2\xb7"));
if (g_use_color) attroff(COLOR_PAIR(cp) | A_BOLD);
}
x += 2;
if (g_use_color) attron(COLOR_PAIR(CP_CYAN));
mvprintw(y, x, "%-6.6s", label);
if (g_use_color) attroff(COLOR_PAIR(CP_CYAN));
x += 7;
draw_bar(y, x + 1, wbar, usage, color_for_usage(usage));
x += wbar + 2;
int cp = color_for_usage(usage);
if (g_use_color) attron(COLOR_PAIR(cp));
mvprintw(y, x, "%6.1f%%", usage);
if (g_use_color) attroff(COLOR_PAIR(cp));
x += 8;
if (show_temp) {
int ct = color_for_temp(temp);
if (g_use_color) attron(COLOR_PAIR(ct));
if (temp >= 0.0) mvprintw(y, x, "%5.1fC", temp);
else mvprintw(y, x, " n/d");
if (g_use_color) attroff(COLOR_PAIR(ct));
x += 7;
}
if (show_freq) {
if (g_use_color) attron(COLOR_PAIR(CP_DIM));
if (freq >= 0.0) mvprintw(y, x, "%7.0f MHz", freq);
else mvprintw(y, x, " n/d");
if (g_use_color) attroff(COLOR_PAIR(CP_DIM));
}
}
static void draw_screen(double elapsed, double limit)
{
erase();
int rows = LINES, cols = COLS;
if (rows < 6 || cols < 24) {
mvprintw(0, 0, "Terminal muy pequena");
refresh();
return;
}
int show_freq = (cols >= 84);
int show_temp = (cols >= 62);
/* ancho de barra: cols - (margen + marca + etiqueta + corchetes + % + extras) */
int fixed = 1 + 2 + 7 + 2 + 8 + (show_temp ? 7 : 0) + (show_freq ? 11 : 0) + 1;
int wbar = cols - fixed;
if (wbar < 6) { show_freq = 0; wbar = cols - (1 + 2 + 7 + 2 + 8 + (show_temp ? 7 : 0) + 1); }
if (wbar < 6) { show_temp = 0; wbar = cols - (1 + 2 + 7 + 2 + 8 + 1); }
if (wbar < 4) wbar = 4;
/* --- barra de titulo --- */
if (g_use_color) attron(COLOR_PAIR(CP_TITLE) | A_BOLD);
for (int i = 0; i < cols; i++) mvaddch(0, i, ' ');
mvprintw(0, 1, " CPU STRESS ");
char rt[64];
if (limit > 0.0) snprintf(rt, sizeof rt, "%.0fs / %.0fs ", elapsed, limit);
else snprintf(rt, sizeof rt, "%02d:%02d:%02d ",
(int)elapsed / 3600, ((int)elapsed / 60) % 60,
(int)elapsed % 60);
mvprintw(0, cols - (int)strlen(rt) - 1, "%s", rt);
if (g_use_color) attroff(COLOR_PAIR(CP_TITLE) | A_BOLD);
/* --- modelo --- */
if (g_use_color) attron(COLOR_PAIR(CP_HEADER) | A_BOLD);
mvprintw(1, 1, "%-*.*s", cols - 2, cols - 2, g_model);
if (g_use_color) attroff(COLOR_PAIR(CP_HEADER) | A_BOLD);
/* --- resumen --- */
char line[512];
char tbuf[32], pbuf[32];
if (g_pkg_temp >= 0.0) snprintf(tbuf, sizeof tbuf, "%.1f C", g_pkg_temp);
else snprintf(tbuf, sizeof tbuf, "n/d");
if (g_power_w >= 0.0) snprintf(pbuf, sizeof pbuf, "%.1f W", g_power_w);
else snprintf(pbuf, sizeof pbuf, "n/d");
snprintf(line, sizeof line,
"Nucleos: %d | Estresados: %d | Hilos: %d | Paquete: %s | Consumo: %s",
g_ncpu, g_nsel, g_nsel * g_threads_per_core, tbuf, pbuf);
if (g_use_color) attron(COLOR_PAIR(CP_DIM));
mvprintw(2, 1, "%-*.*s", cols - 2, cols - 2, line);
if (g_use_color) attroff(COLOR_PAIR(CP_DIM));
/* --- cabecera de tabla --- */
if (g_use_color) attron(COLOR_PAIR(CP_HEADER) | A_BOLD | A_UNDERLINE);
for (int i = 0; i < cols; i++) mvaddch(3, i, ' ');
mvprintw(3, 3, "%-6.6s", "CORE");
mvprintw(3, 11, "%-*.*s", wbar, wbar, "USO");
mvprintw(3, 11 + wbar + 2, "%7.7s", " CARGA");
if (show_temp) mvprintw(3, 11 + wbar + 10, "%6.6s", " TEMP");
if (show_freq) mvprintw(3, 11 + wbar + 10 + (show_temp ? 7 : 0), "%11.11s", " FREQ");
if (g_use_color) attroff(COLOR_PAIR(CP_HEADER) | A_BOLD | A_UNDERLINE);
/* --- filas --- */
int first_row = 4;
int footer_row = rows - 1;
int avail = footer_row - first_row; /* filas utiles */
if (avail >= g_ncpu) {
char lbl[16];
for (int i = 0; i < g_ncpu; i++) {
snprintf(lbl, sizeof lbl, "CPU%d", i);
draw_row(first_row + i, lbl, g_sel[i] ? 1 : 0,
g_usage[i], g_temp[i], g_freq[i],
wbar, show_temp, show_freq);
}
} else {
/* No caben: una sola fila con el promedio global */
double tsum = 0.0, fsum = 0.0;
int tn = 0, fn = 0;
for (int i = 0; i < g_ncpu; i++) {
if (g_temp[i] >= 0.0) { tsum += g_temp[i]; tn++; }
if (g_freq[i] >= 0.0) { fsum += g_freq[i]; fn++; }
}
int y = first_row + (avail > 1 ? 1 : 0);
draw_row(y, "PROM", -1, g_usage_avg,
tn ? tsum / tn : g_pkg_temp,
fn ? fsum / fn : -1.0,
wbar, show_temp, show_freq);
if (avail > 2 && g_use_color) attron(COLOR_PAIR(CP_DIM));
if (avail > 2)
mvprintw(y + 2, 3,
"%-*.*s", cols - 4, cols - 4,
"(terminal demasiado corta: mostrando promedio de todos los nucleos)");
if (avail > 2 && g_use_color) attroff(COLOR_PAIR(CP_DIM));
}
/* --- pie --- */
if (g_use_color) attron(COLOR_PAIR(CP_TITLE));
for (int i = 0; i < cols; i++) mvaddch(footer_row, i, ' ');
mvprintw(footer_row, 1,
" q/ESC salir r redibujar verde<50%% amarillo<85%% rojo>=85%% ");
if (g_use_color) attroff(COLOR_PAIR(CP_TITLE));
refresh();
}
/* ------------------------------------------------------------------ */
/* Argumentos */
/* ------------------------------------------------------------------ */
static int parse_core_list(const char *s)
{
const char *p = s;
int count = 0;
while (*p) {
while (*p == ',' || *p == ' ') p++;
if (!*p) break;
char *end;
long a = strtol(p, &end, 10);
if (end == p) return -1;
long b = a;
p = end;
if (*p == '-') {
p++;
b = strtol(p, &end, 10);
if (end == p) return -1;
p = end;
}
if (a < 0 || b < a || b >= g_ncpu) return -1;
for (long i = a; i <= b; i++)
if (!g_sel[i]) { g_sel[i] = 1; count++; }
}
return count;
}
static void usage_msg(const char *prog)
{
printf(
"Uso: %s [opciones]\n"
"\n"
"Estresa los nucleos de la CPU al 100%% y muestra un monitor en tiempo real.\n"
"Sin opciones, estresa TODOS los nucleos logicos detectados.\n"
"\n"
"Opciones:\n"
" -n, --num N Estresa los primeros N nucleos.\n"
" -c, --cores LISTA Estresa nucleos concretos. Ej: -c 0,2,4-7\n"
" -m, --multi K K hilos por nucleo seleccionado (def. 1).\n"
" -t, --time SEG Duracion en segundos (0 = indefinido).\n"
" -i, --interval MS Intervalo de refresco en ms (def. 500, min. 100).\n"
" -a, --ascii Barras ASCII (para consolas sin UTF-8).\n"
" -l, --list Lista nucleos y sensores detectados y sale.\n"
" -h, --help Muestra esta ayuda.\n"
"\n"
"Teclas: q o ESC para salir, r para redibujar.\n"
"Nota: el consumo (RAPL) suele requerir permisos de root.\n", prog);
}
static void on_signal(int s) { (void)s; g_run = 0; }
/* ------------------------------------------------------------------ */
/* main */
/* ------------------------------------------------------------------ */
int main(int argc, char **argv)
{
setlocale(LC_ALL, "");
long nc = sysconf(_SC_NPROCESSORS_ONLN);
if (nc < 1) nc = 1;
if (nc > MAX_CPUS) nc = MAX_CPUS;
g_ncpu = (int)nc;
int opt_n = -1, opt_list = 0;
const char *opt_c = NULL;
double opt_time = 0.0;
int interval_ms = 500;
static struct option lo[] = {
{"num", required_argument, 0, 'n'},
{"cores", required_argument, 0, 'c'},
{"multi", required_argument, 0, 'm'},
{"time", required_argument, 0, 't'},
{"interval", required_argument, 0, 'i'},
{"ascii", no_argument, 0, 'a'},
{"list", no_argument, 0, 'l'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int c, li;
while ((c = getopt_long(argc, argv, "n:c:m:t:i:alh", lo, &li)) != -1) {
switch (c) {
case 'n': opt_n = atoi(optarg); break;
case 'c': opt_c = optarg; break;
case 'm': g_threads_per_core = atoi(optarg); break;
case 't': opt_time = atof(optarg); break;
case 'i': interval_ms = atoi(optarg); break;
case 'a': g_ascii = 1; break;
case 'l': opt_list = 1; break;
case 'h': usage_msg(argv[0]); return 0;
default: usage_msg(argv[0]); return 1;
}
}
/* un numero suelto tambien vale: ./cpustress 4 */
if (opt_n < 0 && !opt_c && optind < argc && isdigit((unsigned char)argv[optind][0]))
opt_n = atoi(argv[optind]);
if (interval_ms < 100) interval_ms = 100;
if (g_threads_per_core < 1) g_threads_per_core = 1;
if (g_threads_per_core > 16) g_threads_per_core = 16;
detect_model();
detect_topology();
init_sensors();
init_freq();
init_rapl();
/* --- seleccion de nucleos --- */
if (opt_c) {
int n = parse_core_list(opt_c);
if (n <= 0) {
fprintf(stderr, "Lista de nucleos invalida: '%s' (rango 0-%d)\n",
opt_c, g_ncpu - 1);
return 1;
}
g_nsel = n;
} else if (opt_n > 0) {
if (opt_n > g_ncpu) opt_n = g_ncpu;
for (int i = 0; i < opt_n; i++) g_sel[i] = 1;
g_nsel = opt_n;
} else {
for (int i = 0; i < g_ncpu; i++) g_sel[i] = 1;
g_nsel = g_ncpu;
}
if (opt_list) {
printf("CPU : %s\n", g_model);
printf("Nucleos : %d\n", g_ncpu);
printf("Sensores : %d\n", g_nsensors);
for (int i = 0; i < g_nsensors; i++)
printf(" [core %3d] %s\n", g_sensors[i].core_id, g_sensors[i].path);
printf("RAPL : %s (%d dominios)\n",
g_nrapl ? "disponible" : "no disponible (probar con sudo)", g_nrapl);
printf("cpufreq : %s\n", g_have_cpufreq ? "si" : "no (/proc/cpuinfo)");
return 0;
}
/* --- barras unicode si el terminal es UTF-8 --- */
if (!g_ascii) {
const char *cs = nl_langinfo(CODESET);
if (cs && (strstr(cs, "UTF-8") || strstr(cs, "utf8"))) {
g_bar_full = "\xe2\x96\x88"; /* U+2588 bloque lleno */
g_bar_empty = "\xe2\x96\x91"; /* U+2591 sombreado */
} else {
g_ascii = 1;
}
}
signal(SIGINT, on_signal);
signal(SIGTERM, on_signal);
/* --- lanzar hilos de estres --- */
int nthreads = g_nsel * g_threads_per_core;
pthread_t *th = calloc((size_t)nthreads, sizeof(pthread_t));
int *tid_cpu = calloc((size_t)nthreads, sizeof(int));
if (!th || !tid_cpu) { fprintf(stderr, "Sin memoria\n"); return 1; }
int k = 0;
for (int i = 0; i < g_ncpu; i++) {
if (!g_sel[i]) continue;
for (int r = 0; r < g_threads_per_core; r++) {
tid_cpu[k] = i;
if (pthread_create(&th[k], NULL, stress_worker, (void *)(long)i) != 0) {
fprintf(stderr, "No se pudo crear el hilo para CPU%d\n", i);
g_run = 0;
break;
}
k++;
}
}
nthreads = k;
/* --- ncurses --- */
initscr();
cbreak();
noecho();
nonl();
curs_set(0);
keypad(stdscr, TRUE);
nodelay(stdscr, TRUE);
g_use_color = has_colors();
if (g_use_color) {
start_color();
use_default_colors();
init_pair(CP_GREEN, COLOR_GREEN, -1);
init_pair(CP_YELLOW, COLOR_YELLOW, -1);
init_pair(CP_RED, COLOR_RED, -1);
init_pair(CP_HEADER, COLOR_WHITE, -1);
init_pair(CP_DIM, COLOR_BLUE, -1);
init_pair(CP_TITLE, COLOR_BLACK, COLOR_CYAN);
init_pair(CP_CYAN, COLOR_CYAN, -1);
}
sample_stat(1);
update_temps();
update_freq();
struct timespec t0;
clock_gettime(CLOCK_MONOTONIC, &t0);
while (g_run) {
int ch;
while ((ch = getch()) != ERR) {
if (ch == 'q' || ch == 'Q' || ch == 27) { g_run = 0; break; }
if (ch == 'r' || ch == 'R' || ch == KEY_RESIZE) { clear(); }
}
if (!g_run) break;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
double elapsed = (now.tv_sec - t0.tv_sec) +
(now.tv_nsec - t0.tv_nsec) / 1e9;
if (opt_time > 0.0 && elapsed >= opt_time) { g_run = 0; break; }
sample_stat(0);
update_temps();
update_freq();
update_power();
draw_screen(elapsed, opt_time);
struct timespec ts = { interval_ms / 1000,
(long)(interval_ms % 1000) * 1000000L };
nanosleep(&ts, NULL);
}
g_run = 0;
endwin();
for (int i = 0; i < nthreads; i++) pthread_join(th[i], NULL);
free(th);
free(tid_cpu);
printf("Prueba finalizada. Nucleos estresados: %d, hilos: %d\n",
g_nsel, nthreads);
return 0;
}