diff --git a/README.md b/README.md index 8c6d591851cbb80dcaf0bfa4c302f869baecc7cc..9fee622a82e3a0e684fa2eaa67adcb1768e974e7 100644 --- a/README.md +++ b/README.md @@ -59,11 +59,15 @@ make ``` You may want to run `./configure.sh --help` to see configuration options. +To use `amd_rapl` you have to load the module `msr` +```bash +sudo modprobe msr +``` To execute mojitos without being root to monitor performance counters ```bash sudo sh -c 'echo 0 >/proc/sys/kernel/perf_event_paranoid' ``` -To execute mohitos without being root for accessing RAPL +To execute mojitos without being root for accessing RAPL ```bash sudo chmod a+w /sys/class/powercap/intel-rapl/*/* sudo chmod a+w /sys/class/powercap/intel-rapl/*/*/* diff --git a/configure.sh b/configure.sh index 21c94eff4ef28cab03761595e6473b29c80e8b2b..7e10ec378cdb26b03b660f5d79b2ed78fba6c475 100755 --- a/configure.sh +++ b/configure.sh @@ -4,12 +4,15 @@ # Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr> try() { "$@" || die "cannot $*"; } -die() { yell "$*"; exit 111; } +die() { + yell "$*" + exit 111 +} yell() { echo "$0: $*" >&2; } echo() { printf '%s\n' "$*"; } isnum() { case "${1#[+-]}" in - *[!0-9]*|'') return 1 ;; + *[!0-9]* | '') return 1 ;; *) return 0 ;; esac } @@ -43,16 +46,16 @@ usage() { } ls_sensors() { - try cd src + [ -d src ] || die 'fatal: the "src" directory does not exit.' [ -z "$hdr_whitelist" ] && hdr_whitelist='.*' dprint hdr_blacklist >&2 dprint hdr_whitelist >&2 - ls -1 *.h | - grep -xEv "($hdr_blacklist)\.h" | - grep -xE "($hdr_whitelist)\.h" | - sed 's/\.h$//' + try find src -type f -name '*.h' | + sed 's,src/\(.*\)\.h,\1,' | + grep -xEv "($hdr_blacklist)" | + grep -xE "($hdr_whitelist)" } # gen_sensors_h(sensor, nb_sensors) @@ -86,10 +89,10 @@ gen_sensors_h() { printf ' int opt_idx = offset;\n' for sensor in $sensors; do cat <<-! - for (int i = 0; i < ${sensor}.nb_opt; i++) { - opts[opt_idx++] = ${sensor}_opt[i]; - } - sensors[(*nb_defined)++] = ${sensor}; + for (int i = 0; i < ${sensor}.nb_opt; i++) { + opts[opt_idx++] = ${sensor}_opt[i]; + } + sensors[(*nb_defined)++] = ${sensor}; ! done printf ' assert((offset + *nb_defined) <= len);\n' @@ -103,6 +106,11 @@ gen_sensors_mk() { printf '$(OBJ_DIR)/%s.o ' "$sensor" done printf '\n' + for sensor in $sensors; do + printf '$(OBJ_DIR)/%s.o: $(SRC_DIR)/%s.c $(SRC_DIR)/%s.h $(SRC_DIR)/util.h\n' \ + "$sensor" "$sensor" "$sensor" + printf '\t$(CC) $(CFLAGS) -c $< -o $@\n' + done } detect_caps() { @@ -110,11 +118,21 @@ detect_caps() { [ -d /sys/class/infiniband ] && hdr_whitelist="${hdr_whitelist}|infiniband" [ -r /proc/stat ] && hdr_whitelist="${hdr_whitelist}|load" + if [ "$(uname -r | cut -d "." -f 1)" -gt "2" ]; then + hdr_whitelist="${hdr_whitelist}|memory" + fi + if [ -r /proc/net/route ]; then dev=$(awk 'NR == 2 { print $1 }' /proc/net/route) [ -e "/sys/class/net/$dev" ] && hdr_whitelist="${hdr_whitelist}|network" fi + if [ -e /usr/local/cuda/lib64 ] && [ -e /usr/local/cuda/include ]; then + hdr_whitelist="${hdr_whitelist}|nvidia_gpu" + NVML_LDFLAGS="-L/usr/local/cuda/lib64 -lnvidia-ml" + NVML_IFLAGS="-I/usr/local/cuda/include" + fi + vendor=$(awk '/vendor_id/ {print $3; exit}' /proc/cpuinfo) vendor_lc=$(echo "$vendor" | tr 'A-Z' 'a-z') case $vendor_lc in @@ -136,7 +154,7 @@ detect_caps() { } case $1 in ---all|-a) +--all | -a) all=1 ;; esac @@ -144,30 +162,33 @@ esac [ "$all" ] || detect_caps [ "$all" ] || -while [ "$1" ]; do - case $1 in - --include|-i) - shift; [ "$1" ] || usage - hdr_whitelist="${hdr_whitelist}|${1}" - ;; - --exclude|-e) - shift; [ "$1" ] || usage - hdr_blacklist="${hdr_blacklist}|${1}" - ;; - --list-sensors|-l) - ls_sensors - exit 0 - ;; - --unique|-u) - shift; [ "$1" ] || usage - hdr_whitelist=$1 - ;; - --help|-h) - usage - ;; - esac - shift -done + while [ "$1" ]; do + case $1 in + --include | -i) + shift + [ "$1" ] || usage + hdr_whitelist="${hdr_whitelist}|${1}" + ;; + --exclude | -e) + shift + [ "$1" ] || usage + hdr_blacklist="${hdr_blacklist}|${1}" + ;; + --list-sensors | -l) + ls_sensors + exit 0 + ;; + --unique | -u) + shift + [ "$1" ] || usage + hdr_whitelist=$1 + ;; + --help | -h) + usage + ;; + esac + shift + done sensors=$(ls_sensors) nb_sensors=$(echo "$sensors" | sed '/^$/d' | wc -l) @@ -177,12 +198,14 @@ if [ "$nb_sensors" -eq 0 ]; then exit 1 fi -try gen_sensors_h "$sensors" "$nb_sensors" > "$target_hdr" -try gen_sensors_mk "$sensors" > "$target_mk" +try gen_sensors_h "$sensors" "$nb_sensors" >"$target_hdr" +try gen_sensors_mk "$sensors" >"$target_mk" + +try printf "NVML_LDFLAGS = %s\n" "$NVML_LDFLAGS" >>"$target_mk" +try printf "NVML_IFLAGS = %s\n" "$NVML_IFLAGS" >>"$target_mk" printf -- 'Run `make` to build `bin/mojitos`.\n' >&2 printf -- 'The resulting binary will have the %d following sensors:\n' "$nb_sensors" >&2 echo "$sensors" >&2 make clean >/dev/null - diff --git a/doc/counter_ex.h b/doc/counter_ex.h new file mode 100644 index 0000000000000000000000000000000000000000..65fabf3756dddc1b41b3bb16d143b3158854c20f --- /dev/null +++ b/doc/counter_ex.h @@ -0,0 +1,26 @@ +/* + * Example of a basic counter: an accumulator +**/ + +unsigned int init_acc(char *, void **); +unsigned int get_acc(uint64_t *results, void *); +void clean_acc(void *); +void label_acc(char **labels, void *); + +Sensor rapl = { + .init = init_acc, + .get = get_acc, + .clean = clean_acc, + .label = label_acc, + .nb_opt = 1, +}; + +Optparse rapl_opt[1] = { + { + .longname = "accumulator", + .shortname = 'a', + .argtype = OPTPARSE_NONE, /* OPTPARSE_NONE / OPTPARSE_OPTIONAL / OPTPARSE_REQUIRED */ + .usage_arg = NULL, + .usage_msg = "dumb accumulator", + }, +}; diff --git a/doc/network.md b/doc/network.md new file mode 100644 index 0000000000000000000000000000000000000000..b5d1e68304a72a35da213a74dfab918321663247 --- /dev/null +++ b/doc/network.md @@ -0,0 +1,8 @@ +This sensor can autodetect interfaces in use by giving the special +interface name "X". But the total number of interfaces it can autodetect +is currently under a hard-limit. This hard-limit can be changed by +modifying this line in `src/network.c`: + +```c +#define NB_MAX_DEV 8 +``` diff --git a/doc/nvidia_gpu.md b/doc/nvidia_gpu.md new file mode 100644 index 0000000000000000000000000000000000000000..15cc8aa073dfe6cf49de4e820061793d24b55331 --- /dev/null +++ b/doc/nvidia_gpu.md @@ -0,0 +1,57 @@ +# Nvidia Gpu + +The `nvidia_gpu` sensor provides basic information about the gpu. Depending on +the driver version it is possible that not all sensors are supported, so an +error message will be written to `stderr` but the execution will continue. + +For more information you can consult the [nvidia nvml api](https://docs.nvidia.com/deploy/index.html). + +## [Clock](https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceEnumvs.html#group__nvmlDeviceEnumvs_1g805c0647be9996589fc5e3f6ff680c64) + +All speeds are in Mhz. + +|Output |Description | +|--------|-------------------------------| +|graphics|Graphics clock | +|sm |Streaming Multiprocessor clock | +|memory |Memory clock | +|video |Video encoder/decoder clock | + +## [Memory](https://docs.nvidia.com/deploy/nvml-api/structnvmlMemory__t.html#structnvmlMemory__t) + +All values are in bytes. + +|Output |Description | +|--------|-------------------------------------| +|free |Unallocated device memory | +|used |Sum of Reserved and Allocated memory | +|total |Total physical device memory | + + +## [Utilization](https://docs.nvidia.com/deploy/nvml-api/structnvmlUtilization__t.html#structnvmlUtilization__t) + +Utilization information for a device. Each sample period may be between 1 +second and 1/6 second, depending on the product being queried. + +All values are a percent of time over the past sample period. + +|Output |Description | +|--------|---------------------| +|gpu | Usage of the GPU | +|memory | Usage of the Memory | + +## [Power](https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1g7ef7dff0ff14238d08a19ad7fb23fc87) + +Retrieves power usage for this GPU in milliwatts and its associated circuitry (e.g. memory) + +|Output |Description | +|--------|-------------------------| +|power | Power consumption in mW | + +## [Temperature](https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceEnumvs.html#group__nvmlDeviceEnumvs_1g2650b526841fa38b8f293c2d509a1de0) +Temperature of the GPU. + +|Output |Description | +|------------|----------------------------| +|temperature | Temperature of the GPU die | + diff --git a/makefile b/makefile index a197961099a277e4e12d6d442236b2fd5327d14c..f25d5630f9f65be71fb7220785bc97a30ea6030a 100644 --- a/makefile +++ b/makefile @@ -7,8 +7,20 @@ BIN_DIR = bin TESTS_DIR = tests BIN = mojitos +PREFIX = /usr/local + +CC = gcc +CPPFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -Wno-unused-function -I./lib $(NVML_IFLAGS) +CFLAGS = $(CPPFLAGS) -O3 -Werror +LDFLAGS = $(NVML_LDFLAGS) + +ASTYLE = astyle --style=kr -xf -s4 -k3 -n -Z -Q + +all: $(BIN) man CAPTOR_OBJ = +NVML_LDFLAGS = +NVML_IFLAGS = include ./sensors.mk @@ -16,18 +28,15 @@ OBJ = \ $(CAPTOR_OBJ) \ $(OBJ_DIR)/util.o -CC = gcc -CPPFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -Wno-unused-function -I./lib -CFLAGS = $(CPPFLAGS) -O3 -Werror -LDFLAGS = - -ASTYLE = astyle --style=kr -xf -s4 -k3 -n -Z -Q - - -all: $(BIN) man +options: + @echo BIN: $(BIN) + @echo CC: $(CC) + @echo CFLAGS: $(CFLAGS) + @echo LDFLAGS: $(LDFLAGS) + @echo OBJ: $(OBJ) $(BIN): $(BIN_DIR) $(OBJ) $(OBJ_DIR)/$(BIN).o - $(CC) $(LDFLAGS) -o $(BIN_DIR)/$(BIN) $(OBJ) $(OBJ_DIR)/$(BIN).o + $(CC) -o $(BIN_DIR)/$(BIN) $(OBJ) $(OBJ_DIR)/$(BIN).o $(LDFLAGS) $(OBJ): $(OBJ_DIR) $(OBJ_DIR)/counters.o: $(SRC_DIR)/counters_option.h @@ -35,7 +44,7 @@ $(OBJ_DIR)/counters.o: $(SRC_DIR)/counters_option.h $(OBJ_DIR)/$(BIN).o: $(SRC_DIR)/$(BIN).c $(SRC_DIR)/counters_option.h $(CC) $(CFLAGS) -c $< -o $@ -$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(SRC_DIR)/%.h +$(OBJ_DIR)/util.o: $(SRC_DIR)/util.c $(SRC_DIR)/util.h $(CC) $(CFLAGS) -c $< -o $@ $(SRC_DIR)/counters_option.h: $(SRC_DIR)/counters_option.sh @@ -74,4 +83,16 @@ man: $(BIN) '/^USAGE/ { $$0=usage } 1' \ doc/$(BIN).pre.1 > doc/$(BIN).1 2>/dev/null -.PHONY: all clean mojitos debug format tests readme man +install: $(BIN) man + mkdir -p $(PREFIX)/bin + cp $(BIN_DIR)/$(BIN) $(PREFIX)/bin/. + chmod 755 $(PREFIX)/bin/$(BIN) + mkdir -p $(PREFIX)/share/man/man1 + cp $(DOC_DIR)/$(BIN).1 $(PREFIX)/share/man/man1/. + chmod 644 $(PREFIX)/share/man/man1/$(BIN).1 + +uninstall: + rm -f $(PREFIX)/bin/$(BIN) + rm -f $(PREFIX)/share/man/man1/$(BIN).1 + +.PHONY: all clean mojitos debug format tests readme man install uninstall diff --git a/src/amd_rapl.c b/src/amd_rapl.c index 1e08517d7d2f66c5b5404e973f13aed5f366d390..9eaac45938a1ed0f9e44d68408f4a8796103e96e 100644 --- a/src/amd_rapl.c +++ b/src/amd_rapl.c @@ -25,6 +25,7 @@ #include <string.h> #include <errno.h> #include <stdlib.h> +#include <inttypes.h> #include "info_reader.h" #include "util.h" @@ -120,7 +121,7 @@ uint64_t read_msr(int fd, uint64_t msr) { uint64_t data; if (pread(fd, &data, sizeof data, msr) != sizeof data) { - fprintf(stderr, "read_msr(%ld):", msr); + fprintf(stderr, "read_msr(%"PRIu64"):", msr); perror("pread"); exit(127); } @@ -175,7 +176,7 @@ uint64_t raw_to_joule(uint64_t raw, uint64_t unit) void debug_print_sensor(CpuSensor *sensor) { //CASSERT(sizeof(CpuSensor) == 56, amd_rapl_c); - printf("cpu_id : %d, package_id : %d, core_id : %d, name : %s, fd: %d, energy_units : %d, core_energy: %ld\n", + printf("cpu_id : %d, package_id : %d, core_id : %d, name : %s, fd: %d, energy_units : %d, core_energy: %"PRIu64"\n", sensor->cpu_id, sensor->package_id, sensor->core_id, @@ -200,17 +201,36 @@ void debug_print_amd_rapl(AmdRapl *rapl) unsigned int get_nb_cpu() { char filename[BUFFER_SIZE]; + int cpy_errno; unsigned int n_cpu = 0; for (;; n_cpu++) { snprintf(filename, BUFFER_SIZE, base_str, n_cpu); int fd = open(filename, O_RDONLY); + cpy_errno = errno; if (fd < 0) { break; } close(fd); } + + if (n_cpu == 0) { + perror("open()"); + fprintf(stderr, "on the file: '%s'\n", filename); + switch (cpy_errno) { + case ENOENT: + fprintf(stderr, "Amd rapl works with msr module, try to run 'sudo modprobe msr', then retry.\n"); + exit(99); + case EACCES: + fprintf(stderr, "Amd rapl must be executed with the administrator privilege, try with 'sudo'.\n"); + exit(98); + default: + fprintf(stderr, "Unexpected error\n"); + exit(97); + } + } + // n_cpu > 0 return n_cpu; } @@ -294,11 +314,6 @@ unsigned int init_amd_rapl(char *none, void **ptr) UNUSED(none); unsigned int max_cpus = get_nb_cpu(); - if (max_cpus == 0) { - fprintf(stderr, base_str, 0); - perror(":open()"); - exit(127); - } CpuSensor *cpu_information = (CpuSensor *) calloc(max_cpus, sizeof(CpuSensor)); if (parse_cpuinfo(cpu_information, max_cpus)) { diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000000000000000000000000000000000000..3356a82323d3ff8d4a79391c2bb0f36663ece9e2 --- /dev/null +++ b/src/memory.c @@ -0,0 +1,97 @@ +/******************************************************* + Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr> + + This file is part of Mojitos. + + Mojitos is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Mojitos is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MojitO/S. If not, see <https://www.gnu.org/licenses/>. + + *******************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/sysinfo.h> +#include <string.h> + +#include "util.h" + +typedef enum { + TOTALRAM = 0, + FREERAM, + SHAREDRAM, + BUFFERRAM, + TOTALSWAP, + FREESWAP, + PROCS, + TOTALHIGH, + FREEHIGH, + MEM_UNIT, + + MEMORY_COUNT, +} MemoryKind; + +static const char *memory_labels[MEMORY_COUNT] = { + "totalram", "freeram", "sharedram", "bufferram", + "totalswap", "freeswap", + "procs", + "totalhigh", "freehigh", "mem_unit", +}; + +unsigned int init_memory(char *none1, void **none2) +{ + UNUSED(none1); + UNUSED(none2); + struct sysinfo info; + if (sysinfo(&info) < 0) { + fprintf(stderr, "Failed to get the memory information"); + return 0; + } + return MEMORY_COUNT; +} + +unsigned int get_memory(uint64_t *results, void *none) +{ + UNUSED(none); + struct sysinfo info; + if (sysinfo(&info) < 0) { + fprintf(stderr, "Failed to get the memory information"); + exit(99); + } + + // Can't use memcpy, the size isn't always the same + results[TOTALRAM] = info.totalram; + results[FREERAM] = info.freeram; + results[SHAREDRAM] = info.sharedram; + results[BUFFERRAM] = info.bufferram; + results[TOTALSWAP] = info.totalswap; + results[FREESWAP] = info.freeswap; + results[PROCS] = info.procs; + results[TOTALHIGH] = info.totalhigh; + results[FREEHIGH] = info.freehigh; + results[MEM_UNIT] = info.mem_unit; + + return MEMORY_COUNT; +} + +void label_memory(char **labels, void *none) +{ + UNUSED(none); + memcpy(labels, memory_labels, sizeof(char *) * MEMORY_COUNT); +} + +void clean_memory(void *none) +{ + UNUSED(none); + return; +} + diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000000000000000000000000000000000000..6e275c63e41e836364d6ead18ebad548f7952de5 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,43 @@ +/******************************************************* + Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr> + + This file is part of Mojitos. + + Mojitos is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Mojitos is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MojitO/S. If not, see <https://www.gnu.org/licenses/>. + + *******************************************************/ + +unsigned int init_memory(char *, void **); +unsigned int get_memory(uint64_t *results, void *); +void clean_memory(void *); +void label_memory(char **labels, void *); + + +Sensor memory = { + .init = init_memory, + .get = get_memory, + .clean = clean_memory, + .label = label_memory, + .nb_opt = 1, +}; + +Optparse memory_opt[1] = { + { + .longname = "memory", + .shortname = 'm', + .argtype = OPTPARSE_NONE, + .usage_arg = NULL, + .usage_msg = "Retrieves information about the memory via the syscall 'sysinfo(2)'.", + }, +}; diff --git a/src/network.c b/src/network.c index 37d237376be4c893e2c29de8d160b61ba3190628..a668725dcd51825c2245c34b7ae387cd7125b294 100644 --- a/src/network.c +++ b/src/network.c @@ -17,28 +17,40 @@ along with MojitO/S. If not, see <https://www.gnu.org/licenses/>. *******************************************************/ -#include <unistd.h> +#include <errno.h> #include <fcntl.h> -#include <stdlib.h> +#include <stdint.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <stdint.h> +#include <unistd.h> + #include "util.h" +#define NB_MAX_DEV 8 #define NB_SENSOR 4 static char *route = "/proc/net/route"; +char *_labels_network[NB_SENSOR] = { + "%s:rxp", + "%s:rxb", + "%s:txp", + "%s:txb", +}; struct Network { - uint64_t values[NB_SENSOR]; - uint64_t tmp_values[NB_SENSOR]; - int sources[NB_SENSOR]; + uint64_t values[NB_MAX_DEV][NB_SENSOR]; + uint64_t tmp_values[NB_MAX_DEV][NB_SENSOR]; + int sources[NB_MAX_DEV][NB_SENSOR]; + char labels[NB_MAX_DEV][NB_SENSOR][128]; + char devs[NB_MAX_DEV][128]; + int ndev; }; typedef struct Network Network; -unsigned int _get_network(uint64_t *results, int *sources) +static void _get_network(uint64_t *results, int *sources) { if (sources == NULL) { - return 0; + return; } char buffer[128]; @@ -51,18 +63,51 @@ unsigned int _get_network(uint64_t *results, int *sources) results[i] = strtoull(buffer, NULL, 10); } - - return NB_SENSOR; } +/* + * read from fd len chars and store them into buf + * make *s points to the first occurence of c into buf +*/ +static int strchr_refill(int fd, char *buf, int len, char **s, char c) +{ + *s = strchr(*s, c); + + if (*s == NULL) { + int nbytes = read(fd, buf, len - 1); + if (nbytes < 0) { + perror("read"); + return -1; + } + buf[len - 1] = '\0'; + + /* whole file read */ + if (nbytes == 0) { + return 0; + } + *s = strchr(buf, c); + } + + return 1; +} unsigned int init_network(char *dev, void **ptr) { if (dev == NULL) { - return 0; + exit(1); } + char *filenames[] = { + "/sys/class/net/%s/statistics/rx_packets", + "/sys/class/net/%s/statistics/rx_bytes", + "/sys/class/net/%s/statistics/tx_packets", + "/sys/class/net/%s/statistics/tx_bytes", + }; + + struct Network *state = malloc(sizeof(struct Network)); + memset(state, '\0', sizeof(*state)); + if (strcmp(dev, "X") == 0) { int fd = open(route, O_RDONLY); @@ -74,50 +119,110 @@ unsigned int init_network(char *dev, void **ptr) char buffer[1000]; - if (read(fd, buffer, 999) < 0 ) { - perror("read"); + /* skip first line */ + char *s = buffer; + int ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\n'); + if (ret != 1) { close(fd); + free(state); exit(1); } + s++; - char *start_of_dev = index(buffer, '\n') + 1; - char *end_of_dev = index(start_of_dev, '\t'); - *end_of_dev = '\0'; - dev = start_of_dev; - close(fd); - } + char *start_of_dev = s; + /* jump to the end of the device name */ + ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\t'); + if (ret != 1) { + close(fd); + free(state); + exit(1); + } - char *filenames[] = {"/sys/class/net/%s/statistics/rx_packets", - "/sys/class/net/%s/statistics/rx_bytes", - "/sys/class/net/%s/statistics/tx_packets", - "/sys/class/net/%s/statistics/tx_bytes" - }; + state->ndev++; // ndev should be equal to 1 at this point + memcpy(&(state->devs[state->ndev - 1]), start_of_dev, + MIN((size_t)(sizeof(state->devs[0]) - 1), (size_t)(s - start_of_dev))); + + for (;;) { + /* jump to the next line */ + ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\n'); + if (ret != 1) { + break; + } + s++; + + start_of_dev = s; + ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\t'); + if (ret != 1) { + break; + } + + /* compare dev name to the previously saved one */ + int newdev = 1; + for (int i = 0; i < state->ndev && newdev; i++) { + if (strncmp(start_of_dev, state->devs[i], s - start_of_dev) == 0) { + newdev = 0; + } + } + if (newdev) { + if (state->ndev >= NB_MAX_DEV) { + fprintf(stderr, "Maximum amount of network devices exceeded (%d).\n", NB_MAX_DEV); + break; + } + state->ndev++; + memcpy(&(state->devs[state->ndev - 1]), start_of_dev, + MIN((size_t)(sizeof(state->devs[0]) - 1), (size_t)(s - start_of_dev))); + } + } - Network *state = malloc(sizeof(Network)); + close(fd); + } else { + state->ndev = 1; + memcpy(&(state->devs[0]), dev, strlen(dev) + 1); + } char buffer2[256]; - for (int i = 0; i < NB_SENSOR; i++) { - snprintf(buffer2, 256, filenames[i], dev); - state->sources[i] = open(buffer2, O_RDONLY); + for (int i = 0; i < state->ndev; i++) { + for (int j = 0; j < NB_SENSOR; j++) { + snprintf(buffer2, sizeof(buffer2), filenames[j], state->devs[i]); + errno = 0; + int fd = open(buffer2, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "init_network: open: %s: %.*s\n", strerror(errno), + (int)sizeof(buffer2), buffer2); + free(state); + exit(1); + } + state->sources[i][j] = fd; + snprintf(state->labels[i][j], sizeof(state->labels[i][j]), _labels_network[j], + state->devs[i]); + } } *ptr = (void *) state; - _get_network(state->values, state->sources); - return NB_SENSOR; + for (int i = 0; i < state->ndev; i++) { + _get_network(state->values[i], state->sources[i]); + } + + return state->ndev * NB_SENSOR; } unsigned int get_network(uint64_t *results, void *ptr) { - Network *state = (Network *) ptr; - _get_network(state->tmp_values, state->sources); + struct Network *state = (struct Network *) ptr; - for (int i = 0; i < NB_SENSOR; i++) { - results[i] = modulo_substraction(state->tmp_values[i], state->values[i]); + for (int i = 0; i < state->ndev; i++) { + _get_network(state->tmp_values[i], state->sources[i]); + + for (int j = 0; j < NB_SENSOR; j++) { + results[i*NB_SENSOR + j] = modulo_substraction(state->tmp_values[i][j], state->values[i][j]); + } + + memcpy(&(state->values[i]), &(state->tmp_values[i]), + NB_SENSOR * sizeof(state->values[i][0])); } - memcpy(state->values, state->tmp_values, NB_SENSOR * sizeof(uint64_t)); - return NB_SENSOR; + return state->ndev * NB_SENSOR; } void clean_network(void *ptr) @@ -128,19 +233,22 @@ void clean_network(void *ptr) return; } - for (int i = 0; i < NB_SENSOR; i++) { - close(state->sources[i]); + for (int i = 0; i < state->ndev; i++) { + for (int j = 0; j < NB_SENSOR; j++) { + close(state->sources[i][j]); + } } free(state); } -char *_labels_network[NB_SENSOR] = {"rxp", "rxb", "txp", "txb"}; -void label_network(char **labels, void *none) +void label_network(char **labels, void *ptr) { - UNUSED(none); + struct Network *state = (struct Network *) ptr; - for (int i = 0; i < NB_SENSOR; i++) { - labels[i] = _labels_network[i]; + for (int i = 0; i < state->ndev; i++) { + for (int j = 0; j < NB_SENSOR; j++) { + labels[i*NB_SENSOR + j] = state->labels[i][j]; + } } } diff --git a/src/nvidia_gpu.c b/src/nvidia_gpu.c new file mode 100644 index 0000000000000000000000000000000000000000..407800271a09ab0546017018f78f8fed999602bd --- /dev/null +++ b/src/nvidia_gpu.c @@ -0,0 +1,593 @@ +/******************************************************* + Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr> + + This file is part of Mojitos. + + Mojitos is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Mojitos is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MojitO/S. If not, see <https://www.gnu.org/licenses/>. + + *******************************************************/ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +// Pedantic throws a warning in the nvml library +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#include <nvml.h> +#pragma GCC diagnostic pop + +#include "util.h" + +// -----------------------------SENSOR_KIND +typedef enum { + CLOCK_SENSOR = 0, + MEMORY_SENSOR = 1, + UTILIZATION_SENSOR = 2, + POWER_SENSOR = 3, + TEMPERATURE_SENSOR = 4, + + COUNT_SENSOR = 5, +} SENSOR_KIND; + +typedef struct Device Device; +typedef struct NvidiaGpu NvidiaGpu; +typedef struct ISensor ISensor; +typedef struct Sensor Sensor; + +// -- Sensor interface +typedef unsigned int (Initializer) (const Device *, void **); +typedef unsigned int (Getter) (uint64_t *, const Device *, void *); +typedef unsigned int (Labeller) (char **, void *); +typedef void (Cleaner) (void *); + +struct ISensor { + Initializer *init; + Getter *get; + Labeller *label; + Cleaner *clean; +}; + +// -- Sensor +struct Sensor { + void *data; + const ISensor *fun; +}; + +// -- Device: represents a gpu +struct Device { + char name[NVML_DEVICE_NAME_BUFFER_SIZE]; + nvmlDevice_t device; + unsigned int idx; + + Sensor sensors[COUNT_SENSOR]; + unsigned int count; +}; + +// -- NvidiaGpu: represents the devices +struct NvidiaGpu { + Device *devices; + unsigned int count; +}; + +// -- Label template +static const char *label_template = "gpu%u_%s_%s"; +static const char *short_label_template = "gpu%u_%s"; + +// ----------------------------CLOCK_SENSOR + +#define CLOCK_LABEL_SIZE 25 + +// -- All existing clocks +// -- SM : Streaming Multiprocessor +static const nvmlClockType_t clocks[NVML_CLOCK_COUNT] = {NVML_CLOCK_GRAPHICS, NVML_CLOCK_SM, NVML_CLOCK_MEM, NVML_CLOCK_VIDEO}; +static const char *clock_names[NVML_CLOCK_COUNT] = {"graphics", "sm", "memory", "video"}; +static const char *clock_base_name = "clk"; + +// -- Must contain the clocks compatible with the device +typedef struct { + nvmlClockType_t clocks[NVML_CLOCK_COUNT]; + char labels[NVML_CLOCK_COUNT][CLOCK_LABEL_SIZE]; + unsigned int count; +} ClockData; + +unsigned int init_clock_sensor(const Device *device, void **data) +{ + const nvmlDevice_t nvml_device = device->device; + const unsigned int device_idx = device->idx; + ClockData tmp = {0}; + nvmlReturn_t err; + unsigned int clock; + + // -- Test all clocks + for (unsigned int i = 0; i < NVML_CLOCK_COUNT; i++) { + if ((err = nvmlDeviceGetClockInfo(nvml_device, clocks[i], &clock)) == NVML_SUCCESS) { + snprintf(tmp.labels[tmp.count], CLOCK_LABEL_SIZE, label_template, device_idx, clock_base_name, clock_names[i]); + tmp.clocks[tmp.count] = clocks[i]; + tmp.count += 1; + } else { + fprintf(stderr, "Failed to get %s clock : %s\n", clock_names[i], nvmlErrorString(err)); + } + } + + // -- No clock avaible + if (tmp.count == 0) { + return 0; + } + + *data = calloc(1, sizeof(ClockData)); + memcpy(*data, &tmp, sizeof (ClockData)); + return tmp.count; +} + +unsigned int get_clock_sensor(uint64_t *results, const Device *device, void *data) +{ + const nvmlDevice_t nvml_device = device->device; + ClockData *clock_data = (ClockData *) data; + nvmlReturn_t err; + unsigned int clock; + + for (unsigned int i = 0; i < clock_data->count; i++) { + nvmlClockType_t clock_type = clock_data->clocks[i]; + + if((err = nvmlDeviceGetClockInfo(nvml_device, clock_type, &clock)) != NVML_SUCCESS) { + fprintf(stderr, "Failed to get %s clock : %s\n", clock_names[clock_type], nvmlErrorString(err)); + exit(99); + } + results[i] = clock; + } + return clock_data->count; +} + +unsigned int label_clock_sensor(char **labels, void *data) +{ + ClockData *clock_data = (ClockData *) data; + + for (unsigned int i = 0; i < clock_data->count; i++) { + labels[i] = clock_data->labels[i]; + } + + return clock_data->count; +} + +void clean_clock_sensor(void *data) +{ + free(data); +} + +// ---------------------------MEMORY_SENSOR +#define MEMORY_LABEL_SIZE 25 + +typedef enum { + FREE_MEMORY = 0U, + USED_MEMORY = 1U, + TOTAL_MEMORY = 2U, + + COUNT_MEMORY = 3U, +} MemoryKind; + +static const char *memory_names[COUNT_MEMORY] = {"free", "used", "total"}; +static const char *memory_base_name = "mem"; + +typedef struct { + char labels[COUNT_MEMORY][MEMORY_LABEL_SIZE]; +} MemoryData; + +unsigned int init_memory_sensor(const Device *device, void **data) +{ + const nvmlDevice_t nvml_device = device->device; + const unsigned int device_idx = device->idx; + + nvmlMemory_t memory; + nvmlReturn_t err; + if ((err = nvmlDeviceGetMemoryInfo(nvml_device, &memory)) != NVML_SUCCESS) { + fprintf(stderr, "Failed to get device memory : %s\n", nvmlErrorString(err)); + return 0; + } + + MemoryData *memory_data = (MemoryData *) calloc(1, sizeof(MemoryData)); + for (unsigned int i = 0; i < COUNT_MEMORY; i++) { + snprintf(memory_data->labels[i], MEMORY_LABEL_SIZE, label_template, device_idx, memory_base_name, memory_names[i]); + } + + *data = (void *) memory_data; + return COUNT_MEMORY; +} + +unsigned int get_memory_sensor(uint64_t *results, const Device *device, void *none) +{ + UNUSED(none); + const nvmlDevice_t nvml_device = device->device; + + nvmlMemory_t memory; + nvmlReturn_t err; + if ((err = nvmlDeviceGetMemoryInfo(nvml_device, &memory)) != NVML_SUCCESS) { + fprintf(stderr, "Failed to get device memory : %s\n", nvmlErrorString(err)); + exit(99); + } + + results[FREE_MEMORY] = memory.free; + results[USED_MEMORY] = memory.used; + results[TOTAL_MEMORY] = memory.total; + return COUNT_MEMORY; +} + + +unsigned int label_memory_sensor(char **labels, void *data) +{ + MemoryData *memory_data = (MemoryData *) data; + + for (unsigned int i = 0; i < COUNT_MEMORY; i++) { + labels[i] = memory_data->labels[i]; + } + + return COUNT_MEMORY; +} +void clean_memory_sensor(void *data) +{ + free(data); +} + +// ----------------------UTILIZATION_SENSOR +#define UTILIZATION_LABEL_SIZE 35 +typedef enum { + GPU_UTILIZATION = 0U, + MEMORY_UTILIZATION = 1U, + + COUNT_UTILIZATION = 2U, +} UtilizationKind; + +typedef struct { + char labels[COUNT_UTILIZATION][UTILIZATION_LABEL_SIZE]; +} UtilizationData; + +static const char *utilization_names[COUNT_UTILIZATION] = {"gpu", "memory"}; +static const char *utilization_base_name = "utilization"; + +unsigned int init_utilization_sensor(const Device *device, void **data) +{ + const nvmlDevice_t nvml_device = device->device; + const unsigned int device_idx = device->idx; + + nvmlReturn_t err; + nvmlUtilization_t utilization; + if ((err = nvmlDeviceGetUtilizationRates(nvml_device, &utilization)) != NVML_SUCCESS) { + fprintf(stderr, "Failed to get device utilization: %s\n", nvmlErrorString(err)); + return 0; + } + + UtilizationData *utilization_data = (UtilizationData *) calloc(1, sizeof(UtilizationData)); + for (unsigned int i = 0; i < COUNT_UTILIZATION; i++) { + snprintf(utilization_data->labels[i], UTILIZATION_LABEL_SIZE, label_template, device_idx, utilization_base_name, utilization_names[i]); + } + + *data = (void *) utilization_data; + return COUNT_UTILIZATION; +} + +unsigned int get_utilization_sensor(uint64_t *results, const Device *device, void *none) +{ + UNUSED(none); + const nvmlDevice_t nvml_device = device->device; + + nvmlReturn_t err; + nvmlUtilization_t utilization; + if ((err = nvmlDeviceGetUtilizationRates(nvml_device, &utilization)) != NVML_SUCCESS) { + fprintf(stderr, "Failed to get device utilization: %s\n", nvmlErrorString(err)); + exit(99); + } + + results[GPU_UTILIZATION] = utilization.gpu; + results[MEMORY_UTILIZATION] = utilization.memory; + return COUNT_UTILIZATION; +} + +unsigned int label_utilization_sensor(char **labels, void *data) +{ + UtilizationData *utilization_data = (UtilizationData *) data; + + for (unsigned int i = 0; i < COUNT_UTILIZATION; i++) { + labels[i] = utilization_data->labels[i]; + } + + return COUNT_UTILIZATION; +} + +void clean_utilization_sensor(void *data) +{ + free(data); +} + +// ----------------------------POWER_SENSOR + +#define POWER_LABEL_SIZE 25 +#define COUNT_POWER 1 + +static const char *power_base_name = "power"; + +typedef struct { + char label[POWER_LABEL_SIZE]; +} PowerData; + + +unsigned int init_power_sensor(const Device *device, void **data) +{ + const nvmlDevice_t nvml_device = device->device; + const unsigned int device_idx = device->idx; + + unsigned int power; + nvmlReturn_t err; + if ((err = nvmlDeviceGetPowerUsage(nvml_device, &power)) != NVML_SUCCESS) { + printf("Failed to get the device power consumption: %s\n", nvmlErrorString(err)); + return 0; + } + + PowerData *power_data = (PowerData *) calloc(1, sizeof(PowerData)); + snprintf(power_data->label, POWER_LABEL_SIZE, short_label_template, device_idx, power_base_name); + + *data = (void *) power_data; + return COUNT_POWER; +} + +unsigned int get_power_sensor(uint64_t *results, const Device *device, void *none) +{ + UNUSED(none); + const nvmlDevice_t nvml_device = device->device; + + unsigned int power; + nvmlReturn_t err; + if ((err = nvmlDeviceGetPowerUsage(nvml_device, &power)) != NVML_SUCCESS) { + printf("Failed to get the device power consumption: %s\n", nvmlErrorString(err)); + exit(99); + } + + *results = power; + return COUNT_POWER; +} + +unsigned int label_power_sensor(char **labels, void *data) +{ + PowerData *power_data = (PowerData *) data; + *labels = power_data->label; + return COUNT_POWER; +} + +void clean_power_sensor(void *data) +{ + free(data); +} + +// ----------------------TEMPERATURE_SENSOR + + +#define TEMPERATURE_LABEL_SIZE 35 +#define COUNT_TEMPERATURE 1 + +static const char *temperature_base_name = "temperature"; + +typedef struct { + char label[TEMPERATURE_LABEL_SIZE]; +} TemperatureData; + +unsigned int init_temperature_sensor(const Device *device, void **data) +{ + const nvmlDevice_t nvml_device = device->device; + const unsigned int device_idx = device->idx; + + unsigned int temperature; + nvmlReturn_t err; + if ((err = nvmlDeviceGetTemperature(nvml_device, NVML_TEMPERATURE_GPU, &temperature)) != NVML_SUCCESS) { + printf("Failed to get the device temperature: %s\n", nvmlErrorString(err)); + return 0; + } + + TemperatureData *temperature_data = (TemperatureData *) calloc(1, sizeof(TemperatureData)); + snprintf(temperature_data->label, TEMPERATURE_LABEL_SIZE, short_label_template, device_idx, temperature_base_name); + + *data = (void *) temperature_data; + return COUNT_TEMPERATURE; +} + +unsigned int get_temperature_sensor(uint64_t *results, const Device *device, void *none) +{ + UNUSED(none); + const nvmlDevice_t nvml_device = device->device; + + unsigned int temperature; + nvmlReturn_t err; + if ((err = nvmlDeviceGetTemperature(nvml_device, NVML_TEMPERATURE_GPU, &temperature)) != NVML_SUCCESS) { + printf("Failed to get the device temperature: %s\n", nvmlErrorString(err)); + exit(99); + } + + *results = temperature; + return COUNT_TEMPERATURE; +} + +unsigned int label_temperature_sensor(char **labels, void *data) +{ + TemperatureData *temperature_data = (TemperatureData *) data; + *labels = temperature_data->label; + return COUNT_TEMPERATURE; +} + +void clean_temperature_sensor(void *data) +{ + free(data); +} + +// -------------------------AVAIBLE_SENSORS +static const ISensor avaible_sensors[COUNT_SENSOR] = { + {.init = init_clock_sensor, .get = get_clock_sensor, .label = label_clock_sensor, .clean = clean_clock_sensor}, + {.init = init_memory_sensor, .get = get_memory_sensor, .label = label_memory_sensor, .clean = clean_memory_sensor}, + {.init = init_utilization_sensor, .get = get_utilization_sensor, .label = label_utilization_sensor, .clean = clean_utilization_sensor}, + {.init = init_power_sensor, .get = get_power_sensor, .label = label_power_sensor, .clean = clean_power_sensor}, + {.init = init_temperature_sensor, .get = get_temperature_sensor, .label = label_temperature_sensor, .clean = clean_temperature_sensor}, +}; + +// ------------------------DEVICE_FUNCTIONS + +unsigned int init_device(unsigned int device_idx, Device *device) +{ + nvmlReturn_t result; + nvmlDevice_t nvml_device; + if ((result = nvmlDeviceGetHandleByIndex(device_idx, &nvml_device)) != NVML_SUCCESS) { + fprintf(stderr, "Failed to get device handle for device %d: %s\n", device_idx, nvmlErrorString(result)); + return 0; + } + + if ((result = nvmlDeviceGetName(nvml_device, device->name, NVML_DEVICE_NAME_BUFFER_SIZE))) { + fprintf(stderr, "Failed to get device name for device %d: %s\n", device_idx, nvmlErrorString(result)); + return 0; + } + + device->device = nvml_device; + device->idx = device_idx; + + unsigned int sensor_count = 0; + unsigned int total_count = 0; + + for (unsigned int i = 0; i < COUNT_SENSOR; i++) { + Sensor *sensor = &device->sensors[sensor_count]; + sensor->fun = &avaible_sensors[i]; + unsigned int count; + + if ((count = sensor->fun->init(device, &sensor->data)) != 0) { + sensor_count += 1; + total_count += count; + } + } + + device->count = sensor_count; + return total_count; +} + +unsigned int get_device(uint64_t *results, Device *device) +{ + unsigned int count = 0; + for (unsigned int i = 0; i < device->count; i++) { + Sensor *sensor = &device->sensors[i]; + unsigned int result = sensor->fun->get(results, device, sensor->data); + count += result; + results += result; + } + + return count; +} + +unsigned int label_device(char **labels, Device *device) +{ + unsigned int count = 0; + for (unsigned int i = 0; i < device->count; i++) { + Sensor *sensor = &device->sensors[i]; + unsigned int result = sensor->fun->label(labels, sensor->data); + labels += result; + count += result; + } + + return count; +} + +void clean_device(Device *device) +{ + for (unsigned int i = 0; i < device->count; i++) { + Sensor *sensor = &device->sensors[i]; + sensor->fun->clean(sensor->data); + } +} + + +// ------------------------NVIDIA_INTERFACE + +unsigned int init_nvidia_gpu(char *none, void **ptr) +{ + UNUSED(none); + UNUSED(ptr); + + nvmlReturn_t result; + if ((result = nvmlInit()) != NVML_SUCCESS) { + fprintf(stderr, "Failed to initialize NVML: %s\n", nvmlErrorString(result)); + exit(1); + } + + unsigned int avaible_device_count; + if ((result = nvmlDeviceGetCount(&avaible_device_count)) != NVML_SUCCESS) { + fprintf(stderr, "Failed to get device count : %s\n", nvmlErrorString(result)); + nvmlShutdown(); + exit(1); + } + + Device *devices = calloc(avaible_device_count, sizeof(Device)); + + unsigned int sensor_count = 0; + unsigned int device_count = 0; + for (unsigned int i = 0; i < avaible_device_count; i++) { + unsigned int initialized_count; + if ((initialized_count = init_device(i, &devices[device_count])) != 0) { + sensor_count += initialized_count; + device_count += 1; + } + } + + NvidiaGpu *nvidia = (NvidiaGpu *) calloc(1, sizeof(NvidiaGpu)); + nvidia->devices = devices; + nvidia->count = device_count; + + *ptr = (void *) nvidia; + return sensor_count; +} + + +unsigned int get_nvidia_gpu(uint64_t *results, void *ptr) +{ + NvidiaGpu *nvidia = (NvidiaGpu *) ptr; + unsigned count = 0; + + for (unsigned int i = 0; i < nvidia->count; i++) { + unsigned int result = get_device(results, &nvidia->devices[i]); + results += result; + count += result; + } + + return count; +} + +unsigned int label_nvidia_gpu(char **labels, void *ptr) +{ + NvidiaGpu *nvidia = (NvidiaGpu *) ptr; + unsigned count = 0; + + for (unsigned int i = 0; i < nvidia->count; i++) { + unsigned int result = label_device(labels, &nvidia->devices[i]); + labels += result; + count += result; + } + + return count; +} + +void clean_nvidia_gpu(void *ptr) +{ + NvidiaGpu *nvidia = (NvidiaGpu *) ptr; + + for (unsigned int i = 0; i < nvidia->count; i++) { + clean_device(&nvidia->devices[i]); + } + + free(nvidia->devices); + free(nvidia); + nvmlShutdown(); +} + diff --git a/src/nvidia_gpu.h b/src/nvidia_gpu.h new file mode 100644 index 0000000000000000000000000000000000000000..1f3d053d0e4513ee707d7e4658e8a0a7774fe54c --- /dev/null +++ b/src/nvidia_gpu.h @@ -0,0 +1,43 @@ +/******************************************************* + Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr> + + This file is part of Mojitos. + + Mojitos is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Mojitos is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MojitO/S. If not, see <https://www.gnu.org/licenses/>. + + *******************************************************/ + +unsigned int init_nvidia_gpu(char *, void **); +unsigned int get_nvidia_gpu(uint64_t *results, void *); +void clean_nvidia_gpu(void *); +void label_nvidia_gpu(char **labels, void *); + + +Sensor nvidia_gpu = { + .init = init_nvidia_gpu, + .get = get_nvidia_gpu, + .clean = clean_nvidia_gpu, + .label = label_nvidia_gpu, + .nb_opt = 1, +}; + +Optparse nvidia_gpu_opt[1] = { + { + .longname = "nvidia-gpu", + .shortname = 'n', + .argtype = OPTPARSE_NONE, + .usage_arg = NULL, + .usage_msg = "provides basic gpu information [clocks, memory, utilization, power, temperature].", + }, +}; diff --git a/src/util.h b/src/util.h index e422d070693cd28af9613e77a5efdb917ce6b2f6..0015a9867452800633582cdbd9736ab5c11c145e 100644 --- a/src/util.h +++ b/src/util.h @@ -38,6 +38,8 @@ exit(code); \ } while (0) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) /** * @brief Substracts lhs by rhs, assuming that lhs is a cyclic increment from rhs, diff --git a/tests/main.c b/tests/main.c index 3246f0429a7ff7ad065070ac195428af7e099872..e90e8dd69863fcc269d561c07df43dc49a2437ec 100644 --- a/tests/main.c +++ b/tests/main.c @@ -21,9 +21,11 @@ #include "util.c" #include "amd_rapl.c" #include "info_reader.c" +#include "memory.c" TMAIN({ CALL_TFUNCTION(test_util); CALL_TFUNCTION(test_amd_rapl); CALL_TFUNCTION(test_info_reader); + CALL_TFUNCTION(test_memory); }) diff --git a/tests/memory.c b/tests/memory.c new file mode 100644 index 0000000000000000000000000000000000000000..78efd28146558909dc0a940bfa2efce57a102b3d --- /dev/null +++ b/tests/memory.c @@ -0,0 +1,78 @@ +/******************************************************* + Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr> + + This file is part of Mojitos. + + Mojitos is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Mojitos is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MojitO/S. If not, see <https://www.gnu.org/licenses/>. + +*******************************************************/ + +#include "small_test.h" +#include "../src/memory.c" + +// In order to verify the integrity. +TFUNCTION(test_labels, { + // If it fails update the tests + int tested_count = 10; + int expected_count = MEMORY_COUNT; + TEST_INT(&tested_count, &expected_count); + + const char *result = NULL; + char *expected = NULL; + + expected = "totalram"; + result = memory_labels[TOTALRAM]; + TEST_STR(result, expected); + + expected = "freeram"; + result = memory_labels[FREERAM]; + TEST_STR(result, expected); + + expected = "sharedram"; + result = memory_labels[SHAREDRAM]; + TEST_STR(result, expected); + + expected = "bufferram"; + result = memory_labels[BUFFERRAM]; + TEST_STR(result, expected); + + expected = "totalswap"; + result = memory_labels[TOTALSWAP]; + TEST_STR(result, expected); + + expected = "freeswap"; + result = memory_labels[FREESWAP]; + TEST_STR(result, expected); + + expected = "procs"; + result = memory_labels[PROCS]; + TEST_STR(result, expected); + + expected = "totalhigh"; + result = memory_labels[TOTALHIGH]; + TEST_STR(result, expected); + + expected = "freehigh"; + result = memory_labels[FREEHIGH]; + TEST_STR(result, expected); + + expected = "mem_unit"; + result = memory_labels[MEM_UNIT]; + TEST_STR(result, expected); +}) + +TFILE_ENTRY_POINT(test_memory, { + CALL_TFUNCTION(test_labels); +}) + diff --git a/tests/small_test.h b/tests/small_test.h index 66c4de9119ddd3f67b0b9db85e299487074fd6e1..8301ae7ea0f8247b3b6c81cefab86c26f13aa1c7 100644 --- a/tests/small_test.h +++ b/tests/small_test.h @@ -253,8 +253,8 @@ #define DEFERRED_ERROR(nb_error) \ INDENTED_PRINT("|_Deferred Error : %d\n",nb_error); -typedef int (Comparator) (void *, void *); -typedef char *(Formatter) (char[FMT_BUFFER_SIZE], void *); +typedef int (Comparator) (const void *, const void *); +typedef char *(Formatter) (char[FMT_BUFFER_SIZE], const void *); typedef struct { Comparator *compare; @@ -263,7 +263,7 @@ typedef struct { // ---------------------------TEST FUNCTION -int test(char *file, int line, unsigned int __indentation_level, void *result, void *expected, const TestInterface *interface) +int test(char *file, int line, unsigned int __indentation_level, const void *result, const void *expected, const TestInterface *interface) { __indentation_level += 1; static char buffer_result[FMT_BUFFER_SIZE]; @@ -281,7 +281,7 @@ int test(char *file, int line, unsigned int __indentation_level, void *result, v // ------------------------------INTERFACES // -- str_interface -int str_compare(void *ptr1, void *ptr2) +int str_compare(const void *ptr1, const void *ptr2) { char *str1 = (char *) ptr1; char *str2 = (char *) ptr2; @@ -295,7 +295,7 @@ int str_compare(void *ptr1, void *ptr2) } } -char *str_format(char buffer[FMT_BUFFER_SIZE], void *ptr) +char *str_format(char buffer[FMT_BUFFER_SIZE], const void *ptr) { UNUSED(buffer); static char *str_null = "NULL"; @@ -310,14 +310,14 @@ static const TestInterface str_interface = { // -- bool_interface -int bool_compare(void *ptr1, void *ptr2) +int bool_compare(const void *ptr1, const void *ptr2) { bool *bool1 = (bool *) ptr1; bool *bool2 = (bool *) ptr2; return *bool1 == *bool2; } -char *bool_format(char buffer[FMT_BUFFER_SIZE], void *ptr) +char *bool_format(char buffer[FMT_BUFFER_SIZE], const void *ptr) { UNUSED(buffer); bool *_bool = (bool *) ptr; @@ -331,14 +331,14 @@ static const TestInterface bool_interface = { // -- int_interface -int int_compare(void *ptr1, void *ptr2) +int int_compare(const void *ptr1, const void *ptr2) { int *int1 = (int *) ptr1; int *int2 = (int *) ptr2; return *int1 == *int2; } -char *int_format(char buffer[FMT_BUFFER_SIZE], void *ptr) +char *int_format(char buffer[FMT_BUFFER_SIZE], const void *ptr) { int *_int = (int *) ptr; snprintf(buffer, FMT_BUFFER_SIZE, "%d", *_int); @@ -352,12 +352,12 @@ static const TestInterface int_interface = { // -- ptr_interface -int ptr_compare(void *ptr1, void *ptr2) +int ptr_compare(const void *ptr1, const void *ptr2) { return ptr1 == ptr2; } -char *ptr_format(char buffer[FMT_BUFFER_SIZE], void *ptr) +char *ptr_format(char buffer[FMT_BUFFER_SIZE], const void *ptr) { snprintf(buffer, FMT_BUFFER_SIZE, "%p", ptr); return buffer; @@ -370,14 +370,14 @@ static const TestInterface ptr_interface = { // -- u64_interface -int u64_compare(void *ptr1, void *ptr2) +int u64_compare(const void *ptr1, const void *ptr2) { uint64_t *v1 = (uint64_t *) ptr1; uint64_t *v2 = (uint64_t *) ptr2; return *v1 == *v2; } -char *u64_format(char buffer[FMT_BUFFER_SIZE], void *ptr) +char *u64_format(char buffer[FMT_BUFFER_SIZE], const void *ptr) { uint64_t *v = (uint64_t *) ptr; snprintf(buffer, FMT_BUFFER_SIZE, "%"PRIu64"", *v); diff --git a/tests/util.c b/tests/util.c index 69941bfac22fac87d56cd282989721c0c3dc0d76..231ab47487224a3f384d44f6b969be8fee2e25b4 100644 --- a/tests/util.c +++ b/tests/util.c @@ -77,6 +77,50 @@ TFUNCTION(test_modulo_substraction, { TEST_UINT64_T(&result, &expected); }) +TFUNCTION(test_max, { + int expected = 0; + int result = 0; + + expected = 10; + result = MAX(expected, 9); + TEST_INT(&result, &expected); + + expected = -15; + result = MAX(expected, -16); + TEST_INT(&result, &expected); + + expected = 0; + result = MAX(expected, -1); + TEST_INT(&result, &expected); + + expected = 1; + result = MAX(expected, 0); + TEST_INT(&result, &expected); +}) + +TFUNCTION(test_min, { + int expected = 0; + int result = 0; + + expected = 9; + result = MIN(expected, 10); + TEST_INT(&result, &expected); + + expected = -16; + result = MIN(expected, -15); + TEST_INT(&result, &expected); + + expected = -1; + result = MIN(expected, 0); + TEST_INT(&result, &expected); + + expected = 0; + result = MIN(expected, 1); + TEST_INT(&result, &expected); +}) + TFILE_ENTRY_POINT(test_util, { CALL_TFUNCTION(test_modulo_substraction); + CALL_TFUNCTION(test_max); + CALL_TFUNCTION(test_min); })