diff --git a/.gitignore b/.gitignore index 5761abcfdf0c26a75374c945dfe366eaeee04285..31260f1fedc2469e76bbf4c24568ebf1f0b58f7d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,12 @@ -*.o +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/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..95b6d7e63fe2cc81ad34f787a17a12dd8fb43656 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,57 @@ # 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...] -``` -Timing -- If time is 0 then mojitos loops infinitively -- If -e is present, time and freq are not used +## Usage -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 +```bash +Usage : ./bin/mojitos [OPTIONS] [SENSOR ...] [-e <cmd> ...] -MojitO/S is published under the GPL3 license and is part of the [Energumen Project](https://www.irit.fr/energumen/) +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). -<img src="https://www.irit.fr/energumen/images/energumen.png" width="100"> +SENSORS: +-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 +-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 +-c|--cpu-temp + processor temperature +``` ## 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 +66,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..9264caa013798c780bf086ed905c6412b71d6174 --- /dev/null +++ b/configure.sh @@ -0,0 +1,174 @@ +#!/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|optparse|sensors|util|info_reader' + +hdr_blacklist=$nonsensor +hdr_whitelist='' + +usage() { + printf -- 'Usage: %s [-l] [-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 + 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 '+' | + bc + ) + + 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" + ;; + *) + yell "unsupported processor vendor id: $vendor" + ;; + esac + + [ $(ls -1 /sys/class/hwmon | wc -l) -gt 0 ] && hdr_whitelist="${hdr_whitelist}|temperature" +} + +detect_caps + +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/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/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/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..f0fa1b12d47134962335572e6fd52bc5ca001261 100644 --- a/makefile +++ b/makefile @@ -1,30 +1,67 @@ -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 -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) + +format: + $(ASTYLE) $(SRC_DIR)/*.[ch] \ + $(DOC_DIR)/*.[ch] \ clean: - \rm -f *~ *.o mojitos_group mojitos counters_option.h + \rm -f $(OBJ_DIR)/* $(BIN_DIR)/* \ + $(SRC_DIR)/counters_option.h \ + $(DOC_DIR)/test_main_ex \ + $(DOC_DIR)/info_reader_ex + +man: $(BIN) + awk -v "usage=$$($(BIN_DIR)/$(BIN) -1)" \ + '/^USAGE/ { $$0=usage } 1' \ + doc/mojitos.pre.1 > doc/mojitos.1 2>/dev/null + +.PHONY: all clean mojitos debug format 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/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..aa4a8de532fc61e162a1a2069815e262c3ec2b5b --- /dev/null +++ b/src/infiniband.c @@ -0,0 +1,87 @@ +/******************************************************* + 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 <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <glob.h> +#include <stdint.h> +#include "util.h" + +#define NB_SENSOR 4 + +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); + + +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" + }; + + Network *state = malloc(sizeof(Network)); + + 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_network(state->values, state->sources); + + return NB_SENSOR; +} + +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/counters.h b/src/infiniband.h similarity index 56% rename from counters.h rename to src/infiniband.h index 751557ab5b308c0eda13c61f077d5bdff6519cd1..e30d78bee90f936056e8847a071e9fe817c9bea1 100644 --- a/counters.h +++ b/src/infiniband.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,9 +18,24 @@ *******************************************************/ -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_infiniband(char *infi_path, void **ptr); +void label_infiniband(char **labels, void *); + +Sensor infiniband = { + .init = init_infiniband, + .get = NULL, + .clean = NULL, + .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)", + }, +}; -void show_all_counters(); 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..4274b6a114805b6f07fd5c52cae73d8255c2c702 --- /dev/null +++ b/src/mojitos.c @@ -0,0 +1,383 @@ +/******************************************************* + 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 <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 && argv[1][0] == '-' && argv[1][1] == '1' && argv[1][2] == '\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/network.h b/src/network.h similarity index 56% rename from network.h rename to src/network.h index 44759dcf883ac66c90aafdf6447d00d6b02e9a39..a158dbbce25333edf64c5f5dd0cde42856e08c70 100644 --- a/network.h +++ b/src/network.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,8 +18,25 @@ *******************************************************/ -unsigned int init_network(char*, void **); -unsigned int get_network(uint64_t* results, void*); +unsigned int init_network(char *, void **); +unsigned int get_network(uint64_t *results, void *); void clean_network(void *); -void label_network(char **labels, 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]; -}