Skip to content
Snippets Groups Projects
Commit 6799510a authored by Georges Da Costa's avatar Georges Da Costa
Browse files

Merge branch 'pull_request_amd_test_doc' into 'devel'

Amd, Tests, Doc, CI/CD

See merge request !4
parents d4bb13bf d22c9bda
Branches test
Tags
2 merge requests!9fix sensor example (doc),!4Amd, Tests, Doc, CI/CD
Pipeline #5325 failed
*.o src/counters_option.h
src/captors.h
tests/run
doc/test_main_ex
doc/info_reader_ex
doc/mojitos.1
tests/run
src/counters_option.h
src/sensors.h
sensors.mk
bin
obj
*.swp
*.swo
.vscode
stages:
- build-option
- build
job-build-counters:
stage: build-option
script:
- ./configure.sh -u counters
- make
job-build-load:
stage: build-option
script:
- ./configure.sh -u load
- make
job-build-network:
stage: build-option
script:
- ./configure.sh -u network
- make
job-build-rapl:
stage: build-option
script:
- ./configure.sh -u rapl
- make
job-build:
stage: build
script:
- ./configure.sh
- make
job-build-test:
stage: build
script:
- ./configure.sh
- make tests
workflow:
name: 'Pipeline for branch: $CI_COMMIT_BRANCH'
Georges Da Costa georges.da-costa@irit.fr Georges Da Costa georges.da-costa@irit.fr
\ No newline at end of file Floréal Risso floreal.risso@univ-tlse3.fr
Alexis Paronnaud alexis.paronnaud@univ-tlse3.fr
Téo Tinarrage teo.tinarrage@univ-tlse3.fr
# MOJITO/S # MOJITO/S
An Open Source System, Energy and Network Monitoring Tools at the O/S level An Open Source System, Energy and Network Monitoring Tools at the O/S level
## Documentation
MojitO/S runs on GNU/Linux MojitO/S runs on GNU/Linux
Usage
```python ## Usage
Usage : ./mojitos [-t time] [-f freq] [-r] [-p perf_list] [-l] \
[-u] [-d network_device] [-o logfile] [-e command arguments...]
```
Timing ```bash
- If time is 0 then mojitos loops infinitively Usage : ./bin/mojitos [OPTIONS] [SENSOR ...] [-e <cmd> ...]
- If -e is present, time and freq are not used
Configuration OPTIONS:
- -r activates RAPL (deprecated) -f|--freq <freq>
- -R activates RAPL set amount of measurements per second.
- -p activates performance counters. perf_list is coma separated list of performance counters without space. -t|--time <time>
- -l lists the possible performance counters and quits set duration value (seconds). If 0, then loops infinitely.
- -d activates network monitoring -e|--exec <cmd> ...
- -u activates system-level load monitoring Execute a command with optional arguments.
- -s activates statistics of overhead in nanoseconds If this option is used, any usage of -t or -f is ignored.
-o|--logfile <file>
specify a log file.
-s|--overhead-stats
enable overhead statistics (nanoseconds).
MojitO/S is published under the GPL3 license and is part of the [Energumen Project](https://www.irit.fr/energumen/) ```
<img src="https://www.irit.fr/energumen/images/energumen.png" width="100"> The following is an exhaustive list of all the sensors (it is very likely
that one will not have all the sensors activated in his build):
```bash
SENSORS:
-a|--amd-rapl
AMD RAPL
-p|--perf-list <perf_list>
performance counters
perf_list is a coma separated list of performance counters.
Ex: instructions,cache_misses
-l|--list
list the available performance counters and quit
-i|--monitor-infiniband <infiniband_path>
infiniband monitoring (if infiniband_path is X, tries to detect it automatically)
-u|--sysload
system load
-d|--net-dev <net_dev>
network monitoring (if network_device is X, tries to detect it automatically)
-r|--intel-rapl
INTEL RAPL
```
## Installation Instructions ## Installation Instructions
Dependencies
```bash
sudo apt install libpowercap0 libpowercap-dev powercap-utils python3
```
Download the source code Download the source code
```bash ```bash
git clone https://gitlab.irit.fr/sepia-pub/mojitos.git git clone https://gitlab.irit.fr/sepia-pub/mojitos.git
``` ```
Compile the code The quickest way to compile the code is:
```bash ```bash
cd mojitos cd mojitos
./configure.sh
make make
``` ```
You may want to run `./configure.sh --help` to see configuration options.
To execute mojitos without being root to monitor performance counters To execute mojitos without being root to monitor performance counters
```bash ```bash
sudo sh -c 'echo 0 >/proc/sys/kernel/perf_event_paranoid' sudo sh -c 'echo 0 >/proc/sys/kernel/perf_event_paranoid'
...@@ -58,43 +73,49 @@ sudo chmod a+w /sys/class/powercap/intel-rapl/*/*/* ...@@ -58,43 +73,49 @@ sudo chmod a+w /sys/class/powercap/intel-rapl/*/*/*
RAPL values during 2 seconds with a frequency of 2 Hz RAPL values during 2 seconds with a frequency of 2 Hz
```bash ```bash
$ ./mojitos -t 2 -f 2 -r $ ./bin/mojitos -t 2 -f 2 -r
#timestamp package-00 core0 dram0 #timestamp package-00 core0 dram0
1036389.135659868 10986 2869 1526 1036389.135659868 10986 2869 1526
1036389.500183551 1291440 255736 515562 1036389.500183551 1291440 255736 515562
1036390.000754048 1333553 228393 689513 1036390.000754048 1333553 228393 689513
1036390.500113978 1581967 267944 701536 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. 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 ```bash
$ ./mojitos -t 4 -f 1 -p cpu_cycles,cache_ll_r_a,page_faults $ ./bin/mojitos -t 4 -f 1 -p cpu_cycles,cache_ll_r_a,page_faults
#timestamp cpu_cycles cache_ll page_faults #timestamp cpu_cycles cache_ll page_faults
1036846.351749455 571199 1232 0 1036846.351749455 571199 1232 0
1036847.001098880 348173344 2451387 872 1036847.001098880 348173344 2451387 872
1036848.000166158 388112961 2509305 791 1036848.000166158 388112961 2509305 791
1036849.000191883 402255979 2625283 799 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. 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 ```bash
$ ./mojitos -t 0 -f 1 -d enp0s25 $ ./bin/mojitos -t 0 -f 1 -d enp0s25
#timestamp rxp rxb txp txb #timestamp rxp rxb txp txb
1036559.277376027 0 0 0 0 1036559.277376027 0 0 0 0
1036560.000161101 4 581 2 179 1036560.000161101 4 581 2 179
1036561.000083968 178 268675 55 4954 1036561.000083968 178 268675 55 4954
1036562.000076162 11 1010 5 510 1036562.000076162 11 1010 5 510
1036563.000069724 17 1643 12 3602 1036563.000069724 17 1643 12 3602
1036564.000113394 990 1493008 369 27299 1036564.000113394 990 1493008 369 27299
``` ```
Overhead of the monitoring for RAPL and cpu_cycle Overhead of the monitoring for RAPL and cpu_cycle
```bash ```bash
$ ./mojitos -t 5 -f 1 -p cpu_cycles -r -s $ ./bin/mojitos -t 5 -f 1 -p cpu_cycles -r -s
#timestamp cpu_cycles package-00 core0 dram0 overhead #timestamp cpu_cycles package-00 core0 dram0 overhead
1036988.197227391 162214 19898 4944 1586 149612 1036988.197227391 162214 19898 4944 1586 149612
1036989.000151326 332613664 2513116 379577 1115171 739573 1036989.000151326 332613664 2513116 379577 1115171 739573
1036990.000116433 482150700 3321341 587218 1380673 315719 1036990.000116433 482150700 3321341 587218 1380673 315719
1036991.000182835 525984292 3592582 691221 1385982 272182 1036991.000182835 525984292 3592582 691221 1385982 272182
1036992.000165117 397678789 2770561 444030 1375729 510379 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">
#!/bin/sh
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
try() { "$@" || die "cannot $*"; }
die() { yell "$*"; exit 111; }
yell() { echo "$0: $*" >&2; }
echo() { printf '%s\n' "$*"; }
isnum() {
case "${1#[+-]}" in
*[!0-9]*|'') return 1 ;;
*) return 0 ;;
esac
}
dprint() {
for v in "$@"; do
decho "$v : $(eval "echo \$$v")"
done
}
decho() {
[ "$debug" = '1' ] && echo "$@"
}
debug=0
target_hdr=src/sensors.h
target_mk=sensors.mk
nonsensor='counters_option|sensors|util'
hdr_blacklist=$nonsensor
hdr_whitelist=''
usage() {
printf -- 'Usage: %s [-la] [-e <sensor>] [-i <sensor>] [-u <sensor>]\n' "$(basename "$0")" >&2
printf -- '-e | --exclude : exclude sensor, can be called multiple times\n' >&2
printf -- '-i | --include : include sensor, can be called multiple times\n' >&2
printf -- '-l | --list-sensors : list all sensors and exit\n' >&2
printf -- '-u | --unique : only include the specified sensor\n' >&2
printf -- ' if this option is used, any usage of `-e` or `-i` will be ignored\n' >&2
printf -- '-a | --all : include all sensors, meant to be used only by the makefile\n' >&2
exit 1
}
ls_sensors() {
try cd src
[ -z "$hdr_whitelist" ] && hdr_whitelist='.*'
dprint hdr_blacklist >&2
dprint hdr_whitelist >&2
ls -1 *.h |
grep -xEv "($hdr_blacklist)\.h" |
grep -xE "($hdr_whitelist)\.h" |
sed 's/\.h$//'
}
# gen_sensors_h(sensor, nb_sensors)
gen_sensors_h() {
sensors=$1
nb_sensors=$2
nb_sensor_opts=$(
for sensor in $sensors; do
sed -n 's/.*'"${sensor}"'_opt\[\([0-9]\+\)\].*/\1/p' "src/${sensor}.h"
done |
paste -s -d '+'
)
nb_sensor_opts=$(eval "echo \$(($nb_sensor_opts))")
dprint sensors >&2
dprint nb_sensor_opts >&2
isnum "$nb_sensor_opts" || die "could not get total number of sensors's command-line options"
# gen includes
for sensor in $sensors; do
printf '#include "%s.h"\n' "$sensor"
done
printf '\n'
printf '#define NB_SENSOR %d\n' "$nb_sensors"
printf '#define NB_SENSOR_OPT %d\n' "$nb_sensor_opts"
printf '\n'
# gen `init_sensors()`
printf 'void init_sensors(Optparse *opts, Sensor *sensors, size_t len, size_t offset, int *nb_defined)\n{\n'
printf ' int opt_idx = offset;\n'
for sensor in $sensors; do
cat <<-!
for (int i = 0; i < ${sensor}.nb_opt; i++) {
opts[opt_idx++] = ${sensor}_opt[i];
}
sensors[(*nb_defined)++] = ${sensor};
!
done
printf ' assert((offset + *nb_defined) <= len);\n'
printf '}\n'
}
gen_sensors_mk() {
sensors=$1
printf 'CAPTOR_OBJ = '
for sensor in $sensors; do
printf '$(OBJ_DIR)/%s.o ' "$sensor"
done
printf '\n'
}
detect_caps() {
[ -r /usr/include/linux/perf_event.h ] && hdr_whitelist=counters
[ -d /sys/class/infiniband ] && hdr_whitelist="${hdr_whitelist}|infiniband"
[ -r /proc/stat ] && hdr_whitelist="${hdr_whitelist}|load"
if [ -r /proc/net/route ]; then
dev=$(awk 'NR == 2 { print $1 }' /proc/net/route)
[ -e "/sys/class/net/$dev" ] && hdr_whitelist="${hdr_whitelist}|network"
fi
vendor=$(awk '/vendor_id/ {print $3; exit}' /proc/cpuinfo)
vendor_lc=$(echo "$vendor" | tr 'A-Z' 'a-z')
case $vendor_lc in
*intel*)
hdr_whitelist="${hdr_whitelist}|rapl"
;;
*amd*)
family=$(awk '/cpu[ \t]*family/ {print $4; exit}' /proc/cpuinfo)
if isnum "$family"; then
[ $family -ge 17 ] && hdr_whitelist="${hdr_whitelist}|amd_rapl"
fi
;;
*)
yell "unsupported processor vendor id: $vendor"
;;
esac
[ $(ls -1 /sys/class/hwmon | wc -l) -gt 0 ] && hdr_whitelist="${hdr_whitelist}|temperature"
}
case $1 in
--all|-a)
all=1
;;
esac
[ "$all" ] || detect_caps
[ "$all" ] ||
while [ "$1" ]; do
case $1 in
--include|-i)
shift; [ "$1" ] || usage
hdr_whitelist="${hdr_whitelist}|${1}"
;;
--exclude|-e)
shift; [ "$1" ] || usage
hdr_blacklist="${hdr_blacklist}|${1}"
;;
--list-sensors|-l)
ls_sensors
exit 0
;;
--unique|-u)
shift; [ "$1" ] || usage
hdr_whitelist=$1
;;
--help|-h)
usage
;;
esac
shift
done
sensors=$(ls_sensors)
nb_sensors=$(echo "$sensors" | sed '/^$/d' | wc -l)
if [ "$nb_sensors" -eq 0 ]; then
printf -- '0 sensors are selected. cannot build.\n' >&2
exit 1
fi
try gen_sensors_h "$sensors" "$nb_sensors" > "$target_hdr"
try gen_sensors_mk "$sensors" > "$target_mk"
printf -- 'Run `make` to build `bin/mojitos`.\n' >&2
printf -- 'The resulting binary will have the %d following sensors:\n' "$nb_sensors" >&2
echo "$sensors" >&2
make clean >/dev/null
/*******************************************************
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);
}
/*******************************************************
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;
}
#! /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,';')
/*******************************************************
Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
This file is part of Mojitos.
Mojitos is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mojitos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MojitO/S. If not, see <https://www.gnu.org/licenses/>.
*******************************************************/
// ~/mojitos/doc/$ gcc -Wall -Wextra -Wpedantic -O3 -o info_reader_ex info_reader_ex.c ./../src/util.c && ./info_reader_ex
#include "./../lib/info_reader.h"
#define MAX_PROCS 64
// -----------------------------The storage
typedef struct {
unsigned int processor;
char *vendor_id;
unsigned int family;
unsigned int core_id;
unsigned int physical_id;
char *model_name;
} Cpu;
// --------------IMPLEMENTING THE INTERFACE
// -- Define the behaviour if the attempted value is an int
GenericPointer int_allocator(char *s)
{
unsigned int value = atoi(s);
return (GenericPointer) value;
}
// -- Define the behaviour if the attempted value is a string
GenericPointer string_allocator(char *s)
{
char *value = malloc(strlen(s) + 1);
strcpy(value, s);
return (GenericPointer) value;
}
// -- Define the processor setter
void set_processor(GenericPointer storage, GenericPointer data)
{
Cpu *cpu = (Cpu *) storage;
cpu->processor = data;
}
// -- Define the vendor_id setter
void set_vendor_id(GenericPointer storage, GenericPointer data)
{
Cpu *cpu = (Cpu *) storage;
cpu->vendor_id = (char *) data;
}
// -- Define the family setter
void set_family(GenericPointer storage, GenericPointer data)
{
Cpu *cpu = (Cpu *) storage;
cpu->family = data;
}
// -- Define the core_id setter
void set_core_id(GenericPointer storage, GenericPointer data)
{
Cpu *cpu = (Cpu *) storage;
cpu->core_id = data;
}
// -- Define the physical_id setter
void set_physical_id(GenericPointer storage, GenericPointer data)
{
Cpu *cpu = (Cpu *) storage;
cpu->physical_id = data;
}
// -- Define the model_name setter
void set_model_name(GenericPointer storage, GenericPointer data)
{
Cpu *cpu = (Cpu *) storage;
cpu->model_name = (char *) data;
}
int main()
{
Cpu cpus[MAX_PROCS];
// -- Define the setter, the allocator for each key / separator.
KeyFinder keys[] = {
{.key = "processor", .delimiter = ": ", .copy = (CopyAllocator *) int_allocator, .set = (Setter *)set_processor},
{.key = "vendor_id", .delimiter = ": ", .copy = (CopyAllocator *) string_allocator, .set = (Setter *)set_vendor_id},
{.key = "cpu family", .delimiter = ": ", .copy = (CopyAllocator *) int_allocator, .set = (Setter *)set_family},
{.key = "core id", .delimiter = ": ", .copy = (CopyAllocator *) int_allocator, .set = (Setter *)set_core_id},
{.key = "physical id", .delimiter = ": ", .copy = (CopyAllocator *) int_allocator, .set = (Setter *)set_physical_id},
{.key = "model name", .delimiter = ": ", .copy = (CopyAllocator *) string_allocator, .set = (Setter *)set_model_name}
};
size_t nb_keys = sizeof(keys)/sizeof(KeyFinder);
// -- Init the parser
Parser parser = {
.storage = (GenericPointer) cpus,
.capacity = MAX_PROCS,
.storage_struct_size = sizeof(Cpu),
.keys = keys,
.nb_keys = nb_keys,
.file = fopen("/proc/cpuinfo", "r")
};
// -- Parse the file
parse(&parser);
// Print and free the results
for (unsigned int i = 0; i < parser.nb_stored; ++i) {
printf("========== PROC[%d] ==========\n", i);
printf("Processor: %u\n", cpus[i].processor);
printf("Vendor ID: %s\n", cpus[i].vendor_id);
printf("Family: %u\n", cpus[i].family);
printf("Core ID: %u\n", cpus[i].core_id);
printf("Physical ID: %u\n", cpus[i].physical_id);
printf("Model Name: %s\n", cpus[i].model_name);
free(cpus[i].vendor_id);
free(cpus[i].model_name);
}
printf("==============================\n");
return 0;
}
.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.
/*******************************************************
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);
}
/*******************************************************
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",
},
};
/*******************************************************
Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
This file is part of Mojitos.
Mojitos is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mojitos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MojitO/S. If not, see <https://www.gnu.org/licenses/>.
*******************************************************/
// Include of the test library
#include "./../tests/small_test.h"
// Include the file that contains the function you want to test
// #include "a_file.c"
// This example is divided into three parts:
// 1. SOME SIMPLE EXAMPLES
// 2. A MORE COMPLEX EXAMPLE
// 3. FILE ENTRY POINT
// -----------------1. SOME SIMPLE EXAMPLES
// A simple function that should pass
// test_should_pass is the name of the function
// {} is the function code
// Use one of the define TEST_XXX defined in small_test.h
TFUNCTION(test_should_pass, {
int result = 42;
int expected = 42;
TEST_INT(&result, &expected);
})
// A simple function that should pass
// test_should_fail is the name of the function
// {} is the function code
// Use one of the define TEST_XXX defined in small_test.h
TFUNCTION(test_should_fail, {
char *result = "a fail";
char *expected = "a nice fail";
TEST_STR(result, expected);
})
// A simple test on an array
// test_array is the name of the function
// {} is the function code
// Use one of the define TEST_XXX defined in small_test.h
TFUNCTION(test_array, {
static unsigned int size = 10;
int results[10] = {0};
int expecteds[10] = {0};
for (unsigned int i = 0; i < size; i++)
{
TEST_INT(&results[i], &expecteds[i]);
}
})
// See "TFILE_ENTRY_POINT" for how to call these functions.
// ------------2. THE MORE COMPLEX EXAMPLE
// This example shows how to add a new type to the framework
// The type that you want to add :
typedef struct {
int simple_int;
char simple_str[20];
} UserType;
// The test framework needs two functions :
// A function that compares two values.
int usertype_compare(void *ptr1, void *ptr2)
{
return memcmp(ptr1, ptr2, sizeof(UserType)) == 0;
}
// A function to format a value.
char *usertype_format(char buffer[FMT_BUFFER_SIZE], void *ptr)
{
UserType *x = (UserType *) ptr;
snprintf(buffer, FMT_BUFFER_SIZE, "UserType {simple_int: %d, simple_str: %s}", x->simple_int, x->simple_str);
return buffer;
}
// Store the functions in the interface type.
static const TestInterface usertype_interface = {
.compare = usertype_compare,
.format = usertype_format
};
// Create a macro that implements the interface by passing the interface created to the TEST_INTERFACE macro.
#define TEST_USER_TYPE(__result, __expected) \
TEST_INTERFACE(__result, __expected, &usertype_interface)
// Creating a constructor is recommended (C can have problems parsing macros where "," is used).
#define DUMMY_USER_TYPE(__user, __simple_int, __simple_str) \
do { \
__user = (UserType) { \
.simple_int = __simple_int, \
.simple_str = __simple_str \
}; \
} while (0);
// Now you can write test for your new type
// all you have to do is call the macro that implements "TEST_INTERFACE".
// here it's "TEST_USERTYPE"
TFUNCTION(test_user_type, {
UserType result;
UserType expected;
DUMMY_USER_TYPE(result, 1, "John Doe");
DUMMY_USER_TYPE(expected, 1, "John Doe");
TEST_USER_TYPE(&result, &expected);
})
// Now you can write test for your new type
// all you have to do is call the macro that implements "TEST_INTERFACE".
// here it's "TEST_USERTYPE"
TFUNCTION(test_array_user_type, {
UserType results[1];
UserType expecteds[1];
DUMMY_USER_TYPE(results[0], 1, "John Doe");
DUMMY_USER_TYPE(expecteds[0], 1, "John Doe");
for (unsigned int i = 0; i < 1; i++)
{
TEST_USER_TYPE(&results[i], &expecteds[i]);
}
})
// ---------------------3. FILE ENTRY POINT
// Define the entry point of a test file
// Use the macro "CALL_TFUNCTION" where the name of a function is defined with the macro "TFUNCTION".
TFILE_ENTRY_POINT(test_file_ex, {
// Call the simple examples
CALL_TFUNCTION(test_should_pass);
CALL_TFUNCTION(test_should_fail);
CALL_TFUNCTION(test_array);
// Call the more complex example
CALL_TFUNCTION(test_user_type);
CALL_TFUNCTION(test_array_user_type);
})
/*******************************************************
Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
This file is part of Mojitos.
Mojitos is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mojitos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MojitO/S. If not, see <https://www.gnu.org/licenses/>.
*******************************************************/
// ~/mojitos/doc$ gcc -Wall -Wextra -Wpedantic -o test_main_ex test_main_ex.c ./../src/util.c
// Include of the test library
#include "./../tests/small_test.h"
// Include the *.c files containing the tests
#include "./test_file_ex.c"
// #include "./test_another_test_file.c"
// Define the entry point of the programme
TMAIN({
// Must contain the call to the entry point functions of each file
// USE CALL_TFUNCTION(my_function) instead of my_function()
CALL_TFUNCTION(test_file_ex);
// CALL_TFUNCTION(another_file_entry_point_function);
})
/*******************************************************
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];
}
/*******************************************************
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*);
/*******************************************************
Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
This file is part of Mojitos.
Mojitos is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mojitos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MojitO/S. If not, see <https://www.gnu.org/licenses/>.
*******************************************************/
#ifndef _INFO_READER_H
#define _INFO_READER_H
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
/**
* @struct Parser
* @brief The parser struct
* The struct containing all the necessary informations and functions to parse a file
*
* @var storage : GenericPointer : pointer to the storage where the parsed data will be stored
* @var nb_stored : unsigned int : the number of struct stored
* @var capacity : unsigned int : the maximum number of struct that can be stored
* @var storage_struct_size : size_t : the size of the struct stored in the storage
* @var keys : KeyFinder* : pointer to an array of KeyFinder containing the possible keys
* @var nb_keys : unsigned int : number of key finders
* @var file : FILE* : pointer to the file that will be parsed
*/
typedef struct Parser Parser;
/**
* @struct KeyFinder
* @brief The key finder struct
* The struct containing all the necessary informations and functions to find a key in a line of text
*
* @var key : char* : the key to be found
* @var delimiter : char* : the delimiter between the key and the value
* @var copy : CopyAllocator*: the function to use to make a copy of the value
* @var set : Setter*: the function to use to store the value in the storage
*/
typedef struct KeyFinder KeyFinder;
/**
* @brief Split a string into a key and value based on a specified delimiter.
*
* The function takes a string and splits it into two parts: a key and a value.
* The parts are determined by a specified delimiter.
* The key and value are returned through output parameters.
* If the delimiter is not found in the input string, the key and value pointers
* will be set to NULL.
*
* @param[in] string The input string to be split, the string is modified.
* @param[in] delimiter The delimiter string.
* @param[out] key A pointer to a char pointer where the key will be stored.
* @param[out] value A pointer to a char pointer where the value will be stored.
* @return None.
*/
static void split_on_delimiter(char *string, const char *delimiter, char **key, char **value);
/**
* @brief Replace the first occurrence of a character in a string with another character.
*
* The function takes a string and two characters as input, and replaces the first
* occurrence of the first character in the string with the second character.
*
* @param[in,out] string The input string where the replacement should take place.
* @param[in] from The character to be replaced.
* @param[in] to The character to replace with.
* @return None.
*/
static void replace_first(char *string, char from, char to);
/**
* @brief Check if a string starts with a prefix.
*
* @param[in] prefix The prefix to check.
* @param[in] string The string to check.
* @return true The string starts with the prefix.
* @return false The string does not start with the prefix or one of the input pointers is NULL.
*/
static bool start_with(const char *prefix, const char *string);
/**
* @brief Matches a line of text to a key in the parser's list of keys.
*
* @param[in] parser Pointer to the Parser struct.
* @param[in] line Line of text to match.
* @param[out] key_finder Pointer to a KeyFinder pointer where the matched key will be stored.
* @param[out] raw_value Pointer to a char pointer where the value associated with the matched key will be stored.
*
* @return Returns 1 if a key is matched, 0 otherwise.
*/
static unsigned int match(Parser *parser, char *line, KeyFinder **key_finder, char **raw_value);
typedef size_t GenericPointer;
typedef GenericPointer (CopyAllocator) (char *string);
typedef void (Setter) (GenericPointer storage, GenericPointer value);
struct KeyFinder {
char *key;
char *delimiter;
CopyAllocator *copy;
Setter *set;
};
struct Parser {
GenericPointer storage;
unsigned int nb_stored;
unsigned int capacity;
size_t storage_struct_size;
KeyFinder *keys;
unsigned int nb_keys;
FILE *file;
};
static void set_value(Parser *parser, KeyFinder *key_finder, char *raw_value)
{
GenericPointer address = parser->storage + (parser->storage_struct_size * parser->nb_stored);
GenericPointer value = key_finder->copy(raw_value);
key_finder->set(address, value);
}
static unsigned int match(Parser *parser, char *line, KeyFinder **key_finder, char **raw_value)
{
for (unsigned int i = 0; i < parser->nb_keys; i++) {
KeyFinder *finder = &parser->keys[i];
if (start_with(finder->key, line)) {
char *value = NULL;
char *key = NULL;
split_on_delimiter(line, finder->delimiter, &key, &value);
if ( key == NULL || value == NULL) {
return 0;
}
*key_finder = finder;
*raw_value = value;
return 1;
}
}
return 0;
}
static unsigned int move_to_next(Parser *parser)
{
parser->nb_stored += 1;
if (parser->nb_stored >= parser->capacity) {
return 0;
}
return 1;
}
static unsigned int parse(Parser *parser)
{
char *line = NULL;
size_t len = 0;
ssize_t read;
unsigned int key_assigned = 0;
while ((read = getline(&line, &len, parser->file)) != -1) {
if (key_assigned == parser->nb_keys && read > 1) {
continue;
} else if (read == 1) {
if (!move_to_next(parser)) {
return 0;
}
key_assigned = 0;
} else {
KeyFinder *key_finder = NULL;
char *raw_value = NULL;
replace_first(line, '\n', '\0');
if (match(parser, line, &key_finder, &raw_value)) {
set_value(parser, key_finder, raw_value);
++key_assigned;
}
}
}
if (key_assigned > 0) {
parser->nb_stored++;
}
free(line);
return 1;
}
static void replace_first(char *string, char from, char to)
{
for (int i = 0; string[i] != '\0'; i++) {
if (string[i] == from) {
string[i] = to;
break;
}
}
}
static void split_on_delimiter(char *string, const char *delimiter, char **key, char **value)
{
*key = NULL;
*value = NULL;
size_t delimiter_len = strlen(delimiter);
char *start_delimiter = strstr(string, delimiter);
if (start_delimiter != NULL) {
*start_delimiter = '\0';
*key = string;
*value = start_delimiter + delimiter_len;
}
}
static bool start_with(const char *prefix, const char *string)
{
if (prefix == NULL || string == NULL) {
return false;
}
size_t prefix_len = strlen(prefix);
size_t string_len = strlen(string);
if (string_len < prefix_len) {
return false;
} else {
return memcmp(prefix, string, prefix_len) == 0;
}
}
#endif
/* 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 */
all: mojitos .POSIX:
OBJECTS = mojitos.o counters_individual.o rapl.o frapl.o network.o load.o infiniband.o temperature.o SRC_DIR = src
DOC_DIR = doc
OBJ_DIR = obj
BIN_DIR = bin
TESTS_DIR = tests
mojitos:$(OBJECTS) BIN = mojitos
gcc $(DEBUG) -O3 -Wall -o mojitos $(OBJECTS) -lpowercap
OBJECTS_GRP = $(subst _individual,_group, $(OBJECTS)) CAPTOR_OBJ =
mojitos_group: $(OBJECTS_GRP) counters_option.h
gcc $(DEBUG) -O3 -Wall -o mojitos_group $(OBJECTS_GRP) -lpowercap
counters_%.o: counters_%.c counters.h counters_option.h include ./sensors.mk
gcc $(DEBUG) -c -O3 -Wall $< -o $@
counters_option.h: counters_option.py OBJ = \
./counters_option.py > counters_option.h $(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 ASTYLE = astyle --style=kr -xf -s4 -k3 -n -Z -Q
gcc $(DEBUG) -c -O3 -Wall $< -o $@
debug: DEBUG = -DDEBUG -g
debug: all all: $(BIN) man
%.o : %.c %.h $(BIN): $(BIN_DIR) $(OBJ) $(OBJ_DIR)/$(BIN).o
gcc $(DEBUG) -c -O3 -Wall $< -o $@ $(CC) $(LDFLAGS) -o $(BIN_DIR)/$(BIN) $(OBJ) $(OBJ_DIR)/$(BIN).o
$(OBJ): $(OBJ_DIR)
$(OBJ_DIR)/counters.o: $(SRC_DIR)/counters_option.h
$(OBJ_DIR)/$(BIN).o: $(SRC_DIR)/$(BIN).c $(SRC_DIR)/counters_option.h
$(CC) $(CFLAGS) -c $< -o $@
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(SRC_DIR)/%.h
$(CC) $(CFLAGS) -c $< -o $@
$(SRC_DIR)/counters_option.h: $(SRC_DIR)/counters_option.sh
sh ./$(SRC_DIR)/counters_option.sh > $(SRC_DIR)/counters_option.h
$(OBJ_DIR):
mkdir -p $(OBJ_DIR)
$(BIN_DIR):
mkdir -p $(BIN_DIR)
debug: CFLAGS = $(CPPFLAGS) -DDEBUG -g -Og
debug: $(BIN)
tests:
$(CC) $(CPPFLAGS) $(TESTS_DIR)/main.c $(SRC_DIR)/util.c -o $(TESTS_DIR)/run
$(TESTS_DIR)/run
format:
$(ASTYLE) $(SRC_DIR)/*.[ch] \
$(DOC_DIR)/*.[ch] \
$(TESTS_DIR)/*.[ch]
clean: clean:
\rm -f *~ *.o mojitos_group mojitos counters_option.h \rm -f $(OBJ_DIR)/* $(BIN_DIR)/* \
$(SRC_DIR)/counters_option.h \
$(TESTS_DIR)/run \
$(DOC_DIR)/test_main_ex \
$(DOC_DIR)/info_reader_ex
readme: $(BIN)
sh ./tools/update-readme-usage.sh
man: $(BIN)
awk -v "usage=$$($(BIN_DIR)/$(BIN) --dump-opts)" \
'/^USAGE/ { $$0=usage } 1' \
doc/$(BIN).pre.1 > doc/$(BIN).1 2>/dev/null
.PHONY: all clean mojitos debug format tests readme man
File deleted
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment