Compare commits

13 Commits

Author SHA1 Message Date
0cf95e888f write documentation about getoptions 2025-08-20 13:27:40 +02:00
47b3f041bf remove branch 2025-08-20 13:25:22 +02:00
ed1794ffbf improve number parsing in opts.c 2025-08-20 13:18:45 +02:00
6ce633b06b fix opts.c to use correct integer types 2025-08-20 13:18:27 +02:00
dbcf29dc82 remove duplicate checks and put everything on a single line. 2025-08-20 12:49:27 +02:00
53472e6fc6 add help option 2025-08-20 12:48:56 +02:00
d44235ce0f fatal macro is not printing to stderr 2025-08-20 11:46:05 +02:00
96ec279d9a correct main.c to use correct integer types, and optimise cpu_setter. 2025-08-19 10:45:02 +02:00
4200d859aa fix: .editorconfig used indent_size, which should be equal to tab 2025-08-19 10:44:02 +02:00
82a33791de fix: cpu.c was not coded correctly; some edge cases and using static-width integers 2025-08-19 10:44:02 +02:00
bdccc05440 rework some (minor) things 2025-08-19 10:44:02 +02:00
2731f908b3 update makefile
makefile is now more compiler-agnostic and consistent with how one
expects a makefile to be built.
2025-08-11 15:44:40 +02:00
782e78760b update formatting 2025-08-11 15:34:48 +02:00
12 changed files with 211 additions and 151 deletions

View File

@@ -5,8 +5,8 @@ trim_trailing_whitespace = true
[*.{c,h}]
indent_style = tab
indent_size = 4
indent_size = tab
[{makefile,Makefile}]
indent_style = tab
indent_size = 2
indent_size = tab

View File

