diff --git a/.gitignore b/.gitignore index 5761abcfdf0c26a75374c945dfe366eaeee04285..83a8dc00367e5de634dfdb98da09de87e5de9c48 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,15 @@ -*.o +src/counters_option.h +src/captors.h +tests/run +doc/test_main_ex +doc/info_reader_ex +doc/mojitos.1 +tests/run +src/counters_option.h +src/sensors.h +sensors.mk +bin +obj +*.swp +*.swo +.vscode diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..2052da5573562a34c1d611d4b391a780622fbf56 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,42 @@ +stages: + - build-option + - build + +job-build-counters: + stage: build-option + script: + - ./configure.sh -u counters + - make + +job-build-load: + stage: build-option + script: + - ./configure.sh -u load + - make + +job-build-network: + stage: build-option + script: + - ./configure.sh -u network + - make + +job-build-rapl: + stage: build-option + script: + - ./configure.sh -u rapl + - make + +job-build: + stage: build + script: + - ./configure.sh + - make + +job-build-test: + stage: build + script: + - ./configure.sh + - make tests + +workflow: + name: 'Pipeline for branch: $CI_COMMIT_BRANCH' diff --git a/AUTHORS b/AUTHORS index 0bfc663bd4cba8c78a3de80d903028b587a3f338..13ddd11d874c2c46bb4f6792525e0d3a02371bb4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,4 @@ -Georges Da Costa georges.da-costa@irit.fr \ No newline at end of file +Georges Da Costa georges.da-costa@irit.fr +Floréal Risso floreal.risso@univ-tlse3.fr +Alexis Paronnaud alexis.paronnaud@univ-tlse3.fr +Téo Tinarrage teo.tinarrage@univ-tlse3.fr diff --git a/README.md b/README.md index 2cf04f18a0611162f0c19e04fde35b7a26e727ca..8c6d591851cbb80dcaf0bfa4c302f869baecc7cc 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,64 @@ # MOJITO/S An Open Source System, Energy and Network Monitoring Tools at the O/S level - -## Documentation - MojitO/S runs on GNU/Linux -Usage -```python -Usage : ./mojitos [-t time] [-f freq] [-r] [-p perf_list] [-l] \ - [-u] [-d network_device] [-o logfile] [-e command arguments...] -``` +## Usage -Timing -- If time is 0 then mojitos loops infinitively -- If -e is present, time and freq are not used +```bash +Usage : ./bin/mojitos [OPTIONS] [SENSOR ...] [-e <cmd> ...] -Configuration -- -r activates RAPL (deprecated) -- -R activates RAPL -- -p activates performance counters. perf_list is coma separated list of performance counters without space. -- -l lists the possible performance counters and quits -- -d activates network monitoring -- -u activates system-level load monitoring -- -s activates statistics of overhead in nanoseconds +OPTIONS: +-f|--freq <freq> + set amount of measurements per second. +-t|--time <time> + set duration value (seconds). If 0, then loops infinitely. +-e|--exec <cmd> ... + Execute a command with optional arguments. + If this option is used, any usage of -t or -f is ignored. +-o|--logfile <file> + specify a log file. +-s|--overhead-stats + enable overhead statistics (nanoseconds). -MojitO/S is published under the GPL3 license and is part of the [Energumen Project](https://www.irit.fr/energumen/) +``` -<img src="https://www.irit.fr/energumen/images/energumen.png" width="100"> +The following is an exhaustive list of all the sensors (it is very likely +that one will not have all the sensors activated in his build): +```bash +SENSORS: +-a|--amd-rapl + AMD RAPL +-p|--perf-list <perf_list> + performance counters + perf_list is a coma separated list of performance counters. + Ex: instructions,cache_misses +-l|--list + list the available performance counters and quit +-i|--monitor-infiniband <infiniband_path> + infiniband monitoring (if infiniband_path is X, tries to detect it automatically) +-u|--sysload + system load +-d|--net-dev <net_dev> + network monitoring (if network_device is X, tries to detect it automatically) +-r|--intel-rapl + INTEL RAPL +``` ## Installation Instructions -Dependencies -```bash -sudo apt install libpowercap0 libpowercap-dev powercap-utils python3 -``` Download the source code ```bash git clone https://gitlab.irit.fr/sepia-pub/mojitos.git ``` -Compile the code +The quickest way to compile the code is: ```bash cd mojitos +./configure.sh make ``` +You may want to run `./configure.sh --help` to see configuration options. + To execute mojitos without being root to monitor performance counters ```bash sudo sh -c 'echo 0 >/proc/sys/kernel/perf_event_paranoid' @@ -58,43 +73,49 @@ sudo chmod a+w /sys/class/powercap/intel-rapl/*/*/* RAPL values during 2 seconds with a frequency of 2 Hz ```bash -$ ./mojitos -t 2 -f 2 -r -#timestamp package-00 core0 dram0 -1036389.135659868 10986 2869 1526 -1036389.500183551 1291440 255736 515562 -1036390.000754048 1333553 228393 689513 -1036390.500113978 1581967 267944 701536 +$ ./bin/mojitos -t 2 -f 2 -r +#timestamp package-00 core0 dram0 +1036389.135659868 10986 2869 1526 +1036389.500183551 1291440 255736 515562 +1036390.000754048 1333553 228393 689513 +1036390.500113978 1581967 267944 701536 ``` Performance counters (cpu_cycle, cache_ll_r_a and page_faults) during 4 seconds with a frequency of 1Hz. For cache performance counters, _r and _w are respectively read and write, and _a, _m and _p are respectively access, miss, pending. ```bash -$ ./mojitos -t 4 -f 1 -p cpu_cycles,cache_ll_r_a,page_faults -#timestamp cpu_cycles cache_ll page_faults -1036846.351749455 571199 1232 0 -1036847.001098880 348173344 2451387 872 -1036848.000166158 388112961 2509305 791 -1036849.000191883 402255979 2625283 799 +$ ./bin/mojitos -t 4 -f 1 -p cpu_cycles,cache_ll_r_a,page_faults +#timestamp cpu_cycles cache_ll page_faults +1036846.351749455 571199 1232 0 +1036847.001098880 348173344 2451387 872 +1036848.000166158 388112961 2509305 791 +1036849.000191883 402255979 2625283 799 ``` Network values with no time limit with a frequency of 1Hz. rxp and txp are the number of received and sent packets, while rxb and txp are the number of received and sent bytes. ```bash -$ ./mojitos -t 0 -f 1 -d enp0s25 -#timestamp rxp rxb txp txb -1036559.277376027 0 0 0 0 -1036560.000161101 4 581 2 179 -1036561.000083968 178 268675 55 4954 -1036562.000076162 11 1010 5 510 -1036563.000069724 17 1643 12 3602 -1036564.000113394 990 1493008 369 27299 +$ ./bin/mojitos -t 0 -f 1 -d enp0s25 +#timestamp rxp rxb txp txb +1036559.277376027 0 0 0 0 +1036560.000161101 4 581 2 179 +1036561.000083968 178 268675 55 4954 +1036562.000076162 11 1010 5 510 +1036563.000069724 17 1643 12 3602 +1036564.000113394 990 1493008 369 27299 ``` Overhead of the monitoring for RAPL and cpu_cycle ```bash -$ ./mojitos -t 5 -f 1 -p cpu_cycles -r -s -#timestamp cpu_cycles package-00 core0 dram0 overhead -1036988.197227391 162214 19898 4944 1586 149612 -1036989.000151326 332613664 2513116 379577 1115171 739573 -1036990.000116433 482150700 3321341 587218 1380673 315719 -1036991.000182835 525984292 3592582 691221 1385982 272182 -1036992.000165117 397678789 2770561 444030 1375729 510379 +$ ./bin/mojitos -t 5 -f 1 -p cpu_cycles -r -s +#timestamp cpu_cycles package-00 core0 dram0 overhead +1036988.197227391 162214 19898 4944 1586 149612 +1036989.000151326 332613664 2513116 379577 1115171 739573 +1036990.000116433 482150700 3321341 587218 1380673 315719 +1036991.000182835 525984292 3592582 691221 1385982 272182 +1036992.000165117 397678789 2770561 444030 1375729 510379 ``` + +## License + +MojitO/S is published under the GPL3 license and is part of the [Energumen Project](https://www.irit.fr/energumen/) + +<img src="https://www.irit.fr/energumen/images/energumen.png" width="100"> diff --git a/configure.sh b/configure.sh new file mode 100755 index 0000000000000000000000000000000000000000..21c94eff4ef28cab03761595e6473b29c80e8b2b --- /dev/null +++ b/configure.sh @@ -0,0 +1,188 @@ +#!/bin/sh + +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr> + +try() { "$@" || die "cannot $*"; } +die() { yell "$*"; exit 111; } +yell() { echo "$0: $*" >&2; } +echo() { printf '%s\n' "$*"; } +isnum() { + case "${1#[+-]}" in + *[!0-9]*|'') return 1 ;; + *) return 0 ;; + esac +} +dprint() { + for v in "$@"; do + decho "$v : $(eval "echo \$$v")" + done +} +decho() { + [ "$debug" = '1' ] && echo "$@" +} + +debug=0 +target_hdr=src/sensors.h +target_mk=sensors.mk + +nonsensor='counters_option|sensors|util' + +hdr_blacklist=$nonsensor +hdr_whitelist='' + +usage() { + printf -- 'Usage: %s [-la] [-e <sensor>] [-i <sensor>] [-u <sensor>]\n' "$(basename "$0")" >&2 + printf -- '-e | --exclude : exclude sensor, can be called multiple times\n' >&2 + printf -- '-i | --include : include sensor, can be called multiple times\n' >&2 + printf -- '-l | --list-sensors : list all sensors and exit\n' >&2 + printf -- '-u | --unique : only include the specified sensor\n' >&2 + printf -- ' if this option is used, any usage of `-e` or `-i` will be ignored\n' >&2 + printf -- '-a | --all : include all sensors, meant to be used only by the makefile\n' >&2 + exit 1 +} + +ls_sensors() { + try cd src + + [ -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$//' +} + +# gen_sensors_h(sensor, nb_sensors) +gen_sensors_h() { + sensors=$1 + nb_sensors=$2 + nb_sensor_opts=$( + for sensor in $sensors; do + sed -n 's/.*'"${sensor}"'_opt\[\([0-9]\+\)\].*/\1/p' "src/${sensor}.h" + done | + paste -s -d '+' + ) + nb_sensor_opts=$(eval "echo \$(($nb_sensor_opts))") + + dprint sensors >&2 + dprint nb_sensor_opts >&2 + isnum "$nb_sensor_opts" || die "could not get total number of sensors's command-line options" + + # gen includes + for sensor in $sensors; do + printf '#include "%s.h"\n' "$sensor" + done + printf '\n' + + printf '#define NB_SENSOR %d\n' "$nb_sensors" + printf '#define NB_SENSOR_OPT %d\n' "$nb_sensor_opts" + printf '\n' + + # gen `init_sensors()` + printf 'void init_sensors(Optparse *opts, Sensor *sensors, size_t len, size_t offset, int *nb_defined)\n{\n' + 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}; + ! + done + printf ' assert((offset + *nb_defined) <= len);\n' + printf '}\n' +} + +gen_sensors_mk() { + sensors=$1 + printf 'CAPTOR_OBJ = ' + for sensor in $sensors; do + printf '$(OBJ_DIR)/%s.o ' "$sensor" + done + printf '\n' +} + +detect_caps() { + [ -r /usr/include/linux/perf_event.h ] && hdr_whitelist=counters + [ -d /sys/class/infiniband ] && hdr_whitelist="${hdr_whitelist}|infiniband" + [ -r /proc/stat ] && hdr_whitelist="${hdr_whitelist}|load" + + 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 + + vendor=$(awk '/vendor_id/ {print $3; exit}' /proc/cpuinfo) + vendor_lc=$(echo "$vendor" | tr 'A-Z' 'a-z') + case $vendor_lc in + *intel*) + hdr_whitelist="${hdr_whitelist}|rapl" + ;; + *amd*) + family=$(awk '/cpu[ \t]*family/ {print $4; exit}' /proc/cpuinfo) + if isnum "$family"; then + [ $family -ge 17 ] && hdr_whitelist="${hdr_whitelist}|amd_rapl" + fi + ;; + *) + yell "unsupported processor vendor id: $vendor" + ;; + esac + + [ $(ls -1 /sys/class/hwmon | wc -l) -gt 0 ] && hdr_whitelist="${hdr_whitelist}|temperature" +} + +case $1 in +--all|-a) + all=1 + ;; +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 + +sensors=$(ls_sensors) +nb_sensors=$(echo "$sensors" | sed '/^$/d' | wc -l) + +if [ "$nb_sensors" -eq 0 ]; then + printf -- '0 sensors are selected. cannot build.\n' >&2 + exit 1 +fi + +try gen_sensors_h "$sensors" "$nb_sensors" > "$target_hdr" +try gen_sensors_mk "$sensors" > "$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/counters_group.c b/counters_group.c deleted file mode 100644 index bc9b448f818df565565416077a9d5788cfbadf62..0000000000000000000000000000000000000000 --- a/counters_group.c +++ /dev/null @@ -1,119 +0,0 @@ -/******************************************************* - Copyright (C) 2018-2021 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 <linux/perf_event.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <sys/ioctl.h> -#include <stdio.h> -#include <stdint.h> -#include <asm/unistd.h> - -#include "counters.h" - -struct _counter_t { - int nbcores; - int nbperf; - int *counters; -}; - -static long -perf_event_open(struct perf_event_attr *hw_event, pid_t pid, - int cpu, int group_fd, unsigned long flags) { - long res = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); - if (res == -1) { - perror("perf_event_open"); - fprintf(stderr, "Error opening leader %llx\n", hw_event->config); - exit(EXIT_FAILURE); - } - return res; -} - -counter_t init_counters(const int nb_perf, const __u32 *types, const __u64 *names) { - struct perf_event_attr pe; - struct _counter_t *counters = malloc(sizeof(struct _counter_t)); - - unsigned int nbcores = sysconf(_SC_NPROCESSORS_ONLN); - counters->nbcores=nbcores; - counters->nbperf=nb_perf; - memset(&pe, 0, sizeof(struct perf_event_attr)); - pe.size = sizeof(struct perf_event_attr); - pe.disabled = 1; - - pe.read_format = PERF_FORMAT_GROUP; - counters->counters = malloc((nbcores+1)*sizeof(int)); - - for (int core=0; core<nbcores; core++) { - counters->counters[core]=-1; - for(int idperf=0; idperf<nb_perf; idperf ++){ - pe.type = types[idperf]; - pe.config = names[idperf]; - int res=perf_event_open(&pe, -1, core, counters->counters[core], PERF_FLAG_FD_CLOEXEC); - if(counters->counters[core]==-1) - counters->counters[core]=res; - } - } - return counters; -} - -void clean_counters(counter_t counters) { - for(int core=0; core<counters->nbcores; core++) - close(counters->counters[core]); - free(counters->counters); - free(counters); -} - -void start_counters(counter_t counters) { - for(int core=0; core<counters->nbcores; core++) - ioctl(counters->counters[core], PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP); -} -void reset_counters(counter_t counters) { - for(int core=0; core<counters->nbcores; core++) - ioctl(counters->counters[core], PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP); -} - -struct read_format { - uint64_t nr; - struct { - uint64_t value; - } values[]; -}; - -void get_counters(counter_t counters, long long *values) { - int nb_perf = counters->nbperf; - size_t buffer_size = sizeof(uint64_t)*(1+nb_perf); - struct read_format* buffer = NULL; - if(buffer==NULL) - buffer = malloc(buffer_size); - - memset(values, 0, nb_perf*sizeof(long long)); - - for (int core=0; core<counters->nbcores; core++) { - if (-1 == read(counters->counters[core], buffer, buffer_size)) { - perror("PB Lecture resultat"); - exit(EXIT_FAILURE); - } - for(int idperf=0; idperf<=nb_perf; idperf++) { - values[idperf] += buffer->values[idperf].value; - } - } - reset_counters(counters); -} diff --git a/counters_individual.c b/counters_individual.c deleted file mode 100644 index 1437aa38442230ae0196dbb14e63c13a81c7b6d9..0000000000000000000000000000000000000000 --- a/counters_individual.c +++ /dev/null @@ -1,200 +0,0 @@ -/******************************************************* - Copyright (C) 2018-2021 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 <linux/perf_event.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <sys/ioctl.h> -#include <stdio.h> -#include <stdint.h> -#include <asm/unistd.h> -#include <stdint.h> - -struct _counter_t { - int nbcores; - int nbperf; - int **counters; - uint64_t *counters_values; - uint64_t *tmp_counters_values; - - int* perf_indexes; - -}; -typedef struct _counter_t* counter_t; - -#include "counters_option.h" - -void show_all_counters() { - for(int i=0; i<nb_counter_option;i++) - printf("%s\n", perf_static_info[i].name); -} - -void perf_type_key(__u32 **perf_type, __u64 **perf_key, int *indexes, int nb){ - *perf_type = malloc(nb*sizeof(__u32)); - *perf_key = malloc(nb*sizeof(__u64)); - for(int i=0; i<nb; i++) { - (*perf_key)[i] = perf_static_info[indexes[i]].perf_key; - (*perf_type)[i] = perf_static_info[indexes[i]].perf_type; - } -} -void perf_event_list(char *perf_string, int *nb_perf, int **perf_indexes) { - char *token; - *nb_perf=0; - *perf_indexes=NULL; - while((token=strtok(perf_string, ",")) != NULL) { - perf_string = NULL; - int i; - for(i=0; i<nb_counter_option; i++) { - if(strcmp(perf_static_info[i].name, token) == 0) { - (*nb_perf)++; - (*perf_indexes) = realloc(*perf_indexes, sizeof(int)*(*nb_perf)); - (*perf_indexes)[*nb_perf-1]=i; - break; - } - } - if(i == nb_counter_option) { - fprintf(stderr, "Unknown performance counter: %s\n", token); - exit(EXIT_FAILURE); - } - } -} - -static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, - int cpu, int group_fd, unsigned long flags) { - long res = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); - if (res == -1) { - perror("perf_event_open"); - fprintf(stderr, "Error opening leader %llx\n", hw_event->config); - exit(EXIT_FAILURE); - } - return res; -} - -counter_t _init_counters(const int nb_perf, const __u32 *types, const __u64 *names) { - struct perf_event_attr pe; - unsigned int nbcores = sysconf(_SC_NPROCESSORS_ONLN); - memset(&pe, 0, sizeof(struct perf_event_attr)); - pe.size = sizeof(struct perf_event_attr); - pe.disabled = 1; - - counter_t counters = malloc(sizeof(struct _counter_t)); - counters->nbperf = nb_perf; - counters->nbcores=nbcores; - counters->counters=malloc(nb_perf*sizeof(int*)); - for (int i=0; i<nb_perf; i++) { - pe.type = types[i]; - pe.config = names[i]; - counters->counters[i] = malloc(nbcores*sizeof(int)); - - for (int core=0; core<nbcores; core++) { - counters->counters[i][core] = perf_event_open(&pe, -1, core, -1, PERF_FLAG_FD_CLOEXEC); - } - } - return counters; -} - -void clean_counters(void *ptr) { - counter_t counters = (counter_t) ptr; - for(int counter=0; counter<counters->nbperf; counter++) { - for(int core=0; core<counters->nbcores; core++) - close(counters->counters[counter][core]); - free(counters->counters[counter]); - } - free(counters->counters); - free(counters->counters_values); - free(counters->tmp_counters_values); - free(counters->perf_indexes); - - free(counters); -} - -void start_counters(counter_t counters) { - for(int counter=0; counter<counters->nbperf; counter++) - for(int core=0; core<counters->nbcores; core++) - ioctl(counters->counters[counter][core], PERF_EVENT_IOC_ENABLE, 0); -} - -void reset_counters(counter_t counters) { - for(int counter=0; counter<counters->nbperf; counter++) - for(int core=0; core<counters->nbcores; core++) - ioctl(counters->counters[counter][core], PERF_EVENT_IOC_RESET, 0); -} - -void _get_counters(counter_t counters, uint64_t *values) { - for(int i=0; i<counters->nbperf; i++) { - uint64_t accu=0; - uint64_t count=0; - for (int core=0; core<counters->nbcores; core++) { - if (-1 == read(counters->counters[i][core], &count, sizeof(uint64_t))) { - fprintf(stderr, "Cannot read result"); - exit(EXIT_FAILURE); - } - accu += count; - } - values[i] = accu; - } -} - - - - - - -unsigned int init_counters(char* args, void **state) { - int nb_perf; - int* perf_indexes=NULL; - - perf_event_list(args, &nb_perf, &perf_indexes); - - __u32* perf_type; - __u64* perf_key; - perf_type_key(&perf_type, &perf_key, perf_indexes, nb_perf); - counter_t fd = _init_counters(nb_perf, perf_type, perf_key); - free(perf_type); - free(perf_key); - - fd->perf_indexes = perf_indexes; - fd->counters_values = malloc(nb_perf*sizeof(uint64_t)); - fd->tmp_counters_values = malloc(nb_perf*sizeof(uint64_t)); - start_counters(fd); - _get_counters(fd, fd->counters_values); - *state = (void*) fd; - - return nb_perf; -} - -unsigned int get_counters(uint64_t* results, void*ptr) { - counter_t state = (counter_t) ptr; - -_get_counters(state, state->tmp_counters_values); - for(int i=0; i<state->nbperf; i++) - results[i] = state->tmp_counters_values[i] - state->counters_values[i]; - - memcpy(state->counters_values, state->tmp_counters_values, state->nbperf * sizeof(uint64_t)); - return state->nbperf; -} - -void label_counters(char **labels, void*ptr) { - counter_t state = (counter_t) ptr; - for(int i=0; i<state->nbperf;i++) - labels[i] = perf_static_info[state->perf_indexes[i]].name; - -} diff --git a/counters_option.py b/counters_option.py deleted file mode 100755 index 877a33afbd61814acf3ddbb4c5311bd043c4ef37..0000000000000000000000000000000000000000 --- a/counters_option.py +++ /dev/null @@ -1,62 +0,0 @@ -#! /usr/bin/python3 - -# SPDX-License-Identifier: GPL-3.0-or-later -# Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr> - -linux_include = '/usr/include/linux/perf_event.h' - -string = """#include <linux/perf_event.h> - -typedef struct counter_option { - char *name; - __u32 perf_type; - __u64 perf_key; -} counter_option; - -static counter_option perf_static_info[] = {""" -print(string) - -nb = 0 - -black_list = ['stalled_cycles_frontend','stalled_cycles_backend', - 'cache_l1i', 'cache_op_write', 'cache_result_miss'] - -with open(linux_include, 'r') as infile: - mode = '' - for line in infile: - if 'perf_hw_id' in line: - mode = 'PERF_TYPE_HARDWARE' - elif 'perf_hw_cache_' in line: - mode = 'PERF_TYPE_HW_CACHE' - elif 'perf_sw_id' in line: - mode = 'PERF_TYPE_SOFTWARE' - elif 'PERF_COUNT_' in line and '=' in line: - perf_name = line.split()[0] - short_perf = perf_name[14:].lower() - if short_perf in black_list: - continue - if mode == 'PERF_TYPE_HW_CACHE': - for op_id, op_id_str in enumerate(['r', 'w', 'p']): - op_id_names = ['PERF_COUNT_HW_CACHE_OP_READ', 'PERF_COUNT_HW_CACHE_OP_WRITE', 'PERF_COUNT_HW_CACHE_OP_PREFETCH'] - for result_id, result_id_str in enumerate(['a', 'm']): - result_id_names = ['PERF_COUNT_HW_CACHE_RESULT_ACCESS', 'PERF_COUNT_HW_CACHE_RESULT_MISS'] - - res = '{ .name = "%s_%s_%s", .perf_type = %s, .perf_key = %s | (%s >> 8) | (%s >> 16) },' % ( - short_perf, op_id_str, result_id_str, - mode, - perf_name, - op_id_names[op_id], - result_id_names[result_id]) - - print(res) - nb += 1 - - else: - res = '{ .name = "'+short_perf+'", .perf_type = '+mode+', .perf_key = '+perf_name+'},' - print(res) - nb += 1 - - -print('};') - -print('static unsigned int nb_counter_option =',nb,';') diff --git a/doc/info_reader_ex.c b/doc/info_reader_ex.c new file mode 100644 index 0000000000000000000000000000000000000000..f1e6d96118f9bad951c516c5cb8392c5470141fb --- /dev/null +++ b/doc/info_reader_ex.c @@ -0,0 +1,140 @@ +/******************************************************* + 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/>. + +*******************************************************/ + + +// ~/mojitos/doc/$ gcc -Wall -Wextra -Wpedantic -O3 -o info_reader_ex info_reader_ex.c ./../src/util.c && ./info_reader_ex +#include "./../lib/info_reader.h" + +#define MAX_PROCS 64 + +// -----------------------------The storage +typedef struct { + unsigned int processor; + char *vendor_id; + unsigned int family; + unsigned int core_id; + unsigned int physical_id; + char *model_name; +} Cpu; + +// --------------IMPLEMENTING THE INTERFACE + +// -- Define the behaviour if the attempted value is an int +GenericPointer int_allocator(char *s) +{ + unsigned int value = atoi(s); + return (GenericPointer) value; +} + +// -- Define the behaviour if the attempted value is a string +GenericPointer string_allocator(char *s) +{ + char *value = malloc(strlen(s) + 1); + strcpy(value, s); + return (GenericPointer) value; +} + +// -- Define the processor setter +void set_processor(GenericPointer storage, GenericPointer data) +{ + Cpu *cpu = (Cpu *) storage; + cpu->processor = data; +} + +// -- Define the vendor_id setter +void set_vendor_id(GenericPointer storage, GenericPointer data) +{ + Cpu *cpu = (Cpu *) storage; + cpu->vendor_id = (char *) data; +} + +// -- Define the family setter +void set_family(GenericPointer storage, GenericPointer data) +{ + Cpu *cpu = (Cpu *) storage; + cpu->family = data; +} + +// -- Define the core_id setter +void set_core_id(GenericPointer storage, GenericPointer data) +{ + Cpu *cpu = (Cpu *) storage; + cpu->core_id = data; +} + +// -- Define the physical_id setter +void set_physical_id(GenericPointer storage, GenericPointer data) +{ + Cpu *cpu = (Cpu *) storage; + cpu->physical_id = data; +} + +// -- Define the model_name setter +void set_model_name(GenericPointer storage, GenericPointer data) +{ + Cpu *cpu = (Cpu *) storage; + cpu->model_name = (char *) data; +} + +int main() +{ + Cpu cpus[MAX_PROCS]; + + // -- Define the setter, the allocator for each key / separator. + KeyFinder keys[] = { + {.key = "processor", .delimiter = ": ", .copy = (CopyAllocator *) int_allocator, .set = (Setter *)set_processor}, + {.key = "vendor_id", .delimiter = ": ", .copy = (CopyAllocator *) string_allocator, .set = (Setter *)set_vendor_id}, + {.key = "cpu family", .delimiter = ": ", .copy = (CopyAllocator *) int_allocator, .set = (Setter *)set_family}, + {.key = "core id", .delimiter = ": ", .copy = (CopyAllocator *) int_allocator, .set = (Setter *)set_core_id}, + {.key = "physical id", .delimiter = ": ", .copy = (CopyAllocator *) int_allocator, .set = (Setter *)set_physical_id}, + {.key = "model name", .delimiter = ": ", .copy = (CopyAllocator *) string_allocator, .set = (Setter *)set_model_name} + }; + + size_t nb_keys = sizeof(keys)/sizeof(KeyFinder); + + // -- Init the parser + Parser parser = { + .storage = (GenericPointer) cpus, + .capacity = MAX_PROCS, + .storage_struct_size = sizeof(Cpu), + .keys = keys, + .nb_keys = nb_keys, + .file = fopen("/proc/cpuinfo", "r") + }; + + // -- Parse the file + parse(&parser); + + // Print and free the results + for (unsigned int i = 0; i < parser.nb_stored; ++i) { + printf("========== PROC[%d] ==========\n", i); + printf("Processor: %u\n", cpus[i].processor); + printf("Vendor ID: %s\n", cpus[i].vendor_id); + printf("Family: %u\n", cpus[i].family); + printf("Core ID: %u\n", cpus[i].core_id); + printf("Physical ID: %u\n", cpus[i].physical_id); + printf("Model Name: %s\n", cpus[i].model_name); + free(cpus[i].vendor_id); + free(cpus[i].model_name); + } + printf("==============================\n"); + return 0; +} + diff --git a/doc/mojitos.pre.1 b/doc/mojitos.pre.1 new file mode 100644 index 0000000000000000000000000000000000000000..af9cb9bc5681422a8af870eead9cea78e88b0504 --- /dev/null +++ b/doc/mojitos.pre.1 @@ -0,0 +1,81 @@ +.Dd January 9, 2023 +.Dt MOJITOS 1 +.Os +.Sh NAME +.Nm mojitos +.Nd An open source system monitoring tool. +.Sh SYNOPSIS +.Nm mojitos +.Op Ar OPTIONS +.Op Ar SENSOR ... +.Op Fl e Ar cmd ... +.Sh DESCRIPTION +.Nm +is a monitoring tool with a multitude of sensors that does measurements at the OS level. +.Nm +runs on GNU/Linux. +USAGE +.Sh EXIT STATUS +.Ex +.Sh EXAMPLES +RAPL values during 2 seconds with a frequency of 2Hz: +.Bd -literal -offset indent +$ mojitos -t 2 -f 2 -r +#timestamp package-00 core0 dram0 +1036389.135659868 10986 2869 1526 +1036389.500183551 1291440 255736 515562 +1036390.000754048 1333553 228393 689513 +1036390.500113978 1581967 267944 701536 +.Ed +.Pp +Performance counters (cpu_cycle, cache_ll_r_a and page_faults) during 4 seconds +with a frequency of 1Hz. +In the context of cache performance counters (as with "cache_ll_r_a"), + _r and _w suffixes mean respectively read and write, +while the _a, _m and _p ones mean respectively access, miss and pending. +.Pp +.Bd -literal -offset indent +$ ./mojitos -t 4 -f 1 -p cpu_cycles,cache_ll_r_a,page_faults +#timestamp cpu_cycles cache_ll page_faults +1036846.351749455 571199 1232 0 +1036847.001098880 348173344 2451387 872 +1036848.000166158 388112961 2509305 791 +1036849.000191883 402255979 2625283 799 +.Ed +.Pp +Network values with no time limit with a frequency of 1Hz. +rxp and txp are the number of received and sent packets, while rxb and txp are +the number of received and sent bytes. +.Pp +.Bd -literal -offset indent +$ ./mojitos -t 0 -f 1 -d enp0s25 +#timestamp rxp rxb txp txb +1036559.277376027 0 0 0 0 +1036560.000161101 4 581 2 179 +1036561.000083968 178 268675 55 4954 +1036562.000076162 11 1010 5 510 +1036563.000069724 17 1643 12 3602 +1036564.000113394 990 1493008 369 27299 +.Ed +.Pp +overhead of the monitoring when using RAPL and the cpu_cycle performance counter: +.Pp +.Bd -literal -offset indent +$ ./mojitos -t 5 -f 1 -p cpu_cycles -r -s +#timestamp cpu_cycles package-00 core0 dram0 overhead +1036988.197227391 162214 19898 4944 1586 149612 +1036989.000151326 332613664 2513116 379577 1115171 739573 +1036990.000116433 482150700 3321341 587218 1380673 315719 +1036991.000182835 525984292 3592582 691221 1385982 272182 +1036992.000165117 397678789 2770561 444030 1375729 510379 +.Ed +.Sh SEE ALSO +.Xr perf 1 , +.Xr lscpu 1 , +.Xr sysfs 5 , +.Xr proc 5 +.Sh LICENSE +MojitO/S is published under the GPL3 license and is part of the +.Lk https://www.irit.fr/energumen/ [Energumen Project] +.Sh BUGS +Yes. diff --git a/doc/sensor_ex.c b/doc/sensor_ex.c new file mode 100644 index 0000000000000000000000000000000000000000..5fc8eeb559a618e3a30b0354dd94860e2a2b0c21 --- /dev/null +++ b/doc/sensor_ex.c @@ -0,0 +1,124 @@ +/******************************************************* + Copyright (C) 2018-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/>. + + *******************************************************/ + +/* + * compilation: + * + +# normally, this part is done with `configure.sh`, but here we need to +# do this manually +cat <<EOF > src/sensors.h +#include "sensor_ex.h" + +#define NB_SENSOR 1 +#define NB_SENSOR_OPT 1 + +void init_sensors(Optparse *opts, Sensor *sensors, size_t len, size_t offset, int *nb_defined) +{ + int opt_idx = offset; + for (int i = 0; i < sensor_ex.nb_opt; i++) { + opts[opt_idx++] = sensor_ex_opt[i]; + } + sensors[(*nb_defined)++] = sensor_ex; + assert((offset + *nb_defined) <= len); +} +EOF + +# idem +echo 'CAPTOR_OBJ = doc/sensor_ex.o' > sensors.mk + +# actual compilation here +gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og -c doc/sensor_ex.c -o obj/sensor_ex.o +gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og -c doc/util.c -o obj/util.o +gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og -c doc/mojitos.c -o obj/mojitos.o +gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og obj/util.o obj/mojitos.o obj/sensor_ex.o -o bin/mojitos + + * +**/ + +#include <stdint.h> +#include <stdlib.h> + +#include "util.h" + +#define NB_SENSOR 3 + +struct Accumulator { + int v[NB_SENSOR]; +}; +typedef struct Accumulator Accumulator; + +void _get_acc(int v[NB_SENSOR]) +{ + for (int i = 0; i < NB_SENSOR; i++) { + v[i]++; + } +} + +unsigned int init_acc(char *none, void **ptr) +{ + /* "none" refers to an optionnal command-line argument */ + /* there is none in this case, so this parameter is not used */ + UNUSED(none); + + Accumulator *state = malloc(sizeof(Accumulator)); + + for (int i = 0; i < NB_SENSOR; i++) { + state->v[i] = -1; + } + + *ptr = (void *)state; + _get_acc(state->v); + + return NB_SENSOR; +} + +unsigned int get_acc(uint64_t *results, void *ptr) +{ + Accumulator *state = (Accumulator *)ptr; + + _get_acc(state->v); + + for (int i = 0; i < NB_SENSOR; i++) { + results[i] = state->v[i]; + } + + return NB_SENSOR; +} + +char *_labels_accumulator[NB_SENSOR] = {"acc1", "acc2", "acc3"}; +void label_acc(char **labels, void *none) +{ + UNUSED(none); + for (int i = 0; i < NB_SENSOR; i++) { + labels[i] = _labels_accumulator[i]; + } +} + +void clean_acc(void *ptr) +{ + Accumulator *state = (Accumulator *)ptr; + + if (state == NULL) { + return; + } + + free(state); +} diff --git a/doc/sensor_ex.h b/doc/sensor_ex.h new file mode 100644 index 0000000000000000000000000000000000000000..9fd95d3e0f32b0013ca8115edb36f82a9203da69 --- /dev/null +++ b/doc/sensor_ex.h @@ -0,0 +1,46 @@ +/******************************************************* + Copyright (C) 2018-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/>. + + *******************************************************/ + +/* + * Example of a basic sensor: 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 sensor_ex = { + .init = init_acc, + .get = get_acc, + .clean = clean_acc, + .label = label_acc, + .nb_opt = 1, +}; + +Optparse sensor_ex_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/test_file_ex.c b/doc/test_file_ex.c new file mode 100644 index 0000000000000000000000000000000000000000..0595e9a8c41cbfa349d139c0dcb96df2c4f2716a --- /dev/null +++ b/doc/test_file_ex.c @@ -0,0 +1,158 @@ +/******************************************************* + 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 of the test library +#include "./../tests/small_test.h" +// Include the file that contains the function you want to test +// #include "a_file.c" + +// This example is divided into three parts: +// 1. SOME SIMPLE EXAMPLES +// 2. A MORE COMPLEX EXAMPLE +// 3. FILE ENTRY POINT + +// -----------------1. SOME SIMPLE EXAMPLES + +// A simple function that should pass +// test_should_pass is the name of the function +// {} is the function code +// Use one of the define TEST_XXX defined in small_test.h +TFUNCTION(test_should_pass, { + int result = 42; + int expected = 42; + TEST_INT(&result, &expected); +}) + +// A simple function that should pass +// test_should_fail is the name of the function +// {} is the function code +// Use one of the define TEST_XXX defined in small_test.h +TFUNCTION(test_should_fail, { + char *result = "a fail"; + char *expected = "a nice fail"; + TEST_STR(result, expected); +}) + +// A simple test on an array +// test_array is the name of the function +// {} is the function code +// Use one of the define TEST_XXX defined in small_test.h +TFUNCTION(test_array, { + static unsigned int size = 10; + int results[10] = {0}; + int expecteds[10] = {0}; + for (unsigned int i = 0; i < size; i++) + { + TEST_INT(&results[i], &expecteds[i]); + } +}) + +// See "TFILE_ENTRY_POINT" for how to call these functions. + +// ------------2. THE MORE COMPLEX EXAMPLE +// This example shows how to add a new type to the framework + +// The type that you want to add : +typedef struct { + int simple_int; + char simple_str[20]; +} UserType; + + +// The test framework needs two functions : + +// A function that compares two values. +int usertype_compare(void *ptr1, void *ptr2) +{ + return memcmp(ptr1, ptr2, sizeof(UserType)) == 0; +} + +// A function to format a value. +char *usertype_format(char buffer[FMT_BUFFER_SIZE], void *ptr) +{ + UserType *x = (UserType *) ptr; + snprintf(buffer, FMT_BUFFER_SIZE, "UserType {simple_int: %d, simple_str: %s}", x->simple_int, x->simple_str); + return buffer; +} + +// Store the functions in the interface type. +static const TestInterface usertype_interface = { + .compare = usertype_compare, + .format = usertype_format +}; + +// Create a macro that implements the interface by passing the interface created to the TEST_INTERFACE macro. +#define TEST_USER_TYPE(__result, __expected) \ + TEST_INTERFACE(__result, __expected, &usertype_interface) + + +// Creating a constructor is recommended (C can have problems parsing macros where "," is used). +#define DUMMY_USER_TYPE(__user, __simple_int, __simple_str) \ + do { \ + __user = (UserType) { \ + .simple_int = __simple_int, \ + .simple_str = __simple_str \ + }; \ +} while (0); + +// Now you can write test for your new type +// all you have to do is call the macro that implements "TEST_INTERFACE". +// here it's "TEST_USERTYPE" +TFUNCTION(test_user_type, { + UserType result; + UserType expected; + + DUMMY_USER_TYPE(result, 1, "John Doe"); + DUMMY_USER_TYPE(expected, 1, "John Doe"); + + TEST_USER_TYPE(&result, &expected); +}) + +// Now you can write test for your new type +// all you have to do is call the macro that implements "TEST_INTERFACE". +// here it's "TEST_USERTYPE" +TFUNCTION(test_array_user_type, { + UserType results[1]; + UserType expecteds[1]; + + DUMMY_USER_TYPE(results[0], 1, "John Doe"); + DUMMY_USER_TYPE(expecteds[0], 1, "John Doe"); + + for (unsigned int i = 0; i < 1; i++) + { + TEST_USER_TYPE(&results[i], &expecteds[i]); + } +}) + +// ---------------------3. FILE ENTRY POINT + +// Define the entry point of a test file +// Use the macro "CALL_TFUNCTION" where the name of a function is defined with the macro "TFUNCTION". +TFILE_ENTRY_POINT(test_file_ex, { + // Call the simple examples + CALL_TFUNCTION(test_should_pass); + CALL_TFUNCTION(test_should_fail); + CALL_TFUNCTION(test_array); + + // Call the more complex example + CALL_TFUNCTION(test_user_type); + CALL_TFUNCTION(test_array_user_type); +}) + diff --git a/doc/test_main_ex.c b/doc/test_main_ex.c new file mode 100644 index 0000000000000000000000000000000000000000..a5e7841eed202c9a4dd7a7dcad500619fcdaeb30 --- /dev/null +++ b/doc/test_main_ex.c @@ -0,0 +1,36 @@ +/******************************************************* + 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/>. + +*******************************************************/ + +// ~/mojitos/doc$ gcc -Wall -Wextra -Wpedantic -o test_main_ex test_main_ex.c ./../src/util.c +// Include of the test library +#include "./../tests/small_test.h" + +// Include the *.c files containing the tests +#include "./test_file_ex.c" +// #include "./test_another_test_file.c" + +// Define the entry point of the programme +TMAIN({ + // Must contain the call to the entry point functions of each file + // USE CALL_TFUNCTION(my_function) instead of my_function() + CALL_TFUNCTION(test_file_ex); + // CALL_TFUNCTION(another_file_entry_point_function); +}) + diff --git a/frapl.c b/frapl.c deleted file mode 100644 index 433a28c13ac5ecd847c95935fb3fa0e8f7131935..0000000000000000000000000000000000000000 --- a/frapl.c +++ /dev/null @@ -1,161 +0,0 @@ -/******************************************************* - Copyright (C) 2022-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 <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <inttypes.h> - - -#define MAX_HEADER 128 - -char *get_frapl_string(const char *filename) { - int fd = open(filename, O_RDONLY); - if( fd == -1) - return NULL; - char *result = malloc(MAX_HEADER); - int nb = read(fd, result, MAX_HEADER); - close(fd); - result[nb-1] = 0; - return (result); -} - -void test_append(char* name, int i) { - //char last = name[strlen(name)-1]; - //if (last>='0' && last <= '9') - // return; - sprintf(name+strlen(name), "%d", i); -} - - -struct _frapl_t { - unsigned int nb; - char **names; - int *fids; - uint64_t* values; - uint64_t* tmp_values; - -}; -typedef struct _frapl_t _frapl_t; - - -void add_frapl_source(_frapl_t* rapl, char*name, char*energy_uj) { - rapl->nb += 1; - rapl->names = realloc(rapl->names, sizeof(char**)*rapl->nb); - rapl->fids = realloc(rapl->fids, sizeof(int*)*rapl->nb); - - rapl->names[rapl->nb-1] = malloc(strlen(name)+1); - strcpy(rapl->names[rapl->nb-1], name); - //printf("%s\n", energy_uj); - - rapl->fids[rapl->nb-1] = open(energy_uj, O_RDONLY); -} - - -void _get_frapl(uint64_t *values, _frapl_t* rapl) { - static char buffer[512]; - - for (int i = 0; i < rapl->nb; i++) { - - pread(rapl->fids[i], buffer, 100, 0); - values[i] = strtoull(buffer, NULL, 10); - } -} - - -unsigned int init_frapl(char* none, void **ptr) { - _frapl_t *rapl = malloc(sizeof(_frapl_t)); - rapl->nb = 0; - rapl->names = NULL; - rapl->fids = NULL; - - char buffer[1024]; - char *name_base = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:%d/%s"; - char *name_sub = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:%d/intel-rapl:%d:%d/%s"; - - for (int i=0;; i++) { - sprintf(buffer, name_base, i, "name"); - char *tmp = get_frapl_string(buffer); - if (tmp == NULL) break; - //printf("%s\n", tmp); - test_append(tmp, i); - //printf("%s -> %s\n", buffer, tmp); - - sprintf(buffer, name_base, i, "energy_uj"); - add_frapl_source(rapl, tmp, buffer); - free(tmp); - - for (int j=0;; j++) { - sprintf(buffer, name_sub, i, i, j, "name"); - char *tmp_sub = get_frapl_string(buffer); - if (tmp_sub == NULL) break; - //printf("%s\n", tmp_sub); - test_append(tmp_sub, i); - //printf("%s -> %s\n", buffer, tmp_sub); - - - sprintf(buffer, name_sub, i, i, j, "energy_uj"); - add_frapl_source(rapl, tmp_sub, buffer); - - free(tmp_sub); - } - } - - rapl->values = calloc(sizeof(uint64_t), rapl->nb); - rapl->tmp_values = calloc(sizeof(uint64_t), rapl->nb); - - _get_frapl(rapl->values, rapl); - - *ptr = (void*)rapl; - return rapl->nb; -} - - -unsigned int get_frapl(uint64_t* results, void* ptr) { - _frapl_t* state = (_frapl_t*) ptr; - _get_frapl(state->tmp_values, state); - for(int i=0; i<state->nb; i++) - results[i] = state->tmp_values[i] - state->values[i]; - - memcpy(state->values, state->tmp_values, sizeof(uint64_t)*state->nb); - return state->nb; -} - -void clean_frapl(void *ptr) { - _frapl_t* rapl = (_frapl_t*) ptr; - for(int i=0; i<rapl->nb; i++) { - free(rapl->names[i]); - close(rapl->fids[i]); - } - free(rapl->names); - free(rapl->fids); - free(rapl->values); - free(rapl->tmp_values); - free(rapl); -} - - -void label_frapl(char **labels, void *ptr) { - _frapl_t* rapl = (_frapl_t*) ptr; - for(int i=0; i<rapl->nb; i++) - labels[i] = rapl->names[i]; -} diff --git a/infiniband.c b/infiniband.c deleted file mode 100644 index 49dca077f6f8552b5707a75c4ad5bc2ffa2da462..0000000000000000000000000000000000000000 --- a/infiniband.c +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************* - Copyright (C) 2018-2021 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 <stdlib.h> -#include <fcntl.h> -#include <stdio.h> -#include <string.h> - -#include <glob.h> - -#include <stdint.h> - -struct network_t { - uint64_t values[4]; - uint64_t tmp_values[4]; - int sources[4]; -}; -unsigned int _get_network(uint64_t* results, int* sources); - - -unsigned int init_infiniband(char* infi_path, void**ptr) { - if(infi_path==NULL) - return 0; - - if(strcmp(infi_path,"X")==0) { - - glob_t res; - - glob("/sys/class/infiniband/*/ports/*/counters/", 0, NULL, &res); - if(res.gl_pathc == 0) - return 0; - infi_path = res.gl_pathv[0]; - } - - char *filenames[] = {"%s/port_rcv_packets", - "%s/port_rcv_data", - "%s/port_xmit_packets", - "%s/port_xmit_data"}; - - struct network_t *state = malloc(sizeof(struct network_t)); - - char buffer[1024]; - for(int i=0; i<4; i++) { - sprintf(buffer, filenames[i], infi_path); - state->sources[i] = open(buffer, O_RDONLY); - } - - *ptr = (void*) state; - _get_network(state->values, state->sources); - - return 4; -} - -char *_labels_infiniband[4] = {"irxp", "irxb", "itxp", "itxb"}; -void label_infiniband(char **labels, void*none) { - for(int i=0; i<4; i++) - labels[i] = _labels_infiniband[i]; -} diff --git a/infiniband.h b/infiniband.h deleted file mode 100644 index af8b2b6d0993a138dee43e4aa7aa8cd71fad661d..0000000000000000000000000000000000000000 --- a/infiniband.h +++ /dev/null @@ -1,22 +0,0 @@ -/******************************************************* - Copyright (C) 2018-2019 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_infiniband(char* infi_path, void**ptr); -void label_infiniband(char **labels, void*); diff --git a/lib/info_reader.h b/lib/info_reader.h new file mode 100644 index 0000000000000000000000000000000000000000..ec28db62867db4c1b9f64d3e61008dc418fd1bc1 --- /dev/null +++ b/lib/info_reader.h @@ -0,0 +1,243 @@ +/******************************************************* + 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/>. + +*******************************************************/ + +#ifndef _INFO_READER_H +#define _INFO_READER_H + +#include <string.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +/** + * @struct Parser + * @brief The parser struct + * The struct containing all the necessary informations and functions to parse a file + * + * @var storage : GenericPointer : pointer to the storage where the parsed data will be stored + * @var nb_stored : unsigned int : the number of struct stored + * @var capacity : unsigned int : the maximum number of struct that can be stored + * @var storage_struct_size : size_t : the size of the struct stored in the storage + * @var keys : KeyFinder* : pointer to an array of KeyFinder containing the possible keys + * @var nb_keys : unsigned int : number of key finders + * @var file : FILE* : pointer to the file that will be parsed +*/ +typedef struct Parser Parser; + +/** + * @struct KeyFinder + * @brief The key finder struct + * The struct containing all the necessary informations and functions to find a key in a line of text + * + * @var key : char* : the key to be found + * @var delimiter : char* : the delimiter between the key and the value + * @var copy : CopyAllocator*: the function to use to make a copy of the value + * @var set : Setter*: the function to use to store the value in the storage +*/ +typedef struct KeyFinder KeyFinder; + +/** + * @brief Split a string into a key and value based on a specified delimiter. + * + * The function takes a string and splits it into two parts: a key and a value. + * The parts are determined by a specified delimiter. + * The key and value are returned through output parameters. + * If the delimiter is not found in the input string, the key and value pointers + * will be set to NULL. + * + * @param[in] string The input string to be split, the string is modified. + * @param[in] delimiter The delimiter string. + * @param[out] key A pointer to a char pointer where the key will be stored. + * @param[out] value A pointer to a char pointer where the value will be stored. + * @return None. + */ +static void split_on_delimiter(char *string, const char *delimiter, char **key, char **value); + +/** + * @brief Replace the first occurrence of a character in a string with another character. + * + * The function takes a string and two characters as input, and replaces the first + * occurrence of the first character in the string with the second character. + * + * @param[in,out] string The input string where the replacement should take place. + * @param[in] from The character to be replaced. + * @param[in] to The character to replace with. + * @return None. + */ +static void replace_first(char *string, char from, char to); + +/** + * @brief Check if a string starts with a prefix. + * + * @param[in] prefix The prefix to check. + * @param[in] string The string to check. + * @return true The string starts with the prefix. + * @return false The string does not start with the prefix or one of the input pointers is NULL. + */ +static bool start_with(const char *prefix, const char *string); + +/** + * @brief Matches a line of text to a key in the parser's list of keys. + * + * @param[in] parser Pointer to the Parser struct. + * @param[in] line Line of text to match. + * @param[out] key_finder Pointer to a KeyFinder pointer where the matched key will be stored. + * @param[out] raw_value Pointer to a char pointer where the value associated with the matched key will be stored. + * + * @return Returns 1 if a key is matched, 0 otherwise. + */ +static unsigned int match(Parser *parser, char *line, KeyFinder **key_finder, char **raw_value); + +typedef size_t GenericPointer; +typedef GenericPointer (CopyAllocator) (char *string); +typedef void (Setter) (GenericPointer storage, GenericPointer value); + +struct KeyFinder { + char *key; + char *delimiter; + + CopyAllocator *copy; + Setter *set; +}; + +struct Parser { + GenericPointer storage; + unsigned int nb_stored; + unsigned int capacity; + size_t storage_struct_size; + + KeyFinder *keys; + unsigned int nb_keys; + + FILE *file; +}; + +static void set_value(Parser *parser, KeyFinder *key_finder, char *raw_value) +{ + GenericPointer address = parser->storage + (parser->storage_struct_size * parser->nb_stored); + GenericPointer value = key_finder->copy(raw_value); + key_finder->set(address, value); +} + +static unsigned int match(Parser *parser, char *line, KeyFinder **key_finder, char **raw_value) +{ + for (unsigned int i = 0; i < parser->nb_keys; i++) { + KeyFinder *finder = &parser->keys[i]; + + if (start_with(finder->key, line)) { + char *value = NULL; + char *key = NULL; + + split_on_delimiter(line, finder->delimiter, &key, &value); + if ( key == NULL || value == NULL) { + return 0; + } + *key_finder = finder; + *raw_value = value; + return 1; + } + } + return 0; +} + +static unsigned int move_to_next(Parser *parser) +{ + parser->nb_stored += 1; + if (parser->nb_stored >= parser->capacity) { + return 0; + } + return 1; +} + +static unsigned int parse(Parser *parser) +{ + char *line = NULL; + size_t len = 0; + ssize_t read; + unsigned int key_assigned = 0; + + while ((read = getline(&line, &len, parser->file)) != -1) { + if (key_assigned == parser->nb_keys && read > 1) { + continue; + } else if (read == 1) { + if (!move_to_next(parser)) { + return 0; + } + key_assigned = 0; + } else { + KeyFinder *key_finder = NULL; + char *raw_value = NULL; + replace_first(line, '\n', '\0'); + if (match(parser, line, &key_finder, &raw_value)) { + set_value(parser, key_finder, raw_value); + ++key_assigned; + } + } + } + if (key_assigned > 0) { + parser->nb_stored++; + } + free(line); + return 1; +} + + + +static void replace_first(char *string, char from, char to) +{ + for (int i = 0; string[i] != '\0'; i++) { + if (string[i] == from) { + string[i] = to; + break; + } + } +} + +static void split_on_delimiter(char *string, const char *delimiter, char **key, char **value) +{ + *key = NULL; + *value = NULL; + size_t delimiter_len = strlen(delimiter); + char *start_delimiter = strstr(string, delimiter); + if (start_delimiter != NULL) { + *start_delimiter = '\0'; + *key = string; + *value = start_delimiter + delimiter_len; + } +} + +static bool start_with(const char *prefix, const char *string) +{ + if (prefix == NULL || string == NULL) { + return false; + } + + size_t prefix_len = strlen(prefix); + size_t string_len = strlen(string); + + if (string_len < prefix_len) { + return false; + } else { + return memcmp(prefix, string, prefix_len) == 0; + } +} + +#endif + diff --git a/lib/optparse.h b/lib/optparse.h new file mode 100644 index 0000000000000000000000000000000000000000..ee7dcb6c967fc158df36b4a844a13d5f7b6746ac --- /dev/null +++ b/lib/optparse.h @@ -0,0 +1,392 @@ +/* Optparse --- portable, reentrant, embeddable, getopt-like option parser + * + * This is free and unencumbered software released into the public domain. + * + * To get the implementation, define OPTPARSE_IMPLEMENTATION. + * Optionally define OPTPARSE_API to control the API's visibility + * and/or linkage (static, __attribute__, __declspec). + * + * The POSIX getopt() option parser has three fatal flaws. These flaws + * are solved by Optparse. + * + * 1) Parser state is stored entirely in global variables, some of + * which are static and inaccessible. This means only one thread can + * use getopt(). It also means it's not possible to recursively parse + * nested sub-arguments while in the middle of argument parsing. + * Optparse fixes this by storing all state on a local struct. + * + * 2) The POSIX standard provides no way to properly reset the parser. + * This means for portable code that getopt() is only good for one + * run, over one argv with one option string. It also means subcommand + * options cannot be processed with getopt(). Most implementations + * provide a method to reset the parser, but it's not portable. + * Optparse provides an optparse_arg() function for stepping over + * subcommands and continuing parsing of options with another option + * string. The Optparse struct itself can be passed around to + * subcommand handlers for additional subcommand option parsing. A + * full reset can be achieved by with an additional optparse_init(). + * + * 3) Error messages are printed to stderr. This can be disabled with + * opterr, but the messages themselves are still inaccessible. + * Optparse solves this by writing an error message in its errmsg + * field. The downside to Optparse is that this error message will + * always be in English rather than the current locale. + * + * Optparse should be familiar with anyone accustomed to getopt(), and + * it could be a nearly drop-in replacement. The option string is the + * same and the fields have the same names as the getopt() global + * variables (optarg, optind, optopt). + * + * Optparse also supports GNU-style long options with optparse_long(). + * The interface is slightly different and simpler than getopt_long(). + * + * By default, argv is permuted as it is parsed, moving non-option + * arguments to the end. This can be disabled by setting the `permute` + * field to 0 after initialization. + */ +#ifndef OPTPARSE_H +#define OPTPARSE_H + +#ifndef OPTPARSE_API +# define OPTPARSE_API +#endif + +struct optparse { + char **argv; + int permute; + int optind; + int optopt; + char *optarg; + char errmsg[64]; + int subopt; +}; + +enum optparse_argtype { + OPTPARSE_NONE, + OPTPARSE_REQUIRED, + OPTPARSE_OPTIONAL +}; + +struct optparse_long { + const char *longname; + int shortname; + enum optparse_argtype argtype; + char *usage_arg; + char *usage_msg; + void *(*fn)(void *, size_t); /* SUBJECT TO CHANGE */ +}; + +/** + * Initializes the parser state. + */ +OPTPARSE_API +void optparse_init(struct optparse *options, char **argv); + +/** + * Read the next option in the argv array. + * @param optstring a getopt()-formatted option string. + * @return the next option character, -1 for done, or '?' for error + * + * Just like getopt(), a character followed by no colons means no + * argument. One colon means the option has a required argument. Two + * colons means the option takes an optional argument. + */ +OPTPARSE_API +int optparse(struct optparse *options, const char *optstring); + +/** + * Handles GNU-style long options in addition to getopt() options. + * This works a lot like GNU's getopt_long(). The last option in + * longopts must be all zeros, marking the end of the array. The + * longindex argument may be NULL. + */ +OPTPARSE_API +int optparse_long(struct optparse *options, + const struct optparse_long *longopts, + int *longindex); + +/** + * Used for stepping over non-option arguments. + * @return the next non-option argument, or NULL for no more arguments + * + * Argument parsing can continue with optparse() after using this + * function. That would be used to parse the options for the + * subcommand returned by optparse_arg(). This function allows you to + * ignore the value of optind. + */ +OPTPARSE_API +char *optparse_arg(struct optparse *options); + +/* Implementation */ +#ifdef OPTPARSE_IMPLEMENTATION + +#define OPTPARSE_MSG_INVALID "invalid option" +#define OPTPARSE_MSG_MISSING "option requires an argument" +#define OPTPARSE_MSG_TOOMANY "option takes no arguments" + +static int optparse_error(struct optparse *options, const char *msg, const char *data) +{ + unsigned p = 0; + const char *sep = " -- '"; + while (*msg) + options->errmsg[p++] = *msg++; + while (*sep) + options->errmsg[p++] = *sep++; + while (p < sizeof(options->errmsg) - 2 && *data) + options->errmsg[p++] = *data++; + options->errmsg[p++] = '\''; + options->errmsg[p++] = '\0'; + return '?'; +} + +OPTPARSE_API +void optparse_init(struct optparse *options, char **argv) +{ + options->argv = argv; + options->permute = 1; + options->optind = argv[0] != 0; + options->subopt = 0; + options->optarg = 0; + options->errmsg[0] = '\0'; +} + +static int optparse_is_dashdash(const char *arg) +{ + return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0'; +} + +static int optparse_is_shortopt(const char *arg) +{ + return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0'; +} + +static int optparse_is_longopt(const char *arg) +{ + return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0'; +} + +static void optparse_permute(struct optparse *options, int index) +{ + char *nonoption = options->argv[index]; + int i; + for (i = index; i < options->optind - 1; i++) + options->argv[i] = options->argv[i + 1]; + options->argv[options->optind - 1] = nonoption; +} + +static int optparse_argtype(const char *optstring, char c) +{ + int count = OPTPARSE_NONE; + if (c == ':') + return -1; + for (; *optstring && c != *optstring; optstring++); + if (!*optstring) + return -1; + if (optstring[1] == ':') + count += optstring[2] == ':' ? 2 : 1; + return count; +} + +OPTPARSE_API +int optparse(struct optparse *options, const char *optstring) +{ + int type; + char *next; + char *option = options->argv[options->optind]; + options->errmsg[0] = '\0'; + options->optopt = 0; + options->optarg = 0; + if (option == 0) { + return -1; + } else if (optparse_is_dashdash(option)) { + options->optind++; /* consume "--" */ + return -1; + } else if (!optparse_is_shortopt(option)) { + if (options->permute) { + int index = options->optind++; + int r = optparse(options, optstring); + optparse_permute(options, index); + options->optind--; + return r; + } else { + return -1; + } + } + option += options->subopt + 1; + options->optopt = option[0]; + type = optparse_argtype(optstring, option[0]); + next = options->argv[options->optind + 1]; + switch (type) { + case -1: { + char str[2] = {0, 0}; + str[0] = option[0]; + options->optind++; + return optparse_error(options, OPTPARSE_MSG_INVALID, str); + } + case OPTPARSE_NONE: + if (option[1]) { + options->subopt++; + } else { + options->subopt = 0; + options->optind++; + } + return option[0]; + case OPTPARSE_REQUIRED: + options->subopt = 0; + options->optind++; + if (option[1]) { + options->optarg = option + 1; + } else if (next != 0) { + options->optarg = next; + options->optind++; + } else { + char str[2] = {0, 0}; + str[0] = option[0]; + options->optarg = 0; + return optparse_error(options, OPTPARSE_MSG_MISSING, str); + } + return option[0]; + case OPTPARSE_OPTIONAL: + options->subopt = 0; + options->optind++; + if (option[1]) + options->optarg = option + 1; + else + options->optarg = 0; + return option[0]; + } + return 0; +} + +OPTPARSE_API +char *optparse_arg(struct optparse *options) +{ + char *option = options->argv[options->optind]; + options->subopt = 0; + if (option != 0) + options->optind++; + return option; +} + +static int optparse_longopts_end(const struct optparse_long *longopts, int i) +{ + return !longopts[i].longname && !longopts[i].shortname; +} + +static void optparse_from_long(const struct optparse_long *longopts, char *optstring) +{ + char *p = optstring; + int i; + for (i = 0; !optparse_longopts_end(longopts, i); i++) { + if (longopts[i].shortname && longopts[i].shortname < 127) { + int a; + *p++ = longopts[i].shortname; + for (a = 0; a < (int)longopts[i].argtype; a++) + *p++ = ':'; + } + } + *p = '\0'; +} + +/* Unlike strcmp(), handles options containing "=". */ +static int optparse_longopts_match(const char *longname, const char *option) +{ + const char *a = option, *n = longname; + if (longname == 0) + return 0; + for (; *a && *n && *a != '='; a++, n++) + if (*a != *n) + return 0; + return *n == '\0' && (*a == '\0' || *a == '='); +} + +/* Return the part after "=", or NULL. */ +static char *optparse_longopts_arg(char *option) +{ + for (; *option && *option != '='; option++); + if (*option == '=') + return option + 1; + else + return 0; +} + +static int optparse_long_fallback(struct optparse *options, + const struct optparse_long *longopts, + int *longindex) +{ + int result; + char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */ + optparse_from_long(longopts, optstring); + result = optparse(options, optstring); + if (longindex != 0) { + *longindex = -1; + if (result != -1) { + int i; + for (i = 0; !optparse_longopts_end(longopts, i); i++) + if (longopts[i].shortname == options->optopt) + *longindex = i; + } + } + return result; +} + +OPTPARSE_API +int optparse_long(struct optparse *options, + const struct optparse_long *longopts, + int *longindex) +{ + int i; + char *option = options->argv[options->optind]; + if (option == 0) { + return -1; + } else if (optparse_is_dashdash(option)) { + options->optind++; /* consume "--" */ + return -1; + } else if (optparse_is_shortopt(option)) { + return optparse_long_fallback(options, longopts, longindex); + } else if (!optparse_is_longopt(option)) { + if (options->permute) { + int index = options->optind++; + int r = optparse_long(options, longopts, longindex); + optparse_permute(options, index); + options->optind--; + return r; + } else { + return -1; + } + } + + /* Parse as long option. */ + options->errmsg[0] = '\0'; + options->optopt = 0; + options->optarg = 0; + option += 2; /* skip "--" */ + options->optind++; + for (i = 0; !optparse_longopts_end(longopts, i); i++) { + const char *name = longopts[i].longname; + if (optparse_longopts_match(name, option)) { + char *arg; + if (longindex) + *longindex = i; + options->optopt = longopts[i].shortname; + arg = optparse_longopts_arg(option); + if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) { + return optparse_error(options, OPTPARSE_MSG_TOOMANY, name); + } + if (arg != 0) { + options->optarg = arg; + } else if (longopts[i].argtype == OPTPARSE_REQUIRED) { + options->optarg = options->argv[options->optind]; + if (options->optarg == 0) + return optparse_error(options, OPTPARSE_MSG_MISSING, name); + else + options->optind++; + } + return options->optopt; + } + } + return optparse_error(options, OPTPARSE_MSG_INVALID, option); +} + +#endif /* OPTPARSE_IMPLEMENTATION */ +#endif /* OPTPARSE_H */ diff --git a/load.c b/load.c deleted file mode 100644 index b3d154d794c35f96baeb64ae61d447400633cbc0..0000000000000000000000000000000000000000 --- a/load.c +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************* - Copyright (C) 2019-2021 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 <unistd.h> -#include <fcntl.h> -#include <stdlib.h> -#include <stdint.h> -#include <string.h> - -#define LOAD_BUFFER_SIZE 1024 -char buffer[LOAD_BUFFER_SIZE]; - -static int load_fid=-1; -static uint64_t load_values[10]={0,0,0,0,0,0,0,0,0,0}; -static uint64_t tmp_load_values[10]={0,0,0,0,0,0,0,0,0,0}; - -void _get_load(uint64_t* results) { - pread(load_fid, buffer, LOAD_BUFFER_SIZE-1, 0); - int pos=0; - while(buffer[pos] > '9' || buffer[pos] < '0') pos++; - for(int i=0; i<10; i++) { - results[i] = strtoull(buffer+pos, NULL, 10); - while(buffer[pos] <= '9' && buffer[pos] >= '0') pos++; - pos++; - } -} - -// Public interface - -unsigned int init_load(char* argument, void **state) { - load_fid = open("/proc/stat", O_RDONLY); - _get_load(load_values); - return 10; -} - -unsigned int get_load(uint64_t* results, void* state) { - _get_load(tmp_load_values); - for(int i=0; i<10; i++) - results[i] = tmp_load_values[i] - load_values[i]; - - memcpy(load_values, tmp_load_values, sizeof(load_values)); - return 10; -} - -void clean_load(void *state) { - close(load_fid); -} - -char *_labels[10] = {"user","nice","system","idle","iowait","irq", - "softirq","steal","guest","guest_nice"}; -void label_load(char **labels, void*none) { - for(int i=0; i<10; i++) - labels[i] = _labels[i]; -} diff --git a/makefile b/makefile index 433b76f63d171d59b4965f8981f9cfd262d80e11..a197961099a277e4e12d6d442236b2fd5327d14c 100644 --- a/makefile +++ b/makefile @@ -1,30 +1,77 @@ -all: mojitos +.POSIX: -OBJECTS = mojitos.o counters_individual.o rapl.o frapl.o network.o load.o infiniband.o temperature.o +SRC_DIR = src +DOC_DIR = doc +OBJ_DIR = obj +BIN_DIR = bin +TESTS_DIR = tests -mojitos:$(OBJECTS) - gcc $(DEBUG) -O3 -Wall -o mojitos $(OBJECTS) -lpowercap +BIN = mojitos -OBJECTS_GRP = $(subst _individual,_group, $(OBJECTS)) -mojitos_group: $(OBJECTS_GRP) counters_option.h - gcc $(DEBUG) -O3 -Wall -o mojitos_group $(OBJECTS_GRP) -lpowercap +CAPTOR_OBJ = -counters_%.o: counters_%.c counters.h counters_option.h - gcc $(DEBUG) -c -O3 -Wall $< -o $@ +include ./sensors.mk -counters_option.h: counters_option.py - ./counters_option.py > counters_option.h +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 = -mojitos.o: mojitos.c counters_option.h - gcc $(DEBUG) -c -O3 -Wall $< -o $@ +ASTYLE = astyle --style=kr -xf -s4 -k3 -n -Z -Q -debug: DEBUG = -DDEBUG -g -debug: all +all: $(BIN) man -%.o : %.c %.h - gcc $(DEBUG) -c -O3 -Wall $< -o $@ +$(BIN): $(BIN_DIR) $(OBJ) $(OBJ_DIR)/$(BIN).o + $(CC) $(LDFLAGS) -o $(BIN_DIR)/$(BIN) $(OBJ) $(OBJ_DIR)/$(BIN).o + +$(OBJ): $(OBJ_DIR) +$(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 + $(CC) $(CFLAGS) -c $< -o $@ + +$(SRC_DIR)/counters_option.h: $(SRC_DIR)/counters_option.sh + sh ./$(SRC_DIR)/counters_option.sh > $(SRC_DIR)/counters_option.h + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + +$(BIN_DIR): + mkdir -p $(BIN_DIR) + +debug: CFLAGS = $(CPPFLAGS) -DDEBUG -g -Og +debug: $(BIN) + +tests: + $(CC) $(CPPFLAGS) $(TESTS_DIR)/main.c $(SRC_DIR)/util.c -o $(TESTS_DIR)/run + $(TESTS_DIR)/run + +format: + $(ASTYLE) $(SRC_DIR)/*.[ch] \ + $(DOC_DIR)/*.[ch] \ + $(TESTS_DIR)/*.[ch] clean: - \rm -f *~ *.o mojitos_group mojitos counters_option.h + \rm -f $(OBJ_DIR)/* $(BIN_DIR)/* \ + $(SRC_DIR)/counters_option.h \ + $(TESTS_DIR)/run \ + $(DOC_DIR)/test_main_ex \ + $(DOC_DIR)/info_reader_ex + +readme: $(BIN) + sh ./tools/update-readme-usage.sh + +man: $(BIN) + awk -v "usage=$$($(BIN_DIR)/$(BIN) --dump-opts)" \ + '/^USAGE/ { $$0=usage } 1' \ + doc/$(BIN).pre.1 > doc/$(BIN).1 2>/dev/null + +.PHONY: all clean mojitos debug format tests readme man diff --git a/mojitos b/mojitos deleted file mode 100755 index ab3f7b4824740935e193e4d9efee35d0f20849fa..0000000000000000000000000000000000000000 Binary files a/mojitos and /dev/null differ diff --git a/mojitos.c b/mojitos.c deleted file mode 100644 index e80e692f5a4498e02e60761cee1a2d568860c314..0000000000000000000000000000000000000000 --- a/mojitos.c +++ /dev/null @@ -1,259 +0,0 @@ -/******************************************************* - Copyright (C) 2018-2019 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 <stdlib.h> -#include <stdio.h> -#include <time.h> -#include <signal.h> -#include <inttypes.h> -#include <unistd.h> - -#include "counters.h" -#include "rapl.h" -#include "frapl.h" -#include "network.h" -#include "infiniband.h" -#include "load.h" -#include "temperature.h" - - - -void usage(char** argv) { - printf("Usage : %s [-t time] [-f freq] [-r] [-p perf_list] [-l] [-u] [-c] [-d network_device] [-i infiniband_path] [-o logfile] [-e command arguments...]\n", argv[0]); - printf("if time==0 then loops infinitively\n"); - printf("if -e is present, time and freq are not used\n"); - printf("-r activates RAPL\n"); - printf("-R activates the file version of RAPL\n"); - printf("-p activates performance counters\n"); - printf(" perf_list is coma separated list of performance counters without space. Ex: instructions,cache_misses\n"); - printf("-l lists the possible performance counters and quits\n"); - printf("-d activates network monitoring (if network_device is X, tries to detect it automatically)\n"); - printf("-i activates infiniband monitoring (if infiniband_path is X, tries to detect it automatically)\n"); - printf("-s activates statistics of overhead in nanoseconds\n"); - printf("-u activates report of system load\n"); - printf("-c activates report of processor temperature\n"); - exit(EXIT_SUCCESS); -} - -void sighandler(int none) { -} - -void flush(int none) { - exit(0); -} - -FILE *output; -void flushexit() { - fflush(output); - fclose(output); -} - -typedef unsigned int (initializer_t)(char*, void **); -typedef void (labeler_t)(char **, void*); -typedef unsigned int (*getter_t)(uint64_t*, void*); -typedef void (*cleaner_t)(void*); - -unsigned int nb_sources=0; -void **states=NULL; -getter_t *getter=NULL; -cleaner_t *cleaner=NULL; - -unsigned int nb_sensors=0; -char **labels=NULL; -uint64_t *values=NULL; - -void add_source(initializer_t init, char* arg, labeler_t labeler, - getter_t get, cleaner_t clean) { - nb_sources++; - states = realloc(states, nb_sources*sizeof(void*)); - int nb = init(arg, &states[nb_sources-1]); - if (nb == 0) { - nb_sources--; - states = realloc(states, nb_sources*sizeof(void*)); - return; - } - - getter = realloc(getter, nb_sources*sizeof(void*)); - getter[nb_sources-1] = get; - cleaner = realloc(cleaner, nb_sources*sizeof(void*)); - cleaner[nb_sources-1] = clean; - - labels = realloc(labels, (nb_sensors+nb)*sizeof(char*)); - labeler(labels+nb_sensors, states[nb_sources-1]); - - values = realloc(values, (nb_sensors+nb)*sizeof(uint64_t)); - nb_sensors += nb; -} - -int main(int argc, char **argv) { - int total_time=1; - int delta=0; - int frequency=1; - char **application = NULL; - - int stat_mode = -1; - - if(argc==1) - usage(argv); - - output=stdout; - - atexit(flushexit); - signal(15, flush); - - int c; - while ((c = getopt (argc, argv, "ilhcftdeoprRsu")) != -1 && application==NULL) - switch (c) { - case 'f': - frequency=atoi(argv[optind]); - break; - case 't': - total_time=atoi(argv[optind]); - delta=1; - if(total_time==0) { - total_time=1; - delta=0; - } - break; - case 'd': - add_source(init_network, argv[optind], label_network, get_network, clean_network); - break; - case 'i': - add_source(init_infiniband, argv[optind], label_infiniband, get_network, clean_network); - break; - case 'o': - output = fopen(argv[optind],"wb"); - break; - case 'e': - application=&argv[optind]; - signal(17,sighandler); - break; - case 'p': - add_source(init_counters, argv[optind], label_counters, get_counters, clean_counters); - break; - case 'r': - add_source(init_rapl, NULL, label_rapl, get_rapl, clean_rapl); - break; - case 'R': - add_source(init_frapl, NULL, label_frapl, get_frapl, clean_frapl); - break; - case 'u': - add_source(init_load, NULL, label_load, get_load, clean_load); - break; - case 'c': - add_source(init_temperature, NULL, label_temperature, get_temperature, clean_temperature); - break; - case 's': - stat_mode=0; - break; - case 'l': - show_all_counters(); - exit(EXIT_SUCCESS); - default: - usage(argv); - } - - - setvbuf(output, NULL, _IONBF, BUFSIZ); - struct timespec ts; - struct timespec ts_ref; - - fprintf(output, "#timestamp "); - - for(int i=0; i<nb_sensors; i++) - fprintf(output, "%s ", labels[i]); - - if(stat_mode==0) - fprintf(output, "overhead "); - - fprintf(output, "\n"); - - unsigned long int stat_data=0; - - for (int temps = 0; temps <total_time*frequency; temps+=delta) { - clock_gettime(CLOCK_MONOTONIC, &ts_ref); - - // Get Data - unsigned int current = 0; - for(int i=0; i<nb_sources; i++) - current += getter[i](&values[current], states[i]); - - if(application != NULL) { - - if(fork()==0){ - execvp(application[0], application); - exit(0); - } - pause(); - clock_gettime(CLOCK_MONOTONIC, &ts); - if(ts.tv_nsec >= ts_ref.tv_nsec) - fprintf(output, "%ld.%09ld ", (ts.tv_sec-ts_ref.tv_sec), ts.tv_nsec-ts_ref.tv_nsec); - else - fprintf(output, "%ld.%09ld ", (ts.tv_sec-ts_ref.tv_sec)-1,1000000000+ts.tv_nsec-ts_ref.tv_nsec); - } - else { -#ifdef DEBUG - clock_gettime(CLOCK_MONOTONIC, &ts); - fprintf(stderr, "%ld\n", (ts.tv_nsec-ts_ref.tv_nsec)/1000); - //Indiv: mean: 148 std: 31 % med: 141 std: 28 % - //Group: mean: 309 std: 41 % med: 297 std: 39 % -#endif - if(stat_mode==0) { - clock_gettime(CLOCK_MONOTONIC, &ts); - if(ts.tv_nsec >= ts_ref.tv_nsec) - stat_data = ts.tv_nsec-ts_ref.tv_nsec; - else - stat_data = 1000000000+ts.tv_nsec-ts_ref.tv_nsec; - } - - // Treat Data - fprintf(output, "%ld.%09ld ", ts_ref.tv_sec, ts_ref.tv_nsec); - } - - for(int i=0; i<nb_sensors; i++) - fprintf(output, "%" PRIu64 " ", values[i]); - - if(stat_mode==0) - fprintf(output, "%ld ", stat_data); - - fprintf(output, "\n"); - - if(application != NULL) - break; - - clock_gettime(CLOCK_MONOTONIC, &ts); - usleep(1000*1000/frequency-(ts.tv_nsec/1000)%(1000*1000/frequency)); - } - - for(int i=0; i<nb_sources;i++) - cleaner[i](states[i]); - - if(nb_sources > 0) { - free(getter); - free(cleaner); - free(labels); - free(values); - free(states); - } -} - - - - diff --git a/network.c b/network.c deleted file mode 100644 index 6e3f494bd49b86c68e452efd202873821b9c6832..0000000000000000000000000000000000000000 --- a/network.c +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************* - Copyright (C) 2018-2019 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 <unistd.h> -#include <fcntl.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <stdint.h> - -struct network_t { - uint64_t values[4]; - uint64_t tmp_values[4]; - int sources[4]; -}; - -unsigned int _get_network(uint64_t* results, int* sources) { - if(sources==NULL) - return 0; - char buffer[128]; - for(int i=0; i<4; i++){ - pread(sources[i], buffer, 127, 0); - - results[i] = strtoull(buffer, NULL, 10); - } - return 4; -} - - - -unsigned int init_network(char* dev, void**ptr) { - if(dev==NULL) - return 0; - - if(strcmp(dev,"X")==0) { - int f = open("/proc/net/route", O_RDONLY); - char buffer[1000]; - read(f, buffer, 999); - 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(f); - } - - 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_t *state = malloc(sizeof(struct network_t)); - - char buffer2[256]; - for(int i=0; i<4; i++) { - sprintf(buffer2, filenames[i], dev); - state->sources[i] = open(buffer2, O_RDONLY); - } - *ptr = (void*) state; - _get_network(state->values, state->sources); - - return 4; -} - -unsigned int get_network(uint64_t* results, void* ptr) { - struct network_t *state = (struct network_t *) ptr; - _get_network(state->tmp_values, state->sources); - for(int i=0; i<4; i++) - results[i] = state->tmp_values[i] - state->values[i]; - - memcpy(state->values, state->tmp_values, 4*sizeof(uint64_t)); - return 4; -} - -void clean_network(void *ptr) { - struct network_t *state = (struct network_t *) ptr; - if(state==NULL) - return; - for(int i=0;i<4;i++) - close(state->sources[i]); - free(state); -} - -char *_labels_network[4] = {"rxp", "rxb", "txp", "txb"}; -void label_network(char **labels, void*none) { - for(int i=0; i<4; i++) - labels[i] = _labels_network[i]; -} diff --git a/rapl.c b/rapl.c deleted file mode 100644 index 14b4a58ce302d1251680540155fb372d98acec46..0000000000000000000000000000000000000000 --- a/rapl.c +++ /dev/null @@ -1,166 +0,0 @@ -/******************************************************* - Copyright (C) 2018-2019 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 <string.h> - -#include <powercap/powercap-rapl.h> - -struct _rapl_t { - powercap_rapl_pkg* pkgs; - uint32_t nb_pkgs; - uint32_t nb; - char **names; - uint32_t* zones; - uint32_t* packages; - uint64_t* values; - uint64_t* tmp_values; -}; - -typedef struct _rapl_t _rapl_t; - - - - - - -const int nb_zones = 3; -const int rapl_zones[3] = { POWERCAP_RAPL_ZONE_PACKAGE, POWERCAP_RAPL_ZONE_CORE, POWERCAP_RAPL_ZONE_DRAM}; - - -#define MAX_LEN_NAME 100 - -// values [zone + package *nbzones] microjoules -void _get_rapl(uint64_t *values, _rapl_t* rapl) { - for (int i = 0; i < rapl->nb; i++) { -#ifdef DEBUG - int ret = -#endif - powercap_rapl_get_energy_uj(&rapl->pkgs[rapl->packages[i]], - rapl->zones[i], - &values[i]); -#ifdef DEBUG - printf("GETRAPL: package %d, zone %d, name %s, ret: %d\n", rapl->packages[i], rapl->zones[i], rapl->names[i], ret); -#endif - } -} - -unsigned int init_rapl(char* none, void **ptr) { - // get number of processor sockets - _rapl_t* rapl= malloc(sizeof(struct _rapl_t)); - rapl->nb = 0; - rapl->packages = NULL; - rapl->zones = NULL; - - rapl->nb_pkgs = powercap_rapl_get_num_instances(); - //rapl->nb_pkgs = powercap_rapl_get_num_packages(); - - if (rapl->nb_pkgs == 0) { - perror("no packages found (maybe the kernel module isn't loaded?)"); - exit(-1); - } - rapl->pkgs = malloc(rapl->nb_pkgs * sizeof(powercap_rapl_pkg)); - for (int package = 0; package < rapl->nb_pkgs; package++) - if (powercap_rapl_init(package, &rapl->pkgs[package], 0)) { - perror("powercap_rapl_init, check access (root needed ?)"); - exit(-1); - } - - rapl->names = NULL; - - char _name[MAX_LEN_NAME+1]; - char _name2[MAX_LEN_NAME+11]; - - for (unsigned int package = 0; package < rapl->nb_pkgs; package++) { - for(unsigned int zone=0; zone < nb_zones; zone++) { - int length=powercap_rapl_get_name(&rapl->pkgs[package], rapl_zones[zone], - _name, MAX_LEN_NAME); - if (length>0) { - - sprintf(_name2, "%s%u", _name, package); - - rapl->nb++; - rapl->names = realloc(rapl->names, sizeof(char*)*rapl->nb); - rapl->names[rapl->nb-1] = malloc(sizeof(char) * (strlen(_name2)+1)); - rapl->zones = realloc(rapl->zones, sizeof(uint32_t)*rapl->nb); - rapl->packages = realloc(rapl->packages, sizeof(uint32_t)*rapl->nb); - - strcpy(rapl->names[rapl->nb-1], _name2); - rapl->zones[rapl->nb-1] = rapl_zones[zone]; - rapl->packages[rapl->nb-1] = package; - } -#ifdef DEBUG - printf("%d %d %d %d %s\n\n", length, package, zone, rapl_zones[zone], _name2); -#endif - } - } -#ifdef DEBUG - printf("Result of init\n"); - for(int i=0; i<rapl->nb; i++) - printf("package %d, zone %d, name %s\n", rapl->packages[i], rapl->zones[i], rapl->names[i]); -#endif - - rapl->values = calloc(sizeof(uint64_t), rapl->nb); - rapl->tmp_values = calloc(sizeof(uint64_t), rapl->nb); - - _get_rapl(rapl->values, rapl); - - *ptr = (void*)rapl; - return rapl->nb; -} - - - -unsigned int get_rapl(uint64_t* results, void* ptr) { - _rapl_t* state = (_rapl_t*) ptr; - _get_rapl(state->tmp_values, state); - for(int i=0; i<state->nb; i++) - results[i] = state->tmp_values[i] - state->values[i]; - - memcpy(state->values, state->tmp_values, sizeof(uint64_t)*state->nb); - return state->nb; -} - - - - -void clean_rapl(void* ptr) { - _rapl_t* rapl = (_rapl_t*) ptr; - for (int package = 0; package < rapl->nb_pkgs; package++) - if (powercap_rapl_destroy(&rapl->pkgs[package])) - perror("powercap_rapl_destroy"); - for (int elem=0; elem<rapl->nb; elem++) - free(rapl->names[elem]); - - free(rapl->names); - free(rapl->pkgs); - free(rapl->zones); - free(rapl->packages); - free(rapl->values); - free(rapl->tmp_values); - free(rapl); -} - -void label_rapl(char **labels, void *ptr) { - _rapl_t* rapl = (_rapl_t*) ptr; - for(int i=0; i<rapl->nb; i++) - labels[i] = rapl->names[i]; -} diff --git a/src/amd_rapl.c b/src/amd_rapl.c new file mode 100644 index 0000000000000000000000000000000000000000..1e08517d7d2f66c5b5404e973f13aed5f366d390 --- /dev/null +++ b/src/amd_rapl.c @@ -0,0 +1,361 @@ +/******************************************************* + 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 <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +#include "info_reader.h" +#include "util.h" + +#define BUFFER_SIZE 64 +#define __READ_CPUINFO__ + +// ---------------------------MSR_REGISTERS +static const uint64_t amd_energy_mask = 0xFFFFFFFF; +static const uint64_t amd_energy_unit_mask = 0x01F00; +static const uint64_t msr_rapl_power_unit = 0xC0010299; +static const uint64_t energy_core_msr = 0xC001029A; + +// ------------------------------FILE_PATHS +static const char *base_str = "/dev/cpu/%d/msr"; + +struct CpuSensor { + unsigned int cpu_id; + unsigned int package_id; + unsigned int core_id; + + char *name; + int fd; + + unsigned int energy_units; + uint64_t core_energy; +}; +typedef struct CpuSensor CpuSensor; + +struct AmdRapl { + CpuSensor *sensors; + unsigned int sensor_count; +}; +typedef struct AmdRapl AmdRapl; + +// -----------------------------INFO_READER + +#ifdef __READ_CPUINFO__ +#define NB_KEYS 3 + +static char *cpuinfo = "/proc/cpuinfo"; + +static GenericPointer uint_allocator(char *s) +{ + unsigned int value = atoi(s); + return (GenericPointer) value; +} + +static void _set_cpu_id(GenericPointer storage, GenericPointer data) +{ + CpuSensor *cpu = (CpuSensor *) storage; + cpu->cpu_id = (unsigned int) data; +} + +static void _set_package_id(GenericPointer storage, GenericPointer data) +{ + CpuSensor *cpu = (CpuSensor *) storage; + cpu->package_id = (unsigned int) data; +} + +static void _set_core_id(GenericPointer storage, GenericPointer data) +{ + CpuSensor *cpu = (CpuSensor *) storage; + cpu->core_id = (unsigned int) data; +} + +static KeyFinder keys[NB_KEYS] = { + {"processor", ": ", uint_allocator, _set_cpu_id}, + {"physical id", ": ", uint_allocator, _set_package_id}, + {"core id", ": ", uint_allocator, _set_core_id} +}; + + +static unsigned int parse_cpuinfo(CpuSensor *storage, unsigned int capacity) +{ + Parser parser = { + .storage = (GenericPointer) storage, + .nb_stored = 0, + .capacity = capacity, + .storage_struct_size = sizeof(CpuSensor), + .keys = keys, + .nb_keys = NB_KEYS, + .file = fopen(cpuinfo, "r") + }; + return parse(&parser); +} + +#endif + +// --------------------------------READ_MSR + +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); + perror("pread"); + exit(127); + } + return data; +} + +unsigned int read_unit(int fd) +{ + uint64_t unit = read_msr(fd, msr_rapl_power_unit); + return ((unit & amd_energy_unit_mask) >> 8); +} + +uint64_t read_raw_core_energy(int fd) +{ + uint64_t energy = read_msr(fd, energy_core_msr); + return energy & amd_energy_mask; +} + +// -------------------------READ_PKG_ENERGY + +#ifdef __READ_PKG_ENERGY__ +// TODO: Verify if these functions are still useful (the package energy can be calculed) + +static const uint64_t energy_pkg_msr = 0xC001029B; +uint64_t read_raw_pkg_energy(int fd) +{ + uint64_t energy = read_msr(fd, energy_pkg_msr); + return energy & amd_energy_mask; +} +#endif + +// ----------------------------------ENERGY + +uint64_t raw_to_microjoule(uint64_t raw, unsigned int unit) +{ + static const double to_microjoule = 1000000.0; + // raw * (1 / (unit^2)) -> Joule + // Joule * 1000000 -> uJoule + return (uint64_t) (((double) raw * to_microjoule) / (double)(1U << unit)); +} + +uint64_t raw_to_joule(uint64_t raw, uint64_t unit) +{ + // raw * (1 / (unit^2)) -> Joule + int64_t joule = raw / (1UL << unit); + return joule; +} + +// -----------------------------------DEBUG + +#ifdef DEBUG +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", + sensor->cpu_id, + sensor->package_id, + sensor->core_id, + sensor->name, + sensor->fd, + sensor->energy_units, + sensor->core_energy + ); +} + +void debug_print_amd_rapl(AmdRapl *rapl) +{ + for (unsigned int i = 0; i < rapl->sensor_count; i++) { + debug_print_sensor(&rapl->sensors[i]); + } +} + +#endif + +// ---------------------------AMD_RAPL_UTIL + +unsigned int get_nb_cpu() +{ + char filename[BUFFER_SIZE]; + + unsigned int n_cpu = 0; + for (;; n_cpu++) { + snprintf(filename, BUFFER_SIZE, base_str, n_cpu); + + int fd = open(filename, O_RDONLY); + if (fd < 0) { + break; + } + close(fd); + } + return n_cpu; +} + +void get_arch(unsigned int *ret_nb_package, unsigned int *ret_nb_core, CpuSensor *sensors, unsigned int nb_sensor) +{ + unsigned int nb_package = 0; + unsigned int nb_core = 0; + for (unsigned int i = 0; i < nb_sensor; i++) { + if (sensors[i].package_id > nb_package) { + nb_package = sensors[i].package_id; + } + if (sensors[i].core_id > nb_core) { + nb_core = sensors[i].core_id; + } + } + *ret_nb_package = nb_package + 1; + *ret_nb_core = nb_core + 1; +} + +char *get_name(unsigned int cpu_id) +{ + static const char *base_name = "core%d"; + static const size_t max_lenght = 20; + char *name = (char *)calloc(max_lenght, sizeof(char)); + snprintf(name, max_lenght, base_name, cpu_id); + return name; +} + +void update_cpu_sensor(CpuSensor *sensor, uint64_t *energy_consumed) +{ + sensor->energy_units = read_unit(sensor->fd); + uint64_t raw_core_energy = read_raw_core_energy(sensor->fd); + uint64_t core_energy = raw_to_microjoule(raw_core_energy, sensor->energy_units); + + *energy_consumed = modulo_substraction(core_energy, sensor->core_energy); + sensor->core_energy = core_energy; +} + +unsigned int is_duplicate(CpuSensor *sensor,unsigned int nb_core, unsigned int nb_package, unsigned char map[nb_core][nb_package]) +{ + if (map[sensor->core_id][sensor->package_id] == 1) { + return 0; + } + map[sensor->core_id][sensor->package_id] += 1; + return 1; +} + +void init_cpu_sensor(CpuSensor *sensor, CpuSensor *cpu_info) +{ + static char filename[BUFFER_SIZE]; + snprintf(filename,BUFFER_SIZE, base_str, cpu_info->cpu_id); + + int fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, base_str, cpu_info->cpu_id); + perror(":open()"); + exit(127); + } + + memcpy(sensor, cpu_info, sizeof(CpuSensor)); + sensor->name = get_name(sensor->cpu_id); + sensor->fd = fd; +} + +void clean_cpu_sensor(CpuSensor *sensor) +{ + close(sensor->fd); + free(sensor->name); +} + +void free_amd_rapl(AmdRapl *rapl) +{ + free(rapl->sensors); + free(rapl); +} + +// ----------------------AMD_RAPL_INTERFACE + +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)) { + free(cpu_information); + PANIC(1, "cpuinfo"); + } + + unsigned int nb_package; + unsigned int nb_core; + get_arch(&nb_package, &nb_core, cpu_information, max_cpus); + + unsigned char cpu_map[nb_core][nb_package]; + memset(cpu_map, 0, sizeof(cpu_map)); + CpuSensor *sensors = (CpuSensor *) calloc(max_cpus, sizeof(CpuSensor)); + + unsigned int sensor_count = 0; + for (unsigned int i = 0; i < max_cpus; i++) { + if (is_duplicate(&cpu_information[i], nb_core, nb_package, cpu_map) == 1) { + init_cpu_sensor(&sensors[sensor_count],&cpu_information[i]); + sensor_count += 1; + } + } + free(cpu_information); + + AmdRapl *rapl = (AmdRapl *) calloc(1, sizeof(AmdRapl)); + rapl->sensors = sensors; + rapl->sensor_count = sensor_count; + *ptr = (void *) rapl; + return rapl->sensor_count; +} + + +unsigned int get_amd_rapl(uint64_t *results, void *ptr) +{ + AmdRapl *rapl = (AmdRapl *) ptr; + for (unsigned int i = 0; i < rapl->sensor_count; i++) { + update_cpu_sensor(&rapl->sensors[i], &results[i]); + } + return rapl->sensor_count; +} + +void label_amd_rapl(char **labels, void *ptr) +{ + AmdRapl *rapl = (AmdRapl *) ptr; + for (unsigned int i = 0; i < rapl->sensor_count; i++) { + labels[i] = rapl->sensors[i].name; + } +} + +void clean_amd_rapl(void *ptr) +{ + AmdRapl *rapl = (AmdRapl *) ptr; + + for (unsigned int i = 0; i < rapl->sensor_count; ++i) { + clean_cpu_sensor(&rapl->sensors[i]); + } + free(rapl->sensors); + free(rapl); +} + diff --git a/counters.h b/src/amd_rapl.h similarity index 57% rename from counters.h rename to src/amd_rapl.h index 751557ab5b308c0eda13c61f077d5bdff6519cd1..e58cc16498f4b7553f9a7ebf6b6f40e8cd4ee288 100644 --- a/counters.h +++ b/src/amd_rapl.h @@ -1,5 +1,5 @@ /******************************************************* - Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr> + Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr> This file is part of Mojitos. @@ -18,9 +18,25 @@ *******************************************************/ -unsigned int init_counters(char*, void **); -unsigned int get_counters(uint64_t* results, void*); -void clean_counters(void *); -void label_counters(char **labels, void*); +unsigned int init_amd_rapl(char *, void **); +unsigned int get_amd_rapl(uint64_t *results, void *); +void clean_amd_rapl(void *); +void label_amd_rapl(char **labels, void *); -void show_all_counters(); +Sensor amd_rapl = { + .init = init_amd_rapl, + .get = get_amd_rapl, + .clean = clean_amd_rapl, + .label = label_amd_rapl, + .nb_opt = 1, +}; + +Optparse amd_rapl_opt[1] = { + { + .longname = "amd-rapl", + .shortname = 'r', + .argtype = OPTPARSE_NONE, + .usage_arg = NULL, + .usage_msg = "AMD RAPL", + }, +}; diff --git a/src/counters.c b/src/counters.c new file mode 100644 index 0000000000000000000000000000000000000000..8013ff863757b2f614094cd96dc09e12776467c8 --- /dev/null +++ b/src/counters.c @@ -0,0 +1,240 @@ +/******************************************************* + Copyright (C) 2018-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 <linux/perf_event.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include <stdint.h> +#include <asm/unistd.h> +#include <stdint.h> +#include "util.h" + + +struct Counter { + int nbcores; + int nbperf; + int **counters; + uint64_t *counters_values; + uint64_t *tmp_counters_values; + + int *perf_indexes; + +}; +typedef struct Counter Counter; + +#include "counters_option.h" + +void *show_all_counters(void *none1, size_t none2) +{ + for (unsigned int i = 0; i < nb_counter_option; i++) { + printf("%s\n", perf_static_info[i].name); + } + UNUSED(none1); + UNUSED(none2); + exit(EXIT_SUCCESS); + return NULL; /* not reached */ +} + +void perf_type_key(__u32 **perf_type, __u64 **perf_key, int *indexes, int nb) +{ + *perf_type = malloc(nb * sizeof(__u32)); + *perf_key = malloc(nb * sizeof(__u64)); + + for (int i = 0; i < nb; i++) { + (*perf_key)[i] = perf_static_info[indexes[i]].perf_key; + (*perf_type)[i] = perf_static_info[indexes[i]].perf_type; + } +} +void perf_event_list(char *perf_string, int *nb_perf, int **perf_indexes) +{ + char *token; + *nb_perf = 0; + *perf_indexes = NULL; + + while ((token = strtok(perf_string, ",")) != NULL) { + perf_string = NULL; + unsigned int i; + + for (i = 0; i < nb_counter_option; i++) { + if (strcmp(perf_static_info[i].name, token) == 0) { + (*nb_perf)++; + (*perf_indexes) = realloc(*perf_indexes, sizeof(int) * (*nb_perf)); + (*perf_indexes)[*nb_perf - 1] = i; + break; + } + } + + if (i == nb_counter_option) { + fprintf(stderr, "Unknown performance counter: %s\n", token); + exit(EXIT_FAILURE); + } + } +} + +static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, + int cpu, int group_fd, unsigned long flags) +{ + long res = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags); + + if (res == -1) { + perror("perf_event_open"); + fprintf(stderr, "Error opening leader %llx\n", hw_event->config); + exit(EXIT_FAILURE); + } + + return res; +} + +Counter *_init_counters(const int nb_perf, const __u32 *types, const __u64 *names) +{ + struct perf_event_attr pe; + unsigned int nbcores = sysconf(_SC_NPROCESSORS_ONLN); + memset(&pe, 0, sizeof(struct perf_event_attr)); + pe.size = sizeof(struct perf_event_attr); + pe.disabled = 1; + + Counter *counters = malloc(sizeof(struct Counter)); + counters->nbperf = nb_perf; + counters->nbcores = nbcores; + counters->counters = malloc(nb_perf * sizeof(int *)); + + for (int i = 0; i < nb_perf; i++) { + pe.type = types[i]; + pe.config = names[i]; + counters->counters[i] = malloc(nbcores * sizeof(int)); + + for (unsigned int core = 0; core < nbcores; core++) { + counters->counters[i][core] = perf_event_open(&pe, -1, core, -1, PERF_FLAG_FD_CLOEXEC); + } + } + + return counters; +} + +void clean_counters(void *ptr) +{ + Counter *counters = (Counter *) ptr; + + for (int counter = 0; counter < counters->nbperf; counter++) { + for (int core = 0; core < counters->nbcores; core++) { + close(counters->counters[counter][core]); + } + + free(counters->counters[counter]); + } + + free(counters->counters); + free(counters->counters_values); + free(counters->tmp_counters_values); + free(counters->perf_indexes); + + free(counters); +} + +void start_counters(Counter *counters) +{ + for (int counter = 0; counter < counters->nbperf; counter++) { + for (int core = 0; core < counters->nbcores; core++) { + ioctl(counters->counters[counter][core], PERF_EVENT_IOC_ENABLE, 0); + } + } +} + +void reset_counters(Counter *counters) +{ + for (int counter = 0; counter < counters->nbperf; counter++) { + for (int core = 0; core < counters->nbcores; core++) { + ioctl(counters->counters[counter][core], PERF_EVENT_IOC_RESET, 0); + } + } +} + +void _get_counters(Counter *counters, uint64_t *values) +{ + for (int i = 0; i < counters->nbperf; i++) { + uint64_t accu = 0; + uint64_t count = 0; + + for (int core = 0; core < counters->nbcores; core++) { + if (-1 == read(counters->counters[i][core], &count, sizeof(uint64_t))) { + fprintf(stderr, "Cannot read result"); + exit(EXIT_FAILURE); + } + accu += count; + } + + values[i] = accu; + } +} + + + + + + +unsigned int init_counters(char *args, void **state) +{ + int nb_perf; + int *perf_indexes = NULL; + + perf_event_list(args, &nb_perf, &perf_indexes); + + __u32 *perf_type; + __u64 *perf_key; + perf_type_key(&perf_type, &perf_key, perf_indexes, nb_perf); + Counter *fd = _init_counters(nb_perf, perf_type, perf_key); + free(perf_type); + free(perf_key); + + fd->perf_indexes = perf_indexes; + fd->counters_values = malloc(nb_perf * sizeof(uint64_t)); + fd->tmp_counters_values = malloc(nb_perf * sizeof(uint64_t)); + start_counters(fd); + _get_counters(fd, fd->counters_values); + *state = (void *) fd; + + return nb_perf; +} + +unsigned int get_counters(uint64_t *results, void *ptr) +{ + Counter *state = (Counter *) ptr; + + _get_counters(state, state->tmp_counters_values); + + for (int i = 0; i < state->nbperf; i++) { + results[i] = modulo_substraction(state->tmp_counters_values[i], state->counters_values[i]); + } + + memcpy(state->counters_values, state->tmp_counters_values, state->nbperf * sizeof(uint64_t)); + return state->nbperf; +} + +void label_counters(char **labels, void *ptr) +{ + Counter *state = (Counter *) ptr; + + for (int i = 0; i < state->nbperf; i++) { + labels[i] = perf_static_info[state->perf_indexes[i]].name; + } +} diff --git a/src/counters.h b/src/counters.h new file mode 100644 index 0000000000000000000000000000000000000000..4374cd1f6ea0fb4e59d1226b03753d38a2fa3b2b --- /dev/null +++ b/src/counters.h @@ -0,0 +1,54 @@ +/******************************************************* + Copyright (C) 2018-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_counters(char *, void **); +unsigned int get_counters(uint64_t *results, void *); +void clean_counters(void *); +void label_counters(char **labels, void *); +void *show_all_counters(void *, size_t); + +Sensor counters = { + .init = init_counters, + .get = get_counters, + .clean = clean_counters, + .label = label_counters, + .nb_opt = 2, +}; + +Optparse counters_opt[2] = { + { + .longname = "perf-list", + .shortname = 'p', + .argtype = OPTPARSE_REQUIRED, + .usage_arg = "<perf_list>", + .usage_msg = "performance counters\n" + "\tperf_list is a coma separated list of performance counters.\n" + "\tEx: instructions,cache_misses", + }, + { + .longname = "list", + .shortname = 'l', + .argtype = OPTPARSE_NONE, + .usage_arg = NULL, + .usage_msg = "list the available performance counters and quit", + .fn = show_all_counters, + }, +}; + diff --git a/src/counters_option.sh b/src/counters_option.sh new file mode 100644 index 0000000000000000000000000000000000000000..76f92757f1e09207c1113421c76cd6a67d22f3df --- /dev/null +++ b/src/counters_option.sh @@ -0,0 +1,88 @@ +#!/bin/sh + +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr> + +linux_include=/usr/include/linux/perf_event.h + +echo ' +typedef struct CounterOption{ + char *name; + __u32 perf_type; + __u64 perf_key; +} CounterOption; + +static CounterOption perf_static_info[] = {' + +nb=0 + +while IFS= read line; do + case $line in + *perf_hw_id*) + mode=PERF_TYPE_HARDWARE + ;; + *perf_hw_cache_*) + mode=PERF_TYPE_HW_CACHE + ;; + *perf_sw_id*) + mode=PERF_TYPE_SOFTWARE + ;; + *PERF_COUNT_*=*) + perf_name=$(echo "$line" | awk '{print $1}') + short_perf=$(echo "$perf_name" | sed 's/PERF_COUNT_[HS]W_//' | tr 'A-Z' 'a-z') + case $short_perf in + # blacklist + stalled_cycles_frontend|stalled_cycles_backend|cache_l1i|cache_op_write|cache_result_miss) + continue + ;; + esac + + if [ "$mode" != 'PERF_TYPE_HW_CACHE' ]; then + printf ' { .name = "%s", .perf_type = %s, .perf_key = %s},\n' \ + "$short_perf" \ + "$mode" \ + "$perf_name" + + : $((nb += 1)) + continue + fi + + # $mode == PERF_TYPE_HW_CACHE + for op_id in \ + 'r PERF_COUNT_HW_CACHE_OP_READ' \ + 'w PERF_COUNT_HW_CACHE_OP_WRITE' \ + 'p PERF_COUNT_HW_CACHE_OP_PREFETCH' + do + op_id_str=${op_id% *} + op_id_name=${op_id#* } + + for result_id in \ + 'a PERF_COUNT_HW_CACHE_RESULT_ACCESS' \ + 'm PERF_COUNT_HW_CACHE_RESULT_MISS' + do + result_id_str=${result_id% *} + result_id_name=${result_id#* } + + printf ' {' + printf ' .name = "%s_%s_%s",' \ + "$short_perf" \ + "$op_id_str" \ + "$result_id_str" + printf ' .perf_type = %s,' \ + "$mode" + printf ' .perf_key = %s | (%s >> 8) | (%s >> 16)' \ + "$perf_name" \ + "$op_id_name" \ + "$result_id_name" + printf ' },\n' + : $((nb += 1)) + done + done + ;; + esac +done < "$linux_include" + +echo '};' + +printf '\nstatic unsigned int nb_counter_option = %d;\n' "$nb" + diff --git a/src/infiniband.c b/src/infiniband.c new file mode 100644 index 0000000000000000000000000000000000000000..6a5a6b4e2422d819269d1a470783286be8a5c162 --- /dev/null +++ b/src/infiniband.c @@ -0,0 +1,135 @@ +/******************************************************* + Copyright (C) 2018-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 <fcntl.h> +#include <glob.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "util.h" + +#define NB_SENSOR 4 + +struct Infiniband { + uint64_t values[NB_SENSOR]; + uint64_t tmp_values[NB_SENSOR]; + int sources[NB_SENSOR]; +}; +typedef struct Infiniband Infiniband; + +unsigned int _get_infiniband(uint64_t *results, int *sources) +{ + if (sources == NULL) { + return 0; + } + + char buffer[128]; + + for (int i = 0; i < NB_SENSOR; i++) { + if (pread(sources[i], buffer, 127, 0) < 0) { + perror("pread"); + exit(1); + } + + results[i] = strtoull(buffer, NULL, 10); + } + + return NB_SENSOR; +} + +unsigned int init_infiniband(char *infi_path, void **ptr) +{ + if (infi_path == NULL) { + return 0; + } + + if (strcmp(infi_path, "X") == 0) { + + glob_t res; + + glob("/sys/class/infiniband/*/ports/*/counters/", 0, NULL, &res); + + if (res.gl_pathc == 0) { + fprintf(stderr, "No infiniband found.\n"); + return 0; + } + + infi_path = res.gl_pathv[0]; + } + + char *filenames[] = {"%s/port_rcv_packets", + "%s/port_rcv_data", + "%s/port_xmit_packets", + "%s/port_xmit_data" + }; + + Infiniband *state = malloc(sizeof(Infiniband)); + + char buffer[1024]; + for (int i = 0; i < NB_SENSOR; i++) { + snprintf(buffer, 1024, filenames[i], infi_path); + state->sources[i] = open(buffer, O_RDONLY); + } + + *ptr = (void *) state; + _get_infiniband(state->values, state->sources); + + return NB_SENSOR; +} + +unsigned int get_infiniband(uint64_t *results, void *ptr) +{ + Infiniband *state = (Infiniband *) ptr; + _get_infiniband(state->tmp_values, state->sources); + + for (int i = 0; i < NB_SENSOR; i++) { + results[i] = modulo_substraction(state->tmp_values[i], state->values[i]); + } + + memcpy(state->values, state->tmp_values, NB_SENSOR * sizeof(uint64_t)); + return NB_SENSOR; +} + +void clean_infiniband(void *ptr) +{ + Infiniband *state = (Infiniband *) ptr; + + if (state == NULL) { + return; + } + + for (int i = 0; i < NB_SENSOR; i++) { + close(state->sources[i]); + } + + free(state); +} + +char *_labels_infiniband[NB_SENSOR] = {"irxp", "irxb", "itxp", "itxb"}; +void label_infiniband(char **labels, void *none) +{ + UNUSED(none); + + for (int i = 0; i < NB_SENSOR; i++) { + labels[i] = _labels_infiniband[i]; + } +} diff --git a/src/infiniband.h b/src/infiniband.h new file mode 100644 index 0000000000000000000000000000000000000000..8098cf5c68773326e2bda38e3748e81dfb52ee0c --- /dev/null +++ b/src/infiniband.h @@ -0,0 +1,43 @@ +/******************************************************* + Copyright (C) 2018-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_infiniband(char *infi_path, void **ptr); +unsigned int get_infiniband(uint64_t *results, void *ptr); +void clean_infiniband(void *ptr); +void label_infiniband(char **labels, void *); + +Sensor infiniband = { + .init = init_infiniband, + .get = get_infiniband, + .clean = clean_infiniband, + .label = label_infiniband, + .nb_opt = 1, +}; + +Optparse infiniband_opt[1] = { + { + .longname = "monitor-infiniband", + .shortname = 'i', + .argtype = OPTPARSE_REQUIRED, + .usage_arg = "<infiniband_path>", + .usage_msg = "infiniband monitoring (if infiniband_path is X, tries to detect it automatically)", + }, +}; + diff --git a/src/load.c b/src/load.c new file mode 100644 index 0000000000000000000000000000000000000000..64502e587c1472e603d96d36f53a3068d67053e9 --- /dev/null +++ b/src/load.c @@ -0,0 +1,108 @@ +/******************************************************* + Copyright (C) 2019-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 <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <stdio.h> +#include "util.h" + +#define LOAD_BUFFER_SIZE 1024 +#define LOAD_VALUES_SIZE 10 +char buffer[LOAD_BUFFER_SIZE]; + +static int load_fid = -1; +static uint64_t load_values[LOAD_VALUES_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static uint64_t tmp_load_values[LOAD_VALUES_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static char *stat = "/proc/stat"; + +void _get_load(uint64_t *results) +{ + if (pread(load_fid, buffer, LOAD_BUFFER_SIZE - 1, 0) < 0) { + perror("pread"); + exit(1); + } + + int pos = 0; + + while (buffer[pos] > '9' || buffer[pos] < '0') { + pos++; + } + + for (int i = 0; i < LOAD_VALUES_SIZE; i++) { + results[i] = strtoull(buffer + pos, NULL, LOAD_VALUES_SIZE); + + while (buffer[pos] <= '9' && buffer[pos] >= '0') { + pos++; + } + + pos++; + } +} + +// Public interface + +unsigned int init_load(char *argument, void **state) +{ + UNUSED(argument); + UNUSED(state); + load_fid = open(stat, O_RDONLY); + if (load_fid < 0) { + fprintf(stderr, "%s ", stat); + perror("open"); + exit(1); + } + + _get_load(load_values); + return LOAD_VALUES_SIZE; +} + +unsigned int get_load(uint64_t *results, void *state) +{ + UNUSED(state); + _get_load(tmp_load_values); + + for (int i = 0; i < LOAD_VALUES_SIZE; i++) { + results[i] = tmp_load_values[i] - load_values[i]; + } + + memcpy(load_values, tmp_load_values, sizeof(load_values)); + return LOAD_VALUES_SIZE; +} + +void clean_load(void *state) +{ + UNUSED(state); + close(load_fid); +} + +char *_labels[LOAD_VALUES_SIZE] = { + "user", "nice", "system", "idle", "iowait", "irq", + "softirq", "steal", "guest", "guest_nice" +}; +void label_load(char **labels, void *none) +{ + UNUSED(none); + + for (int i = 0; i < LOAD_VALUES_SIZE; i++) { + labels[i] = _labels[i]; + } +} diff --git a/load.h b/src/load.h similarity index 61% rename from load.h rename to src/load.h index 42ce968062dcb68a13ebf67713eabc865abe1d7a..6d8d29804f93c6be0805b6670486210f74452fdf 100644 --- a/load.h +++ b/src/load.h @@ -1,5 +1,5 @@ /******************************************************* - Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr> + Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr> This file is part of Mojitos. @@ -18,7 +18,25 @@ *******************************************************/ -unsigned int init_load(char*, void **); -unsigned int get_load(uint64_t* results, void*); +unsigned int init_load(char *, void **); +unsigned int get_load(uint64_t *results, void *); void clean_load(void *); -void label_load(char **labels, void*); +void label_load(char **labels, void *); + +Sensor load = { + .init = init_load, + .get = get_load, + .clean = clean_load, + .label = label_load, + .nb_opt = 1, +}; + +Optparse load_opt[1] = { + { + .longname = "sysload", + .shortname = 'u', + .argtype = OPTPARSE_NONE, + .usage_arg = NULL, + .usage_msg = "system load", + }, +}; diff --git a/src/mojitos.c b/src/mojitos.c new file mode 100644 index 0000000000000000000000000000000000000000..e8cb6fa2a2a8100de93cfa40dee93ec20f3c0a93 --- /dev/null +++ b/src/mojitos.c @@ -0,0 +1,384 @@ +/******************************************************* + Copyright (C) 2018-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 <assert.h> +#include <inttypes.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include "util.h" + +#define OPTPARSE_IMPLEMENTATION +#define OPTPARSE_API static +#include "optparse.h" + +typedef unsigned int (*initializer_t)(char *, void **); +typedef void (*labeler_t)(char **, void *); +typedef unsigned int (*getter_t)(uint64_t *, void *); +typedef void (*cleaner_t)(void *); + +typedef struct Opt Opt; +typedef struct Sensor Sensor; +/* optparse typedef */ +typedef struct optparse_long Optparse; + +struct Sensor { + initializer_t init; + getter_t get; + cleaner_t clean; + labeler_t label; + int nb_opt; +}; + +int nb_defined_sensors = 0; + +#include "sensors.h" + +Sensor sensors[NB_SENSOR]; + +#define NB_OPT 5 +Optparse opts[NB_OPT + NB_SENSOR_OPT + 1] = { + { + .longname = "freq", .shortname = 'f', .argtype = OPTPARSE_REQUIRED, + .usage_arg = "<freq>", + .usage_msg = "set amount of measurements per second.", + }, + { + .longname = "time", .shortname = 't', .argtype = OPTPARSE_REQUIRED, + .usage_arg = "<time>", + .usage_msg = "set duration value (seconds). If 0, then loops infinitely.", + }, + { + .longname = "exec", .shortname = 'e', .argtype = OPTPARSE_REQUIRED, + .usage_arg = "<cmd> ...", + .usage_msg = "Execute a command with optional arguments.\n" + "\tIf this option is used, any usage of -t or -f is ignored.", + }, + { + .longname = "logfile", .shortname = 'o', .argtype = OPTPARSE_REQUIRED, + .usage_arg = "<file>", + .usage_msg = "specify a log file.", + }, + { + .longname = "overhead-stats", .shortname = 's', .argtype = OPTPARSE_NONE, + .usage_arg = NULL, + .usage_msg = "enable overhead statistics (nanoseconds).", + }, +}; + +void dumpopt(Optparse *opt) +{ + printf(".It Fl %c | Fl \\-%s", opt->shortname, opt->longname); + if (opt->usage_arg != NULL) { + printf(" Ar %s", opt->usage_arg); + } + printf("\n"); + printf("%s\n", opt->usage_msg); +} + +void dumpopts(Optparse *opts, size_t nb_opt, size_t nb_sensor_opt) +{ + size_t i; + + /* options */ + printf(".Pp\nOPTIONS:\n.Bl -tag -width Ds\n"); + for (i = 0; i < nb_opt; i++) { + dumpopt(&opts[i]); + } + printf(".El\n"); + + /* sensors */ + printf(".Pp\nSENSORS:\n.Bl -tag -width Ds\n"); + for (i++; i < nb_opt + nb_sensor_opt; i++) { + dumpopt(&opts[i]); + } + printf(".El\n"); +} + +void printopt(Optparse *opt) +{ + printf("-%c", opt->shortname); + printf("|--%s", opt->longname); + if (opt->usage_arg != NULL) { + printf(" %s", opt->usage_arg); + } + printf("\n\t%s\n", opt->usage_msg); +} + +void usage(char **argv) +{ + printf("Usage : %s [OPTIONS] [SENSOR ...] [-e <cmd> ...]\n", argv[0]); + + printf("\nOPTIONS:\n"); + for (int i = 0; i < NB_OPT; i++) { + printopt(&opts[i]); + } + + if (nb_defined_sensors == 0) { + // no captor to show + exit(EXIT_FAILURE); + } + + printf("\nSENSORS:\n"); + for (int i = 0; i < NB_SENSOR_OPT; i++) { + printopt(&opts[NB_OPT + i]); + } + + exit(EXIT_FAILURE); +} + +void sighandler(int none) +{ + UNUSED(none); +} + +void flush(int none) +{ + UNUSED(none); + exit(0); +} + +FILE *output; +void flushexit() +{ + if (output != NULL) { + fflush(output); + fclose(output); + } +} + +unsigned int nb_sources = 0; +void **states = NULL; +getter_t *getter = NULL; +cleaner_t *cleaner = NULL; + +unsigned int nb_sensors = 0; +char **labels = NULL; +uint64_t *values = NULL; + +void add_source(Sensor *cpt, char *arg) +{ + nb_sources++; + initializer_t init = cpt->init; + labeler_t labeler = cpt->label; + getter_t get = cpt->get; + cleaner_t clean = cpt->clean; + + states = realloc(states, nb_sources * sizeof(void *)); + int nb = init(arg, &states[nb_sources - 1]); + + if (nb == 0) { + nb_sources--; + states = realloc(states, nb_sources * sizeof(void *)); + return; + } + + getter = realloc(getter, nb_sources * sizeof(void *)); + getter[nb_sources - 1] = get; + cleaner = realloc(cleaner, nb_sources * sizeof(void *)); + cleaner[nb_sources - 1] = clean; + + labels = realloc(labels, (nb_sensors + nb) * sizeof(char *)); + labeler(labels + nb_sensors, states[nb_sources - 1]); + + values = realloc(values, (nb_sensors + nb) * sizeof(uint64_t)); + nb_sensors += nb; +} + +int main(int argc, char **argv) +{ + int total_time = 1; + int delta = 0; + int frequency = 1; + char **application = NULL; + int stat_mode = -1; + + init_sensors(opts, sensors, NB_OPT + NB_SENSOR_OPT, NB_OPT, &nb_defined_sensors); + + if (argc == 1) { + usage(argv); + } + + if (argc == 2 && strcmp(argv[1], "--dump-opts") == 0) { + dumpopts(opts, NB_OPT, NB_SENSOR_OPT); + exit(EXIT_SUCCESS); + } + + output = stdout; + + atexit(flushexit); + signal(15, flush); + + int opt; + struct optparse options; + options.permute = 0; + + optparse_init(&options, argv); + while ((opt = optparse_long(&options, opts, NULL)) != -1 && application == NULL) { + switch (opt) { + case 'f': + frequency = atoi(options.optarg); + break; + case 't': + total_time = atoi(options.optarg); + delta = 1; + if (total_time == 0) { + total_time = 1; + delta = 0; + } + break; + case 's': + stat_mode = 0; + break; + case 'o': + if ((output = fopen(options.optarg, "wb")) == NULL) { + perror("fopen"); + PANIC(1, "-o %s", options.optarg); + } + break; + case 'e': + application = options.argv; + signal(17, sighandler); + break; + default: { + int ismatch = 0; + int opt_idx = NB_OPT; + for (int i = 0; i < nb_defined_sensors && !ismatch; i++) { + for (int j = 0; j < sensors[i].nb_opt; j++) { + if (opt == opts[opt_idx].shortname) { + ismatch = 1; + if (opts[opt_idx].fn != NULL) { + (void) opts[opt_idx].fn(NULL, 0); + } else { + add_source(&sensors[i], options.optarg); + } + break; + } + opt_idx++; + } + } + if (!ismatch) { + fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); + usage(argv); + } + } + } + } + + setvbuf(output, NULL, _IONBF, BUFSIZ); + struct timespec ts; + struct timespec ts_ref; + + fprintf(output, "#timestamp "); + + for (unsigned int i = 0; i < nb_sensors; i++) { + fprintf(output, "%s ", labels[i]); + } + + if (stat_mode == 0) { + fprintf(output, "overhead "); + } + + fprintf(output, "\n"); + + unsigned long int stat_data = 0; + + for (int temps = 0; temps < total_time * frequency; temps += delta) { + clock_gettime(CLOCK_MONOTONIC, &ts_ref); + + // Get Data + unsigned int current = 0; + + for (unsigned int i = 0; i < nb_sources; i++) { + current += getter[i](&values[current], states[i]); + } + + if (application != NULL) { + + if (fork() == 0) { + execvp(application[0], application); + exit(0); + } + + pause(); + clock_gettime(CLOCK_MONOTONIC, &ts); + + if (ts.tv_nsec >= ts_ref.tv_nsec) { + fprintf(output, "%ld.%09ld ", (ts.tv_sec - ts_ref.tv_sec), ts.tv_nsec - ts_ref.tv_nsec); + } else { + fprintf(output, "%ld.%09ld ", (ts.tv_sec - ts_ref.tv_sec) - 1, 1000 * 1000 * 1000 + ts.tv_nsec - ts_ref.tv_nsec); + } + } else { +#ifdef DEBUG + clock_gettime(CLOCK_MONOTONIC, &ts); + fprintf(stderr, "%ld\n", (ts.tv_nsec - ts_ref.tv_nsec) / 1000); + //Indiv: mean: 148 std: 31 % med: 141 std: 28 % + //Group: mean: 309 std: 41 % med: 297 std: 39 % +#endif + + if (stat_mode == 0) { + clock_gettime(CLOCK_MONOTONIC, &ts); + + if (ts.tv_nsec >= ts_ref.tv_nsec) { + stat_data = ts.tv_nsec - ts_ref.tv_nsec; + } else { + stat_data = 1000 * 1000 * 1000 + ts.tv_nsec - ts_ref.tv_nsec; + } + } + + // Treat Data + fprintf(output, "%ld.%09ld ", ts_ref.tv_sec, ts_ref.tv_nsec); + } + + for (unsigned int i = 0; i < nb_sensors; i++) { + /* "PRIu64" is a format specifier to print uint64_t values */ + fprintf(output, "%" PRIu64 " ", values[i]); + } + + if (stat_mode == 0) { + fprintf(output, "%ld ", stat_data); + } + + fprintf(output, "\n"); + + if (application != NULL) { + break; + } + + clock_gettime(CLOCK_MONOTONIC, &ts); + usleep(1000 * 1000 / frequency - (ts.tv_nsec / 1000) % (1000 * 1000 / frequency)); + } + + for (unsigned int i = 0; i < nb_sources; i++) { + cleaner[i](states[i]); + } + + if (nb_sources > 0) { + free(getter); + free(cleaner); + free(labels); + free(values); + free(states); + } +} + diff --git a/src/network.c b/src/network.c new file mode 100644 index 0000000000000000000000000000000000000000..37d237376be4c893e2c29de8d160b61ba3190628 --- /dev/null +++ b/src/network.c @@ -0,0 +1,146 @@ +/******************************************************* + Copyright (C) 2018-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 <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include "util.h" + +#define NB_SENSOR 4 + +static char *route = "/proc/net/route"; +struct Network { + uint64_t values[NB_SENSOR]; + uint64_t tmp_values[NB_SENSOR]; + int sources[NB_SENSOR]; +}; +typedef struct Network Network; + +unsigned int _get_network(uint64_t *results, int *sources) +{ + if (sources == NULL) { + return 0; + } + + char buffer[128]; + + for (int i = 0; i < NB_SENSOR; i++) { + if (pread(sources[i], buffer, 127, 0) < 0) { + perror("pread"); + exit(1); + } + + results[i] = strtoull(buffer, NULL, 10); + } + + return NB_SENSOR; +} + + + +unsigned int init_network(char *dev, void **ptr) +{ + if (dev == NULL) { + return 0; + } + + if (strcmp(dev, "X") == 0) { + int fd = open(route, O_RDONLY); + + if (fd < 0) { + fprintf(stderr, "%s ", route); + perror("open"); + exit(1); + } + + char buffer[1000]; + + if (read(fd, buffer, 999) < 0 ) { + perror("read"); + close(fd); + exit(1); + } + + 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 *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" + }; + + Network *state = malloc(sizeof(Network)); + + char buffer2[256]; + for (int i = 0; i < NB_SENSOR; i++) { + snprintf(buffer2, 256, filenames[i], dev); + state->sources[i] = open(buffer2, O_RDONLY); + } + + *ptr = (void *) state; + _get_network(state->values, state->sources); + + return NB_SENSOR; +} + +unsigned int get_network(uint64_t *results, void *ptr) +{ + Network *state = (Network *) ptr; + _get_network(state->tmp_values, state->sources); + + for (int i = 0; i < NB_SENSOR; i++) { + results[i] = modulo_substraction(state->tmp_values[i], state->values[i]); + } + + memcpy(state->values, state->tmp_values, NB_SENSOR * sizeof(uint64_t)); + return NB_SENSOR; +} + +void clean_network(void *ptr) +{ + Network *state = (Network *) ptr; + + if (state == NULL) { + return; + } + + for (int i = 0; i < NB_SENSOR; i++) { + close(state->sources[i]); + } + + free(state); +} + +char *_labels_network[NB_SENSOR] = {"rxp", "rxb", "txp", "txb"}; +void label_network(char **labels, void *none) +{ + UNUSED(none); + + for (int i = 0; i < NB_SENSOR; i++) { + labels[i] = _labels_network[i]; + } +} diff --git a/src/network.h b/src/network.h new file mode 100644 index 0000000000000000000000000000000000000000..a158dbbce25333edf64c5f5dd0cde42856e08c70 --- /dev/null +++ b/src/network.h @@ -0,0 +1,42 @@ +/******************************************************* + Copyright (C) 2018-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_network(char *, void **); +unsigned int get_network(uint64_t *results, void *); +void clean_network(void *); +void label_network(char **labels, void *); + +Sensor network = { + .init = init_network, + .get = get_network, + .clean = clean_network, + .label = label_network, + .nb_opt = 1, +}; + +Optparse network_opt[1] = { + { + .longname = "net-dev", + .shortname = 'd', + .argtype = OPTPARSE_REQUIRED, + .usage_arg = "<net_dev>", + .usage_msg = "network monitoring (if network_device is X, tries to detect it automatically)", + }, +}; diff --git a/src/rapl.c b/src/rapl.c new file mode 100644 index 0000000000000000000000000000000000000000..3d13367a47fb009b7f0a0f1567a9169daf9a08a4 --- /dev/null +++ b/src/rapl.c @@ -0,0 +1,194 @@ +/******************************************************* + Copyright (C) 2022-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 <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <errno.h> +#include "util.h" + +#define MAX_HEADER 128 +#define BUFFER_SIZE 1024 + +char *get_rapl_string(const char *filename) +{ + int fd = open(filename, O_RDONLY); + if (fd == -1) { + return NULL; + } + + char *result = malloc(MAX_HEADER); + int nb = read(fd, result, MAX_HEADER); + close(fd); + result[nb - 1] = 0; + return result; +} + +void append(char *name, int i, size_t buffer_size) +{ + size_t name_len = strlen(name); + char *ptr = name + name_len; + + size_t remaining_space = buffer_size - name_len; + snprintf(ptr, remaining_space, "%d", i); +} + + +struct IntelRapl { + unsigned int nb; + char **names; + int *fids; + uint64_t *values; + uint64_t *tmp_values; + +}; +typedef struct IntelRapl IntelRapl; + + +void add_rapl_source(IntelRapl *rapl, char *name, char *energy_uj) +{ + rapl->nb += 1; + rapl->names = realloc(rapl->names, sizeof(char **)*rapl->nb); + rapl->fids = realloc(rapl->fids, sizeof(int *)*rapl->nb); + + rapl->names[rapl->nb - 1] = malloc(strlen(name) + 1); + strcpy(rapl->names[rapl->nb - 1], name); + //printf("%s\n", energy_uj); + + int fd = open(energy_uj, O_RDONLY); + + if (fd < 0) { + fprintf(stderr, "%s ", energy_uj); + perror("open"); + exit(1); + } + + rapl->fids[rapl->nb - 1] = fd; +} + + +void _get_rapl(uint64_t *values, IntelRapl *rapl) +{ + static char buffer[512]; + + for (unsigned int i = 0; i < rapl->nb; i++) { + + if (pread(rapl->fids[i], buffer, 100, 0) < 0) { + perror("pread"); + exit(1); + } + + values[i] = strtoull(buffer, NULL, 10); + } +} + + +unsigned int init_rapl(char *none, void **ptr) +{ + UNUSED(none); + IntelRapl *rapl = malloc(sizeof(IntelRapl)); + rapl->nb = 0; + rapl->names = NULL; + rapl->fids = NULL; + + char buffer[BUFFER_SIZE]; + char *name_base = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:%d/%s"; + char *name_sub = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:%d/intel-rapl:%d:%d/%s"; + + for (unsigned int i = 0;; i++) { + snprintf(buffer, BUFFER_SIZE, name_base, i, "name"); + char *tmp = get_rapl_string(buffer); + + if (tmp == NULL) { + break; + } + + append(tmp, i, MAX_HEADER); + snprintf(buffer, BUFFER_SIZE, name_base, i, "energy_uj"); + add_rapl_source(rapl, tmp, buffer); + free(tmp); + + for (unsigned int j = 0;; j++) { + snprintf(buffer, BUFFER_SIZE, name_sub, i, i, j, "name"); + char *tmp_sub = get_rapl_string(buffer); + + if (tmp_sub == NULL) { + break; + } + + append(tmp_sub, i, MAX_HEADER); + snprintf(buffer, BUFFER_SIZE, name_sub, i, i, j, "energy_uj"); + add_rapl_source(rapl, tmp_sub, buffer); + + free(tmp_sub); + } + } + + rapl->values = calloc(sizeof(uint64_t), rapl->nb); + rapl->tmp_values = calloc(sizeof(uint64_t), rapl->nb); + + _get_rapl(rapl->values, rapl); + + *ptr = (void *)rapl; + return rapl->nb; +} + + +unsigned int get_rapl(uint64_t *results, void *ptr) +{ + IntelRapl *state = (IntelRapl *) ptr; + _get_rapl(state->tmp_values, state); + + for (unsigned int i = 0; i < state->nb; i++) { + results[i] = modulo_substraction(state->tmp_values[i], state->values[i]); + } + + memcpy(state->values, state->tmp_values, sizeof(uint64_t)*state->nb); + return state->nb; +} + +void clean_rapl(void *ptr) +{ + IntelRapl *rapl = (IntelRapl *) ptr; + + for (unsigned int i = 0; i < rapl->nb; i++) { + free(rapl->names[i]); + close(rapl->fids[i]); + } + + free(rapl->names); + free(rapl->fids); + free(rapl->values); + free(rapl->tmp_values); + free(rapl); +} + + +void label_rapl(char **labels, void *ptr) +{ + IntelRapl *rapl = (IntelRapl *) ptr; + + for (unsigned int i = 0; i < rapl->nb; i++) { + labels[i] = rapl->names[i]; + } +} diff --git a/rapl.h b/src/rapl.h similarity index 61% rename from rapl.h rename to src/rapl.h index 9ec5f3afdc88d5f1bcdd4dcb5fdb4208261e2db0..7720f071a4be6b98dad5cb72d537bd69a430799d 100644 --- a/rapl.h +++ b/src/rapl.h @@ -1,5 +1,5 @@ /******************************************************* - Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr> + Copyright (C) 2022-2023 Georges Da Costa <georges.da-costa@irit.fr> This file is part of Mojitos. @@ -18,8 +18,26 @@ *******************************************************/ -unsigned int init_rapl(char*, void **); -unsigned int get_rapl(uint64_t* results, void*); +unsigned int init_rapl(char *, void **); +unsigned int get_rapl(uint64_t *results, void *); void clean_rapl(void *); -void label_rapl(char **labels, void*); +void label_rapl(char **labels, void *); + +Sensor rapl = { + .init = init_rapl, + .get = get_rapl, + .clean = clean_rapl, + .label = label_rapl, + .nb_opt = 1, +}; + +Optparse rapl_opt[1] = { + { + .longname = "intel-rapl", + .shortname = 'r', + .argtype = OPTPARSE_NONE, + .usage_arg = NULL, + .usage_msg = "INTEL RAPL", + }, +}; diff --git a/src/temperature.c b/src/temperature.c new file mode 100644 index 0000000000000000000000000000000000000000..1dc1099d15e73128ec9d5124b521b958ae9efe46 --- /dev/null +++ b/src/temperature.c @@ -0,0 +1,177 @@ +/******************************************************* + Copyright (C) 2018-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 <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include "util.h" + +#define BUFFER_SIZE 512 + +struct Temperature { + char **label_list; + int *fid_list; + int nb_elem; +}; +typedef struct Temperature Temperature; + +int get_string(char *filename, char *buffer, int max_size) +{ + int fid = open(filename, O_RDONLY); + + //printf("Tries to open : %s : %d\n", filename, fid); + if (fid == -1) { + return -1; + } + + int nb = read(fid, buffer, max_size); + if (nb == -1) { + close(fid); + return -1; + } + + buffer[nb] = 0; + close(fid); + return 0; +} + +void add_to_list(char ***list_name, char *source, int nb_elem) +{ + //printf("Adds: %s\n", source); + *list_name = realloc(*list_name, (nb_elem + 1) * sizeof(char *)); + (*list_name)[nb_elem] = malloc(strlen(source) + 1); + strcpy((*list_name)[nb_elem], source); + +} + +void add_temperature_sensor(int id_rep, Temperature *state) +{ + static int key = 0; + static char buffer_filename[BUFFER_SIZE]; + static char buffer_label[BUFFER_SIZE]; + + int delta = snprintf(buffer_label, BUFFER_SIZE, "Temp_%d_", key); + + for (int i = 1;; i++) { + snprintf(buffer_filename, BUFFER_SIZE, "/sys/class/hwmon/hwmon%d/temp%d_label", id_rep, i); + + if (get_string(buffer_filename, buffer_label + delta, 100) == -1) { + break; + } + + for (unsigned int pos = 0; pos < strlen(buffer_label); pos++) { + if (buffer_label[pos] == ' ') { + buffer_label[pos] = '_'; + } + + if (buffer_label[pos] == '\n') { + buffer_label[pos] = '\0'; + } + } + + add_to_list(&state->label_list, buffer_label, state->nb_elem); + + snprintf(buffer_filename, BUFFER_SIZE, "/sys/class/hwmon/hwmon%d/temp%d_input", id_rep, i); + state->fid_list = realloc(state->fid_list, (state->nb_elem + 1) * sizeof(int)); + int fd = open(buffer_filename, O_RDONLY); + + if (fd < 0) { + fprintf(stderr, "%s ", buffer_filename); + perror("open"); + exit(1); + } + + state->fid_list[state->nb_elem] = fd; + state->nb_elem++; + // printf("%s : %s\n", buffer_label, buffer_filename); + } + + key++; +} + +unsigned int init_temperature(char *args, void **ptr) +{ + UNUSED(args); + Temperature *state = malloc(sizeof(Temperature)); + state->nb_elem = 0; + state->label_list = NULL; + state->fid_list = NULL; + + char base_name[] = "/sys/class/hwmon/hwmon%d/name"; + static char name[BUFFER_SIZE]; + static char buffer[BUFFER_SIZE]; + + int i = 0; + snprintf(name, BUFFER_SIZE, base_name, i); + + while (get_string(name, buffer, 8) != -1) { + if (strcmp(buffer, "coretemp") == 0) { + add_temperature_sensor(i, state); + } + + i++; + snprintf(name, BUFFER_SIZE, base_name, i); + } + + *ptr = (void *) state; + return state->nb_elem; +} + +unsigned int get_temperature(uint64_t *results, void *ptr) +{ + Temperature *state = (Temperature *)ptr; + static char buffer[BUFFER_SIZE]; + + for (int i = 0; i < state->nb_elem; i++) { + if (pread(state->fid_list[i], buffer, 100, 0) < 0) { + perror("pread"); + exit(1); + } + results[i] = strtoull(buffer, NULL, 10); + } + + return state->nb_elem; +} + +void clean_temperature(void *ptr) +{ + Temperature *state = (Temperature *)ptr; + + for (int i = 0; i < state->nb_elem; i++) { + free(state->label_list[i]); + close(state->fid_list[i]); + } + + free(state->label_list); + free(state->fid_list); + free(state); +} + +void label_temperature(char **labels, void *ptr) +{ + Temperature *state = (Temperature *)ptr; + + for (int i = 0; i < state->nb_elem; i++) { + labels[i] = state->label_list[i]; + } +} diff --git a/temperature.h b/src/temperature.h similarity index 58% rename from temperature.h rename to src/temperature.h index c6c34df6c612e709764e2b7d9dfd37ec234ccbf5..e70554d17ab7878b788bbee3afa96da72620cfe2 100644 --- a/temperature.h +++ b/src/temperature.h @@ -1,5 +1,5 @@ /******************************************************* - Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr> + Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr> This file is part of Mojitos. @@ -18,7 +18,25 @@ *******************************************************/ -unsigned int init_temperature(char*, void **); -unsigned int get_temperature(uint64_t* results, void*); +unsigned int init_temperature(char *, void **); +unsigned int get_temperature(uint64_t *results, void *); void clean_temperature(void *); -void label_temperature(char **labels, void*); +void label_temperature(char **labels, void *); + +Sensor temperature = { + .init = init_temperature, + .get = get_temperature, + .clean = clean_temperature, + .label = label_temperature, + .nb_opt = 1, +}; + +Optparse temperature_opt[1] = { + { + .longname = "cpu-temp", + .shortname = 'c', + .argtype = OPTPARSE_NONE, + .usage_arg = NULL, + .usage_msg = "processor temperature" + }, +}; diff --git a/frapl.h b/src/util.c similarity index 77% rename from frapl.h rename to src/util.c index 6b2d15424723ba2f670718525e294aa75eaba90e..5e835a7f21583cdca51707b97ecf122aa3e4910f 100644 --- a/frapl.h +++ b/src/util.c @@ -16,10 +16,13 @@ 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_frapl(char*, void **); -unsigned int get_frapl(uint64_t* results, void*); -void clean_frapl(void *); -void label_frapl(char **labels, void*); +#include "util.h" + +uint64_t modulo_substraction(const uint64_t lhs, const uint64_t rhs) +{ + return lhs >= rhs ? (lhs - rhs) + : (UINT64_MAX - rhs + 1) + lhs; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000000000000000000000000000000000000..e422d070693cd28af9613e77a5efdb917ce6b2f6 --- /dev/null +++ b/src/util.h @@ -0,0 +1,51 @@ +/******************************************************* + 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/>. + +*******************************************************/ + +#ifndef _UTIL_H +#define _UTIL_H + +#include <stdint.h> + +#define CASSERT(predicate, file) _impl_CASSERT_LINE(predicate,__LINE__,file) + +#define _impl_PASTE(a,b) a##b +#define _impl_CASSERT_LINE(predicate, line, file) \ + typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1]; + +#define UNUSED(expr) do { (void)(expr); } while (0) +#define PANIC(code, fmt, ...) \ + do { \ + fprintf(stderr, "Exit on error: "); \ + fprintf(stderr, fmt, ##__VA_ARGS__); \ + fprintf(stderr, "\n"); \ + exit(code); \ + } while (0) + + +/** + * @brief Substracts lhs by rhs, assuming that lhs is a cyclic increment from rhs, + * meaning that if rhs is greater, lhs's value overflowed. + * @param lhs + * @param rhs + * @return uint64_t + */ +uint64_t modulo_substraction(const uint64_t lhs, const uint64_t rhs); + +#endif diff --git a/temperature.c b/temperature.c deleted file mode 100644 index 142e86ae61604d920c92563196cffef9ad043386..0000000000000000000000000000000000000000 --- a/temperature.c +++ /dev/null @@ -1,137 +0,0 @@ -/******************************************************* - Copyright (C) 2018-2021 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 <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <string.h> -#include <stdio.h> -#include <stdint.h> - -struct temperature_t { - char **label_list; - int *fid_list; - int nb_elem; -}; - -int get_string(char *filename, char *buffer, int max_size) { - int fid = open(filename, O_RDONLY); - //printf("Tries to open : %s : %d\n", filename, fid); - if(fid == -1) - return -1; - - int nb = read(fid, buffer, max_size); - if(nb == -1) { - close(fid); - return -1; - } - - buffer[nb]=0; - close(fid); - return 0; -} - -void add_to_list(char ***list_name, char *source, int nb_elem) { - //printf("Adds: %s\n", source); - *list_name = realloc(*list_name, (nb_elem+1)*sizeof(char*)); - (*list_name)[nb_elem] = malloc(strlen(source)+1); - strcpy((*list_name)[nb_elem], source); - -} - -void add_temperature_sensor(int id_rep, struct temperature_t *state) { - static int key=0; - static char buffer_filename[512]; - static char buffer_label[512]; - - int delta = sprintf(buffer_label, "Temp_%d_", key); - - for(int i=1;;i++) { - sprintf(buffer_filename, "/sys/class/hwmon/hwmon%d/temp%d_label", id_rep, i); - if(get_string(buffer_filename, buffer_label+delta, 100) == -1) - break; - - for(int pos = 0; pos < strlen(buffer_label); pos++) { - if (buffer_label[pos] == ' ') buffer_label[pos] = '_'; - if (buffer_label[pos] == '\n') buffer_label[pos] = '\0'; - } - add_to_list(&state->label_list, buffer_label, state->nb_elem); - - sprintf(buffer_filename, "/sys/class/hwmon/hwmon%d/temp%d_input", id_rep, i); - state->fid_list = realloc(state->fid_list, (state->nb_elem+1)*sizeof(int)); - state->fid_list[state->nb_elem] = open(buffer_filename, O_RDONLY); - - state->nb_elem++; - // printf("%s : %s\n", buffer_label, buffer_filename); - } - - key++; -} - -unsigned int init_temperature(char *args, void** ptr) { - struct temperature_t *state = malloc(sizeof(struct temperature_t)); - state->nb_elem = 0; - state->label_list = NULL; - state->fid_list = NULL; - - char base_name[] = "/sys/class/hwmon/hwmon%d/name"; - static char name[512]; - static char buffer[512]; - - int i = 0; - sprintf(name, base_name, i); - while(get_string(name, buffer, 8) != -1) { - if (strcmp(buffer, "coretemp")==0) - add_temperature_sensor(i, state); - - i++; - sprintf(name, base_name, i); - } - *ptr = (void*) state; - return state->nb_elem; -} - -unsigned int get_temperature(uint64_t* results, void* ptr) { - struct temperature_t *state = (struct temperature_t*)ptr; - static char buffer[512]; - for(int i=0; i<state->nb_elem; i++) { - pread(state->fid_list[i], buffer, 100, 0); - results[i] = strtoull(buffer, NULL, 10); - } - return state->nb_elem; -} - -void clean_temperature(void* ptr) { - struct temperature_t *state = (struct temperature_t*)ptr; - - for(int i=0; i<state->nb_elem; i++) { - free(state->label_list[i]); - close(state->fid_list[i]); - } - free(state->label_list); - free(state->fid_list); - free(state); -} - -void label_temperature(char**labels, void* ptr) { - struct temperature_t *state = (struct temperature_t*)ptr; - for(int i=0; i<state->nb_elem; i++) - labels[i] = state->label_list[i]; -} diff --git a/tests/amd_rapl.c b/tests/amd_rapl.c new file mode 100644 index 0000000000000000000000000000000000000000..7c05ab5826954a7f4d7127c9fa3b18896af31e86 --- /dev/null +++ b/tests/amd_rapl.c @@ -0,0 +1,349 @@ +/******************************************************* + 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/amd_rapl.c" + +TFUNCTION(test_raw_to_microjoule, { + uint64_t raw = 0; + uint64_t unit = 0; + uint64_t result = 0; + uint64_t expected = 0; + + // Test 1: + // -- Setup + raw = 100; + unit = 1000; + expected = 390625; + // -- Run + result = raw_to_microjoule(raw, unit); + // -- Verification + TEST_UINT64_T(&result, &expected); + + // TEST 2: + // -- Setup + raw = 200; + unit = 1; + expected = 100000000; + // -- Run + result = raw_to_microjoule(raw, unit); + // -- Verification + TEST_UINT64_T(&result, &expected); + + // TEST 3: + // -- Setup + raw = 500; + unit = 2; + expected = 125000000; + // -- Run + result = raw_to_microjoule(raw, unit); + // -- Verification + TEST_UINT64_T(&result, &expected); + + // TEST 4: + // -- Setup + raw = 1000; + unit = 3; + expected = 125000000; + // -- Run + result = raw_to_microjoule(raw, unit); + // -- Verification + TEST_UINT64_T(&result, &expected); + + // TEST 5: + // -- Setup + raw = 10000; + unit = 4; + expected = 625000000; + // -- Run + result = raw_to_microjoule(raw, unit); + // -- Verification + TEST_UINT64_T(&result, &expected); +}) + +TFUNCTION(test_get_name, { + size_t cpu_id = 0; + char *result = NULL; + char expected[100]; + + // TEST 1: + // -- Setup + cpu_id = 0; + strcpy(expected, "core0"); + // -- Run + result = get_name(cpu_id); + // -- Verification + TEST_STR(result, expected); + free(result); + + // TEST 2: + // -- Setup + cpu_id = 10000; + strcpy(expected, "core10000"); + // -- Run + result = get_name(cpu_id); + // -- Verification + TEST_STR(result, expected); + free(result); +}) + +#define NONE 0 +#define DUMMY_SENSOR(__sensor, __cpu_id, __name) \ +do { \ + __sensor = (CpuSensor) { \ + .cpu_id = __cpu_id, \ + .package_id = NONE, \ + .core_id = NONE, \ + .name = __name, \ + .fd = NONE, \ + .energy_units = NONE, \ + .core_energy = NONE, \ + }; \ +} while(0); + +#define DUMMY_RAPL(__rapl, __sensors, __sensors_count) \ +do { \ + __rapl = (AmdRapl) { \ + .sensors = __sensors, \ + .sensor_count = __sensors_count \ + }; \ +} while(0); + +TFUNCTION(test_label_amd_rapl, { + CpuSensor sensors[100]; + AmdRapl rapl; + char *results[100]; + char expecteds[10][100]; + uint64_t nb = 0; + + // Test 1: + // -- Setup + nb = 1; + DUMMY_SENSOR(sensors[0], 0, "core0"); + DUMMY_RAPL(rapl, sensors, nb); + strcpy(expecteds[0], "core0"); + // -- Run + label_amd_rapl(results, (void *) &rapl); + // -- Verification + for(unsigned int i = 0; i < nb; i++) + { + TEST_STR(results[i], expecteds[i]); + } + + // Test 2: + // -- Setup + nb = 4; + DUMMY_SENSOR(sensors[0], 0, "core0"); + DUMMY_SENSOR(sensors[1], 1, "core1"); + DUMMY_SENSOR(sensors[2], 2, "core2"); + DUMMY_SENSOR(sensors[3], 3, "core3"); + DUMMY_RAPL(rapl, sensors, nb); + strcpy(expecteds[0], "core0"); + strcpy(expecteds[1], "core1"); + strcpy(expecteds[2], "core2"); + strcpy(expecteds[3], "core3"); + // -- Run + label_amd_rapl(results, (void *) &rapl); + // -- Verification + for(unsigned int i = 0; i < nb; i++) + { + TEST_STR(results[i], expecteds[i]); + } + // Test 3: + // -- Setup + nb = 4; + DUMMY_SENSOR(sensors[0], 0, "core0"); + DUMMY_SENSOR(sensors[1], 3, "core3"); + DUMMY_SENSOR(sensors[2], 1, "core1"); + DUMMY_SENSOR(sensors[3], 2, "core2"); + DUMMY_RAPL(rapl, sensors, nb); + strcpy(expecteds[0], "core0"); + strcpy(expecteds[1], "core3"); + strcpy(expecteds[2], "core1"); + strcpy(expecteds[3], "core2"); + // -- Run + label_amd_rapl(results, (void *) &rapl); + // -- Verification + for(unsigned int i = 0; i < nb; i++) + { + TEST_STR(results[i], expecteds[i]); + } +}) + + +#define DUMMY_CPUINFO(__sensor, __cpu_id, __package_id, __core_id) \ +do { \ + __sensor = (CpuSensor) { \ + .cpu_id = __cpu_id, \ + .package_id = __package_id, \ + .core_id = __core_id, \ + .name = NULL, \ + .fd = NONE, \ + .energy_units = NONE, \ + .core_energy = NONE \ + }; \ +} while (0); + +TFUNCTION(test_is_duplicate, { + static const unsigned int nb_core = 4; + static const unsigned int nb_package = 2; + static const unsigned int max_cpu = 10; + + unsigned char map[nb_core][nb_package]; + CpuSensor cpu_information[max_cpu]; + unsigned int results[max_cpu]; + unsigned int expecteds[max_cpu]; + + // -- Setup + memset(map, NONE, sizeof(map)); + memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu); + DUMMY_CPUINFO(cpu_information[0], 0, 1, 1); + expecteds[0] = 1; + expecteds[1] = 0; + // -- Run + results[0] = is_duplicate(&cpu_information[0], nb_core, nb_package,map); + results[1] = is_duplicate(&cpu_information[0], nb_core, nb_package,map); + // -- Verification + TEST_BOOL(&results[0], &expecteds[0]); + TEST_BOOL(&results[1], &expecteds[1]); + + // -- Setup + memset(map, NONE, sizeof(map)); + memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu); + DUMMY_CPUINFO(cpu_information[0], 0, 1, 1); + DUMMY_CPUINFO(cpu_information[1], 0, 1, 1); + expecteds[0] = 1; + expecteds[1] = 0; + // -- Run + results[0] = is_duplicate(&cpu_information[0], nb_core, nb_package, map); + results[1] = is_duplicate(&cpu_information[1], nb_core, nb_package, map); + // -- Verification + TEST_BOOL(&results[0], &expecteds[0]); + TEST_BOOL(&results[1], &expecteds[1]); + + // -- Setup + memset(map, NONE, sizeof(map)); + memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu); + DUMMY_CPUINFO(cpu_information[0], 0, 1, 1); + DUMMY_CPUINFO(cpu_information[1], 0, 0, 0); + expecteds[0] = 1; + expecteds[1] = 1; + // -- Run + results[0] = is_duplicate(&cpu_information[0], nb_core, nb_package, map); + results[1] = is_duplicate(&cpu_information[1], nb_core, nb_package, map); + // -- Verification + TEST_BOOL(&results[0], &expecteds[0]); + TEST_BOOL(&results[1], &expecteds[1]); + + // -- Setup + memset(map, NONE, sizeof(map)); + memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu); + DUMMY_CPUINFO(cpu_information[0], 0, 1, 1); + DUMMY_CPUINFO(cpu_information[1], 0, 1, 0); + expecteds[0] = 1; + expecteds[1] = 1; + // -- Run + results[0] = is_duplicate(&cpu_information[0], nb_core, nb_package, map); + results[1] = is_duplicate(&cpu_information[1], nb_core, nb_package, map); + // -- Verification + TEST_BOOL(&results[0], &expecteds[0]); + TEST_BOOL(&results[1], &expecteds[1]); + + // -- Setup + memset(map, NONE, sizeof(map)); + memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu); + DUMMY_CPUINFO(cpu_information[0], 0, 1, 1); + DUMMY_CPUINFO(cpu_information[1], 0, 0, 1); + expecteds[0] = 1; + expecteds[1] = 1; + // -- Run + results[0] = is_duplicate(&cpu_information[0], nb_core, nb_package, map); + results[1] = is_duplicate(&cpu_information[1], nb_core, nb_package, map); + // -- Verification + TEST_BOOL(&results[0], &expecteds[0]); + TEST_BOOL(&results[1], &expecteds[1]); + + // -- Setup + memset(map, NONE, sizeof(map)); + memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu); + DUMMY_CPUINFO(cpu_information[0], 0, 0, 0); + DUMMY_CPUINFO(cpu_information[1], 0, 0, 1); + DUMMY_CPUINFO(cpu_information[2], 0, 1, 0); + DUMMY_CPUINFO(cpu_information[3], 0, 1, 1); + DUMMY_CPUINFO(cpu_information[4], 0, 0, 0); + DUMMY_CPUINFO(cpu_information[5], 0, 0, 1); + DUMMY_CPUINFO(cpu_information[6], 0, 1, 0); + DUMMY_CPUINFO(cpu_information[7], 0, 1, 1); + memset(expecteds, 1, sizeof(unsigned int) * 4); + memset(&expecteds[4], 0, sizeof(unsigned int) * 4); + // -- Run + for (unsigned int i = 0; i < 8; i++) + { + results[i] = is_duplicate(&cpu_information[i], nb_core, nb_package, map ); + } + // -- Verification + for(unsigned int i = 0; i < 8; i++) + { + TEST_BOOL(&results[i], &expecteds[i]); + } +}) + + +TFILE_ENTRY_POINT(test_amd_rapl, { + CALL_TFUNCTION(test_raw_to_microjoule); + CALL_TFUNCTION(test_get_name); + CALL_TFUNCTION(test_label_amd_rapl); + CALL_TFUNCTION(test_is_duplicate); +}) + +#ifdef __TESTING__AMD__ +int main() +{ + static const unsigned int time = 10; + AmdRapl *rapl = NULL; + unsigned int count_cpu = init_amd_rapl(NULL, (void **) &rapl); + uint64_t results[count_cpu]; + char *labels[count_cpu]; + + label_amd_rapl(labels, (void *) rapl); + + for (unsigned int i = 0; i < rapl->sensor_count; ++i) { + printf("%s ", labels[i]); + } + printf("\n"); + + // -- Run + + for (unsigned int i = 0; i < time; ++i) { + sleep(1); + get_amd_rapl(results, (void *)rapl); + + for (unsigned int j = 0; j < rapl->sensor_count; ++j) { + printf("%ld ", results[j]); + } + printf("\n"); + } + + clean_amd_rapl(rapl); + return 0; +} + +#endif + diff --git a/tests/info_reader.c b/tests/info_reader.c new file mode 100644 index 0000000000000000000000000000000000000000..5000f485e7218676974af828bc75d33da474ddd4 --- /dev/null +++ b/tests/info_reader.c @@ -0,0 +1,284 @@ +/******************************************************* + 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 "./../lib/info_reader.h" + +TFUNCTION(test_replace_first, { + // useful variables : + char result[100]; + char expected[100]; + + // -- Setup + strcpy(result, "This is my string"); + strcpy(expected, "ThIs is my string"); + // -- Run + replace_first(result, 'i', 'I'); + // -- Verification + TEST_STR(result, expected); + + // -- Setup + strcpy(result, "This is my string"); + strcpy(expected, "This is my string"); + // -- Run + replace_first(result, 'x', 'X'); + // -- Verification + TEST_STR(result, expected); + + // -- Setup + strcpy(result, "This is my string"); + strcpy(expected, "This_is my string"); + // -- Run + replace_first(result, ' ', '_'); + // -- Verification + TEST_STR(result, expected); + + // -- Setup + strcpy(result, "This is my string"); + strcpy(expected, "This_is my string"); + // -- Run + replace_first(result, ' ', '_'); + // -- Verification + TEST_STR(result, expected); + + // -- Setup + strcpy(result, "This is my string"); + strcpy(expected, "This is my string"); + // -- Run + replace_first(result, 'T', 'T'); + // -- Verification + TEST_STR(result, expected); +}) + +TFUNCTION(test_split_on_delimiter, { + // Useful variables + char string[100]; + char *result_key; + char *result_value; + char expected_key[100]; + char expected_value[100]; + + // Setup + strcpy(string, "key:value"); + strcpy(expected_key, "key"); + strcpy(expected_value, "value"); + // Run + split_on_delimiter(string, ":", &result_key, &result_value); + // Verification + TEST_STR(result_key, expected_key); + TEST_STR(result_value, expected_value); + + // Setup + strcpy(string, "key: value"); + strcpy(expected_key, "key"); + strcpy(expected_value, " value"); + // Run + split_on_delimiter(string, ":", &result_key, &result_value); + // Verification + TEST_STR(result_key, expected_key); + TEST_STR(result_value, expected_value); + + // Setup + strcpy(string, "key:value"); + strcpy(expected_key, "key"); + strcpy(expected_value, "value"); + replace_first(string, ':', ' '); + // Run + split_on_delimiter(string, " ", &result_key, &result_value); + // Verification + TEST_STR(result_key, expected_key); + TEST_STR(result_value, expected_value); + + // Setup + strcpy(string, ""); + // Run + split_on_delimiter(string, ":", &result_key, &result_value); + // Verification + TEST_STR(result_key, NULL); + TEST_STR(result_value, NULL); + + // Setup + strcpy(string, "key:value:extra"); + strcpy(expected_key, "key"); + strcpy(expected_value, "value:extra"); + // Run + split_on_delimiter(string, ":", &result_key, &result_value); + // Verification + TEST_STR(result_key, expected_key); + TEST_STR(result_value, expected_value); + + // Setup + strcpy(string, "key: value :extra"); + strcpy(expected_key, "key"); + strcpy(expected_value, " value :extra"); + // Run + split_on_delimiter(string, ":", &result_key, &result_value); + // Verification + TEST_STR(result_key, expected_key); + TEST_STR(result_value, expected_value); +}) + +TFUNCTION(test_start_with, { + char *prefix = NULL; + char *string = NULL; + bool result = false; + bool _true = true; + bool _false = false; + + prefix = "Hello"; + string = "Hello World"; + result = start_with(prefix, string); + TEST_BOOL(&result, &_true); + + prefix = "Goodbye"; + string = "Hello World"; + result = start_with(prefix, string); + TEST_BOOL(&result, &_false); + + prefix = "Hello World"; + string = "Hello"; + result = start_with(prefix, string); + TEST_BOOL(&result, &_false); + + prefix = "Hello"; + string = "Hello"; + result = start_with(prefix, string); + TEST_BOOL(&result, &_true); + + prefix = NULL; + string = "Hello World"; + result = start_with(prefix, string); + TEST_BOOL(&result, &_false); +}) + +#define NONE 0 +#define DUMMY_KEYFINDER(__key_finder, __key, __delimiter) \ + do { \ + __key_finder = (KeyFinder) { \ + .key = __key, \ + .delimiter = __delimiter, \ + .copy = NONE, \ + .set = NONE \ + }; \ + } while (0); + +#define DUMMY_PARSER(__parser, __keys, __nb_keys) \ + do { \ + __parser = (Parser) { \ + .storage = NONE, \ + .nb_stored = NONE, \ + .capacity = NONE, \ + .storage_struct_size = NONE, \ + .keys = __keys, \ + .nb_keys = __nb_keys, \ + .file = NONE \ + }; \ + } while (0); + + +TFUNCTION(test_match, { + // useful variables : + bool _true = true; + bool _false = false; + KeyFinder keys[10]; + char line[100]; + Parser parser; + bool result; + KeyFinder *found_key_finder = NULL; + char *raw_value = NULL; + + // Test 1: + // -- Setup + DUMMY_KEYFINDER(keys[0], "key", ": "); + DUMMY_PARSER(parser, keys, 1); + strcpy(line, "key: value"); + found_key_finder = NULL; + raw_value = NULL; + // -- Run + result = match(&parser, line, &found_key_finder, &raw_value); + // -- Verification + TEST_BOOL(&result, &_true); + TEST_PTR(found_key_finder, &keys[0]); + TEST_STR(raw_value, "value"); + + // Test 2: + // -- Setup + DUMMY_KEYFINDER(keys[0], "key", ": "); + DUMMY_PARSER(parser, keys, 1) + strcpy(line, "not a key: value"); + found_key_finder = NULL; + raw_value = NULL; + // -- Run + result = match(&parser, line, &found_key_finder, &raw_value); + // -- Verification + TEST_BOOL(&result, &_false); + TEST_PTR(found_key_finder, NULL); + TEST_STR(raw_value, NULL); + + // Test 3: + // -- Setup + DUMMY_KEYFINDER(keys[0],"key", ": "); + DUMMY_PARSER(parser, keys, 1); + strcpy(line, "key:value"); + found_key_finder = NULL; + raw_value = NULL; + // -- Run + result = match(&parser, line, &found_key_finder, &raw_value); + // -- Verification + TEST_BOOL(&result, &_false); + TEST_PTR(found_key_finder, NULL); + TEST_STR(raw_value, NULL); + + // Test 4: + // -- Setup + DUMMY_KEYFINDER(keys[0], "key", ": "); + DUMMY_KEYFINDER(keys[1], "second_key", ": "); + DUMMY_PARSER(parser, keys, 2); + strcpy(line, "second_key: value"); + found_key_finder = NULL; + raw_value = NULL; + // -- Run + result = match(&parser, line, &found_key_finder, &raw_value); + // -- Verification + TEST_BOOL(&result, &_true); + TEST_PTR(found_key_finder, &keys[1]); + TEST_STR(raw_value, "value"); + + // Test 5: + // -- Setup + DUMMY_KEYFINDER(keys[0], "key", ": "); + DUMMY_PARSER(parser, keys, 1); + strcpy(line, ""); + found_key_finder = NULL; + raw_value = NULL; + // -- Run + result = match(&parser, line, &found_key_finder, &raw_value); + TEST_BOOL(&result, &_false); + TEST_PTR(found_key_finder, NULL); + TEST_STR(raw_value, NULL); +}) + +TFILE_ENTRY_POINT(test_info_reader, { + CALL_TFUNCTION(test_replace_first); + CALL_TFUNCTION(test_split_on_delimiter); + CALL_TFUNCTION(test_start_with); + CALL_TFUNCTION(test_match); +}) + diff --git a/network.h b/tests/main.c similarity index 71% rename from network.h rename to tests/main.c index 44759dcf883ac66c90aafdf6447d00d6b02e9a39..3246f0429a7ff7ad065070ac195428af7e099872 100644 --- a/network.h +++ b/tests/main.c @@ -1,5 +1,5 @@ /******************************************************* - Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr> + Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr> This file is part of Mojitos. @@ -16,10 +16,14 @@ 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_network(char*, void **); -unsigned int get_network(uint64_t* results, void*); -void clean_network(void *); -void label_network(char **labels, void*); +#include "util.c" +#include "amd_rapl.c" +#include "info_reader.c" +TMAIN({ + CALL_TFUNCTION(test_util); + CALL_TFUNCTION(test_amd_rapl); + CALL_TFUNCTION(test_info_reader); +}) diff --git a/tests/small_test.h b/tests/small_test.h new file mode 100644 index 0000000000000000000000000000000000000000..66c4de9119ddd3f67b0b9db85e299487074fd6e1 --- /dev/null +++ b/tests/small_test.h @@ -0,0 +1,393 @@ +/******************************************************* + 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/>. + +*******************************************************/ + +#ifndef __SMALL_TEST_H +#define __SMALL_TEST_H + +#include <stdbool.h> +#include <string.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> + +#include "../src/util.h" + +#define FMT_BUFFER_SIZE 1000 + +// ---------------------------API_INTERFACE +/** + * @def TMAIN(__code) + * + * @brief Define the entry point for the tests. + * It initialises any useful variables and acts as the main one. + * + * @param __code The code that contains the calls to the test functions. + */ + +#define TMAIN(__code) \ + int main() \ +{ \ + unsigned int __indentation_level = 0; \ + INDENTED_PRINT("%s:%s\n", __FILE__, __func__); \ + unsigned int __error_counter__ = 0; \ + do __code while (0); \ + DEFERRED_ERROR(__error_counter__); \ + return __error_counter__; \ +} + +/** + * @def TFILE_ENTRY_POINT(__filename, __code) + * + * @brief Define the entry point of a test file. + * This macro is used to define the entry point of a test file. + * It defines a function with the specified __filename that contains the test code specified in code. + * + * When the function is called, it initialises the test file using the INIT_TEST_FILE macro, + * declares an integer variable __error_counter__ to keep track of any errors encountered during the tests, + * executes the test code in a do-while loop, and then checks for any deferred errors using the DEFERRED_ERROR macro. + * The function returns the value of __error_counter__, + * which indicates the number of errors encountered during the tests. + * + * @param __filename The name of the function to be used as an entry point for the test file. + * @param __code The test code to be executed in the function. + */ +#define TFILE_ENTRY_POINT(__filename, __code) \ + int __filename (unsigned int __indentation_level) \ +{ \ + INIT_TEST_FILE(); \ + int __error_counter__ = 0; \ + do __code while(0); \ + DEFERRED_ERROR(__error_counter__); \ + return __error_counter__; \ +} + +/** + * @def TFUNCTION(__function_name, __code) + * + * @brief Define a test function within a test file. + * This macro is used to define a test function within a test file. + * It defines a function with the given __function_name containing the test code specified in __code. + * + * When the function is called, it initialises the test function using the INIT_TEST_FUNCTION macro, + * declares an integer variable __error_counter__ to keep track of any errors encountered during the tests, + * executes the test code in a do-while loop, and then checks for any deferred errors using the DEFERRED_ERROR macro. + * The function returns the value of __error_counter__, which indicates the number of errors encountered during the tests. + * + * @param __function_name The name of the test function. + * @param __code The test code to be executed in the function. + */ +#define TFUNCTION(__function_name, __code) \ + int __function_name(unsigned int __indentation_level) \ +{ \ + INIT_TEST_FUNCTION(); \ + int __error_counter__ = 0; \ + do __code while(0); \ + DEFERRED_ERROR(__error_counter__); \ + return __error_counter__; \ +} + +/** + * @def CALL_TFUNCTION(__function_name) + * + * @brief Call a test function within a test file. + * This macro is used to call a test function within a test file. + * It calls the function specified by __function_name and adds the return value to the __error_counter__ variable. + * This allows multiple test functions to be executed and their error count to be accumulated. + * + * @param __function_name The name of the test function to be called. + */ +#define CALL_TFUNCTION(__function_name) \ + do {__error_counter__ += __function_name(__indentation_level + 1);} while(0) + +/** + * @def TEST_STR(__result, __expected) + * + * @brief Test strings + * This macro is used to test strings. It takes two arguments: `__result`, which is the string to test, and `__expected`, + * which is the expected value of the string. + * + * @param __result the string to test. + * @param __expected the expected value of the string. + * + * @code + * TEST_STR("Hello", "Hello"); + * @endcode + */ +#define TEST_STR(__result, __expected) \ + do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, &str_interface);} while(0) + +/** + * @def TEST_BOOL(__result, __expected) + * + * @brief Test bools + * This macro is used to test bools. It takes two arguments: `__result`, which is the bool to test, and `__expected`, + * which is the expected value of the bool. + * + * @param __result the pointer to bool to test. + * @param __expected the pointer to the expected value of the bool. + * + * @code + * bool x = true; + * bool y = true; + * TEST_BOOL(&x, &y); + * @endcode + */ +#define TEST_BOOL(__result, __expected) \ + do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, &bool_interface);} while (0) + +/** + * @def TEST_INT(__result, __expected) + * + * @brief Test ints + * This macro is used to test ints. It takes two arguments: `__result`, which is the int to be test, and `__expected`, + * which is the expected value of the int. + * + * @param __result the pointer to int to test. + * @param __expected the pointer to expected value of the int. + * + * @code + * int x = 1; + * int y = 1; + * TEST_INT(&x, &y) + * @endcode + */ +#define TEST_INT(__result, __expected) \ + do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, &int_interface);} while (0) + +/** + * @def TEST_PTR(__result, __expected) + * + * @brief Test pointers + * This macro is used to test pointers. It takes two arguments: `__result`, which is the pointer to test, and `__expected`, + * which is the expected value of the pointer. + * + * @param __result the pointer to test. + * @param __expected the expected value of the pointer. + * + * @code + * int x = 5; + * int *ptr = &x; + * TEST_PTR(ptr, &x); + * @endcode + */ +#define TEST_PTR(__result, __expected) \ + do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, &ptr_interface);} while(0) + + +/** + * @def TEST_UINT64_T(__result, __expected) + * + * @brief Test 64-bit unsigned integers + * This macro is used to test 64-bit unsigned integers. It takes two arguments: `__result`, + * which is the integer to test, and `__expected`, which is the expected value of the integer. + * + * @param __result the pointer to integer to test. + * @param __expected the pointer to expected value of the integer. + * + * @code + * uint64_t x = 5; + * uint64_t y = 5; + * TEST_UINT64_T(&x, &y); + * @endcode + */ +#define TEST_UINT64_T(__result, __expected) \ + do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, &u64_interface);} while(0) + +/** + * @def TEST_INTERFACE(__result, __expected, __interface) + * + * @brief Define a macro on a usertype with the given __interface. + * This macro is used by the user to define a new test macro on a usertype. + * + * @param __result + * @param __expected + * @param __interface the interface of the usertype. + * + * @code + * TestInterface usertype_interface = {.compare = ..., .format = ...}; + * #define TEST_USERTYPE(__result, __expected) \ + * TEST_INTERFACE(__result, __expected, &usertype_interface) + * @endcode + */ +#define TEST_INTERFACE(__result, __expected, __interface) \ + do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, __interface);} while(0) + + + + +// ------------------------------------CODE +// These functions should not be in use, only the previous macros should be in use. + +#define INDENTED_PRINT(__fmt, ...) \ + do { \ + for(unsigned int i = 0; i < __indentation_level; i++) { \ + printf("| "); \ + } \ + printf(__fmt, ##__VA_ARGS__); \ + } while(0) + + +#define INIT_TEST_FILE() \ + INDENTED_PRINT("%s:%s\n", __FILE__, __func__) + +#define INIT_TEST_FUNCTION() \ + INDENTED_PRINT("%s()\n", __func__); + +#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 struct { + Comparator *compare; + Formatter *format; +} TestInterface; + +// ---------------------------TEST FUNCTION + +int test(char *file, int line, unsigned int __indentation_level, void *result, void *expected, const TestInterface *interface) +{ + __indentation_level += 1; + static char buffer_result[FMT_BUFFER_SIZE]; + static char buffer_expected[FMT_BUFFER_SIZE]; + int is_equal = interface->compare(result, expected); + + char *fmt_result = interface->format(buffer_expected, expected); + char *fmt_expected = interface->format(buffer_result, result); + if (!is_equal) { + INDENTED_PRINT("%s:%d: failed, expected <%s>, got <%s>\n", file, line, fmt_expected, fmt_result); + } + return !is_equal; +} + +// ------------------------------INTERFACES + +// -- str_interface +int str_compare(void *ptr1, void *ptr2) +{ + char *str1 = (char *) ptr1; + char *str2 = (char *) ptr2; + + if (str1 == NULL && str2 == NULL) { + return 1; + } else if (str1 == NULL || str2 == NULL) { + return 0; + } else { + return (strcmp(str1, str2) == 0); + } +} + +char *str_format(char buffer[FMT_BUFFER_SIZE], void *ptr) +{ + UNUSED(buffer); + static char *str_null = "NULL"; + char *str = (char *) ptr; + return str ? str : str_null; +} + +static const TestInterface str_interface = { + .compare = str_compare, + .format = str_format +}; + +// -- bool_interface + +int bool_compare(void *ptr1, void *ptr2) +{ + bool *bool1 = (bool *) ptr1; + bool *bool2 = (bool *) ptr2; + return *bool1 == *bool2; +} + +char *bool_format(char buffer[FMT_BUFFER_SIZE], void *ptr) +{ + UNUSED(buffer); + bool *_bool = (bool *) ptr; + return *_bool ? "True" : "False"; +} + +static const TestInterface bool_interface = { + .compare = bool_compare, + .format = bool_format +}; + +// -- int_interface + +int int_compare(void *ptr1, void *ptr2) +{ + int *int1 = (int *) ptr1; + int *int2 = (int *) ptr2; + return *int1 == *int2; +} + +char *int_format(char buffer[FMT_BUFFER_SIZE], void *ptr) +{ + int *_int = (int *) ptr; + snprintf(buffer, FMT_BUFFER_SIZE, "%d", *_int); + return buffer; +} + +static const TestInterface int_interface = { + .compare = int_compare, + .format = int_format +}; + +// -- ptr_interface + +int ptr_compare(void *ptr1, void *ptr2) +{ + return ptr1 == ptr2; +} + +char *ptr_format(char buffer[FMT_BUFFER_SIZE], void *ptr) +{ + snprintf(buffer, FMT_BUFFER_SIZE, "%p", ptr); + return buffer; +} + +static const TestInterface ptr_interface = { + .compare = ptr_compare, + .format = ptr_format +}; + +// -- u64_interface + +int u64_compare(void *ptr1, 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) +{ + uint64_t *v = (uint64_t *) ptr; + snprintf(buffer, FMT_BUFFER_SIZE, "%"PRIu64"", *v); + return buffer; +} + +static const TestInterface u64_interface = { + .compare = u64_compare, + .format = u64_format +}; + +#endif + diff --git a/tests/util.c b/tests/util.c new file mode 100644 index 0000000000000000000000000000000000000000..69941bfac22fac87d56cd282989721c0c3dc0d76 --- /dev/null +++ b/tests/util.c @@ -0,0 +1,82 @@ +/******************************************************* + 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 "../src/util.h" +#include "small_test.h" + + +TFUNCTION(test_modulo_substraction, { + uint64_t lhs = 0; + uint64_t rhs = 0; + uint64_t result = 0; + uint64_t expected = 0; + + // Test 1: + // -- Setup + lhs = 10; + rhs = 10; + expected = 0; + // -- Run + result = modulo_substraction(lhs, rhs); + // -- Verification + TEST_UINT64_T(&result, &expected); + + // Test 2: + // -- Setup + lhs = UINT64_MAX; + rhs = 0; + expected = UINT64_MAX; + // -- Run + result = modulo_substraction(lhs, rhs); + // -- Verification + TEST_UINT64_T(&result, &expected); + + // Test 3: + // -- Setup + lhs = 0; + rhs = UINT64_MAX; + expected = 1; + // -- Run + result = modulo_substraction(lhs, rhs); + // -- Verification + TEST_UINT64_T(&result, &expected); + + // Test 4: + // -- Setup + lhs = 10; + rhs = 20; + expected = UINT64_MAX - 9; + // -- Run + result = modulo_substraction(lhs, rhs); + // -- Verification + TEST_UINT64_T(&result, &expected); + + // Test 5: + // -- Setup + lhs = 1000; + rhs = 1000; + expected = 0; + result = modulo_substraction(lhs, rhs); + TEST_UINT64_T(&result, &expected); +}) + +TFILE_ENTRY_POINT(test_util, { + CALL_TFUNCTION(test_modulo_substraction); +}) diff --git a/tools/update-readme-usage.sh b/tools/update-readme-usage.sh new file mode 100755 index 0000000000000000000000000000000000000000..2c1950ec6ad9f5e2681e968b251d80f8c335969d --- /dev/null +++ b/tools/update-readme-usage.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr> + +die() { yell "$*"; exit 111; } +try() { "$@" || die "cannot $*"; } +yell() { echo "$0: $*" >&2; } +echo() { printf '%s\n' "$*"; } + +try ./configure.sh --all +try make mojitos +usage=$( + ./bin/mojitos | + awk ' + /^SENSORS/ { + $0 = "" + printf "```\n" + printf "\n" + printf "The following is an exhaustive list of all the sensors (it is very likely\n" + printf "that one will not have all the sensors activated in his build):\n" + printf "```bash\n" + printf "SENSORS:" + } + { print } + ' +) +[ -n "$usage" ] || die 'empty usage. cannot continue.' + +try awk -v "usage=$usage" ' + /^Usage/ { + print usage + del = 1 + } + { + if (del == 1 || del == 2) { + if (match($0, "^```")) { + del++ + } + } else if (del == 3) { + if (match($0, "^```")) { + del = 0 + print $0 + } + } else { + print $0 + } + } +' README.md > README.tmp +try mv README.tmp README.md