@@ -1,72 +1,32 @@
# project name = the workspace directory name
NAME := $(shell basename $(PWD))
SHELL = bash -e -o pipefail -O globstar
# compiler settings
CC := clang
STD := c17
LANG = c
CFLAGS := -target x86_64-pc-linux-gnu -Wall -Wextra -Wpedantic -Wno-pointer-arith
LDFLAGS := -lc
NAME = cpusetcores
VERSION = 0.0.2
DEBUG ?= 0
CC ?= cc
ifeq ($(DEBUG),1)
CFLAGS += -DDEBUG -fsanitize=address,undefined -g -Og
PROF := dbg
else
REL_FLAGS += -O2
PROF := rel
endif
CFLAGS = -c -std=gnu99 -Wall -Wextra -Wpedantic -MMD -MP
LFLAGS = -flto
# dirs
DIR_BIN := bin
DIR_OBJ := obj
DIR_BUILD := $(DIR_BIN)/$(PROF)
DIR := $(DIR_BIN)/$(PROF) $(DIR_OBJ)/$(PROF)
SRC := $(wildcard src/*.c) $(wildcard src/**/*.c)
OBJ := $(SRC:%.c=obj/%.o)
BIN := bin/$(NAME)
# source files
SRC := $(wildcard src/*.c) $(wildcard src/**/*.c) $(wildcard src/**/**/*.c) $(wildcard src/**/**/**/*.c) $(wildcard src/**/**/**/**/*.c)
.PHONY:
compile: $(BIN)
# output locations
OBJ := $(patsubst src/%,$(DIR_OBJ)/$(PROF)/%,$(SRC:.c=.o))
DEP := $(OBJ:.o=.d)
TARGET := $(DIR_BUILD)/$(NAME)
$(BIN): $(OBJ)
$(info [CC/LD] $@)
@mkdir -p $(@D)
@$(CC) -o $@ $^ $(LDFLAGS)
define wr_colour
@echo -e "\033[$(2)m$(1)\033[0m"
endef
obj/%.o: %.c
$(info [CC] $@)
@mkdir -p $(@D)
@$(CC) $(CFLAGS) -o $@ $<
compile: compile_commands.json $(DIR) $(TARGET)
clean:
rm -rf $(DIR_BIN) $(DIR_OBJ) compile_commands.json
# create the binary (linking step)
$(TARGET): $(OBJ)
@$(call wr_colour,"CC: '$(CC)'",94)
@$(call wr_colour,"CFLAGS: '$(CFLAGS)'",94)
@$(call wr_colour,"LDFLAGS: '$(LDFLAGS)'",94)
@$(call wr_colour,"linking to: '$@'",92)
@$(CC) -o $(TARGET) $^ $(CFLAGS) $(LDFLAGS)
@$(call wr_colour,"current profile: '$(PROF)'",93)
# create .o and .d files
$(DIR_OBJ)/$(PROF)/%.o: src/%.c
@$(call wr_colour,"compiling $(notdir $@) from $(notdir $<)",92)
@mkdir -p $(dir $@)
@$(CC) -o $@ -MD -MP -c $< $(CFLAGS) -std=$(STD) -x $(LANG) -Wno-unused-command-line-argument
# create directories
$(DIR):
mkdir -p $@
# update compile commands if the makefile has been updated (for linting)
ifeq ($(DEBUG),1)
compile_commands.json: makefile
$(MAKE) clean
@touch compile_commands.json
bear -- make compile
else
compile_commands.json:
endif
rm -rv obj/ bin/
# include the dependencies
-include $(DEP)

59
src/atrb.h Normal file
View File

@@ -0,0 +1,59 @@
// Copyright (c) 2025 Quinn
// Licensed under the MIT Licence. See LICENSE for details
#pragma once
#if defined(__GNUC__)
#if __has_attribute(__pure__)
#define PURE __attribute__((__pure__))
#else
#define PURE
#endif
#if __has_attribute(__const__)
#define CONST __attribute__((__const__))
#else
#define CONST
#endif
#if __has_attribute(__noreturn__)
#define NORET __attribute__((__noreturn__))
#else
#define NORET
#endif
#if __has_attribute(__malloc__)
#define MALLOC __attribute__((__malloc__))
#else
#define MALLOC
#endif
#if __has_attribute(__used__)
#define USED __attribute__((__used__))
#else
#define USED
#endif
#if __has_attribute(__unused__)
#define UNUSED __attribute__((__unused__))
#else
#define UNUSED
#endif
#if __has_attribute(__deprecated__)
#define DEPRECATED __attribute__((__deprecated__))
#else
#define DEPRECATED
#endif
#if __has_attribute(__format__)
#define FORMAT(...) __attribute__((format(__VA_ARGS__)))
#else
#define FORMAT(...)
#endif
#if __has_attribute(__nonnull__)
#define NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
#else
#define NONNULL(...)
#endif
#endif

View File

@@ -1,39 +1,44 @@
#include "cpu.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
char const* const cpu_path = "/sys/devices/system/cpu/cpu%i/online";
const char cpu_path[] = "/sys/devices/system/cpu/cpu%i/online";
bool getcpu(uint32_t id) {
// get the file path
char path[64]; // contains the file path (max length is 64 due to the path and a bunch of extra wiggle room)
snprintf(path, 64, cpu_path, id); // writes the path using the id
int cpu_getenabled(uint id) {
const size_t pathlen = sizeof(cpu_path) + (uint)(log10(-1u) + 1);
char path[pathlen];
snprintf(path, pathlen, cpu_path, id);
// if the file doesn't exist; return true
if (access(path, R_OK) != 0)
return true;
return 1;
// read a character from the file, store in state
char state = '\0';
FILE *f = fopen(path, "r");
assert(f);
fread(&state, 1, 1, f);
fclose(f);
// return whether state is truthy
return !!(state - 0x30);
return state != '0';
}
void setcpu(uint32_t id, bool state) {
char path[64];
snprintf(path, 64, cpu_path, id);
int cpu_setenabled(uint id, int state) {
const size_t pathlen = sizeof(cpu_path) + (uint)(log10(-1u) + 1);
char path[pathlen];
snprintf(path, pathlen, cpu_path, id);
char s = 0x30 + (char)state; // convert the state to a character
char s = 0x30 + (state & 1);
if (access(path, W_OK) != 0)
return 1;
// write the state to the file (creates file if it doesn't exist)
FILE *f = fopen(path, "w");
assert(f);
fwrite(&s, 1, 1, f);
fclose(f);
return 0;
}

View File

@@ -1,6 +1,10 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "util/intdef.h"
bool getcpu(uint32_t); // gets the state of core (id)
void setcpu(uint32_t, bool); // sets the state of core (id)
/* gets the current state of a CPU thread,
* returns a boolean value (`1` for enabled, `0` for disabled) */
int cpu_getenabled(uint id);
/* sets the current state of a CPU thread.
* returns `0` upon success, `1` upon failure */
int cpu_setenabled(uint id, int state);

View File

@@ -1,18 +0,0 @@
#include "error.h"
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
noreturn void fatal(char const* fmt, ...) {
char buf[128];
va_list args;
va_start(args, fmt);
vsnprintf(buf, 128, fmt, args);
va_end(args);
fprintf(stderr, "\033[91mE: %s\033[0m\n", buf);
exit(1);
}

View File

@@ -1,7 +1,21 @@
// Copyright (c) 2025 Quinn
// Licensed under the MIT Licence. See LICENSE for details
#pragma once
#include <stdarg.h>
#include <stdint.h>
#include <stdnoreturn.h>
// prints an error message and aborts execution
noreturn void fatal(char const*, ...);
#if __INCLUDE_LEVEL__ > 0
#include <stdio.h>
#include <stdlib.h>
#include "util/macro.h"
#endif
#define debug(s, ...) printf("\033[95m" __FILE__ ":" MACRO_STR2(__LINE__) ": [DBG]: " s "\033[0m\n", ##__VA_ARGS__)
#define info(s, ...) printf(__FILE__ ":" MACRO_STR2(__LINE__) ": [INF]: " s "\n", ##__VA_ARGS__)
#define warn(s, ...) fprintf(stderr, "\033[93m" __FILE__ ":" MACRO_STR2(__LINE__) ": [WAR]: " s "\033[0m\n", ##__VA_ARGS__)
#define error(s, ...) fprintf(stderr, "\033[91m" __FILE__ ":" MACRO_STR2(__LINE__) ": [ERR]: " s "\033[0m\n", ##__VA_ARGS__)
#define fatal(s, ...) \
do { \
fprintf(stderr, "\033[101m" __FILE__ ":" MACRO_STR2(__LINE__) ": [FAT]: " s "\033[0m\n", ##__VA_ARGS__); \
exit(EXIT_FAILURE); \
} while (0)

View File

@@ -1,3 +1,4 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/sysinfo.h>
@@ -7,28 +8,25 @@
#include "error.h"
#include "opts.h"
static inline bool cpu_setter(uint32_t id, bool nstate, uint8_t opts) {
bool cstate = !nstate;
if (!(opts & OPT_SET_ALL))
cstate = getcpu(id);
if (cstate != nstate) {
setcpu(id, nstate);
return true;
}
return false;
/* sets CPU `id` to `nstate`, if it hasn't been set yet.
* If `opt` contains `OPT_SET_ALL`, this check is ignored.
* Returns `0` upon success, `1` upon failure. */
static int setcpu(uint id, int nstate, u8 opts) {
bool nsetall = !(opts & OPT_SET_ALL); // if SET_ALL has not been set
int cstate = nsetall && cpu_getenabled(id); // get state if nsetall=1
return (nsetall && cstate == nstate) || cpu_setenabled(id, nstate); // set if cstate matches nstate, unless nsetall=1
}
static inline void print_cpu_count(int32_t mcpus) {
static inline void print_cpu_count(uint mcpus) {
printf("%i/%i cpus enabled.\n", get_nprocs(), mcpus); // get the number of available processors
}
int32_t main(int32_t argc, char** argv) {
int main(int argc, char **argv) {
if (geteuid() != 0) fatal("must be executed as the root user!");
int32_t ncpus; // the number of CPUs to activate
uint8_t opts = getoptions(argc, argv, &ncpus); // the options to use
int32_t mcpus = get_nprocs_conf(); // the max number of CPUs that are available
int mcpus, ncpus; // the number of CPUs to activate
u8 opts = getoptions(argc, argv, &ncpus); // the options to use
mcpus = get_nprocs_conf(); // the max number of CPUs that are available
if (opts & OPT_LIST_CORES && ncpus < 0) {
print_cpu_count(mcpus);
@@ -42,18 +40,18 @@ int32_t main(int32_t argc, char** argv) {
if (ncpus > mcpus) fatal("%i exeeds the maximum numbers of cpus available (%i)", ncpus, mcpus);
if (ncpus < 1) fatal("may not keep less than 1 cpu enabled, requested to enable %i", ncpus);
char const* const cpu_set_log = "set cpu %i to %hi\n";
for (int32_t id = 1; id < mcpus; id++) { // start at CPU 1, as CPU 0 is not writeable
const char *const cpu_set_log = "set cpu %i to %hi\n";
for (int id = 1; id < mcpus; id++) { // start at CPU 1, as CPU 0 is not writeable
// whilst the id is less then the amount of cpus to enable
if (id < ncpus) {
// enable the cpu & print if it was actually set (if -v was given)
if (cpu_setter(id, true, opts) && (opts & OPT_VERBOSE))
if (!setcpu(id, true, opts) && (opts & OPT_VERBOSE))
printf(cpu_set_log, id, 1);
continue;
}
// disable the cpu & print if it was actually set (if -v was given)
if (cpu_setter(id, false, opts) && (opts & OPT_VERBOSE)) {
if (!setcpu(id, false, opts) && (opts & OPT_VERBOSE)) {
printf(cpu_set_log, id, 0);
continue;
}

View File

@@ -1,45 +1,49 @@
#include "opts.h"
#include <errno.h>
#include <getopt.h>
#include <stdint.h>
#include <stdlib.h>
#include "error.h"
#include "util/intdef.h"
uint8_t getoptions(int32_t argc, char* const* argv, int32_t* ncpus) {
u8 getoptions(int argc, char *const *argv, int *ncpus) {
*ncpus = -1;
uint8_t opts = 0;
char opt;
char const* const msg = "-%c has already been set";
while ((opt = getopt(argc, argv, "lavi")) != -1) {
while ((opt = getopt(argc, argv, "lavih")) != -1) {
switch (opt) {
case 'l':
if (opts & OPT_LIST_CORES) fatal(msg, 'l');
opts |= OPT_LIST_CORES;
break;
case 'a':
if (opts & OPT_SET_ALL) fatal(msg, 'a');
opts |= OPT_SET_ALL;
break;
case 'v':
if (opts & OPT_VERBOSE) fatal(msg, 'v');
opts |= OPT_VERBOSE | OPT_LIST_CORES;
break;
case 'i':
if (opts & OPT_INVERT) fatal(msg, 'i');
opts |= OPT_INVERT;
break;
case 'l': opts |= OPT_LIST_CORES; break;
case 'a': opts |= OPT_SET_ALL; break;
case 'v': opts |= OPT_VERBOSE | OPT_LIST_CORES; break;
case 'i': opts |= OPT_INVERT; break;
case '?':
fatal("usage: %s [number] [-l] [-a] [-v] [-i]", argv[0]);
printf("specify -h for help\n");
exit(EXIT_FAILURE);
case 'h':
printf(
"%s: a tool for turning on/off CPU threads\n"
"USAGE: %s [integer] [-l] [-a] [-v] [-i] [-h]\n"
"ARGUMENTS:\n"
"\t-l : List cores after executing the command. May be specified without integer.\n"
"\t-a : Writes to all cores, regardless of their state.\n"
"\t-v : Print the cores that are written. (implies -l)\n"
"\t-i : Instead of the given number.\n"
"\t-h : Shows this dialogue.\n",
argv[0], argv[0]);
exit(EXIT_SUCCESS);
}
}
if (optind < argc) {
char *end;
char *num = argv[optind];
*ncpus = atoi(num);
} else if (opts & OPT_LIST_CORES) {
*ncpus = -1;
} else
*ncpus = strtol(num, &end, 0);
if (errno || end == num || *end != '\0')
fatal("failed to parse numeric value from string '%s'", num);
} else if (!(opts & OPT_LIST_CORES))
fatal("must execute with either a number or [-l]");
return opts;

View File

@@ -1,6 +1,8 @@
#pragma once
#include <stdint.h>
#include "util/intdef.h"
enum opt {
OPT_LIST_CORES = 1, // option that lists the total amount of cores that are set
OPT_SET_ALL = 2, // option to set all cores, regardless of their assumed state
@@ -8,4 +10,7 @@ enum opt {
OPT_INVERT = 8, // 'num' now represents the amount of cores to disable
};
uint8_t getoptions(int32_t, char* const*, int32_t*);
/* acquires the options given by the user through arguments.
* returns the bitmap of these options.
* `ncpus` is set to `-1`, or the amount of CPUs that the user requested to enable.*/
u8 getoptions(int argc, char *const *argv, int *ncpus);

20
src/util/intdef.h Normal file
View File

@@ -0,0 +1,20 @@
// Copyright (c) 2025 Quinn
// Licensed under the MIT Licence. See LICENSE for details
#pragma once
/* variable-width integer types */
typedef unsigned int uint; // ≥16 bit unsigned integer
typedef unsigned long ulong; // ≥32 bit unsigned integer
typedef signed long long llong; // ≥64 bit signed integer
typedef unsigned long long ullong; // ≥64 bit unsigned integer
/* fixed-width integer types */
#include <stdint.h>
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;

9
src/util/macro.h Normal file
View File

@@ -0,0 +1,9 @@
// Copyright (c) 2025 Quinn
// Licensed under the MIT Licence. See LICENSE for details
#pragma once
#define WIDTHOF(t) (sizeof(t) * 8) // gets the bit width of a type
#define MACRO_CAT(x, y) x##y // concatenate two macro variables together
#define MACRO_CAT2(x, y) MACRO_CAT(x, y) // concatenate two macro variables together
#define MACRO_STR(v) #v // for converting macro variable into a string
#define MACRO_STR2(v) MACRO_STR(v) // for a recursive string generation