diff --git a/.gitignore b/.gitignore
index 5761abcfdf0c26a75374c945dfe366eaeee04285..d6525496ab19c98c64b26a4bd57cc249881139c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,16 @@
-*.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/memory_option.h
+src/sensors.h
+sensors.mk
+bin
+obj
+*.swp
+*.swo
+.vscode
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2052da5573562a34c1d611d4b391a780622fbf56
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,42 @@
+stages:
+  - build-option
+  - build
+
+job-build-counters:
+  stage: build-option
+  script:
+    - ./configure.sh -u counters
+    - make
+
+job-build-load:
+  stage: build-option
+  script:
+    - ./configure.sh -u load
+    - make
+
+job-build-network:
+  stage: build-option
+  script:
+    - ./configure.sh -u network
+    - make
+
+job-build-rapl:
+  stage: build-option
+  script:
+    - ./configure.sh -u rapl
+    - make
+
+job-build:
+  stage: build
+  script:
+    - ./configure.sh
+    - make
+
+job-build-test:
+  stage: build
+  script: 
+    - ./configure.sh
+    - make tests
+
+workflow:
+  name: 'Pipeline for branch: $CI_COMMIT_BRANCH'
diff --git a/AUTHORS b/AUTHORS
index 0bfc663bd4cba8c78a3de80d903028b587a3f338..13ddd11d874c2c46bb4f6792525e0d3a02371bb4 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1,4 @@
-Georges Da Costa georges.da-costa@irit.fr
\ No newline at end of file
+Georges Da Costa georges.da-costa@irit.fr
+Floréal Risso floreal.risso@univ-tlse3.fr
+Alexis Paronnaud alexis.paronnaud@univ-tlse3.fr
+Téo Tinarrage teo.tinarrage@univ-tlse3.fr
diff --git a/README.md b/README.md
index 2cf04f18a0611162f0c19e04fde35b7a26e727ca..4c0b18600b5a436352ed8ce9a5aeeb7385fe7130 100644
--- a/README.md
+++ b/README.md
@@ -1,54 +1,85 @@
 # MOJITO/S
 
 An Open Source System, Energy and Network Monitoring Tools at the O/S level
-
-## Documentation
-
 MojitO/S runs on GNU/Linux
-Usage
 
-```python
-Usage : ./mojitos [-t time] [-f freq] [-r] [-p perf_list] [-l]  \
-                  [-u] [-d network_device] [-o logfile] [-e command arguments...]
-```
+## Usage
 
-Timing
-- If time is 0 then mojitos loops infinitively
-- If -e is present, time and freq are not used
+```bash
+Usage : ./bin/mojitos [OPTIONS] [SENSOR ...] [-e <cmd> ...]
 
-Configuration
-- -r activates RAPL (deprecated)
-- -R activates RAPL
-- -p activates performance counters. perf_list is coma separated list of performance counters without space.
-- -l lists the possible performance counters and quits
-- -d activates network monitoring
-- -u activates system-level load monitoring
-- -s activates statistics of overhead in nanoseconds
+OPTIONS:
+-f|--freq <freq>
+	set amount of measurements per second.
+-t|--time <time>
+	set duration value (seconds). If 0, then loops infinitely.
+-e|--exec <cmd> ...
+	Execute a command with optional arguments.
+	If this option is used, any usage of -t or -f is ignored.
+-o|--logfile <file>
+	specify a log file.
+-s|--overhead-stats
+	enable overhead statistics (nanoseconds).
 
-MojitO/S is published under the GPL3 license and is part of the [Energumen Project](https://www.irit.fr/energumen/)
+```
 
-<img src="https://www.irit.fr/energumen/images/energumen.png" width="100">
+The following is an exhaustive list of all the sensors (it is very likely
+that one will not have all the sensors activated in his build):
+```bash
+SENSORS:
+-r|--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)
+-n|--nvidia-gpu
+	provides basic gpu information [clocks, memory, utilization, power, temperature].
+-r|--intel-rapl
+	INTEL RAPL
+-c|--cpu-temp
+	processor temperature
+-m|--memory
+	Retrieves information about the memory via the syscall 'sysinfo(2)'.
+-M|--memory-counters <memory_list>
+	memory counters
+	memory_list is a coma separated list of memory counters.
+	Ex: Zswap,Zswapped
+-L|--memory-list
+	list the available memory counters and quit
+```
 
 ## 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 use `amd_rapl` you have to load the module `msr`
+```bash
+sudo modprobe msr
+```
 To execute mojitos without being root to monitor performance counters
 ```bash
 sudo sh -c 'echo 0 >/proc/sys/kernel/perf_event_paranoid'
 ```
-To execute mohitos without being root for accessing RAPL
+To execute mojitos without being root for accessing RAPL
 ```bash
 sudo chmod a+w /sys/class/powercap/intel-rapl/*/*
 sudo chmod a+w /sys/class/powercap/intel-rapl/*/*/*
@@ -58,43 +89,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..4d0eb766dd95ecd6374176cc30933eefe10872ab
--- /dev/null
+++ b/configure.sh
@@ -0,0 +1,219 @@
+#!/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|memory_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() {
+	[ -d src ] || die 'fatal: the "src" directory does not exit.'
+
+	[ -z "$hdr_whitelist" ] && hdr_whitelist='.*'
+	dprint hdr_blacklist >&2
+	dprint hdr_whitelist >&2
+
+	try find src -type f -name '*.h' |
+		sed 's,src/\(.*\)\.h,\1,' |
+		grep -xEv "($hdr_blacklist)" |
+		grep -xE "($hdr_whitelist)"
+}
+
+# gen_sensors_h(sensor, nb_sensors)
+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'
+	for sensor in $sensors; do
+		printf '$(OBJ_DIR)/%s.o: $(SRC_DIR)/%s.c $(SRC_DIR)/%s.h $(SRC_DIR)/util.h\n' \
+			"$sensor" "$sensor" "$sensor"
+		printf '\t$(CC) $(CFLAGS) -c $< -o $@\n'
+	done
+}
+
+detect_caps() {
+	[ -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"
+	[ -r /proc/meminfo ] && hdr_whitelist="${hdr_whitelist}|memory_counters"
+
+	if [ "$(uname -r | cut -d "." -f 1)" -gt "2" ]; then
+		hdr_whitelist="${hdr_whitelist}|memory"
+	fi
+
+	if [ -r /proc/net/route ]; then
+		dev=$(awk 'NR == 2 { print $1 }' /proc/net/route)
+		[ -e "/sys/class/net/$dev" ] && hdr_whitelist="${hdr_whitelist}|network"
+	fi
+
+	if [ -e /usr/local/cuda/lib64 ] && [ -e /usr/local/cuda/include ]; then
+		hdr_whitelist="${hdr_whitelist}|nvidia_gpu"
+		NVML_LDFLAGS='-L/usr/local/cuda/lib64 -lnvidia-ml'
+		NVML_IFLAGS='-I/usr/local/cuda/include'
+	fi
+
+	vendor=$(awk '/vendor_id/ {print $3; exit}' /proc/cpuinfo)
+	vendor_lc=$(echo "$vendor" | tr 'A-Z' 'a-z')
+	case $vendor_lc in
+	*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
+	NVML_LDFLAGS="-L/usr/local/cuda/lib64 -lnvidia-ml"
+	NVML_IFLAGS="-I/usr/local/cuda/include"
+	;;
+--unique | -u)
+	unique=1
+	;;
+esac
+
+if ! [ "$all" ] && ! [ "$unique" ]; then
+	detect_caps
+fi
+
+[ "$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"
+
+try printf "NVML_LDFLAGS = %s\n" "$NVML_LDFLAGS" >>"$target_mk"
+try printf "NVML_IFLAGS = %s\n" "$NVML_IFLAGS" >>"$target_mk"
+
+printf -- 'Run `make` to build `bin/mojitos`.\n' >&2
+printf -- 'The resulting binary will have the %d following sensors:\n' "$nb_sensors" >&2
+echo "$sensors" >&2
+
+make clean >/dev/null
diff --git a/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/counter_ex.h b/doc/counter_ex.h
new file mode 100644
index 0000000000000000000000000000000000000000..65fabf3756dddc1b41b3bb16d143b3158854c20f
--- /dev/null
+++ b/doc/counter_ex.h
@@ -0,0 +1,26 @@
+/*
+ * Example of a basic counter: an accumulator
+**/
+
+unsigned int init_acc(char *, void **);
+unsigned int get_acc(uint64_t *results, void *);
+void clean_acc(void *);
+void label_acc(char **labels, void *);
+
+Sensor rapl = {
+    .init = init_acc,
+    .get = get_acc,
+    .clean = clean_acc,
+    .label = label_acc,
+    .nb_opt = 1,
+};
+
+Optparse rapl_opt[1] = {
+    {
+        .longname = "accumulator",
+        .shortname = 'a',
+        .argtype = OPTPARSE_NONE,		/* OPTPARSE_NONE / OPTPARSE_OPTIONAL / OPTPARSE_REQUIRED */
+        .usage_arg = NULL,
+        .usage_msg = "dumb accumulator",
+    },
+};
diff --git a/doc/info_reader_ex.c b/doc/info_reader_ex.c
new file mode 100644
index 0000000000000000000000000000000000000000..d99e916fc6c98f5dc22bc0d99d8bb85d433cce33
--- /dev/null
+++ b/doc/info_reader_ex.c
@@ -0,0 +1,169 @@
+/*******************************************************
+ 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.c"
+#include "./../lib/info_reader.h"
+
+#define MAX_PROCS 64
+
+// -----------------------------The storage
+typedef struct {
+    unsigned int processor;
+    char *vendor_id;
+    unsigned int family;
+    unsigned int core_id;
+    unsigned int physical_id;
+    char *model_name;
+} Cpu;
+
+// --------------IMPLEMENTING THE INTERFACE
+
+// -- Define the behaviour if the attempted value is an int
+GenericPointer int_allocator(char *s)
+{
+    unsigned int value = atoi(s);
+    return (GenericPointer)value;
+}
+
+// -- Define the behaviour if the attempted value is a string
+GenericPointer string_allocator(char *s)
+{
+    char *value = malloc(strlen(s) + 1);
+    strcpy(value, s);
+    return (GenericPointer)value;
+}
+
+// -- Define the processor setter
+void set_processor(GenericPointer storage, GenericPointer data)
+{
+    Cpu *cpu = (Cpu *)storage;
+    cpu->processor = data;
+}
+
+// -- Define the vendor_id setter
+void set_vendor_id(GenericPointer storage, GenericPointer data)
+{
+    Cpu *cpu = (Cpu *)storage;
+    cpu->vendor_id = (char *)data;
+}
+
+// -- Define the family setter
+void set_family(GenericPointer storage, GenericPointer data)
+{
+    Cpu *cpu = (Cpu *)storage;
+    cpu->family = data;
+}
+
+// -- Define the core_id setter
+void set_core_id(GenericPointer storage, GenericPointer data)
+{
+    Cpu *cpu = (Cpu *)storage;
+    cpu->core_id = data;
+}
+
+// -- Define the physical_id setter
+void set_physical_id(GenericPointer storage, GenericPointer data)
+{
+    Cpu *cpu = (Cpu *)storage;
+    cpu->physical_id = data;
+}
+
+// -- Define the model_name setter
+void set_model_name(GenericPointer storage, GenericPointer data)
+{
+    Cpu *cpu = (Cpu *)storage;
+    cpu->model_name = (char *)data;
+}
+
+int main()
+{
+    Cpu cpus[MAX_PROCS];
+
+    // -- Define the setter, the allocator for each key / separator.
+    KeyFinder keys[] = {{
+            .key = "processor",
+            .delimiter = ": ",
+            .copy = (CopyAllocator *)int_allocator,
+            .set = (Setter *)set_processor
+        },
+        {
+            .key = "vendor_id",
+            .delimiter = ": ",
+            .copy = (CopyAllocator *)string_allocator,
+            .set = (Setter *)set_vendor_id
+        },
+        {
+            .key = "cpu family",
+            .delimiter = ": ",
+            .copy = (CopyAllocator *)int_allocator,
+            .set = (Setter *)set_family
+        },
+        {
+            .key = "core id",
+            .delimiter = ": ",
+            .copy = (CopyAllocator *)int_allocator,
+            .set = (Setter *)set_core_id
+        },
+        {
+            .key = "physical id",
+            .delimiter = ": ",
+            .copy = (CopyAllocator *)int_allocator,
+            .set = (Setter *)set_physical_id
+        },
+        {
+            .key = "model name",
+            .delimiter = ": ",
+            .copy = (CopyAllocator *)string_allocator,
+            .set = (Setter *)set_model_name
+        }
+    };
+
+    size_t nb_keys = sizeof(keys) / sizeof(KeyFinder);
+
+    // -- Init the parser
+    Parser parser = {.storage = (GenericPointer)cpus,
+                     .capacity = MAX_PROCS,
+                     .storage_struct_size = sizeof(Cpu),
+                     .keys = keys,
+                     .nb_keys = nb_keys,
+                     .file = fopen("/proc/cpuinfo", "r")
+                    };
+
+    // -- Parse the file
+    parse(&parser);
+
+    // Print and free the results
+    for (unsigned int i = 0; i < parser.nb_stored; ++i) {
+        printf("========== PROC[%d] ==========\n", i);
+        printf("Processor: %u\n", cpus[i].processor);
+        printf("Vendor ID: %s\n", cpus[i].vendor_id);
+        printf("Family: %u\n", cpus[i].family);
+        printf("Core ID: %u\n", cpus[i].core_id);
+        printf("Physical ID: %u\n", cpus[i].physical_id);
+        printf("Model Name: %s\n", cpus[i].model_name);
+        free(cpus[i].vendor_id);
+        free(cpus[i].model_name);
+    }
+    printf("==============================\n");
+    return 0;
+}
diff --git a/doc/mojitos.pre.1 b/doc/mojitos.pre.1
new file mode 100644
index 0000000000000000000000000000000000000000..af9cb9bc5681422a8af870eead9cea78e88b0504
--- /dev/null
+++ b/doc/mojitos.pre.1
@@ -0,0 +1,81 @@
+.Dd January 9, 2023
+.Dt MOJITOS 1
+.Os
+.Sh NAME
+.Nm mojitos
+.Nd An open source system monitoring tool.
+.Sh SYNOPSIS
+.Nm mojitos
+.Op Ar OPTIONS
+.Op Ar SENSOR ...
+.Op Fl e Ar cmd ...
+.Sh DESCRIPTION
+.Nm
+is a monitoring tool with a multitude of sensors that does measurements at the OS level.
+.Nm
+runs on GNU/Linux.
+USAGE
+.Sh EXIT STATUS
+.Ex
+.Sh EXAMPLES
+RAPL values during 2 seconds with a frequency of 2Hz:
+.Bd -literal -offset indent
+$ mojitos -t 2 -f 2 -r
+#timestamp package-00 core0 dram0
+1036389.135659868 10986 2869 1526
+1036389.500183551 1291440 255736 515562
+1036390.000754048 1333553 228393 689513
+1036390.500113978 1581967 267944 701536
+.Ed
+.Pp
+Performance counters (cpu_cycle, cache_ll_r_a and page_faults) during 4 seconds
+with a frequency of 1Hz.
+In the context of cache performance counters (as with "cache_ll_r_a"),
+ _r and _w suffixes mean respectively read and write,
+while the _a, _m and _p ones mean respectively access, miss and pending.
+.Pp
+.Bd -literal -offset indent
+$ ./mojitos -t 4 -f 1 -p cpu_cycles,cache_ll_r_a,page_faults
+#timestamp cpu_cycles cache_ll page_faults
+1036846.351749455 571199 1232 0
+1036847.001098880 348173344 2451387 872
+1036848.000166158 388112961 2509305 791
+1036849.000191883 402255979 2625283 799
+.Ed
+.Pp
+Network values with no time limit with a frequency of 1Hz.
+rxp and txp are the number of received and sent packets, while rxb and txp are
+the number of received and sent bytes.
+.Pp
+.Bd -literal -offset indent
+$ ./mojitos -t 0 -f 1 -d enp0s25
+#timestamp rxp rxb txp txb
+1036559.277376027 0 0 0 0
+1036560.000161101 4 581 2 179
+1036561.000083968 178 268675 55 4954
+1036562.000076162 11 1010 5 510
+1036563.000069724 17 1643 12 3602
+1036564.000113394 990 1493008 369 27299
+.Ed
+.Pp
+overhead of the monitoring when using RAPL and the cpu_cycle performance counter:
+.Pp
+.Bd -literal -offset indent
+$ ./mojitos -t 5 -f 1 -p cpu_cycles -r -s
+#timestamp cpu_cycles package-00 core0 dram0 overhead
+1036988.197227391 162214 19898 4944 1586 149612
+1036989.000151326 332613664 2513116 379577 1115171 739573
+1036990.000116433 482150700 3321341 587218 1380673 315719
+1036991.000182835 525984292 3592582 691221 1385982 272182
+1036992.000165117 397678789 2770561 444030 1375729 510379
+.Ed
+.Sh SEE ALSO
+.Xr perf 1 ,
+.Xr lscpu 1 ,
+.Xr sysfs 5 ,
+.Xr proc 5
+.Sh LICENSE
+MojitO/S is published under the GPL3 license and is part of the
+.Lk https://www.irit.fr/energumen/ [Energumen Project]
+.Sh BUGS
+Yes.
diff --git a/doc/network.md b/doc/network.md
new file mode 100644
index 0000000000000000000000000000000000000000..b5d1e68304a72a35da213a74dfab918321663247
--- /dev/null
+++ b/doc/network.md
@@ -0,0 +1,8 @@
+This sensor can autodetect interfaces in use by giving the special
+interface name "X".  But the total number of interfaces it can autodetect
+is currently under a hard-limit.  This hard-limit can be changed by
+modifying this line in `src/network.c`:
+
+```c
+#define NB_MAX_DEV 8
+```
diff --git a/doc/nvidia_gpu.md b/doc/nvidia_gpu.md
new file mode 100644
index 0000000000000000000000000000000000000000..15cc8aa073dfe6cf49de4e820061793d24b55331
--- /dev/null
+++ b/doc/nvidia_gpu.md
@@ -0,0 +1,57 @@
+# Nvidia Gpu
+
+The `nvidia_gpu` sensor provides basic information about the gpu. Depending on
+the driver version it is possible that not all sensors are supported, so an
+error message will be written to `stderr` but the execution will continue.
+
+For more information you can consult the [nvidia nvml api](https://docs.nvidia.com/deploy/index.html).
+
+## [Clock](https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceEnumvs.html#group__nvmlDeviceEnumvs_1g805c0647be9996589fc5e3f6ff680c64)
+
+All speeds are in Mhz.
+
+|Output  |Description                    |
+|--------|-------------------------------|
+|graphics|Graphics clock                 |
+|sm      |Streaming Multiprocessor clock |
+|memory  |Memory clock                   |
+|video   |Video encoder/decoder clock    |
+
+## [Memory](https://docs.nvidia.com/deploy/nvml-api/structnvmlMemory__t.html#structnvmlMemory__t)
+
+All values are in bytes.
+
+|Output  |Description                          |
+|--------|-------------------------------------|
+|free    |Unallocated device memory            |
+|used    |Sum of Reserved and Allocated memory |
+|total   |Total physical device memory         |
+
+
+## [Utilization](https://docs.nvidia.com/deploy/nvml-api/structnvmlUtilization__t.html#structnvmlUtilization__t)
+
+Utilization information for a device. Each sample period may be between 1
+second and 1/6 second, depending on the product being queried.
+
+All values are a percent of time over the past sample period.
+
+|Output  |Description          |
+|--------|---------------------|
+|gpu     | Usage of the GPU    |
+|memory  | Usage of the Memory |
+
+## [Power](https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceQueries.html#group__nvmlDeviceQueries_1g7ef7dff0ff14238d08a19ad7fb23fc87)
+
+Retrieves power usage for this GPU in milliwatts and its associated circuitry (e.g. memory)
+
+|Output  |Description              |
+|--------|-------------------------|
+|power   | Power consumption in mW |
+
+## [Temperature](https://docs.nvidia.com/deploy/nvml-api/group__nvmlDeviceEnumvs.html#group__nvmlDeviceEnumvs_1g2650b526841fa38b8f293c2d509a1de0)
+Temperature of the GPU.
+
+|Output      |Description                 |
+|------------|----------------------------|
+|temperature | Temperature of the GPU die |
+
diff --git a/doc/sensor_ex.c b/doc/sensor_ex.c
new file mode 100644
index 0000000000000000000000000000000000000000..5fc8eeb559a618e3a30b0354dd94860e2a2b0c21
--- /dev/null
+++ b/doc/sensor_ex.c
@@ -0,0 +1,124 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+/*
+ * compilation:
+ *
+
+# normally, this part is done with `configure.sh`, but here we need to
+# do this manually
+cat <<EOF > src/sensors.h
+#include "sensor_ex.h"
+
+#define NB_SENSOR 1
+#define NB_SENSOR_OPT 1
+
+void init_sensors(Optparse *opts, Sensor *sensors, size_t len, size_t offset, int *nb_defined)
+{
+    int opt_idx = offset;
+    for (int i = 0; i < sensor_ex.nb_opt; i++) {
+        opts[opt_idx++] = sensor_ex_opt[i];
+    }
+    sensors[(*nb_defined)++] = sensor_ex;
+    assert((offset + *nb_defined) <= len);
+}
+EOF
+
+# idem
+echo 'CAPTOR_OBJ = doc/sensor_ex.o' > sensors.mk
+
+# actual compilation here
+gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og -c doc/sensor_ex.c -o obj/sensor_ex.o
+gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og -c doc/util.c -o obj/util.o
+gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og -c doc/mojitos.c -o obj/mojitos.o
+gcc -std=gnu99 -Wall -Wpedantic -I./lib -I./doc -I./src -g -Og obj/util.o obj/mojitos.o obj/sensor_ex.o -o bin/mojitos
+
+ *
+**/
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+#define NB_SENSOR 3
+
+struct Accumulator {
+    int v[NB_SENSOR];
+};
+typedef struct Accumulator Accumulator;
+
+void _get_acc(int v[NB_SENSOR])
+{
+    for (int i = 0; i < NB_SENSOR; i++) {
+        v[i]++;
+    }
+}
+
+unsigned int init_acc(char *none, void **ptr)
+{
+    /* "none" refers to an optionnal command-line argument */
+    /* there is none in this case, so this parameter is not used */
+    UNUSED(none);
+
+    Accumulator *state = malloc(sizeof(Accumulator));
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        state->v[i] = -1;
+    }
+
+    *ptr = (void *)state;
+    _get_acc(state->v);
+
+    return NB_SENSOR;
+}
+
+unsigned int get_acc(uint64_t *results, void *ptr)
+{
+    Accumulator *state = (Accumulator *)ptr;
+
+    _get_acc(state->v);
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        results[i] = state->v[i];
+    }
+
+    return NB_SENSOR;
+}
+
+char *_labels_accumulator[NB_SENSOR] = {"acc1", "acc2", "acc3"};
+void label_acc(char **labels, void *none)
+{
+    UNUSED(none);
+    for (int i = 0; i < NB_SENSOR; i++) {
+        labels[i] = _labels_accumulator[i];
+    }
+}
+
+void clean_acc(void *ptr)
+{
+    Accumulator *state = (Accumulator *)ptr;
+
+    if (state == NULL) {
+        return;
+    }
+
+    free(state);
+}
diff --git a/doc/sensor_ex.h b/doc/sensor_ex.h
new file mode 100644
index 0000000000000000000000000000000000000000..9fd95d3e0f32b0013ca8115edb36f82a9203da69
--- /dev/null
+++ b/doc/sensor_ex.h
@@ -0,0 +1,46 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+/*
+ * Example of a basic sensor: an accumulator
+**/
+
+unsigned int init_acc(char *, void **);
+unsigned int get_acc(uint64_t *results, void *);
+void clean_acc(void *);
+void label_acc(char **labels, void *);
+
+Sensor sensor_ex = {
+    .init = init_acc,
+    .get = get_acc,
+    .clean = clean_acc,
+    .label = label_acc,
+    .nb_opt = 1,
+};
+
+Optparse sensor_ex_opt[1] = {
+    {
+        .longname = "accumulator",
+        .shortname = 'a',
+        .argtype = OPTPARSE_NONE,		/* OPTPARSE_NONE / OPTPARSE_OPTIONAL / OPTPARSE_REQUIRED */
+        .usage_arg = NULL,
+        .usage_msg = "dumb accumulator",
+    },
+};
diff --git a/doc/test_file_ex.c b/doc/test_file_ex.c
new file mode 100644
index 0000000000000000000000000000000000000000..0595e9a8c41cbfa349d139c0dcb96df2c4f2716a
--- /dev/null
+++ b/doc/test_file_ex.c
@@ -0,0 +1,158 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+*******************************************************/
+
+// Include of the test library
+#include "./../tests/small_test.h"
+// Include the file that contains the function you want to test
+// #include "a_file.c"
+
+// This example is divided into three parts:
+// 1. SOME SIMPLE EXAMPLES
+// 2. A MORE COMPLEX EXAMPLE
+// 3. FILE ENTRY POINT
+
+//  -----------------1. SOME SIMPLE EXAMPLES
+
+// A simple function that should pass
+// test_should_pass is the name of the function
+// {} is the function code
+// Use one of the define TEST_XXX defined in small_test.h
+TFUNCTION(test_should_pass, {
+    int result = 42;
+    int expected = 42;
+    TEST_INT(&result, &expected);
+})
+
+// A simple function that should pass
+// test_should_fail is the name of the function
+// {} is the function code
+// Use one of the define TEST_XXX defined in small_test.h
+TFUNCTION(test_should_fail, {
+    char *result = "a fail";
+    char *expected = "a nice fail";
+    TEST_STR(result, expected);
+})
+
+// A simple test on an array
+// test_array is the name of the function
+// {} is the function code
+// Use one of the define TEST_XXX defined in small_test.h
+TFUNCTION(test_array, {
+    static unsigned int size = 10;
+    int results[10] = {0};
+    int expecteds[10] = {0};
+    for (unsigned int i = 0; i < size; i++)
+    {
+        TEST_INT(&results[i], &expecteds[i]);
+    }
+})
+
+// See "TFILE_ENTRY_POINT" for how to call these functions.
+
+// ------------2. THE MORE COMPLEX EXAMPLE
+// This example shows how to add a new type to the framework
+
+// The type that you want to add :
+typedef struct {
+    int simple_int;
+    char simple_str[20];
+} UserType;
+
+
+// The test framework needs two functions :
+
+// A function that compares two values.
+int usertype_compare(void *ptr1, void *ptr2)
+{
+    return memcmp(ptr1, ptr2, sizeof(UserType)) == 0;
+}
+
+// A function to format a value.
+char *usertype_format(char buffer[FMT_BUFFER_SIZE], void *ptr)
+{
+    UserType *x = (UserType *) ptr;
+    snprintf(buffer, FMT_BUFFER_SIZE, "UserType {simple_int: %d, simple_str: %s}", x->simple_int, x->simple_str);
+    return buffer;
+}
+
+// Store the functions in the interface type.
+static const TestInterface usertype_interface = {
+    .compare = usertype_compare,
+    .format = usertype_format
+};
+
+// Create a macro that implements the interface by passing the interface created to the TEST_INTERFACE macro.
+#define TEST_USER_TYPE(__result, __expected) \
+    TEST_INTERFACE(__result, __expected, &usertype_interface)
+
+
+// Creating a constructor is recommended (C can have problems parsing macros where "," is used).
+#define DUMMY_USER_TYPE(__user, __simple_int, __simple_str) \
+    do {                                                    \
+      __user = (UserType) {                                 \
+          .simple_int = __simple_int,                       \
+          .simple_str = __simple_str                        \
+    };                                                      \
+} while (0);
+
+// Now you can write test for your new type
+// all you have to do is call the macro that implements "TEST_INTERFACE".
+// here it's "TEST_USERTYPE"
+TFUNCTION(test_user_type, {
+    UserType result;
+    UserType expected;
+
+    DUMMY_USER_TYPE(result, 1, "John Doe");
+    DUMMY_USER_TYPE(expected, 1, "John Doe");
+
+    TEST_USER_TYPE(&result, &expected);
+})
+
+// Now you can write test for your new type
+// all you have to do is call the macro that implements "TEST_INTERFACE".
+// here it's "TEST_USERTYPE"
+TFUNCTION(test_array_user_type, {
+    UserType results[1];
+    UserType expecteds[1];
+
+    DUMMY_USER_TYPE(results[0], 1, "John Doe");
+    DUMMY_USER_TYPE(expecteds[0], 1, "John Doe");
+
+    for (unsigned int i = 0; i < 1; i++)
+    {
+        TEST_USER_TYPE(&results[i], &expecteds[i]);
+    }
+})
+
+// ---------------------3. FILE ENTRY POINT
+
+// Define the entry point of a test file
+// Use the macro "CALL_TFUNCTION" where the name of a function is defined with the macro "TFUNCTION".
+TFILE_ENTRY_POINT(test_file_ex, {
+    // Call the simple examples
+    CALL_TFUNCTION(test_should_pass);
+    CALL_TFUNCTION(test_should_fail);
+    CALL_TFUNCTION(test_array);
+
+    // Call the more complex example
+    CALL_TFUNCTION(test_user_type);
+    CALL_TFUNCTION(test_array_user_type);
+})
+
diff --git a/doc/test_main_ex.c b/doc/test_main_ex.c
new file mode 100644
index 0000000000000000000000000000000000000000..a5e7841eed202c9a4dd7a7dcad500619fcdaeb30
--- /dev/null
+++ b/doc/test_main_ex.c
@@ -0,0 +1,36 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+*******************************************************/
+
+// ~/mojitos/doc$ gcc -Wall -Wextra -Wpedantic -o test_main_ex test_main_ex.c ./../src/util.c
+// Include of the test library
+#include "./../tests/small_test.h"
+
+// Include the *.c files containing the tests
+#include "./test_file_ex.c"
+// #include "./test_another_test_file.c"
+
+// Define the entry point of the programme
+TMAIN({
+    // Must contain the call to the entry point functions of each file
+    // USE CALL_TFUNCTION(my_function) instead of my_function()
+    CALL_TFUNCTION(test_file_ex);
+    // CALL_TFUNCTION(another_file_entry_point_function);
+})
+
diff --git a/frapl.c b/frapl.c
deleted file mode 100644
index 433a28c13ac5ecd847c95935fb3fa0e8f7131935..0000000000000000000000000000000000000000
--- a/frapl.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*******************************************************
- Copyright (C) 2022-2023 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <inttypes.h>
-
-
-#define MAX_HEADER 128
-
-char *get_frapl_string(const char *filename) {
-  int fd = open(filename, O_RDONLY);
-  if( fd == -1)
-    return NULL;
-  char *result = malloc(MAX_HEADER);
-  int nb = read(fd, result, MAX_HEADER);
-  close(fd);
-  result[nb-1] = 0;
-  return (result);
-}  
-
-void test_append(char* name, int i) {
-  //char last = name[strlen(name)-1];
-  //if (last>='0' && last <= '9')
-  //  return;
-  sprintf(name+strlen(name), "%d", i);
-}
-
-
-struct _frapl_t {
-  unsigned int nb;
-  char **names;
-  int *fids;
-  uint64_t* values;
-  uint64_t* tmp_values;
-
-};
-typedef struct _frapl_t _frapl_t;
-
-
-void add_frapl_source(_frapl_t* rapl, char*name, char*energy_uj) {
-  rapl->nb += 1;
-  rapl->names = realloc(rapl->names, sizeof(char**)*rapl->nb);
-  rapl->fids = realloc(rapl->fids, sizeof(int*)*rapl->nb);
-  
-  rapl->names[rapl->nb-1] = malloc(strlen(name)+1);
-  strcpy(rapl->names[rapl->nb-1], name);
-  //printf("%s\n", energy_uj);
-
-  rapl->fids[rapl->nb-1] = open(energy_uj, O_RDONLY);
-}
-
-
-void _get_frapl(uint64_t *values, _frapl_t* rapl) {
-  static char buffer[512];
-
-  for (int i = 0; i < rapl->nb; i++) {
-
-    pread(rapl->fids[i], buffer, 100, 0);
-    values[i] = strtoull(buffer, NULL, 10);
-  }
-}
-
-
-unsigned int init_frapl(char* none, void **ptr) {
-  _frapl_t *rapl = malloc(sizeof(_frapl_t));
-  rapl->nb = 0;
-  rapl->names = NULL;
-  rapl->fids = NULL;
-
-  char buffer[1024];
-  char *name_base = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:%d/%s";
-  char *name_sub = "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:%d/intel-rapl:%d:%d/%s";
-
-  for (int i=0;; i++) {
-    sprintf(buffer, name_base, i, "name");
-    char *tmp = get_frapl_string(buffer);
-    if (tmp == NULL) break;
-    //printf("%s\n", tmp);
-    test_append(tmp, i);
-    //printf("%s -> %s\n", buffer, tmp);
-
-    sprintf(buffer, name_base, i, "energy_uj");
-    add_frapl_source(rapl, tmp, buffer);
-    free(tmp);
-
-    for (int j=0;; j++) {
-      sprintf(buffer, name_sub, i, i, j, "name");
-      char *tmp_sub = get_frapl_string(buffer);
-      if (tmp_sub == NULL) break;
-      //printf("%s\n", tmp_sub);
-      test_append(tmp_sub, i);
-      //printf("%s -> %s\n", buffer, tmp_sub);
-
-
-      sprintf(buffer, name_sub, i, i, j, "energy_uj");
-      add_frapl_source(rapl, tmp_sub, buffer);
-      
-      free(tmp_sub);
-    }
-  }
-
-  rapl->values = calloc(sizeof(uint64_t), rapl->nb);
-  rapl->tmp_values = calloc(sizeof(uint64_t), rapl->nb);
-
-  _get_frapl(rapl->values, rapl);
-
-  *ptr = (void*)rapl;
-  return rapl->nb;
-}
-
-
-unsigned int get_frapl(uint64_t* results, void* ptr) {
-  _frapl_t* state = (_frapl_t*) ptr;
-  _get_frapl(state->tmp_values, state);
-  for(int i=0; i<state->nb; i++)
-    results[i] = state->tmp_values[i] - state->values[i];
-
-  memcpy(state->values, state->tmp_values, sizeof(uint64_t)*state->nb);
-  return state->nb;
-}
-
-void clean_frapl(void *ptr) {
-  _frapl_t* rapl = (_frapl_t*) ptr;
-  for(int i=0; i<rapl->nb; i++) {
-    free(rapl->names[i]);
-    close(rapl->fids[i]);
-  }
-  free(rapl->names);
-  free(rapl->fids);
-  free(rapl->values);
-  free(rapl->tmp_values);
-  free(rapl);
-}
-
-
-void label_frapl(char **labels, void *ptr) {
-  _frapl_t* rapl = (_frapl_t*) ptr;
-  for(int i=0; i<rapl->nb; i++)
-    labels[i] = rapl->names[i];
-}
diff --git a/infiniband.c b/infiniband.c
deleted file mode 100644
index 49dca077f6f8552b5707a75c4ad5bc2ffa2da462..0000000000000000000000000000000000000000
--- a/infiniband.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-#include <stdlib.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <glob.h>
-
-#include <stdint.h>
-
-struct network_t {
-  uint64_t values[4];
-  uint64_t tmp_values[4];
-  int sources[4];
-};
-unsigned int _get_network(uint64_t* results, int* sources);
-
-
-unsigned int init_infiniband(char* infi_path, void**ptr) {
-  if(infi_path==NULL)
-    return 0;
-
-  if(strcmp(infi_path,"X")==0) {
-
-    glob_t res;
-  
-    glob("/sys/class/infiniband/*/ports/*/counters/", 0, NULL, &res);
-    if(res.gl_pathc == 0)
-      return 0;
-    infi_path = res.gl_pathv[0];
-  }
-  
-  char *filenames[] = {"%s/port_rcv_packets",
-		       "%s/port_rcv_data",
-		       "%s/port_xmit_packets",
-		       "%s/port_xmit_data"};
-
-  struct network_t *state = malloc(sizeof(struct network_t));
-
-  char buffer[1024];
-  for(int i=0; i<4; i++) {
-    sprintf(buffer, filenames[i], infi_path);
-    state->sources[i] = open(buffer, O_RDONLY);
-  }
-  
-  *ptr = (void*) state;
-  _get_network(state->values, state->sources);
-
-  return 4;
-}
-
-char *_labels_infiniband[4] = {"irxp", "irxb", "itxp", "itxb"};
-void label_infiniband(char **labels, void*none) {
-  for(int i=0; i<4; i++)
-    labels[i] = _labels_infiniband[i];
-}
diff --git a/infiniband.h b/infiniband.h
deleted file mode 100644
index af8b2b6d0993a138dee43e4aa7aa8cd71fad661d..0000000000000000000000000000000000000000
--- a/infiniband.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-unsigned int init_infiniband(char* infi_path, void**ptr);
-void label_infiniband(char **labels, void*);
diff --git a/lib/info_reader.c b/lib/info_reader.c
new file mode 100644
index 0000000000000000000000000000000000000000..d23f5547c03e3d507fc292748e87b892be0b43db
--- /dev/null
+++ b/lib/info_reader.c
@@ -0,0 +1,146 @@
+#include <info_reader.h>
+
+ 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);
+}
+
+ 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;
+}
+
+ unsigned int move_to_next(Parser *parser)
+{
+    parser->nb_stored += 1;
+    if (parser->nb_stored >= parser->capacity) {
+        return 0;
+    }
+    return 1;
+}
+
+
+#define PAGE_SIZE 4096
+ssize_t buffer_getline(char **lineptr, FILE *stream) {
+    ssize_t num_chars_read = 0;
+    static char buffer[PAGE_SIZE] = {0};
+
+    if (!lineptr || !stream) {
+        return -1;
+    }
+
+    while (1) {
+        int ch = fgetc(stream);
+        if (ch == EOF) {
+            if (num_chars_read == 0) {
+                return -1;
+            } else {
+                break;
+            }
+        }
+
+        if (num_chars_read == PAGE_SIZE - 1) {
+            return -1;
+        }
+
+        buffer[num_chars_read++] = ch;
+        if (ch == '\n') {
+            break;
+        }
+    }
+
+    buffer[num_chars_read] = '\0';
+    *lineptr = buffer;
+
+    return num_chars_read;
+}
+
+
+ unsigned int parse(Parser *parser)
+{
+    char *line = NULL;
+    ssize_t read;
+    unsigned int key_assigned = 0;
+
+    while ((read = buffer_getline(&line, 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++;
+    }
+    return 1;
+}
+
+
+
+ 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;
+        }
+    }
+}
+
+ 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;
+    }
+}
+
+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;
+    }
+}
diff --git a/lib/info_reader.h b/lib/info_reader.h
new file mode 100644
index 0000000000000000000000000000000000000000..403bb197f4995654e63e25bdf3acd22b9489eab4
--- /dev/null
+++ b/lib/info_reader.h
@@ -0,0 +1,172 @@
+/*******************************************************
+ 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 <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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.
+ */
+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.
+ */
+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.
+ */
+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.
+ */
+unsigned int match(Parser *parser, char *line, KeyFinder **key_finder,
+                   char **raw_value);
+
+/**
+* @brief Reads a line of text from a file stream and stores it in a static
+  buffer with a maximum size of PAGE_SIZE.
+
+* This function reads a line of text from the input stream pointed to by
+* 'stream'. The line of text is stored in a static buffer with a maximum size of
+* PAGE_SIZE. The function updates the pointer pointed to by 'lineptr' to point
+to
+* the buffer containing the line of text. If the line of text is longer than the
+* buffer, the function returns -1. If an error occurs,
+
+* @param lineptr A pointer to a pointer to the buffer where the line of text
+  will be stored.
+* @param stream A pointer to the input stream to read from.
+* @return The number of characters read, or -1 if an error occurred the
+  function returns -1.
+*/
+ssize_t buffer_getline(char **lineptr, FILE *stream);
+
+/**
+ * @brief Parse with the configured parser.
+ *
+ * @param parser the parser.
+ */
+unsigned int parse(Parser *parser);
+
+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;
+};
+
+#endif
diff --git a/lib/optparse.h b/lib/optparse.h
new file mode 100644
index 0000000000000000000000000000000000000000..ee7dcb6c967fc158df36b4a844a13d5f7b6746ac
--- /dev/null
+++ b/lib/optparse.h
@@ -0,0 +1,392 @@
+/* Optparse --- portable, reentrant, embeddable, getopt-like option parser
+ *
+ * This is free and unencumbered software released into the public domain.
+ *
+ * To get the implementation, define OPTPARSE_IMPLEMENTATION.
+ * Optionally define OPTPARSE_API to control the API's visibility
+ * and/or linkage (static, __attribute__, __declspec).
+ *
+ * The POSIX getopt() option parser has three fatal flaws. These flaws
+ * are solved by Optparse.
+ *
+ * 1) Parser state is stored entirely in global variables, some of
+ * which are static and inaccessible. This means only one thread can
+ * use getopt(). It also means it's not possible to recursively parse
+ * nested sub-arguments while in the middle of argument parsing.
+ * Optparse fixes this by storing all state on a local struct.
+ *
+ * 2) The POSIX standard provides no way to properly reset the parser.
+ * This means for portable code that getopt() is only good for one
+ * run, over one argv with one option string. It also means subcommand
+ * options cannot be processed with getopt(). Most implementations
+ * provide a method to reset the parser, but it's not portable.
+ * Optparse provides an optparse_arg() function for stepping over
+ * subcommands and continuing parsing of options with another option
+ * string. The Optparse struct itself can be passed around to
+ * subcommand handlers for additional subcommand option parsing. A
+ * full reset can be achieved by with an additional optparse_init().
+ *
+ * 3) Error messages are printed to stderr. This can be disabled with
+ * opterr, but the messages themselves are still inaccessible.
+ * Optparse solves this by writing an error message in its errmsg
+ * field. The downside to Optparse is that this error message will
+ * always be in English rather than the current locale.
+ *
+ * Optparse should be familiar with anyone accustomed to getopt(), and
+ * it could be a nearly drop-in replacement. The option string is the
+ * same and the fields have the same names as the getopt() global
+ * variables (optarg, optind, optopt).
+ *
+ * Optparse also supports GNU-style long options with optparse_long().
+ * The interface is slightly different and simpler than getopt_long().
+ *
+ * By default, argv is permuted as it is parsed, moving non-option
+ * arguments to the end. This can be disabled by setting the `permute`
+ * field to 0 after initialization.
+ */
+#ifndef OPTPARSE_H
+#define OPTPARSE_H
+
+#ifndef OPTPARSE_API
+#  define OPTPARSE_API
+#endif
+
+struct optparse {
+    char **argv;
+    int permute;
+    int optind;
+    int optopt;
+    char *optarg;
+    char errmsg[64];
+    int subopt;
+};
+
+enum optparse_argtype {
+    OPTPARSE_NONE,
+    OPTPARSE_REQUIRED,
+    OPTPARSE_OPTIONAL
+};
+
+struct optparse_long {
+    const char *longname;
+    int shortname;
+    enum optparse_argtype argtype;
+    char *usage_arg;
+    char *usage_msg;
+    void *(*fn)(void *, size_t);	/* SUBJECT TO CHANGE */
+};
+
+/**
+ * Initializes the parser state.
+ */
+OPTPARSE_API
+void optparse_init(struct optparse *options, char **argv);
+
+/**
+ * Read the next option in the argv array.
+ * @param optstring a getopt()-formatted option string.
+ * @return the next option character, -1 for done, or '?' for error
+ *
+ * Just like getopt(), a character followed by no colons means no
+ * argument. One colon means the option has a required argument. Two
+ * colons means the option takes an optional argument.
+ */
+OPTPARSE_API
+int optparse(struct optparse *options, const char *optstring);
+
+/**
+ * Handles GNU-style long options in addition to getopt() options.
+ * This works a lot like GNU's getopt_long(). The last option in
+ * longopts must be all zeros, marking the end of the array. The
+ * longindex argument may be NULL.
+ */
+OPTPARSE_API
+int optparse_long(struct optparse *options,
+                  const struct optparse_long *longopts,
+                  int *longindex);
+
+/**
+ * Used for stepping over non-option arguments.
+ * @return the next non-option argument, or NULL for no more arguments
+ *
+ * Argument parsing can continue with optparse() after using this
+ * function. That would be used to parse the options for the
+ * subcommand returned by optparse_arg(). This function allows you to
+ * ignore the value of optind.
+ */
+OPTPARSE_API
+char *optparse_arg(struct optparse *options);
+
+/* Implementation */
+#ifdef OPTPARSE_IMPLEMENTATION
+
+#define OPTPARSE_MSG_INVALID "invalid option"
+#define OPTPARSE_MSG_MISSING "option requires an argument"
+#define OPTPARSE_MSG_TOOMANY "option takes no arguments"
+
+static int optparse_error(struct optparse *options, const char *msg, const char *data)
+{
+    unsigned p = 0;
+    const char *sep = " -- '";
+    while (*msg)
+        options->errmsg[p++] = *msg++;
+    while (*sep)
+        options->errmsg[p++] = *sep++;
+    while (p < sizeof(options->errmsg) - 2 && *data)
+        options->errmsg[p++] = *data++;
+    options->errmsg[p++] = '\'';
+    options->errmsg[p++] = '\0';
+    return '?';
+}
+
+OPTPARSE_API
+void optparse_init(struct optparse *options, char **argv)
+{
+    options->argv = argv;
+    options->permute = 1;
+    options->optind = argv[0] != 0;
+    options->subopt = 0;
+    options->optarg = 0;
+    options->errmsg[0] = '\0';
+}
+
+static int optparse_is_dashdash(const char *arg)
+{
+    return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
+}
+
+static int optparse_is_shortopt(const char *arg)
+{
+    return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
+}
+
+static int optparse_is_longopt(const char *arg)
+{
+    return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
+}
+
+static void optparse_permute(struct optparse *options, int index)
+{
+    char *nonoption = options->argv[index];
+    int i;
+    for (i = index; i < options->optind - 1; i++)
+        options->argv[i] = options->argv[i + 1];
+    options->argv[options->optind - 1] = nonoption;
+}
+
+static int optparse_argtype(const char *optstring, char c)
+{
+    int count = OPTPARSE_NONE;
+    if (c == ':')
+        return -1;
+    for (; *optstring && c != *optstring; optstring++);
+    if (!*optstring)
+        return -1;
+    if (optstring[1] == ':')
+        count += optstring[2] == ':' ? 2 : 1;
+    return count;
+}
+
+OPTPARSE_API
+int optparse(struct optparse *options, const char *optstring)
+{
+    int type;
+    char *next;
+    char *option = options->argv[options->optind];
+    options->errmsg[0] = '\0';
+    options->optopt = 0;
+    options->optarg = 0;
+    if (option == 0) {
+        return -1;
+    } else if (optparse_is_dashdash(option)) {
+        options->optind++; /* consume "--" */
+        return -1;
+    } else if (!optparse_is_shortopt(option)) {
+        if (options->permute) {
+            int index = options->optind++;
+            int r = optparse(options, optstring);
+            optparse_permute(options, index);
+            options->optind--;
+            return r;
+        } else {
+            return -1;
+        }
+    }
+    option += options->subopt + 1;
+    options->optopt = option[0];
+    type = optparse_argtype(optstring, option[0]);
+    next = options->argv[options->optind + 1];
+    switch (type) {
+    case -1: {
+        char str[2] = {0, 0};
+        str[0] = option[0];
+        options->optind++;
+        return optparse_error(options, OPTPARSE_MSG_INVALID, str);
+    }
+    case OPTPARSE_NONE:
+        if (option[1]) {
+            options->subopt++;
+        } else {
+            options->subopt = 0;
+            options->optind++;
+        }
+        return option[0];
+    case OPTPARSE_REQUIRED:
+        options->subopt = 0;
+        options->optind++;
+        if (option[1]) {
+            options->optarg = option + 1;
+        } else if (next != 0) {
+            options->optarg = next;
+            options->optind++;
+        } else {
+            char str[2] = {0, 0};
+            str[0] = option[0];
+            options->optarg = 0;
+            return optparse_error(options, OPTPARSE_MSG_MISSING, str);
+        }
+        return option[0];
+    case OPTPARSE_OPTIONAL:
+        options->subopt = 0;
+        options->optind++;
+        if (option[1])
+            options->optarg = option + 1;
+        else
+            options->optarg = 0;
+        return option[0];
+    }
+    return 0;
+}
+
+OPTPARSE_API
+char *optparse_arg(struct optparse *options)
+{
+    char *option = options->argv[options->optind];
+    options->subopt = 0;
+    if (option != 0)
+        options->optind++;
+    return option;
+}
+
+static int optparse_longopts_end(const struct optparse_long *longopts, int i)
+{
+    return !longopts[i].longname && !longopts[i].shortname;
+}
+
+static void optparse_from_long(const struct optparse_long *longopts, char *optstring)
+{
+    char *p = optstring;
+    int i;
+    for (i = 0; !optparse_longopts_end(longopts, i); i++) {
+        if (longopts[i].shortname && longopts[i].shortname < 127) {
+            int a;
+            *p++ = longopts[i].shortname;
+            for (a = 0; a < (int)longopts[i].argtype; a++)
+                *p++ = ':';
+        }
+    }
+    *p = '\0';
+}
+
+/* Unlike strcmp(), handles options containing "=". */
+static int optparse_longopts_match(const char *longname, const char *option)
+{
+    const char *a = option, *n = longname;
+    if (longname == 0)
+        return 0;
+    for (; *a && *n && *a != '='; a++, n++)
+        if (*a != *n)
+            return 0;
+    return *n == '\0' && (*a == '\0' || *a == '=');
+}
+
+/* Return the part after "=", or NULL. */
+static char *optparse_longopts_arg(char *option)
+{
+    for (; *option && *option != '='; option++);
+    if (*option == '=')
+        return option + 1;
+    else
+        return 0;
+}
+
+static int optparse_long_fallback(struct optparse *options,
+                                  const struct optparse_long *longopts,
+                                  int *longindex)
+{
+    int result;
+    char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
+    optparse_from_long(longopts, optstring);
+    result = optparse(options, optstring);
+    if (longindex != 0) {
+        *longindex = -1;
+        if (result != -1) {
+            int i;
+            for (i = 0; !optparse_longopts_end(longopts, i); i++)
+                if (longopts[i].shortname == options->optopt)
+                    *longindex = i;
+        }
+    }
+    return result;
+}
+
+OPTPARSE_API
+int optparse_long(struct optparse *options,
+                  const struct optparse_long *longopts,
+                  int *longindex)
+{
+    int i;
+    char *option = options->argv[options->optind];
+    if (option == 0) {
+        return -1;
+    } else if (optparse_is_dashdash(option)) {
+        options->optind++; /* consume "--" */
+        return -1;
+    } else if (optparse_is_shortopt(option)) {
+        return optparse_long_fallback(options, longopts, longindex);
+    } else if (!optparse_is_longopt(option)) {
+        if (options->permute) {
+            int index = options->optind++;
+            int r = optparse_long(options, longopts, longindex);
+            optparse_permute(options, index);
+            options->optind--;
+            return r;
+        } else {
+            return -1;
+        }
+    }
+
+    /* Parse as long option. */
+    options->errmsg[0] = '\0';
+    options->optopt = 0;
+    options->optarg = 0;
+    option += 2; /* skip "--" */
+    options->optind++;
+    for (i = 0; !optparse_longopts_end(longopts, i); i++) {
+        const char *name = longopts[i].longname;
+        if (optparse_longopts_match(name, option)) {
+            char *arg;
+            if (longindex)
+                *longindex = i;
+            options->optopt = longopts[i].shortname;
+            arg = optparse_longopts_arg(option);
+            if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
+                return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
+            }
+            if (arg != 0) {
+                options->optarg = arg;
+            } else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
+                options->optarg = options->argv[options->optind];
+                if (options->optarg == 0)
+                    return optparse_error(options, OPTPARSE_MSG_MISSING, name);
+                else
+                    options->optind++;
+            }
+            return options->optopt;
+        }
+    }
+    return optparse_error(options, OPTPARSE_MSG_INVALID, option);
+}
+
+#endif /* OPTPARSE_IMPLEMENTATION */
+#endif /* OPTPARSE_H */
diff --git a/load.c b/load.c
deleted file mode 100644
index b3d154d794c35f96baeb64ae61d447400633cbc0..0000000000000000000000000000000000000000
--- a/load.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/*******************************************************
- Copyright (C) 2019-2021 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-
-#define LOAD_BUFFER_SIZE 1024
-char buffer[LOAD_BUFFER_SIZE];
-
-static int load_fid=-1;
-static uint64_t load_values[10]={0,0,0,0,0,0,0,0,0,0};
-static uint64_t tmp_load_values[10]={0,0,0,0,0,0,0,0,0,0};
-
-void _get_load(uint64_t* results) {
-  pread(load_fid, buffer, LOAD_BUFFER_SIZE-1, 0);
-  int pos=0;
-  while(buffer[pos] > '9' || buffer[pos] < '0') pos++;
-  for(int i=0; i<10; i++) {
-    results[i] = strtoull(buffer+pos, NULL, 10);
-    while(buffer[pos] <= '9' && buffer[pos] >= '0') pos++;
-    pos++;
-  }
-}
-
-// Public interface
-
-unsigned int init_load(char* argument, void **state) {
-  load_fid = open("/proc/stat", O_RDONLY);
-  _get_load(load_values);
-  return 10;
-}
-
-unsigned int get_load(uint64_t* results, void* state) {
-  _get_load(tmp_load_values);
-  for(int i=0; i<10; i++)
-    results[i] = tmp_load_values[i] - load_values[i];
-
-  memcpy(load_values, tmp_load_values, sizeof(load_values));
-  return 10;
-}
-
-void clean_load(void *state) {
-  close(load_fid);
-}
-
-char *_labels[10] = {"user","nice","system","idle","iowait","irq",
-  "softirq","steal","guest","guest_nice"};
-void label_load(char **labels, void*none) {
-  for(int i=0; i<10; i++)
-    labels[i] = _labels[i];
-}
diff --git a/makefile b/makefile
index 433b76f63d171d59b4965f8981f9cfd262d80e11..4dcbce48455abcce4d567201af99f7fb24a39552 100644
--- a/makefile
+++ b/makefile
@@ -1,30 +1,108 @@
-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
+LIB_DIR = lib
+BIN_DIR = bin
+TESTS_DIR = tests
 
-mojitos:$(OBJECTS)
-	gcc $(DEBUG) -O3 -Wall -o mojitos $(OBJECTS) -lpowercap
+BIN = mojitos
+PREFIX = /usr/local
 
-OBJECTS_GRP = $(subst _individual,_group, $(OBJECTS))
-mojitos_group: $(OBJECTS_GRP) counters_option.h
-	gcc $(DEBUG) -O3 -Wall -o mojitos_group $(OBJECTS_GRP) -lpowercap
+CC = gcc
+CPPFLAGS = -std=gnu99 -Wall -Wextra -Wpedantic -Wno-unused-function -I./lib $(NVML_IFLAGS)
+CFLAGS = $(CPPFLAGS) -O3 -Werror
+LDFLAGS = $(NVML_LDFLAGS)
 
-counters_%.o: counters_%.c counters.h counters_option.h
-	gcc $(DEBUG) -c -O3 -Wall $< -o $@
+ASTYLE = astyle --style=kr -xf -s4 -k3 -n -Z -Q
 
-counters_option.h: counters_option.py
-	./counters_option.py > counters_option.h
+all: $(BIN) man
 
+CAPTOR_OBJ =
+NVML_LDFLAGS =
+NVML_IFLAGS =
 
-mojitos.o: mojitos.c counters_option.h
-	gcc $(DEBUG) -c -O3 -Wall $< -o $@
+include ./sensors.mk
 
-debug: DEBUG = -DDEBUG -g
+OBJ =  \
+	$(OBJ_DIR)/util.o \
+	$(OBJ_DIR)/info_reader.o \
+	$(CAPTOR_OBJ)
 
-debug: all
+options:
+	@echo BIN: $(BIN)
+	@echo CC: $(CC)
+	@echo CFLAGS: $(CFLAGS)
+	@echo LDFLAGS: $(LDFLAGS)
+	@echo OBJ: $(OBJ)
 
-%.o : %.c %.h
-	gcc $(DEBUG) -c -O3 -Wall $< -o $@
+$(BIN): $(BIN_DIR) $(OBJ) $(OBJ_DIR)/$(BIN).o
+	$(CC) -o $(BIN_DIR)/$(BIN) $(OBJ) $(OBJ_DIR)/$(BIN).o $(LDFLAGS)
+
+$(OBJ): $(OBJ_DIR)
+$(OBJ_DIR)/counters.o: $(SRC_DIR)/counters_option.h
+$(OBJ_DIR)/memory_counters.o: $(SRC_DIR)/memory_option.h
+
+$(OBJ_DIR)/$(BIN).o: $(SRC_DIR)/$(BIN).c
+	$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJ_DIR)/util.o: $(SRC_DIR)/util.c $(SRC_DIR)/util.h
+	$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJ_DIR)/info_reader.o: $(LIB_DIR)/info_reader.c
+	$(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
+
+$(SRC_DIR)/memory_option.h: $(SRC_DIR)/memory_option.sh
+	sh ./$(SRC_DIR)/memory_option.sh > $(SRC_DIR)/memory_option.h
+
+$(OBJ_DIR):
+	mkdir -p $(OBJ_DIR)
+
+$(BIN_DIR):
+	mkdir -p $(BIN_DIR)
+
+debug: CFLAGS = $(CPPFLAGS) -DDEBUG -g -Og
+debug: $(BIN)
+
+tests: $(OBJ_DIR)/util.o $(OBJ_DIR)/info_reader.o
+	$(CC) $(CPPFLAGS) $(OBJ_DIR)/util.o $(OBJ_DIR)/info_reader.o $(TESTS_DIR)/main.c  -o $(TESTS_DIR)/run
+	$(TESTS_DIR)/run
+
+format:
+	$(ASTYLE) $(SRC_DIR)/*.[ch] \
+		$(DOC_DIR)/*.[ch] \
+		$(TESTS_DIR)/*.[ch]
 
 clean:
-	\rm -f *~ *.o mojitos_group mojitos counters_option.h
+	\rm -f $(OBJ_DIR)/* $(BIN_DIR)/* \
+		$(SRC_DIR)/counters_option.h \
+		$(SRC_DIR)/memory_option.h \
+		$(TESTS_DIR)/run \
+		$(DOC_DIR)/test_main_ex \
+		$(DOC_DIR)/info_reader_ex
+
+readme:
+	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
+
+install: $(BIN) man
+	mkdir -p $(PREFIX)/bin
+	cp $(BIN_DIR)/$(BIN) $(PREFIX)/bin/.
+	chmod 755 $(PREFIX)/bin/$(BIN)
+	mkdir -p $(PREFIX)/share/man/man1
+	cp $(DOC_DIR)/$(BIN).1 $(PREFIX)/share/man/man1/.
+	chmod 644 $(PREFIX)/share/man/man1/$(BIN).1
+
+uninstall:
+	rm -f $(PREFIX)/bin/$(BIN)
+	rm -f $(PREFIX)/share/man/man1/$(BIN).1
+
+.PHONY: all clean mojitos debug format tests readme man install uninstall
diff --git a/mojitos b/mojitos
deleted file mode 100755
index ab3f7b4824740935e193e4d9efee35d0f20849fa..0000000000000000000000000000000000000000
Binary files a/mojitos and /dev/null differ
diff --git a/mojitos.c b/mojitos.c
deleted file mode 100644
index e80e692f5a4498e02e60761cee1a2d568860c314..0000000000000000000000000000000000000000
--- a/mojitos.c
+++ /dev/null
@@ -1,259 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-#include <signal.h>
-#include <inttypes.h>
-#include <unistd.h>
-
-#include "counters.h"
-#include "rapl.h"
-#include "frapl.h"
-#include "network.h"
-#include "infiniband.h"
-#include "load.h"
-#include "temperature.h"
-
-
-
-void usage(char** argv) {
-  printf("Usage : %s [-t time] [-f freq] [-r] [-p perf_list] [-l] [-u] [-c] [-d network_device] [-i infiniband_path] [-o logfile] [-e command arguments...]\n", argv[0]);
-  printf("if time==0 then loops infinitively\n");
-  printf("if -e is present, time and freq are not used\n");
-  printf("-r activates RAPL\n");
-  printf("-R activates the file version of RAPL\n");
-  printf("-p activates performance counters\n");
-  printf("   perf_list is coma separated list of performance counters without space. Ex: instructions,cache_misses\n");
-  printf("-l lists the possible performance counters and quits\n");
-  printf("-d activates network monitoring (if network_device is X, tries to detect it automatically)\n");
-  printf("-i activates infiniband monitoring (if infiniband_path is X, tries to detect it automatically)\n");
-  printf("-s activates statistics of overhead in nanoseconds\n");
-  printf("-u activates report of system load\n");
-  printf("-c activates report of processor temperature\n");
-  exit(EXIT_SUCCESS);
-}
-
-void sighandler(int none) {
-}
-
-void flush(int none) {
-  exit(0);
-}
-
-FILE *output;
-void flushexit() {
-  fflush(output);
-  fclose(output);
-}
-
-typedef unsigned int (initializer_t)(char*, void **);
-typedef void (labeler_t)(char **, void*);
-typedef unsigned int (*getter_t)(uint64_t*, void*);
-typedef void (*cleaner_t)(void*);
-
-unsigned int nb_sources=0;
-void **states=NULL;
-getter_t *getter=NULL;
-cleaner_t *cleaner=NULL;
-
-unsigned int nb_sensors=0;
-char **labels=NULL;
-uint64_t *values=NULL;
-
-void add_source(initializer_t init, char* arg, labeler_t labeler,
-		getter_t get, cleaner_t clean) {
-    nb_sources++;
-    states = realloc(states, nb_sources*sizeof(void*));
-    int nb = init(arg, &states[nb_sources-1]);
-    if (nb == 0) {
-      nb_sources--;
-      states = realloc(states, nb_sources*sizeof(void*));
-      return;
-    }
-
-    getter = realloc(getter, nb_sources*sizeof(void*));
-    getter[nb_sources-1] = get;
-    cleaner = realloc(cleaner, nb_sources*sizeof(void*));
-    cleaner[nb_sources-1] = clean;
-
-    labels = realloc(labels, (nb_sensors+nb)*sizeof(char*));
-    labeler(labels+nb_sensors, states[nb_sources-1]);
-    
-    values = realloc(values, (nb_sensors+nb)*sizeof(uint64_t));
-    nb_sensors += nb;
-}
-
-int main(int argc, char **argv) {
-  int total_time=1;
-  int delta=0;
-  int frequency=1;
-  char **application = NULL;
-
-  int stat_mode = -1;
-
-  if(argc==1)
-    usage(argv);
-  
-  output=stdout;
-  
-  atexit(flushexit);
-  signal(15, flush);
-  
-  int c;
-  while ((c = getopt (argc, argv, "ilhcftdeoprRsu")) != -1 && application==NULL)
-    switch (c) {
-    case 'f':
-      frequency=atoi(argv[optind]);
-      break;
-    case 't':
-      total_time=atoi(argv[optind]);
-      delta=1;
-      if(total_time==0) {
-	total_time=1;
-	delta=0;
-      }
-      break;
-    case 'd':
-      add_source(init_network, argv[optind], label_network, get_network, clean_network);
-      break;
-    case 'i':
-      add_source(init_infiniband, argv[optind], label_infiniband, get_network, clean_network);
-      break;
-    case 'o':
-      output = fopen(argv[optind],"wb");
-      break;
-    case 'e':
-      application=&argv[optind];
-      signal(17,sighandler);
-      break;
-    case 'p':
-      add_source(init_counters, argv[optind], label_counters, get_counters, clean_counters);
-      break;
-    case 'r':
-      add_source(init_rapl, NULL, label_rapl, get_rapl, clean_rapl);
-      break;
-    case 'R':
-      add_source(init_frapl, NULL, label_frapl, get_frapl, clean_frapl);
-      break;
-    case 'u':
-      add_source(init_load, NULL, label_load, get_load, clean_load);
-      break;
-    case 'c':
-      add_source(init_temperature, NULL, label_temperature, get_temperature, clean_temperature);
-      break;
-    case 's':
-      stat_mode=0;
-      break;
-    case 'l':
-      show_all_counters();
-      exit(EXIT_SUCCESS);
-    default:
-      usage(argv);
-    }
-
-
-  setvbuf(output, NULL, _IONBF, BUFSIZ);
-  struct timespec ts;
-  struct timespec ts_ref;
-
-  fprintf(output, "#timestamp ");
-
-  for(int i=0; i<nb_sensors; i++)
-    fprintf(output, "%s ", labels[i]);
-
-  if(stat_mode==0)
-    fprintf(output, "overhead ");
-
-  fprintf(output, "\n");
-
-  unsigned long int stat_data=0;
-
-  for (int temps = 0; temps <total_time*frequency; temps+=delta) {
-    clock_gettime(CLOCK_MONOTONIC, &ts_ref);
-
-    // Get Data
-    unsigned int current = 0;
-    for(int i=0; i<nb_sources; i++)
-      current += getter[i](&values[current], states[i]);
-    
-    if(application != NULL) {
-
-      if(fork()==0){
-	execvp(application[0], application);
-	exit(0);
-      }
-      pause();
-      clock_gettime(CLOCK_MONOTONIC, &ts);
-      if(ts.tv_nsec >= ts_ref.tv_nsec)
-	fprintf(output, "%ld.%09ld ", (ts.tv_sec-ts_ref.tv_sec), ts.tv_nsec-ts_ref.tv_nsec);
-      else
-	fprintf(output, "%ld.%09ld ", (ts.tv_sec-ts_ref.tv_sec)-1,1000000000+ts.tv_nsec-ts_ref.tv_nsec);
-    }
-    else {
-#ifdef DEBUG
-      clock_gettime(CLOCK_MONOTONIC, &ts);
-      fprintf(stderr, "%ld\n", (ts.tv_nsec-ts_ref.tv_nsec)/1000);
-      //Indiv: mean: 148 std: 31 % med: 141 std: 28 %
-      //Group: mean: 309 std: 41 % med: 297 std: 39 %
-#endif
-      if(stat_mode==0) {
-	clock_gettime(CLOCK_MONOTONIC, &ts);
-      if(ts.tv_nsec >= ts_ref.tv_nsec)
-	stat_data = ts.tv_nsec-ts_ref.tv_nsec;
-      else
-	stat_data = 1000000000+ts.tv_nsec-ts_ref.tv_nsec;
-    }
-    
-      // Treat Data
-      fprintf(output, "%ld.%09ld ", ts_ref.tv_sec, ts_ref.tv_nsec);
-    }
-    
-    for(int i=0; i<nb_sensors; i++)
-      fprintf(output, "%" PRIu64 " ", values[i]);
-
-    if(stat_mode==0)
-      fprintf(output, "%ld ", stat_data);
-
-    fprintf(output, "\n");
-
-    if(application != NULL)
-      break;
-
-    clock_gettime(CLOCK_MONOTONIC, &ts);
-    usleep(1000*1000/frequency-(ts.tv_nsec/1000)%(1000*1000/frequency));
-  }
-
-  for(int i=0; i<nb_sources;i++)
-    cleaner[i](states[i]);
-
-  if(nb_sources > 0) {
-    free(getter);
-    free(cleaner);
-    free(labels);
-    free(values);
-    free(states);
-  }
-}
-
-
-
-
diff --git a/network.c b/network.c
deleted file mode 100644
index 6e3f494bd49b86c68e452efd202873821b9c6832..0000000000000000000000000000000000000000
--- a/network.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-
-struct network_t {
-  uint64_t values[4];
-  uint64_t tmp_values[4];
-  int sources[4];
-};
-
-unsigned int _get_network(uint64_t* results, int* sources) {
-  if(sources==NULL)
-    return 0;
-  char buffer[128];
-  for(int i=0; i<4; i++){
-    pread(sources[i], buffer, 127, 0);
-
-    results[i] = strtoull(buffer, NULL, 10);
-  }
-  return 4;
-}
-
-
-
-unsigned int init_network(char* dev, void**ptr) {
-  if(dev==NULL)
-    return 0;
-
-  if(strcmp(dev,"X")==0) {
-    int f = open("/proc/net/route", O_RDONLY);
-    char buffer[1000];
-    read(f, buffer, 999);
-    char *start_of_dev = index(buffer, '\n')+1;
-    char *end_of_dev = index(start_of_dev, '\t');
-    *end_of_dev='\0';
-    dev = start_of_dev;
-    close(f);
-  }
-  
-  char *filenames[] = {"/sys/class/net/%s/statistics/rx_packets",
-		       "/sys/class/net/%s/statistics/rx_bytes",
-		       "/sys/class/net/%s/statistics/tx_packets",
-		       "/sys/class/net/%s/statistics/tx_bytes"};
-
-  struct network_t *state = malloc(sizeof(struct network_t));
-
-  char buffer2[256];
-  for(int i=0; i<4; i++) {
-    sprintf(buffer2, filenames[i], dev);
-    state->sources[i] = open(buffer2, O_RDONLY);
-  }
-  *ptr = (void*) state;
-  _get_network(state->values, state->sources);
-
-  return 4;
-}
-
-unsigned int get_network(uint64_t* results, void* ptr) {
-  struct network_t *state = (struct network_t *) ptr;
-  _get_network(state->tmp_values, state->sources);
-  for(int i=0; i<4; i++)
-    results[i] = state->tmp_values[i] - state->values[i];
-
-  memcpy(state->values, state->tmp_values, 4*sizeof(uint64_t));
-  return 4;
-}
-
-void clean_network(void *ptr) {
-  struct network_t *state = (struct network_t *) ptr;
-  if(state==NULL)
-    return;
-  for(int i=0;i<4;i++)
-    close(state->sources[i]);
-  free(state);
-}    
-
-char *_labels_network[4] = {"rxp", "rxb", "txp", "txb"};
-void label_network(char **labels, void*none) {
-  for(int i=0; i<4; i++)
-    labels[i] = _labels_network[i];
-}
diff --git a/rapl.c b/rapl.c
deleted file mode 100644
index 14b4a58ce302d1251680540155fb372d98acec46..0000000000000000000000000000000000000000
--- a/rapl.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <powercap/powercap-rapl.h>
-
-struct _rapl_t {
-  powercap_rapl_pkg* pkgs;
-  uint32_t nb_pkgs;
-  uint32_t nb;
-  char **names;
-  uint32_t* zones;
-  uint32_t* packages;
-  uint64_t* values;
-  uint64_t* tmp_values;
-};
-
-typedef struct _rapl_t _rapl_t;
-
-
-
-
-
-
-const int nb_zones = 3;
-const int rapl_zones[3] = { POWERCAP_RAPL_ZONE_PACKAGE,   POWERCAP_RAPL_ZONE_CORE,   POWERCAP_RAPL_ZONE_DRAM};
-
-
-#define MAX_LEN_NAME 100
-
-// values [zone + package *nbzones] microjoules
-void _get_rapl(uint64_t *values, _rapl_t* rapl) {
-  for (int i = 0; i < rapl->nb; i++) {
-#ifdef DEBUG
-    int ret =
-#endif
-      powercap_rapl_get_energy_uj(&rapl->pkgs[rapl->packages[i]],
-				rapl->zones[i],
-				&values[i]);
-#ifdef DEBUG
-    printf("GETRAPL: package %d, zone %d, name %s, ret: %d\n", rapl->packages[i], rapl->zones[i], rapl->names[i], ret);
-#endif
-  }
-}
-
-unsigned int init_rapl(char* none, void **ptr) {
-  // get number of processor sockets
-  _rapl_t* rapl= malloc(sizeof(struct _rapl_t));
-  rapl->nb = 0;
-  rapl->packages = NULL;
-  rapl->zones = NULL;
-
-  rapl->nb_pkgs = powercap_rapl_get_num_instances();
-  //rapl->nb_pkgs = powercap_rapl_get_num_packages();
-  
-  if (rapl->nb_pkgs == 0) {
-    perror("no packages found (maybe the kernel module isn't loaded?)");
-    exit(-1);
-  }
-  rapl->pkgs = malloc(rapl->nb_pkgs * sizeof(powercap_rapl_pkg));  
-  for (int package = 0; package < rapl->nb_pkgs; package++)
-    if (powercap_rapl_init(package, &rapl->pkgs[package], 0)) {
-      perror("powercap_rapl_init, check access (root needed ?)");
-      exit(-1);
-    }
-
-  rapl->names = NULL;
-    
-  char _name[MAX_LEN_NAME+1];
-  char _name2[MAX_LEN_NAME+11];
-
-  for (unsigned int package = 0; package < rapl->nb_pkgs; package++) {
-    for(unsigned int zone=0; zone < nb_zones; zone++) {
-      int length=powercap_rapl_get_name(&rapl->pkgs[package], rapl_zones[zone],
-			     _name, MAX_LEN_NAME);
-      if (length>0) {
-
-	sprintf(_name2, "%s%u", _name, package);
-
-	rapl->nb++;
-	rapl->names = realloc(rapl->names, sizeof(char*)*rapl->nb);
-	rapl->names[rapl->nb-1] = malloc(sizeof(char) * (strlen(_name2)+1));
-	rapl->zones = realloc(rapl->zones, sizeof(uint32_t)*rapl->nb);
-	rapl->packages = realloc(rapl->packages, sizeof(uint32_t)*rapl->nb);
-	
-	strcpy(rapl->names[rapl->nb-1], _name2);
-	rapl->zones[rapl->nb-1] = rapl_zones[zone];
-	rapl->packages[rapl->nb-1] = package;
-      }
-#ifdef DEBUG
-      printf("%d %d %d %d %s\n\n", length, package, zone, rapl_zones[zone], _name2);
-#endif
-    }
-  }
-#ifdef DEBUG
-  printf("Result of init\n");
-  for(int i=0; i<rapl->nb; i++)
-    printf("package %d, zone %d, name %s\n", rapl->packages[i], rapl->zones[i], rapl->names[i]);
-#endif
-
-  rapl->values = calloc(sizeof(uint64_t), rapl->nb);
-  rapl->tmp_values = calloc(sizeof(uint64_t), rapl->nb);
-
-  _get_rapl(rapl->values, rapl);
-
-  *ptr = (void*)rapl;
-  return rapl->nb;
-}
-
-
-
-unsigned int get_rapl(uint64_t* results, void* ptr) {
-  _rapl_t* state = (_rapl_t*) ptr;
-  _get_rapl(state->tmp_values, state);
-  for(int i=0; i<state->nb; i++)
-    results[i] = state->tmp_values[i] - state->values[i];
-
-  memcpy(state->values, state->tmp_values, sizeof(uint64_t)*state->nb);
-  return state->nb;
-}
-
-
-
-
-void clean_rapl(void* ptr) {
-  _rapl_t* rapl = (_rapl_t*) ptr;
-  for (int package = 0; package < rapl->nb_pkgs; package++)
-    if (powercap_rapl_destroy(&rapl->pkgs[package]))
-      perror("powercap_rapl_destroy");
-  for (int elem=0; elem<rapl->nb; elem++) 
-      free(rapl->names[elem]);
-  
-  free(rapl->names);
-  free(rapl->pkgs);
-  free(rapl->zones);
-  free(rapl->packages);
-  free(rapl->values);
-  free(rapl->tmp_values);
-  free(rapl);
-}
-
-void label_rapl(char **labels, void *ptr) {
-  _rapl_t* rapl = (_rapl_t*) ptr;
-  for(int i=0; i<rapl->nb; i++)
-    labels[i] = rapl->names[i];
-}
diff --git a/src/amd_rapl.c b/src/amd_rapl.c
new file mode 100644
index 0000000000000000000000000000000000000000..572ac564693db3b4f5c221595a5f6629047d60d0
--- /dev/null
+++ b/src/amd_rapl.c
@@ -0,0 +1,376 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+*******************************************************/
+
+#include <stdio.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "info_reader.h"
+#include "util.h"
+
+#define BUFFER_SIZE 64
+#define __READ_CPUINFO__
+
+// ---------------------------MSR_REGISTERS
+static const uint64_t amd_energy_mask = 0xFFFFFFFF;
+static const uint64_t amd_energy_unit_mask = 0x01F00;
+static const uint64_t msr_rapl_power_unit = 0xC0010299;
+static const uint64_t energy_core_msr = 0xC001029A;
+
+// ------------------------------FILE_PATHS
+static const char *base_str = "/dev/cpu/%d/msr";
+
+struct CpuSensor {
+    unsigned int cpu_id;
+    unsigned int package_id;
+    unsigned int core_id;
+
+    char *name;
+    int fd;
+
+    unsigned int energy_units;
+    uint64_t core_energy;
+};
+typedef struct CpuSensor CpuSensor;
+
+struct AmdRapl {
+    CpuSensor *sensors;
+    unsigned int sensor_count;
+};
+typedef struct AmdRapl AmdRapl;
+
+// -----------------------------INFO_READER
+
+#ifdef __READ_CPUINFO__
+#define NB_KEYS 3
+
+static char *cpuinfo = "/proc/cpuinfo";
+
+static GenericPointer uint_allocator(char *s)
+{
+    unsigned int value = atoi(s);
+    return (GenericPointer) value;
+}
+
+static void _set_cpu_id(GenericPointer storage, GenericPointer data)
+{
+    CpuSensor *cpu = (CpuSensor *) storage;
+    cpu->cpu_id = (unsigned int) data;
+}
+
+static void _set_package_id(GenericPointer storage, GenericPointer data)
+{
+    CpuSensor *cpu = (CpuSensor *) storage;
+    cpu->package_id = (unsigned int) data;
+}
+
+static void _set_core_id(GenericPointer storage, GenericPointer data)
+{
+    CpuSensor *cpu = (CpuSensor *) storage;
+    cpu->core_id = (unsigned int) data;
+}
+
+static KeyFinder keys[NB_KEYS] = {
+    {"processor", ": ", uint_allocator, _set_cpu_id},
+    {"physical id", ": ", uint_allocator, _set_package_id},
+    {"core id", ": ", uint_allocator, _set_core_id}
+};
+
+
+static unsigned int parse_cpuinfo(CpuSensor *storage, unsigned int capacity)
+{
+    Parser parser = {
+        .storage = (GenericPointer) storage,
+        .nb_stored = 0,
+        .capacity = capacity,
+        .storage_struct_size = sizeof(CpuSensor),
+        .keys = keys,
+        .nb_keys = NB_KEYS,
+        .file = fopen(cpuinfo, "r")
+    };
+    return parse(&parser);
+}
+
+#endif
+
+// --------------------------------READ_MSR
+
+uint64_t read_msr(int fd, uint64_t msr)
+{
+    uint64_t data;
+    if (pread(fd, &data, sizeof data, msr) != sizeof data) {
+        fprintf(stderr, "read_msr(%"PRIu64"):", msr);
+        perror("pread");
+        exit(127);
+    }
+    return data;
+}
+
+unsigned int read_unit(int fd)
+{
+    uint64_t unit = read_msr(fd, msr_rapl_power_unit);
+    return ((unit & amd_energy_unit_mask) >> 8);
+}
+
+uint64_t read_raw_core_energy(int fd)
+{
+    uint64_t energy = read_msr(fd, energy_core_msr);
+    return energy & amd_energy_mask;
+}
+
+// -------------------------READ_PKG_ENERGY
+
+#ifdef __READ_PKG_ENERGY__
+// TODO: Verify if these functions are still useful (the package energy can be calculed)
+
+static const uint64_t energy_pkg_msr = 0xC001029B;
+uint64_t read_raw_pkg_energy(int fd)
+{
+    uint64_t energy = read_msr(fd, energy_pkg_msr);
+    return energy & amd_energy_mask;
+}
+#endif
+
+// ----------------------------------ENERGY
+
+uint64_t raw_to_microjoule(uint64_t raw, unsigned int unit)
+{
+    static const double to_microjoule = 1000000.0;
+    // raw * (1 / (unit^2)) -> Joule
+    // Joule * 1000000 -> uJoule
+    return (uint64_t) (((double) raw * to_microjoule) / (double)(1U << unit));
+}
+
+uint64_t raw_to_joule(uint64_t raw, uint64_t unit)
+{
+    // raw * (1 / (unit^2)) -> Joule
+    int64_t joule = raw / (1UL << unit);
+    return joule;
+}
+
+// -----------------------------------DEBUG
+
+#ifdef DEBUG
+void debug_print_sensor(CpuSensor *sensor)
+{
+    //CASSERT(sizeof(CpuSensor) == 56, amd_rapl_c);
+    printf("cpu_id : %d, package_id : %d, core_id : %d, name : %s, fd: %d,  energy_units : %d, core_energy: %"PRIu64"\n",
+           sensor->cpu_id,
+           sensor->package_id,
+           sensor->core_id,
+           sensor->name,
+           sensor->fd,
+           sensor->energy_units,
+           sensor->core_energy
+          );
+}
+
+void debug_print_amd_rapl(AmdRapl *rapl)
+{
+    for (unsigned int i = 0; i < rapl->sensor_count; i++) {
+        debug_print_sensor(&rapl->sensors[i]);
+    }
+}
+
+#endif
+
+// ---------------------------AMD_RAPL_UTIL
+
+unsigned int get_nb_cpu()
+{
+    char filename[BUFFER_SIZE];
+    int	cpy_errno;
+
+    unsigned int n_cpu = 0;
+    for (;; n_cpu++) {
+        snprintf(filename, BUFFER_SIZE, base_str, n_cpu);
+
+        int fd = open(filename, O_RDONLY);
+        cpy_errno = errno;
+        if (fd < 0) {
+            break;
+        }
+        close(fd);
+    }
+
+    if (n_cpu == 0) {
+        perror("open()");
+        fprintf(stderr, "on the file: '%s'\n", filename);
+        switch (cpy_errno) {
+        case ENOENT:
+            fprintf(stderr, "Amd rapl works with msr module, try to run 'sudo modprobe msr', then retry.\n");
+            exit(99);
+        case EACCES:
+            fprintf(stderr, "Amd rapl must be executed with the administrator privilege, try with 'sudo'.\n");
+            exit(98);
+        default:
+            fprintf(stderr, "Unexpected error\n");
+            exit(97);
+        }
+    }
+    // n_cpu > 0
+    return n_cpu;
+}
+
+void get_arch(unsigned int *ret_nb_package, unsigned int *ret_nb_core, CpuSensor *sensors, unsigned int nb_sensor)
+{
+    unsigned int nb_package = 0;
+    unsigned int nb_core = 0;
+    for (unsigned int i = 0; i < nb_sensor; i++) {
+        if (sensors[i].package_id > nb_package) {
+            nb_package = sensors[i].package_id;
+        }
+        if (sensors[i].core_id > nb_core) {
+            nb_core = sensors[i].core_id;
+        }
+    }
+    *ret_nb_package = nb_package + 1;
+    *ret_nb_core = nb_core + 1;
+}
+
+char *get_name(unsigned int cpu_id)
+{
+    static const char *base_name = "core%d";
+    static const size_t max_lenght = 20;
+    char *name = (char *)calloc(max_lenght, sizeof(char));
+    snprintf(name, max_lenght, base_name, cpu_id);
+    return name;
+}
+
+void update_cpu_sensor(CpuSensor *sensor, uint64_t *energy_consumed)
+{
+    sensor->energy_units = read_unit(sensor->fd);
+    uint64_t raw_core_energy = read_raw_core_energy(sensor->fd);
+    uint64_t core_energy = raw_to_microjoule(raw_core_energy, sensor->energy_units);
+
+    *energy_consumed = modulo_substraction(core_energy, sensor->core_energy);
+    sensor->core_energy = core_energy;
+}
+
+unsigned int is_duplicate(CpuSensor *sensor,unsigned int nb_core, unsigned int nb_package, unsigned char map[nb_core][nb_package])
+{
+    if (map[sensor->core_id][sensor->package_id] == 1) {
+        return 0;
+    }
+    map[sensor->core_id][sensor->package_id] += 1;
+    return 1;
+}
+
+void init_cpu_sensor(CpuSensor *sensor, CpuSensor *cpu_info)
+{
+    static char filename[BUFFER_SIZE];
+    snprintf(filename,BUFFER_SIZE, base_str, cpu_info->cpu_id);
+
+    int fd = open(filename, O_RDONLY);
+    if (fd < 0) {
+        fprintf(stderr, base_str, cpu_info->cpu_id);
+        perror(":open()");
+        exit(127);
+    }
+
+    memcpy(sensor, cpu_info, sizeof(CpuSensor));
+    sensor->name = get_name(sensor->cpu_id);
+    sensor->fd = fd;
+}
+
+void clean_cpu_sensor(CpuSensor *sensor)
+{
+    close(sensor->fd);
+    free(sensor->name);
+}
+
+void free_amd_rapl(AmdRapl *rapl)
+{
+    free(rapl->sensors);
+    free(rapl);
+}
+
+// ----------------------AMD_RAPL_INTERFACE
+
+unsigned int init_amd_rapl(char *none, void **ptr)
+{
+    UNUSED(none);
+
+    unsigned int max_cpus = get_nb_cpu();
+
+    CpuSensor *cpu_information = (CpuSensor *) calloc(max_cpus, sizeof(CpuSensor));
+    if (parse_cpuinfo(cpu_information, max_cpus)) {
+        free(cpu_information);
+        PANIC(1, "cpuinfo");
+    }
+
+    unsigned int nb_package;
+    unsigned int nb_core;
+    get_arch(&nb_package, &nb_core, cpu_information, max_cpus);
+
+    unsigned char cpu_map[nb_core][nb_package];
+    memset(cpu_map, 0, sizeof(cpu_map));
+    CpuSensor *sensors = (CpuSensor *) calloc(max_cpus, sizeof(CpuSensor));
+
+    unsigned int sensor_count = 0;
+    for (unsigned int i = 0; i < max_cpus; i++) {
+        if (is_duplicate(&cpu_information[i], nb_core, nb_package, cpu_map) == 1) {
+            init_cpu_sensor(&sensors[sensor_count],&cpu_information[i]);
+            sensor_count += 1;
+        }
+    }
+    free(cpu_information);
+
+    AmdRapl *rapl = (AmdRapl *) calloc(1, sizeof(AmdRapl));
+    rapl->sensors = sensors;
+    rapl->sensor_count = sensor_count;
+    *ptr = (void *) rapl;
+    return rapl->sensor_count;
+}
+
+
+unsigned int get_amd_rapl(uint64_t *results, void *ptr)
+{
+    AmdRapl *rapl = (AmdRapl *) ptr;
+    for (unsigned int i = 0; i < rapl->sensor_count; i++) {
+        update_cpu_sensor(&rapl->sensors[i], &results[i]);
+    }
+    return rapl->sensor_count;
+}
+
+void label_amd_rapl(char **labels, void *ptr)
+{
+    AmdRapl *rapl = (AmdRapl *) ptr;
+    for (unsigned int i = 0; i < rapl->sensor_count; i++) {
+        labels[i] = rapl->sensors[i].name;
+    }
+}
+
+void clean_amd_rapl(void *ptr)
+{
+    AmdRapl *rapl = (AmdRapl *) ptr;
+
+    for (unsigned int i = 0; i < rapl->sensor_count; ++i) {
+        clean_cpu_sensor(&rapl->sensors[i]);
+    }
+    free(rapl->sensors);
+    free(rapl);
+}
+
diff --git a/network.h b/src/amd_rapl.h
similarity index 57%
rename from network.h
rename to src/amd_rapl.h
index 44759dcf883ac66c90aafdf6447d00d6b02e9a39..e58cc16498f4b7553f9a7ebf6b6f40e8cd4ee288 100644
--- a/network.h
+++ b/src/amd_rapl.h
@@ -1,5 +1,5 @@
 /*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
 
     This file is part of Mojitos.
 
@@ -18,8 +18,25 @@
 
  *******************************************************/
 
-unsigned int init_network(char*, void **);
-unsigned int get_network(uint64_t* results, void*);
-void clean_network(void *);
-void label_network(char **labels, void*);
+unsigned int init_amd_rapl(char *, void **);
+unsigned int get_amd_rapl(uint64_t *results, void *);
+void clean_amd_rapl(void *);
+void label_amd_rapl(char **labels, void *);
 
+Sensor amd_rapl = {
+    .init = init_amd_rapl,
+    .get = get_amd_rapl,
+    .clean = clean_amd_rapl,
+    .label = label_amd_rapl,
+    .nb_opt = 1,
+};
+
+Optparse amd_rapl_opt[1] = {
+    {
+        .longname = "amd-rapl",
+        .shortname = 'r',
+        .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "AMD RAPL",
+    },
+};
diff --git a/src/counters.c b/src/counters.c
new file mode 100644
index 0000000000000000000000000000000000000000..8013ff863757b2f614094cd96dc09e12776467c8
--- /dev/null
+++ b/src/counters.c
@@ -0,0 +1,240 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+#include <linux/perf_event.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <asm/unistd.h>
+#include <stdint.h>
+#include "util.h"
+
+
+struct Counter {
+    int nbcores;
+    int nbperf;
+    int **counters;
+    uint64_t *counters_values;
+    uint64_t *tmp_counters_values;
+
+    int *perf_indexes;
+
+};
+typedef struct Counter Counter;
+
+#include "counters_option.h"
+
+void *show_all_counters(void *none1, size_t none2)
+{
+    for (unsigned int i = 0; i < nb_counter_option; i++) {
+        printf("%s\n", perf_static_info[i].name);
+    }
+    UNUSED(none1);
+    UNUSED(none2);
+    exit(EXIT_SUCCESS);
+    return NULL; /* not reached */
+}
+
+void perf_type_key(__u32 **perf_type, __u64 **perf_key, int *indexes, int nb)
+{
+    *perf_type = malloc(nb * sizeof(__u32));
+    *perf_key  = malloc(nb * sizeof(__u64));
+
+    for (int i = 0; i < nb; i++) {
+        (*perf_key)[i]  = perf_static_info[indexes[i]].perf_key;
+        (*perf_type)[i] = perf_static_info[indexes[i]].perf_type;
+    }
+}
+void perf_event_list(char *perf_string, int *nb_perf, int **perf_indexes)
+{
+    char *token;
+    *nb_perf = 0;
+    *perf_indexes = NULL;
+
+    while ((token = strtok(perf_string, ",")) != NULL) {
+        perf_string = NULL;
+        unsigned int i;
+
+        for (i = 0; i < nb_counter_option; i++) {
+            if (strcmp(perf_static_info[i].name, token) == 0) {
+                (*nb_perf)++;
+                (*perf_indexes) = realloc(*perf_indexes, sizeof(int) * (*nb_perf));
+                (*perf_indexes)[*nb_perf - 1] = i;
+                break;
+            }
+        }
+
+        if (i == nb_counter_option) {
+            fprintf(stderr, "Unknown performance counter: %s\n", token);
+            exit(EXIT_FAILURE);
+        }
+    }
+}
+
+static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
+                            int cpu, int group_fd, unsigned long flags)
+{
+    long res = syscall(__NR_perf_event_open, hw_event, pid, cpu, group_fd, flags);
+
+    if (res == -1) {
+        perror("perf_event_open");
+        fprintf(stderr, "Error opening leader %llx\n", hw_event->config);
+        exit(EXIT_FAILURE);
+    }
+
+    return res;
+}
+
+Counter *_init_counters(const int nb_perf, const __u32 *types, const __u64 *names)
+{
+    struct perf_event_attr pe;
+    unsigned int nbcores = sysconf(_SC_NPROCESSORS_ONLN);
+    memset(&pe, 0, sizeof(struct perf_event_attr));
+    pe.size = sizeof(struct perf_event_attr);
+    pe.disabled = 1;
+
+    Counter *counters = malloc(sizeof(struct Counter));
+    counters->nbperf = nb_perf;
+    counters->nbcores = nbcores;
+    counters->counters = malloc(nb_perf * sizeof(int *));
+
+    for (int i = 0; i < nb_perf; i++) {
+        pe.type = types[i];
+        pe.config = names[i];
+        counters->counters[i] = malloc(nbcores * sizeof(int));
+
+        for (unsigned int core = 0; core < nbcores; core++) {
+            counters->counters[i][core] = perf_event_open(&pe, -1, core, -1, PERF_FLAG_FD_CLOEXEC);
+        }
+    }
+
+    return counters;
+}
+
+void clean_counters(void *ptr)
+{
+    Counter *counters = (Counter *) ptr;
+
+    for (int counter = 0; counter < counters->nbperf; counter++) {
+        for (int core = 0; core < counters->nbcores; core++) {
+            close(counters->counters[counter][core]);
+        }
+
+        free(counters->counters[counter]);
+    }
+
+    free(counters->counters);
+    free(counters->counters_values);
+    free(counters->tmp_counters_values);
+    free(counters->perf_indexes);
+
+    free(counters);
+}
+
+void start_counters(Counter *counters)
+{
+    for (int counter = 0; counter < counters->nbperf; counter++) {
+        for (int core = 0; core < counters->nbcores; core++) {
+            ioctl(counters->counters[counter][core], PERF_EVENT_IOC_ENABLE, 0);
+        }
+    }
+}
+
+void reset_counters(Counter *counters)
+{
+    for (int counter = 0; counter < counters->nbperf; counter++) {
+        for (int core = 0; core < counters->nbcores; core++) {
+            ioctl(counters->counters[counter][core], PERF_EVENT_IOC_RESET, 0);
+        }
+    }
+}
+
+void _get_counters(Counter *counters, uint64_t *values)
+{
+    for (int i = 0; i < counters->nbperf; i++) {
+        uint64_t accu = 0;
+        uint64_t count = 0;
+
+        for (int core = 0; core < counters->nbcores; core++) {
+            if (-1 == read(counters->counters[i][core], &count, sizeof(uint64_t))) {
+                fprintf(stderr, "Cannot read result");
+                exit(EXIT_FAILURE);
+            }
+            accu += count;
+        }
+
+        values[i] = accu;
+    }
+}
+
+
+
+
+
+
+unsigned int init_counters(char *args, void **state)
+{
+    int nb_perf;
+    int *perf_indexes = NULL;
+
+    perf_event_list(args, &nb_perf, &perf_indexes);
+
+    __u32 *perf_type;
+    __u64 *perf_key;
+    perf_type_key(&perf_type, &perf_key, perf_indexes, nb_perf);
+    Counter *fd = _init_counters(nb_perf, perf_type, perf_key);
+    free(perf_type);
+    free(perf_key);
+
+    fd->perf_indexes = perf_indexes;
+    fd->counters_values = malloc(nb_perf * sizeof(uint64_t));
+    fd->tmp_counters_values = malloc(nb_perf * sizeof(uint64_t));
+    start_counters(fd);
+    _get_counters(fd, fd->counters_values);
+    *state = (void *) fd;
+
+    return nb_perf;
+}
+
+unsigned int get_counters(uint64_t *results, void *ptr)
+{
+    Counter *state = (Counter *) ptr;
+
+    _get_counters(state, state->tmp_counters_values);
+
+    for (int i = 0; i < state->nbperf; i++) {
+        results[i] = modulo_substraction(state->tmp_counters_values[i], state->counters_values[i]);
+    }
+
+    memcpy(state->counters_values, state->tmp_counters_values, state->nbperf * sizeof(uint64_t));
+    return state->nbperf;
+}
+
+void label_counters(char **labels, void *ptr)
+{
+    Counter *state = (Counter *) ptr;
+
+    for (int i = 0; i < state->nbperf; i++) {
+        labels[i] = perf_static_info[state->perf_indexes[i]].name;
+    }
+}
diff --git a/src/counters.h b/src/counters.h
new file mode 100644
index 0000000000000000000000000000000000000000..4374cd1f6ea0fb4e59d1226b03753d38a2fa3b2b
--- /dev/null
+++ b/src/counters.h
@@ -0,0 +1,54 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+unsigned int init_counters(char *, void **);
+unsigned int get_counters(uint64_t *results, void *);
+void clean_counters(void *);
+void label_counters(char **labels, void *);
+void *show_all_counters(void *, size_t);
+
+Sensor counters = {
+    .init = init_counters,
+    .get = get_counters,
+    .clean = clean_counters,
+    .label = label_counters,
+    .nb_opt = 2,
+};
+
+Optparse counters_opt[2] = {
+    {
+        .longname = "perf-list",
+        .shortname = 'p',
+        .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<perf_list>",
+        .usage_msg = "performance counters\n"
+        "\tperf_list is a coma separated list of performance counters.\n"
+        "\tEx: instructions,cache_misses",
+    },
+    {
+        .longname = "list",
+        .shortname = 'l',
+        .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "list the available performance counters and quit",
+        .fn = show_all_counters,
+    },
+};
+
diff --git a/src/counters_option.sh b/src/counters_option.sh
new file mode 100644
index 0000000000000000000000000000000000000000..76f92757f1e09207c1113421c76cd6a67d22f3df
--- /dev/null
+++ b/src/counters_option.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: GPL-3.0-or-later
+# Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+linux_include=/usr/include/linux/perf_event.h
+
+echo '
+typedef struct CounterOption{
+    char *name;
+    __u32 perf_type;
+    __u64 perf_key;
+} CounterOption;
+
+static CounterOption perf_static_info[] = {'
+
+nb=0
+
+while IFS= read line; do
+	case $line in
+	*perf_hw_id*)
+        mode=PERF_TYPE_HARDWARE
+		;;
+    *perf_hw_cache_*)
+        mode=PERF_TYPE_HW_CACHE
+		;;
+    *perf_sw_id*)
+        mode=PERF_TYPE_SOFTWARE
+		;;
+    *PERF_COUNT_*=*)
+		perf_name=$(echo "$line" | awk '{print $1}')
+		short_perf=$(echo "$perf_name" | sed 's/PERF_COUNT_[HS]W_//' | tr 'A-Z' 'a-z')
+		case $short_perf in
+		# blacklist
+		stalled_cycles_frontend|stalled_cycles_backend|cache_l1i|cache_op_write|cache_result_miss)
+			continue
+			;;
+		esac
+
+		if [ "$mode" != 'PERF_TYPE_HW_CACHE' ]; then
+			printf '    { .name = "%s", .perf_type = %s, .perf_key = %s},\n' \
+					"$short_perf" \
+					"$mode" \
+					"$perf_name"
+
+            : $((nb += 1))
+			continue
+		fi
+
+		# $mode == PERF_TYPE_HW_CACHE
+        for op_id in \
+        	'r PERF_COUNT_HW_CACHE_OP_READ' \
+        	'w PERF_COUNT_HW_CACHE_OP_WRITE' \
+        	'p PERF_COUNT_HW_CACHE_OP_PREFETCH'
+        do
+            op_id_str=${op_id% *}
+            op_id_name=${op_id#* }
+
+            for result_id in \
+            	'a PERF_COUNT_HW_CACHE_RESULT_ACCESS' \
+            	'm PERF_COUNT_HW_CACHE_RESULT_MISS'
+            do
+                result_id_str=${result_id% *}
+                result_id_name=${result_id#* }
+
+            	printf '    {'
+            	printf ' .name = "%s_%s_%s",' \
+            		"$short_perf" \
+            		"$op_id_str" \
+            		"$result_id_str"
+            	printf ' .perf_type = %s,' \
+            		"$mode"
+            	printf ' .perf_key = %s | (%s >> 8) | (%s >> 16)' \
+            		"$perf_name" \
+            		"$op_id_name" \
+            		"$result_id_name"
+            	printf ' },\n'
+                : $((nb += 1))
+            done
+        done
+		;;
+	esac
+done < "$linux_include"
+
+echo '};'
+
+printf '\nstatic unsigned int nb_counter_option = %d;\n' "$nb"
+
diff --git a/src/infiniband.c b/src/infiniband.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a5a6b4e2422d819269d1a470783286be8a5c162
--- /dev/null
+++ b/src/infiniband.c
@@ -0,0 +1,135 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+#include <fcntl.h>
+#include <glob.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define NB_SENSOR 4
+
+struct Infiniband {
+    uint64_t values[NB_SENSOR];
+    uint64_t tmp_values[NB_SENSOR];
+    int sources[NB_SENSOR];
+};
+typedef struct Infiniband Infiniband;
+
+unsigned int _get_infiniband(uint64_t *results, int *sources)
+{
+    if (sources == NULL) {
+        return 0;
+    }
+
+    char buffer[128];
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        if (pread(sources[i], buffer, 127, 0) < 0) {
+            perror("pread");
+            exit(1);
+        }
+
+        results[i] = strtoull(buffer, NULL, 10);
+    }
+
+    return NB_SENSOR;
+}
+
+unsigned int init_infiniband(char *infi_path, void **ptr)
+{
+    if (infi_path == NULL) {
+        return 0;
+    }
+
+    if (strcmp(infi_path, "X") == 0) {
+
+        glob_t res;
+
+        glob("/sys/class/infiniband/*/ports/*/counters/", 0, NULL, &res);
+
+        if (res.gl_pathc == 0) {
+            fprintf(stderr, "No infiniband found.\n");
+            return 0;
+        }
+
+        infi_path = res.gl_pathv[0];
+    }
+
+    char *filenames[] = {"%s/port_rcv_packets",
+                         "%s/port_rcv_data",
+                         "%s/port_xmit_packets",
+                         "%s/port_xmit_data"
+                        };
+
+    Infiniband *state = malloc(sizeof(Infiniband));
+
+    char buffer[1024];
+    for (int i = 0; i < NB_SENSOR; i++) {
+        snprintf(buffer, 1024, filenames[i], infi_path);
+        state->sources[i] = open(buffer, O_RDONLY);
+    }
+
+    *ptr = (void *) state;
+    _get_infiniband(state->values, state->sources);
+
+    return NB_SENSOR;
+}
+
+unsigned int get_infiniband(uint64_t *results, void *ptr)
+{
+    Infiniband *state = (Infiniband *) ptr;
+    _get_infiniband(state->tmp_values, state->sources);
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        results[i] = modulo_substraction(state->tmp_values[i], state->values[i]);
+    }
+
+    memcpy(state->values, state->tmp_values, NB_SENSOR * sizeof(uint64_t));
+    return NB_SENSOR;
+}
+
+void clean_infiniband(void *ptr)
+{
+    Infiniband *state = (Infiniband *) ptr;
+
+    if (state == NULL) {
+        return;
+    }
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        close(state->sources[i]);
+    }
+
+    free(state);
+}
+
+char *_labels_infiniband[NB_SENSOR] = {"irxp", "irxb", "itxp", "itxb"};
+void label_infiniband(char **labels, void *none)
+{
+    UNUSED(none);
+
+    for (int i = 0; i < NB_SENSOR; i++) {
+        labels[i] = _labels_infiniband[i];
+    }
+}
diff --git a/src/infiniband.h b/src/infiniband.h
new file mode 100644
index 0000000000000000000000000000000000000000..8098cf5c68773326e2bda38e3748e81dfb52ee0c
--- /dev/null
+++ b/src/infiniband.h
@@ -0,0 +1,43 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+unsigned int init_infiniband(char *infi_path, void **ptr);
+unsigned int get_infiniband(uint64_t *results, void *ptr);
+void clean_infiniband(void *ptr);
+void label_infiniband(char **labels, void *);
+
+Sensor infiniband = {
+    .init = init_infiniband,
+    .get = get_infiniband,
+    .clean = clean_infiniband,
+    .label = label_infiniband,
+    .nb_opt = 1,
+};
+
+Optparse infiniband_opt[1] = {
+    {
+        .longname = "monitor-infiniband",
+        .shortname = 'i',
+        .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<infiniband_path>",
+        .usage_msg = "infiniband monitoring (if infiniband_path is X, tries to detect it automatically)",
+    },
+};
+
diff --git a/src/load.c b/src/load.c
new file mode 100644
index 0000000000000000000000000000000000000000..64502e587c1472e603d96d36f53a3068d67053e9
--- /dev/null
+++ b/src/load.c
@@ -0,0 +1,108 @@
+/*******************************************************
+ Copyright (C) 2019-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include "util.h"
+
+#define LOAD_BUFFER_SIZE 1024
+#define LOAD_VALUES_SIZE 10
+char buffer[LOAD_BUFFER_SIZE];
+
+static int load_fid = -1;
+static uint64_t load_values[LOAD_VALUES_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static uint64_t tmp_load_values[LOAD_VALUES_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static char *stat = "/proc/stat";
+
+void _get_load(uint64_t *results)
+{
+    if (pread(load_fid, buffer, LOAD_BUFFER_SIZE - 1, 0) < 0) {
+        perror("pread");
+        exit(1);
+    }
+
+    int pos = 0;
+
+    while (buffer[pos] > '9' || buffer[pos] < '0') {
+        pos++;
+    }
+
+    for (int i = 0; i < LOAD_VALUES_SIZE; i++) {
+        results[i] = strtoull(buffer + pos, NULL, LOAD_VALUES_SIZE);
+
+        while (buffer[pos] <= '9' && buffer[pos] >= '0') {
+            pos++;
+        }
+
+        pos++;
+    }
+}
+
+// Public interface
+
+unsigned int init_load(char *argument, void **state)
+{
+    UNUSED(argument);
+    UNUSED(state);
+    load_fid = open(stat, O_RDONLY);
+    if (load_fid < 0) {
+        fprintf(stderr, "%s ", stat);
+        perror("open");
+        exit(1);
+    }
+
+    _get_load(load_values);
+    return LOAD_VALUES_SIZE;
+}
+
+unsigned int get_load(uint64_t *results, void *state)
+{
+    UNUSED(state);
+    _get_load(tmp_load_values);
+
+    for (int i = 0; i < LOAD_VALUES_SIZE; i++) {
+        results[i] = tmp_load_values[i] - load_values[i];
+    }
+
+    memcpy(load_values, tmp_load_values, sizeof(load_values));
+    return LOAD_VALUES_SIZE;
+}
+
+void clean_load(void *state)
+{
+    UNUSED(state);
+    close(load_fid);
+}
+
+char *_labels[LOAD_VALUES_SIZE] = {
+    "user", "nice", "system", "idle", "iowait", "irq",
+    "softirq", "steal", "guest", "guest_nice"
+};
+void label_load(char **labels, void *none)
+{
+    UNUSED(none);
+
+    for (int i = 0; i < LOAD_VALUES_SIZE; i++) {
+        labels[i] = _labels[i];
+    }
+}
diff --git a/load.h b/src/load.h
similarity index 61%
rename from load.h
rename to src/load.h
index 42ce968062dcb68a13ebf67713eabc865abe1d7a..6d8d29804f93c6be0805b6670486210f74452fdf 100644
--- a/load.h
+++ b/src/load.h
@@ -1,5 +1,5 @@
 /*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
 
     This file is part of Mojitos.
 
@@ -18,7 +18,25 @@
 
  *******************************************************/
 
-unsigned int init_load(char*, void **);
-unsigned int get_load(uint64_t* results, void*);
+unsigned int init_load(char *, void **);
+unsigned int get_load(uint64_t *results, void *);
 void clean_load(void *);
-void label_load(char **labels, void*);
+void label_load(char **labels, void *);
+
+Sensor load = {
+    .init = init_load,
+    .get = get_load,
+    .clean = clean_load,
+    .label = label_load,
+    .nb_opt = 1,
+};
+
+Optparse load_opt[1] = {
+    {
+        .longname = "sysload",
+        .shortname = 'u',
+        .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "system load",
+    },
+};
diff --git a/src/memory.c b/src/memory.c
new file mode 100644
index 0000000000000000000000000000000000000000..3356a82323d3ff8d4a79391c2bb0f36663ece9e2
--- /dev/null
+++ b/src/memory.c
@@ -0,0 +1,97 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/sysinfo.h>
+#include <string.h>
+
+#include "util.h"
+
+typedef enum {
+    TOTALRAM = 0,
+    FREERAM,
+    SHAREDRAM,
+    BUFFERRAM,
+    TOTALSWAP,
+    FREESWAP,
+    PROCS,
+    TOTALHIGH,
+    FREEHIGH,
+    MEM_UNIT,
+
+    MEMORY_COUNT,
+} MemoryKind;
+
+static const char *memory_labels[MEMORY_COUNT] = {
+    "totalram", "freeram", "sharedram", "bufferram",
+    "totalswap", "freeswap",
+    "procs",
+    "totalhigh", "freehigh", "mem_unit",
+};
+
+unsigned int init_memory(char *none1, void **none2)
+{
+    UNUSED(none1);
+    UNUSED(none2);
+    struct sysinfo info;
+    if (sysinfo(&info) < 0) {
+        fprintf(stderr, "Failed to get the memory information");
+        return 0;
+    }
+    return MEMORY_COUNT;
+}
+
+unsigned int get_memory(uint64_t *results, void *none)
+{
+    UNUSED(none);
+    struct sysinfo info;
+    if (sysinfo(&info) < 0) {
+        fprintf(stderr, "Failed to get the memory information");
+        exit(99);
+    }
+
+    // Can't use memcpy, the size isn't always the same
+    results[TOTALRAM]  = info.totalram;
+    results[FREERAM]   = info.freeram;
+    results[SHAREDRAM] = info.sharedram;
+    results[BUFFERRAM] = info.bufferram;
+    results[TOTALSWAP] = info.totalswap;
+    results[FREESWAP]  = info.freeswap;
+    results[PROCS]     = info.procs;
+    results[TOTALHIGH] = info.totalhigh;
+    results[FREEHIGH]  = info.freehigh;
+    results[MEM_UNIT]  = info.mem_unit;
+
+    return MEMORY_COUNT;
+}
+
+void label_memory(char **labels, void *none)
+{
+    UNUSED(none);
+    memcpy(labels, memory_labels, sizeof(char *) * MEMORY_COUNT);
+}
+
+void clean_memory(void *none)
+{
+    UNUSED(none);
+    return;
+}
+
diff --git a/src/memory.h b/src/memory.h
new file mode 100644
index 0000000000000000000000000000000000000000..6e275c63e41e836364d6ead18ebad548f7952de5
--- /dev/null
+++ b/src/memory.h
@@ -0,0 +1,43 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+unsigned int init_memory(char *, void **);
+unsigned int get_memory(uint64_t *results, void *);
+void clean_memory(void *);
+void label_memory(char **labels, void *);
+
+
+Sensor memory = {
+    .init = init_memory,
+    .get = get_memory,
+    .clean = clean_memory,
+    .label = label_memory,
+    .nb_opt = 1,
+};
+
+Optparse memory_opt[1] = {
+    {
+        .longname = "memory",
+        .shortname = 'm',
+        .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "Retrieves information about the memory via the syscall 'sysinfo(2)'.",
+    },
+};
diff --git a/src/memory_counters.c b/src/memory_counters.c
new file mode 100644
index 0000000000000000000000000000000000000000..185f454e0bbe2b1e84606c8c7179386087971313
--- /dev/null
+++ b/src/memory_counters.c
@@ -0,0 +1,130 @@
+#include "memory_option.h"
+#include "util.h"
+#include <fcntl.h>
+#include <info_reader.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char *path = "/proc/meminfo";
+
+typedef struct {
+    KeyFinder *keys;
+    unsigned int count;
+    FILE *file;
+} MemoryCounters;
+
+GenericPointer long_allocator(char *s)
+{
+    long value = atol(s);
+    return (GenericPointer)value;
+}
+
+KeyFinder *build_keyfinder(unsigned int count, unsigned int *indexes)
+{
+    KeyFinder *keys = (KeyFinder *)calloc(count, sizeof(KeyFinder));
+    for (unsigned int i = 0; i < count; i++) {
+        unsigned int idx = indexes[i];
+        KeyFinder key = {.key = memory_counters[idx],
+                         .delimiter = ":",
+                         .copy = long_allocator,
+                         .set = setter_functions[i]
+                        };
+        memcpy(&keys[i], &key, sizeof(KeyFinder));
+    }
+    return keys;
+}
+
+void memory_list(char *memory_string, unsigned int *count,
+                 unsigned int *indexes)
+{
+    char *token;
+    *count = 0;
+
+    while ((token = strtok(memory_string, ",")) != NULL) {
+        memory_string = NULL;
+
+        unsigned int i;
+        for (i = 0; i < NB_COUNTERS; i++) {
+            if (strcmp(memory_counters[i], token) == 0) {
+                (*count)++;
+                indexes[*count - 1] = i;
+                break;
+            }
+        }
+
+        if (i == NB_COUNTERS) {
+            fprintf(stderr, "Unknown memory counter: %s\n", token);
+            exit(EXIT_FAILURE);
+        }
+
+        if ((*count) > NB_COUNTERS) {
+            fprintf(stderr, "Too much counters, there are probably duplicates\n");
+            exit(EXIT_FAILURE);
+        }
+    }
+}
+
+unsigned int init_memory_counters(char *args, void **ptr)
+{
+    unsigned int indexes[NB_COUNTERS];
+    unsigned int count = 0;
+    memory_list(args, &count, indexes);
+
+    KeyFinder *keys = build_keyfinder(count, indexes);
+    FILE *file = fopen(path, "r");
+
+    MemoryCounters *counters = calloc(1, sizeof(MemoryCounters));
+    counters->keys = keys;
+    counters->count = count;
+    counters->file = file;
+
+    *ptr = (void *)counters;
+    return count;
+}
+
+unsigned int get_memory_counters(uint64_t *results, void *ptr)
+{
+    MemoryCounters *counters = (MemoryCounters *)ptr;
+    fseek(counters->file, 0, SEEK_SET);
+    Parser parser = {.storage = (GenericPointer)results,
+                     .capacity = 1,
+                     .nb_stored = 0,
+                     .storage_struct_size = sizeof(uint64_t) * counters->count,
+                     .keys = counters->keys,
+                     .nb_keys = counters->count,
+                     .file = counters->file
+                    };
+
+    parse(&parser);
+    return counters->count;
+}
+
+void label_memory_counters(char **labels, void *ptr)
+{
+    MemoryCounters *counters = (MemoryCounters *)ptr;
+    for (unsigned int i = 0; i < counters->count; i++) {
+        labels[i] = counters->keys[i].key;
+    }
+}
+
+void clean_memory_counters(void *ptr)
+{
+    MemoryCounters *counters = (MemoryCounters *)ptr;
+    fclose(counters->file);
+    free(counters->keys);
+    free(ptr);
+}
+
+void *show_all_memory_counters(void *none1, size_t none2)
+{
+    for (unsigned int i = 0; i < NB_COUNTERS; i++) {
+        printf("%s\n", memory_counters[i]);
+    }
+
+    UNUSED(none1);
+    UNUSED(none2);
+    exit(EXIT_SUCCESS);
+    return NULL; /* not reached */
+}
diff --git a/src/memory_counters.h b/src/memory_counters.h
new file mode 100644
index 0000000000000000000000000000000000000000..eaf9f369463e92f7a6f3c1b6bb0989bf8b4d5a11
--- /dev/null
+++ b/src/memory_counters.h
@@ -0,0 +1,54 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+unsigned int init_memory_counters(char *args, void **ptr);
+unsigned int get_memory_counters(uint64_t *results, void *ptr);
+void label_memory_counters(char **labels, void *ptr);
+void clean_memory_counters(void *ptr);
+void *show_all_memory_counters(void *, size_t);
+
+Sensor memory_counters = {
+    .init = init_memory_counters,
+    .get = get_memory_counters,
+    .clean = clean_memory_counters,
+    .label = label_memory_counters,
+    .nb_opt = 2,
+};
+
+Optparse memory_counters_opt[2] = {
+    {
+        .longname = "memory-counters",
+        .shortname = 'M',
+        .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<memory_list>",
+        .usage_msg =
+        "memory counters\n"
+        "\tmemory_list is a coma separated list of memory counters.\n"
+        "\tEx: Zswap,Zswapped",
+    },
+    {
+        .longname = "memory-list",
+        .shortname = 'L',
+        .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "list the available memory counters and quit",
+        .fn = show_all_memory_counters,
+    },
+};
diff --git a/src/memory_option.sh b/src/memory_option.sh
new file mode 100755
index 0000000000000000000000000000000000000000..30a3831264af3075170e20cd68ff5add5b9f1e75
--- /dev/null
+++ b/src/memory_option.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: GPL-3.0-or-later
+# Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+meminfo_path=/proc/meminfo
+
+function_template="void set_result_offset%s(GenericPointer ptr, GenericPointer data)
+{
+    uint64_t* result = (uint64_t *) ptr;
+    result[%s] = (uint64_t) data;
+}\n\n"
+
+nb_counters=0
+
+echo '#include <inttypes.h>'
+echo '#include <info_reader.h>'
+echo
+
+echo 'static char *memory_counters[] = {'
+while read line; do
+  : $((nb_counters += 1))
+  echo "$line" | awk -F ':' '{printf("    \"%s\",\n", $1)}'
+done < "$meminfo_path"
+echo '};'
+echo
+
+echo "#define NB_COUNTERS $nb_counters"
+echo
+
+count=0
+while [ $count -lt $nb_counters ]; do
+  printf "$function_template" $count $count
+  : $((count += 1))
+done
+
+echo 'static void (*setter_functions[])(GenericPointer, GenericPointer) = {'
+count=0
+while [ $count -lt $nb_counters ]; do
+  echo "    set_result_offset$count,"
+  : $((count += 1))
+done
+echo '};'
+echo
+
diff --git a/src/mojitos.c b/src/mojitos.c
new file mode 100644
index 0000000000000000000000000000000000000000..e8cb6fa2a2a8100de93cfa40dee93ec20f3c0a93
--- /dev/null
+++ b/src/mojitos.c
@@ -0,0 +1,384 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include "util.h"
+
+#define OPTPARSE_IMPLEMENTATION
+#define OPTPARSE_API static
+#include "optparse.h"
+
+typedef unsigned int (*initializer_t)(char *, void **);
+typedef void (*labeler_t)(char **, void *);
+typedef unsigned int (*getter_t)(uint64_t *, void *);
+typedef void (*cleaner_t)(void *);
+
+typedef struct Opt Opt;
+typedef struct Sensor Sensor;
+/* optparse typedef */
+typedef struct optparse_long Optparse;
+
+struct Sensor {
+    initializer_t init;
+    getter_t get;
+    cleaner_t clean;
+    labeler_t label;
+    int nb_opt;
+};
+
+int nb_defined_sensors = 0;
+
+#include "sensors.h"
+
+Sensor sensors[NB_SENSOR];
+
+#define NB_OPT 5
+Optparse opts[NB_OPT + NB_SENSOR_OPT + 1] = {
+    {
+        .longname = "freq", .shortname = 'f', .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<freq>",
+        .usage_msg = "set amount of measurements per second.",
+    },
+    {
+        .longname = "time", .shortname = 't', .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<time>",
+        .usage_msg = "set duration value (seconds). If 0, then loops infinitely.",
+    },
+    {
+        .longname = "exec", .shortname = 'e', .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<cmd> ...",
+        .usage_msg = "Execute a command with optional arguments.\n"
+        "\tIf this option is used, any usage of -t or -f is ignored.",
+    },
+    {
+        .longname = "logfile", .shortname = 'o', .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<file>",
+        .usage_msg = "specify a log file.",
+    },
+    {
+        .longname = "overhead-stats", .shortname = 's', .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "enable overhead statistics (nanoseconds).",
+    },
+};
+
+void dumpopt(Optparse *opt)
+{
+    printf(".It Fl %c | Fl \\-%s", opt->shortname, opt->longname);
+    if (opt->usage_arg != NULL) {
+        printf(" Ar %s", opt->usage_arg);
+    }
+    printf("\n");
+    printf("%s\n", opt->usage_msg);
+}
+
+void dumpopts(Optparse *opts, size_t nb_opt, size_t nb_sensor_opt)
+{
+    size_t i;
+
+    /* options */
+    printf(".Pp\nOPTIONS:\n.Bl -tag -width Ds\n");
+    for (i = 0; i < nb_opt; i++) {
+        dumpopt(&opts[i]);
+    }
+    printf(".El\n");
+
+    /* sensors */
+    printf(".Pp\nSENSORS:\n.Bl -tag -width Ds\n");
+    for (i++; i < nb_opt + nb_sensor_opt; i++) {
+        dumpopt(&opts[i]);
+    }
+    printf(".El\n");
+}
+
+void printopt(Optparse *opt)
+{
+    printf("-%c", opt->shortname);
+    printf("|--%s", opt->longname);
+    if (opt->usage_arg != NULL) {
+        printf(" %s", opt->usage_arg);
+    }
+    printf("\n\t%s\n", opt->usage_msg);
+}
+
+void usage(char **argv)
+{
+    printf("Usage : %s [OPTIONS] [SENSOR ...] [-e <cmd> ...]\n", argv[0]);
+
+    printf("\nOPTIONS:\n");
+    for (int i = 0; i < NB_OPT; i++) {
+        printopt(&opts[i]);
+    }
+
+    if (nb_defined_sensors == 0) {
+        // no captor to show
+        exit(EXIT_FAILURE);
+    }
+
+    printf("\nSENSORS:\n");
+    for (int i = 0; i < NB_SENSOR_OPT; i++) {
+        printopt(&opts[NB_OPT + i]);
+    }
+
+    exit(EXIT_FAILURE);
+}
+
+void sighandler(int none)
+{
+    UNUSED(none);
+}
+
+void flush(int none)
+{
+    UNUSED(none);
+    exit(0);
+}
+
+FILE *output;
+void flushexit()
+{
+    if (output != NULL) {
+        fflush(output);
+        fclose(output);
+    }
+}
+
+unsigned int nb_sources = 0;
+void **states = NULL;
+getter_t *getter = NULL;
+cleaner_t *cleaner = NULL;
+
+unsigned int nb_sensors = 0;
+char **labels = NULL;
+uint64_t *values = NULL;
+
+void add_source(Sensor *cpt, char *arg)
+{
+    nb_sources++;
+    initializer_t init = cpt->init;
+    labeler_t labeler = cpt->label;
+    getter_t get = cpt->get;
+    cleaner_t clean = cpt->clean;
+
+    states = realloc(states, nb_sources * sizeof(void *));
+    int nb = init(arg, &states[nb_sources - 1]);
+
+    if (nb == 0) {
+        nb_sources--;
+        states = realloc(states, nb_sources * sizeof(void *));
+        return;
+    }
+
+    getter = realloc(getter, nb_sources * sizeof(void *));
+    getter[nb_sources - 1] = get;
+    cleaner = realloc(cleaner, nb_sources * sizeof(void *));
+    cleaner[nb_sources - 1] = clean;
+
+    labels = realloc(labels, (nb_sensors + nb) * sizeof(char *));
+    labeler(labels + nb_sensors, states[nb_sources - 1]);
+
+    values = realloc(values, (nb_sensors + nb) * sizeof(uint64_t));
+    nb_sensors += nb;
+}
+
+int main(int argc, char **argv)
+{
+    int total_time = 1;
+    int delta = 0;
+    int frequency = 1;
+    char **application = NULL;
+    int stat_mode = -1;
+
+    init_sensors(opts, sensors, NB_OPT + NB_SENSOR_OPT, NB_OPT, &nb_defined_sensors);
+
+    if (argc == 1) {
+        usage(argv);
+    }
+
+    if (argc == 2 && strcmp(argv[1], "--dump-opts") == 0) {
+        dumpopts(opts, NB_OPT, NB_SENSOR_OPT);
+        exit(EXIT_SUCCESS);
+    }
+
+    output = stdout;
+
+    atexit(flushexit);
+    signal(15, flush);
+
+    int opt;
+    struct optparse options;
+    options.permute = 0;
+
+    optparse_init(&options, argv);
+    while ((opt = optparse_long(&options, opts, NULL)) != -1 && application == NULL) {
+        switch (opt) {
+        case 'f':
+            frequency = atoi(options.optarg);
+            break;
+        case 't':
+            total_time = atoi(options.optarg);
+            delta = 1;
+            if (total_time == 0) {
+                total_time = 1;
+                delta = 0;
+            }
+            break;
+        case 's':
+            stat_mode = 0;
+            break;
+        case 'o':
+            if ((output = fopen(options.optarg, "wb")) == NULL) {
+                perror("fopen");
+                PANIC(1, "-o %s", options.optarg);
+            }
+            break;
+        case 'e':
+            application = options.argv;
+            signal(17, sighandler);
+            break;
+        default: {
+            int ismatch = 0;
+            int opt_idx = NB_OPT;
+            for (int i = 0; i < nb_defined_sensors && !ismatch; i++) {
+                for (int j = 0; j < sensors[i].nb_opt; j++) {
+                    if (opt == opts[opt_idx].shortname) {
+                        ismatch = 1;
+                        if (opts[opt_idx].fn != NULL) {
+                            (void) opts[opt_idx].fn(NULL, 0);
+                        } else {
+                            add_source(&sensors[i], options.optarg);
+                        }
+                        break;
+                    }
+                    opt_idx++;
+                }
+            }
+            if (!ismatch) {
+                fprintf(stderr, "%s: %s\n", argv[0], options.errmsg);
+                usage(argv);
+            }
+        }
+        }
+    }
+
+    setvbuf(output, NULL, _IONBF, BUFSIZ);
+    struct timespec ts;
+    struct timespec ts_ref;
+
+    fprintf(output, "#timestamp ");
+
+    for (unsigned int i = 0; i < nb_sensors; i++) {
+        fprintf(output, "%s ", labels[i]);
+    }
+
+    if (stat_mode == 0) {
+        fprintf(output, "overhead ");
+    }
+
+    fprintf(output, "\n");
+
+    unsigned long int stat_data = 0;
+
+    for (int temps = 0; temps < total_time * frequency; temps += delta) {
+        clock_gettime(CLOCK_MONOTONIC, &ts_ref);
+
+        // Get Data
+        unsigned int current = 0;
+
+        for (unsigned int i = 0; i < nb_sources; i++) {
+            current += getter[i](&values[current], states[i]);
+        }
+
+        if (application != NULL) {
+
+            if (fork() == 0) {
+                execvp(application[0], application);
+                exit(0);
+            }
+
+            pause();
+            clock_gettime(CLOCK_MONOTONIC, &ts);
+
+            if (ts.tv_nsec >= ts_ref.tv_nsec) {
+                fprintf(output, "%ld.%09ld ", (ts.tv_sec - ts_ref.tv_sec), ts.tv_nsec - ts_ref.tv_nsec);
+            } else {
+                fprintf(output, "%ld.%09ld ", (ts.tv_sec - ts_ref.tv_sec) - 1, 1000 * 1000 * 1000 + ts.tv_nsec - ts_ref.tv_nsec);
+            }
+        } else {
+#ifdef DEBUG
+            clock_gettime(CLOCK_MONOTONIC, &ts);
+            fprintf(stderr, "%ld\n", (ts.tv_nsec - ts_ref.tv_nsec) / 1000);
+            //Indiv: mean: 148 std: 31 % med: 141 std: 28 %
+            //Group: mean: 309 std: 41 % med: 297 std: 39 %
+#endif
+
+            if (stat_mode == 0) {
+                clock_gettime(CLOCK_MONOTONIC, &ts);
+
+                if (ts.tv_nsec >= ts_ref.tv_nsec) {
+                    stat_data = ts.tv_nsec - ts_ref.tv_nsec;
+                } else {
+                    stat_data = 1000 * 1000 * 1000 + ts.tv_nsec - ts_ref.tv_nsec;
+                }
+            }
+
+            // Treat Data
+            fprintf(output, "%ld.%09ld ", ts_ref.tv_sec, ts_ref.tv_nsec);
+        }
+
+        for (unsigned int i = 0; i < nb_sensors; i++) {
+            /* "PRIu64" is a format specifier to print uint64_t values */
+            fprintf(output, "%" PRIu64 " ", values[i]);
+        }
+
+        if (stat_mode == 0) {
+            fprintf(output, "%ld ", stat_data);
+        }
+
+        fprintf(output, "\n");
+
+        if (application != NULL) {
+            break;
+        }
+
+        clock_gettime(CLOCK_MONOTONIC, &ts);
+        usleep(1000 * 1000 / frequency - (ts.tv_nsec / 1000) % (1000 * 1000 / frequency));
+    }
+
+    for (unsigned int i = 0; i < nb_sources; i++) {
+        cleaner[i](states[i]);
+    }
+
+    if (nb_sources > 0) {
+        free(getter);
+        free(cleaner);
+        free(labels);
+        free(values);
+        free(states);
+    }
+}
+
diff --git a/src/network.c b/src/network.c
new file mode 100644
index 0000000000000000000000000000000000000000..fec9424365ee933537aae64c0fb7d41858908951
--- /dev/null
+++ b/src/network.c
@@ -0,0 +1,254 @@
+/*******************************************************
+ 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 <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+#define NB_MAX_DEV 8
+#define NB_SENSOR 4
+
+static char *route = "/proc/net/route";
+char *_labels_network[NB_SENSOR] = {
+    "%s:rxp",
+    "%s:rxb",
+    "%s:txp",
+    "%s:txb",
+};
+struct Network {
+    uint64_t values[NB_MAX_DEV][NB_SENSOR];
+    uint64_t tmp_values[NB_MAX_DEV][NB_SENSOR];
+    int sources[NB_MAX_DEV][NB_SENSOR];
+    char labels[NB_MAX_DEV][NB_SENSOR][128];
+    char devs[NB_MAX_DEV][128];
+    int ndev;
+};
+typedef struct Network Network;
+
+static void _get_network(uint64_t *results, int *sources)
+{
+    if (sources == NULL) {
+        return;
+    }
+
+    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);
+    }
+}
+
+/*
+ * read from fd len chars and store them into buf
+ * make *s points to the first occurence of c into buf
+*/
+static int strchr_refill(int fd, char *buf, int len, char **s, char c)
+{
+    *s = strchr(*s, c);
+
+    if (*s == NULL) {
+        int nbytes = read(fd, buf, len - 1);
+        if (nbytes < 0) {
+            perror("read");
+            return -1;
+        }
+        buf[len - 1] = '\0';
+
+        /* whole file read */
+        if (nbytes == 0) {
+            return 0;
+        }
+
+        *s = strchr(buf, c);
+    }
+
+    return 1;
+}
+
+unsigned int init_network(char *dev, void **ptr)
+{
+    if (dev == NULL) {
+        exit(1);
+    }
+
+    char *filenames[] = {
+        "/sys/class/net/%s/statistics/rx_packets",
+        "/sys/class/net/%s/statistics/rx_bytes",
+        "/sys/class/net/%s/statistics/tx_packets",
+        "/sys/class/net/%s/statistics/tx_bytes",
+    };
+
+    struct Network *state = malloc(sizeof(struct Network));
+    memset(state, '\0', sizeof(*state));
+
+    if (strcmp(dev, "X") == 0) {
+        int fd = open(route, O_RDONLY);
+
+        if (fd < 0) {
+            fprintf(stderr, "%s ", route);
+            perror("open");
+            exit(1);
+        }
+
+        char buffer[1000];
+
+        /* skip first line */
+        char *s = buffer;
+        int ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\n');
+        if (ret != 1) {
+            close(fd);
+            free(state);
+            exit(1);
+        }
+        s++;
+
+        char *start_of_dev = s;
+        /* jump to the end of the device name */
+        ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\t');
+        if (ret != 1) {
+            close(fd);
+            free(state);
+            exit(1);
+        }
+
+        state->ndev++;	// ndev should be equal to 1 at this point
+        memcpy(&(state->devs[state->ndev - 1]), start_of_dev,
+               MIN((size_t)(sizeof(state->devs[0]) - 1), (size_t)(s - start_of_dev)));
+
+        for (;;) {
+            /* jump to the next line */
+            ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\n');
+            if (ret != 1) {
+                break;
+            }
+            s++;
+
+            start_of_dev = s;
+            ret = strchr_refill(fd, buffer, sizeof(buffer), &s, '\t');
+            if (ret != 1) {
+                break;
+            }
+
+            /* compare dev name to the previously saved one */
+            int newdev = 1;
+            for (int i = 0; i < state->ndev && newdev; i++) {
+                if (strncmp(start_of_dev, state->devs[i], s - start_of_dev) == 0) {
+                    newdev = 0;
+                }
+            }
+            if (newdev) {
+                if (state->ndev >= NB_MAX_DEV) {
+                    fprintf(stderr, "Maximum amount of network devices exceeded (%d).\n", NB_MAX_DEV);
+                    break;
+                }
+                state->ndev++;
+                memcpy(&(state->devs[state->ndev - 1]), start_of_dev,
+                       MIN((size_t)(sizeof(state->devs[0]) - 1), (size_t)(s - start_of_dev)));
+            }
+        }
+
+        close(fd);
+    } else {
+        state->ndev = 1;
+        memcpy(&(state->devs[0]), dev, strlen(dev) + 1);
+    }
+
+    char buffer2[256];
+    for (int i = 0; i < state->ndev; i++) {
+        for (int j = 0; j < NB_SENSOR; j++) {
+            snprintf(buffer2, sizeof(buffer2), filenames[j], state->devs[i]);
+            errno = 0;
+            int fd = open(buffer2, O_RDONLY);
+            if (fd < 0) {
+                fprintf(stderr, "init_network: open: %s: %.*s\n", strerror(errno),
+                        (int)sizeof(buffer2), buffer2);
+                free(state);
+                exit(1);
+            }
+            state->sources[i][j] = fd;
+            snprintf(state->labels[i][j], sizeof(state->labels[i][j]), _labels_network[j],
+                     state->devs[i]);
+        }
+    }
+
+    *ptr = (void *) state;
+
+    for (int i = 0; i < state->ndev; i++) {
+        _get_network(state->values[i], state->sources[i]);
+    }
+
+    return state->ndev * NB_SENSOR;
+}
+
+unsigned int get_network(uint64_t *results, void *ptr)
+{
+    struct Network *state = (struct Network *) ptr;
+
+    for (int i = 0; i < state->ndev; i++) {
+        _get_network(state->tmp_values[i], state->sources[i]);
+
+        for (int j = 0; j < NB_SENSOR; j++) {
+            results[i*NB_SENSOR + j] = modulo_substraction(state->tmp_values[i][j], state->values[i][j]);
+        }
+
+        memcpy(&(state->values[i]), &(state->tmp_values[i]),
+               NB_SENSOR * sizeof(state->values[i][0]));
+    }
+
+    return state->ndev * NB_SENSOR;
+}
+
+void clean_network(void *ptr)
+{
+    Network *state = (Network *) ptr;
+
+    if (state == NULL) {
+        return;
+    }
+
+    for (int i = 0; i < state->ndev; i++) {
+        for (int j = 0; j < NB_SENSOR; j++) {
+            close(state->sources[i][j]);
+        }
+    }
+
+    free(state);
+}
+
+void label_network(char **labels, void *ptr)
+{
+    struct Network *state = (struct Network *) ptr;
+
+    for (int i = 0; i < state->ndev; i++) {
+        for (int j = 0; j < NB_SENSOR; j++) {
+            labels[i*NB_SENSOR + j] = state->labels[i][j];
+        }
+    }
+}
diff --git a/src/network.h b/src/network.h
new file mode 100644
index 0000000000000000000000000000000000000000..a158dbbce25333edf64c5f5dd0cde42856e08c70
--- /dev/null
+++ b/src/network.h
@@ -0,0 +1,42 @@
+/*******************************************************
+ Copyright (C) 2018-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+unsigned int init_network(char *, void **);
+unsigned int get_network(uint64_t *results, void *);
+void clean_network(void *);
+void label_network(char **labels, void *);
+
+Sensor network = {
+    .init = init_network,
+    .get = get_network,
+    .clean = clean_network,
+    .label = label_network,
+    .nb_opt = 1,
+};
+
+Optparse network_opt[1] = {
+    {
+        .longname = "net-dev",
+        .shortname = 'd',
+        .argtype = OPTPARSE_REQUIRED,
+        .usage_arg = "<net_dev>",
+        .usage_msg = "network monitoring (if network_device is X, tries to detect it automatically)",
+    },
+};
diff --git a/src/nvidia_gpu.c b/src/nvidia_gpu.c
new file mode 100644
index 0000000000000000000000000000000000000000..407800271a09ab0546017018f78f8fed999602bd
--- /dev/null
+++ b/src/nvidia_gpu.c
@@ -0,0 +1,593 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Pedantic throws a warning in the nvml library
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#include <nvml.h>
+#pragma GCC diagnostic pop
+
+#include "util.h"
+
+// -----------------------------SENSOR_KIND
+typedef enum {
+    CLOCK_SENSOR       = 0,
+    MEMORY_SENSOR      = 1,
+    UTILIZATION_SENSOR = 2,
+    POWER_SENSOR       = 3,
+    TEMPERATURE_SENSOR = 4,
+
+    COUNT_SENSOR       = 5,
+} SENSOR_KIND;
+
+typedef struct Device Device;
+typedef struct NvidiaGpu NvidiaGpu;
+typedef struct ISensor ISensor;
+typedef struct Sensor Sensor;
+
+// -- Sensor interface
+typedef unsigned int (Initializer) (const Device *, void **);
+typedef unsigned int (Getter)      (uint64_t *, const Device *, void *);
+typedef unsigned int (Labeller)    (char **, void *);
+typedef void         (Cleaner)     (void *);
+
+struct ISensor {
+    Initializer *init;
+    Getter *get;
+    Labeller *label;
+    Cleaner *clean;
+};
+
+// -- Sensor
+struct Sensor {
+    void *data;
+    const ISensor *fun;
+};
+
+// -- Device: represents a gpu
+struct Device {
+    char name[NVML_DEVICE_NAME_BUFFER_SIZE];
+    nvmlDevice_t device;
+    unsigned int idx;
+
+    Sensor sensors[COUNT_SENSOR];
+    unsigned int count;
+};
+
+// -- NvidiaGpu: represents the devices
+struct NvidiaGpu {
+    Device *devices;
+    unsigned int count;
+};
+
+// -- Label template
+static const char *label_template = "gpu%u_%s_%s";
+static const char *short_label_template = "gpu%u_%s";
+
+// ----------------------------CLOCK_SENSOR
+
+#define CLOCK_LABEL_SIZE 25
+
+// -- All existing clocks
+// -- SM : Streaming Multiprocessor
+static const nvmlClockType_t clocks[NVML_CLOCK_COUNT] = {NVML_CLOCK_GRAPHICS, NVML_CLOCK_SM, NVML_CLOCK_MEM, NVML_CLOCK_VIDEO};
+static const char *clock_names[NVML_CLOCK_COUNT] = {"graphics", "sm", "memory", "video"};
+static const char *clock_base_name = "clk";
+
+// -- Must contain the clocks compatible with the device
+typedef struct {
+    nvmlClockType_t clocks[NVML_CLOCK_COUNT];
+    char labels[NVML_CLOCK_COUNT][CLOCK_LABEL_SIZE];
+    unsigned int count;
+} ClockData;
+
+unsigned int init_clock_sensor(const Device *device, void **data)
+{
+    const nvmlDevice_t nvml_device = device->device;
+    const unsigned int device_idx = device->idx;
+    ClockData tmp = {0};
+    nvmlReturn_t err;
+    unsigned int clock;
+
+    // -- Test all clocks
+    for (unsigned int i = 0; i < NVML_CLOCK_COUNT; i++) {
+        if ((err = nvmlDeviceGetClockInfo(nvml_device, clocks[i], &clock)) == NVML_SUCCESS) {
+            snprintf(tmp.labels[tmp.count], CLOCK_LABEL_SIZE, label_template, device_idx, clock_base_name, clock_names[i]);
+            tmp.clocks[tmp.count] = clocks[i];
+            tmp.count += 1;
+        } else {
+            fprintf(stderr, "Failed to get %s clock : %s\n", clock_names[i], nvmlErrorString(err));
+        }
+    }
+
+    // -- No clock avaible
+    if (tmp.count == 0) {
+        return 0;
+    }
+
+    *data = calloc(1, sizeof(ClockData));
+    memcpy(*data, &tmp, sizeof (ClockData));
+    return tmp.count;
+}
+
+unsigned int get_clock_sensor(uint64_t *results, const Device *device, void *data)
+{
+    const nvmlDevice_t nvml_device = device->device;
+    ClockData *clock_data = (ClockData *) data;
+    nvmlReturn_t err;
+    unsigned int clock;
+
+    for (unsigned int i = 0; i < clock_data->count; i++) {
+        nvmlClockType_t clock_type = clock_data->clocks[i];
+
+        if((err = nvmlDeviceGetClockInfo(nvml_device, clock_type, &clock)) != NVML_SUCCESS) {
+            fprintf(stderr, "Failed to get %s clock : %s\n", clock_names[clock_type], nvmlErrorString(err));
+            exit(99);
+        }
+        results[i] = clock;
+    }
+    return clock_data->count;
+}
+
+unsigned int label_clock_sensor(char **labels, void *data)
+{
+    ClockData *clock_data = (ClockData *) data;
+
+    for (unsigned int i = 0; i < clock_data->count; i++) {
+        labels[i] = clock_data->labels[i];
+    }
+
+    return clock_data->count;
+}
+
+void clean_clock_sensor(void *data)
+{
+    free(data);
+}
+
+// ---------------------------MEMORY_SENSOR
+#define MEMORY_LABEL_SIZE 25
+
+typedef enum {
+    FREE_MEMORY  = 0U,
+    USED_MEMORY  = 1U,
+    TOTAL_MEMORY = 2U,
+
+    COUNT_MEMORY = 3U,
+} MemoryKind;
+
+static const char *memory_names[COUNT_MEMORY] = {"free", "used", "total"};
+static const char *memory_base_name = "mem";
+
+typedef struct {
+    char labels[COUNT_MEMORY][MEMORY_LABEL_SIZE];
+} MemoryData;
+
+unsigned int init_memory_sensor(const Device *device, void **data)
+{
+    const nvmlDevice_t nvml_device = device->device;
+    const unsigned int device_idx = device->idx;
+
+    nvmlMemory_t memory;
+    nvmlReturn_t err;
+    if ((err = nvmlDeviceGetMemoryInfo(nvml_device, &memory)) != NVML_SUCCESS) {
+        fprintf(stderr, "Failed to get device memory : %s\n", nvmlErrorString(err));
+        return 0;
+    }
+
+    MemoryData *memory_data = (MemoryData *) calloc(1, sizeof(MemoryData));
+    for (unsigned int i = 0; i < COUNT_MEMORY; i++) {
+        snprintf(memory_data->labels[i], MEMORY_LABEL_SIZE, label_template, device_idx, memory_base_name, memory_names[i]);
+    }
+
+    *data = (void *) memory_data;
+    return COUNT_MEMORY;
+}
+
+unsigned int get_memory_sensor(uint64_t *results, const Device *device, void *none)
+{
+    UNUSED(none);
+    const nvmlDevice_t nvml_device = device->device;
+
+    nvmlMemory_t memory;
+    nvmlReturn_t err;
+    if ((err = nvmlDeviceGetMemoryInfo(nvml_device, &memory)) != NVML_SUCCESS) {
+        fprintf(stderr, "Failed to get device memory : %s\n", nvmlErrorString(err));
+        exit(99);
+    }
+
+    results[FREE_MEMORY] = memory.free;
+    results[USED_MEMORY] = memory.used;
+    results[TOTAL_MEMORY] = memory.total;
+    return COUNT_MEMORY;
+}
+
+
+unsigned int label_memory_sensor(char **labels, void *data)
+{
+    MemoryData *memory_data = (MemoryData *) data;
+
+    for (unsigned int i = 0; i < COUNT_MEMORY; i++) {
+        labels[i] = memory_data->labels[i];
+    }
+
+    return COUNT_MEMORY;
+}
+void clean_memory_sensor(void *data)
+{
+    free(data);
+}
+
+// ----------------------UTILIZATION_SENSOR
+#define UTILIZATION_LABEL_SIZE 35
+typedef enum {
+    GPU_UTILIZATION    = 0U,
+    MEMORY_UTILIZATION = 1U,
+
+    COUNT_UTILIZATION  = 2U,
+} UtilizationKind;
+
+typedef struct {
+    char labels[COUNT_UTILIZATION][UTILIZATION_LABEL_SIZE];
+} UtilizationData;
+
+static const char *utilization_names[COUNT_UTILIZATION] = {"gpu", "memory"};
+static const char *utilization_base_name = "utilization";
+
+unsigned int init_utilization_sensor(const Device *device, void **data)
+{
+    const nvmlDevice_t nvml_device = device->device;
+    const unsigned int device_idx = device->idx;
+
+    nvmlReturn_t err;
+    nvmlUtilization_t utilization;
+    if ((err = nvmlDeviceGetUtilizationRates(nvml_device, &utilization)) != NVML_SUCCESS) {
+        fprintf(stderr, "Failed to get device utilization: %s\n", nvmlErrorString(err));
+        return 0;
+    }
+
+    UtilizationData *utilization_data = (UtilizationData *) calloc(1, sizeof(UtilizationData));
+    for (unsigned int i = 0; i < COUNT_UTILIZATION; i++) {
+        snprintf(utilization_data->labels[i], UTILIZATION_LABEL_SIZE, label_template, device_idx, utilization_base_name, utilization_names[i]);
+    }
+
+    *data = (void *) utilization_data;
+    return COUNT_UTILIZATION;
+}
+
+unsigned int get_utilization_sensor(uint64_t *results, const Device *device, void *none)
+{
+    UNUSED(none);
+    const nvmlDevice_t nvml_device = device->device;
+
+    nvmlReturn_t err;
+    nvmlUtilization_t utilization;
+    if ((err = nvmlDeviceGetUtilizationRates(nvml_device, &utilization)) != NVML_SUCCESS) {
+        fprintf(stderr, "Failed to get device utilization: %s\n", nvmlErrorString(err));
+        exit(99);
+    }
+
+    results[GPU_UTILIZATION] = utilization.gpu;
+    results[MEMORY_UTILIZATION] = utilization.memory;
+    return COUNT_UTILIZATION;
+}
+
+unsigned int label_utilization_sensor(char **labels, void *data)
+{
+    UtilizationData *utilization_data = (UtilizationData *) data;
+
+    for (unsigned int i = 0; i < COUNT_UTILIZATION; i++) {
+        labels[i] = utilization_data->labels[i];
+    }
+
+    return COUNT_UTILIZATION;
+}
+
+void clean_utilization_sensor(void *data)
+{
+    free(data);
+}
+
+// ----------------------------POWER_SENSOR
+
+#define POWER_LABEL_SIZE 25
+#define COUNT_POWER 1
+
+static const char *power_base_name = "power";
+
+typedef struct {
+    char label[POWER_LABEL_SIZE];
+} PowerData;
+
+
+unsigned int init_power_sensor(const Device *device, void **data)
+{
+    const nvmlDevice_t nvml_device = device->device;
+    const unsigned int device_idx = device->idx;
+
+    unsigned int power;
+    nvmlReturn_t err;
+    if ((err = nvmlDeviceGetPowerUsage(nvml_device, &power)) != NVML_SUCCESS) {
+        printf("Failed to get the device power consumption: %s\n", nvmlErrorString(err));
+        return 0;
+    }
+
+    PowerData *power_data = (PowerData *) calloc(1, sizeof(PowerData));
+    snprintf(power_data->label, POWER_LABEL_SIZE, short_label_template, device_idx, power_base_name);
+
+    *data = (void *) power_data;
+    return COUNT_POWER;
+}
+
+unsigned int get_power_sensor(uint64_t *results, const Device *device, void *none)
+{
+    UNUSED(none);
+    const nvmlDevice_t nvml_device = device->device;
+
+    unsigned int power;
+    nvmlReturn_t err;
+    if ((err = nvmlDeviceGetPowerUsage(nvml_device, &power)) != NVML_SUCCESS) {
+        printf("Failed to get the device power consumption: %s\n", nvmlErrorString(err));
+        exit(99);
+    }
+
+    *results = power;
+    return COUNT_POWER;
+}
+
+unsigned int label_power_sensor(char **labels, void *data)
+{
+    PowerData *power_data = (PowerData *) data;
+    *labels = power_data->label;
+    return COUNT_POWER;
+}
+
+void clean_power_sensor(void *data)
+{
+    free(data);
+}
+
+// ----------------------TEMPERATURE_SENSOR
+
+
+#define TEMPERATURE_LABEL_SIZE 35
+#define COUNT_TEMPERATURE 1
+
+static const char *temperature_base_name = "temperature";
+
+typedef struct {
+    char label[TEMPERATURE_LABEL_SIZE];
+} TemperatureData;
+
+unsigned int init_temperature_sensor(const Device *device, void **data)
+{
+    const nvmlDevice_t nvml_device = device->device;
+    const unsigned int device_idx = device->idx;
+
+    unsigned int temperature;
+    nvmlReturn_t err;
+    if ((err = nvmlDeviceGetTemperature(nvml_device, NVML_TEMPERATURE_GPU, &temperature)) != NVML_SUCCESS) {
+        printf("Failed to get the device temperature: %s\n", nvmlErrorString(err));
+        return 0;
+    }
+
+    TemperatureData *temperature_data = (TemperatureData *) calloc(1, sizeof(TemperatureData));
+    snprintf(temperature_data->label, TEMPERATURE_LABEL_SIZE, short_label_template, device_idx, temperature_base_name);
+
+    *data = (void *) temperature_data;
+    return COUNT_TEMPERATURE;
+}
+
+unsigned int get_temperature_sensor(uint64_t *results, const Device *device, void *none)
+{
+    UNUSED(none);
+    const nvmlDevice_t nvml_device = device->device;
+
+    unsigned int temperature;
+    nvmlReturn_t err;
+    if ((err = nvmlDeviceGetTemperature(nvml_device, NVML_TEMPERATURE_GPU, &temperature)) != NVML_SUCCESS) {
+        printf("Failed to get the device temperature: %s\n", nvmlErrorString(err));
+        exit(99);
+    }
+
+    *results = temperature;
+    return COUNT_TEMPERATURE;
+}
+
+unsigned int label_temperature_sensor(char **labels, void *data)
+{
+    TemperatureData *temperature_data = (TemperatureData *) data;
+    *labels = temperature_data->label;
+    return COUNT_TEMPERATURE;
+}
+
+void clean_temperature_sensor(void *data)
+{
+    free(data);
+}
+
+// -------------------------AVAIBLE_SENSORS
+static const ISensor avaible_sensors[COUNT_SENSOR] = {
+    {.init = init_clock_sensor, .get = get_clock_sensor, .label = label_clock_sensor, .clean = clean_clock_sensor},
+    {.init = init_memory_sensor, .get = get_memory_sensor, .label = label_memory_sensor, .clean = clean_memory_sensor},
+    {.init = init_utilization_sensor, .get = get_utilization_sensor, .label = label_utilization_sensor, .clean = clean_utilization_sensor},
+    {.init = init_power_sensor, .get = get_power_sensor, .label = label_power_sensor, .clean = clean_power_sensor},
+    {.init = init_temperature_sensor, .get = get_temperature_sensor, .label = label_temperature_sensor, .clean = clean_temperature_sensor},
+};
+
+// ------------------------DEVICE_FUNCTIONS
+
+unsigned int init_device(unsigned int device_idx, Device *device)
+{
+    nvmlReturn_t result;
+    nvmlDevice_t nvml_device;
+    if ((result = nvmlDeviceGetHandleByIndex(device_idx, &nvml_device)) != NVML_SUCCESS) {
+        fprintf(stderr, "Failed to get device handle for device %d: %s\n", device_idx, nvmlErrorString(result));
+        return 0;
+    }
+
+    if ((result = nvmlDeviceGetName(nvml_device, device->name, NVML_DEVICE_NAME_BUFFER_SIZE))) {
+        fprintf(stderr, "Failed to get device name for device %d: %s\n", device_idx, nvmlErrorString(result));
+        return 0;
+    }
+
+    device->device = nvml_device;
+    device->idx = device_idx;
+
+    unsigned int sensor_count = 0;
+    unsigned int total_count = 0;
+
+    for (unsigned int i = 0; i < COUNT_SENSOR; i++) {
+        Sensor *sensor = &device->sensors[sensor_count];
+        sensor->fun = &avaible_sensors[i];
+        unsigned int count;
+
+        if ((count = sensor->fun->init(device, &sensor->data)) != 0) {
+            sensor_count += 1;
+            total_count += count;
+        }
+    }
+
+    device->count = sensor_count;
+    return total_count;
+}
+
+unsigned int get_device(uint64_t *results, Device *device)
+{
+    unsigned int count = 0;
+    for (unsigned int i = 0; i < device->count; i++) {
+        Sensor *sensor = &device->sensors[i];
+        unsigned int result = sensor->fun->get(results, device, sensor->data);
+        count += result;
+        results += result;
+    }
+
+    return count;
+}
+
+unsigned int label_device(char **labels, Device *device)
+{
+    unsigned int count = 0;
+    for (unsigned int i = 0; i < device->count; i++) {
+        Sensor *sensor = &device->sensors[i];
+        unsigned int result = sensor->fun->label(labels, sensor->data);
+        labels += result;
+        count += result;
+    }
+
+    return count;
+}
+
+void clean_device(Device *device)
+{
+    for (unsigned int i = 0; i < device->count; i++) {
+        Sensor *sensor = &device->sensors[i];
+        sensor->fun->clean(sensor->data);
+    }
+}
+
+
+// ------------------------NVIDIA_INTERFACE
+
+unsigned int init_nvidia_gpu(char *none, void **ptr)
+{
+    UNUSED(none);
+    UNUSED(ptr);
+
+    nvmlReturn_t result;
+    if ((result = nvmlInit()) != NVML_SUCCESS) {
+        fprintf(stderr, "Failed to initialize NVML: %s\n", nvmlErrorString(result));
+        exit(1);
+    }
+
+    unsigned int avaible_device_count;
+    if ((result = nvmlDeviceGetCount(&avaible_device_count)) != NVML_SUCCESS) {
+        fprintf(stderr, "Failed to get device count : %s\n", nvmlErrorString(result));
+        nvmlShutdown();
+        exit(1);
+    }
+
+    Device *devices = calloc(avaible_device_count, sizeof(Device));
+
+    unsigned int sensor_count = 0;
+    unsigned int device_count = 0;
+    for (unsigned int i = 0; i < avaible_device_count; i++) {
+        unsigned int initialized_count;
+        if ((initialized_count = init_device(i, &devices[device_count])) != 0) {
+            sensor_count += initialized_count;
+            device_count += 1;
+        }
+    }
+
+    NvidiaGpu *nvidia = (NvidiaGpu *) calloc(1, sizeof(NvidiaGpu));
+    nvidia->devices = devices;
+    nvidia->count = device_count;
+
+    *ptr = (void *) nvidia;
+    return sensor_count;
+}
+
+
+unsigned int get_nvidia_gpu(uint64_t *results, void *ptr)
+{
+    NvidiaGpu *nvidia = (NvidiaGpu *) ptr;
+    unsigned count = 0;
+
+    for (unsigned int i = 0; i < nvidia->count; i++) {
+        unsigned int result = get_device(results, &nvidia->devices[i]);
+        results += result;
+        count += result;
+    }
+
+    return count;
+}
+
+unsigned int label_nvidia_gpu(char **labels, void *ptr)
+{
+    NvidiaGpu *nvidia = (NvidiaGpu *) ptr;
+    unsigned count = 0;
+
+    for (unsigned int i = 0; i < nvidia->count; i++) {
+        unsigned int result = label_device(labels, &nvidia->devices[i]);
+        labels += result;
+        count += result;
+    }
+
+    return count;
+}
+
+void clean_nvidia_gpu(void *ptr)
+{
+    NvidiaGpu *nvidia = (NvidiaGpu *) ptr;
+
+    for (unsigned int i = 0; i < nvidia->count; i++) {
+        clean_device(&nvidia->devices[i]);
+    }
+
+    free(nvidia->devices);
+    free(nvidia);
+    nvmlShutdown();
+}
+
diff --git a/src/nvidia_gpu.h b/src/nvidia_gpu.h
new file mode 100644
index 0000000000000000000000000000000000000000..1f3d053d0e4513ee707d7e4658e8a0a7774fe54c
--- /dev/null
+++ b/src/nvidia_gpu.h
@@ -0,0 +1,43 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+ *******************************************************/
+
+unsigned int init_nvidia_gpu(char *, void **);
+unsigned int get_nvidia_gpu(uint64_t *results, void *);
+void clean_nvidia_gpu(void *);
+void label_nvidia_gpu(char **labels, void *);
+
+
+Sensor nvidia_gpu = {
+    .init = init_nvidia_gpu,
+    .get = get_nvidia_gpu,
+    .clean = clean_nvidia_gpu,
+    .label = label_nvidia_gpu,
+    .nb_opt = 1,
+};
+
+Optparse nvidia_gpu_opt[1] = {
+    {
+        .longname = "nvidia-gpu",
+        .shortname = 'n',
+        .argtype = OPTPARSE_NONE,
+        .usage_arg = NULL,
+        .usage_msg = "provides basic gpu information [clocks, memory, utilization, power, temperature].",
+    },
+};
diff --git a/src/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..0015a9867452800633582cdbd9736ab5c11c145e
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,53 @@
+/*******************************************************
+ 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)
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+/**
+ * @brief Substracts lhs by rhs, assuming that lhs is a cyclic increment from rhs,
+ * meaning that if rhs is greater, lhs's value overflowed.
+ * @param lhs
+ * @param rhs
+ * @return uint64_t
+ */
+uint64_t modulo_substraction(const uint64_t lhs, const uint64_t rhs);
+
+#endif
diff --git a/temperature.c b/temperature.c
deleted file mode 100644
index 142e86ae61604d920c92563196cffef9ad043386..0000000000000000000000000000000000000000
--- a/temperature.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*******************************************************
- Copyright (C) 2018-2021 Georges Da Costa <georges.da-costa@irit.fr>
-
-    This file is part of Mojitos.
-
-    Mojitos is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    Mojitos is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
-
- *******************************************************/
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdint.h>
-
-struct temperature_t {
-  char **label_list;
-  int *fid_list;
-  int nb_elem;
-};
-  
-int get_string(char *filename, char *buffer, int max_size) {
-  int fid = open(filename, O_RDONLY);
-  //printf("Tries to open : %s : %d\n", filename, fid);
-  if(fid == -1)
-    return -1;
-      
-  int nb = read(fid, buffer, max_size);
-  if(nb == -1) {
-    close(fid);
-    return -1;
-  }
-
-  buffer[nb]=0;
-  close(fid);
-  return 0;
-}
-
-void add_to_list(char ***list_name, char *source, int nb_elem) {
-  //printf("Adds: %s\n", source);
-  *list_name = realloc(*list_name, (nb_elem+1)*sizeof(char*));
-  (*list_name)[nb_elem] = malloc(strlen(source)+1);
-  strcpy((*list_name)[nb_elem], source);
-
-}
-
-void add_temperature_sensor(int id_rep, struct temperature_t *state) {
-  static int key=0;
-  static char buffer_filename[512];
-  static char buffer_label[512];
-  
-  int delta = sprintf(buffer_label, "Temp_%d_", key);
-  
-  for(int i=1;;i++) {
-    sprintf(buffer_filename, "/sys/class/hwmon/hwmon%d/temp%d_label", id_rep, i);
-    if(get_string(buffer_filename, buffer_label+delta, 100) == -1)
-      break;
-
-    for(int pos = 0; pos < strlen(buffer_label); pos++) {
-      if (buffer_label[pos] == ' ')  buffer_label[pos] = '_';
-      if (buffer_label[pos] == '\n') buffer_label[pos] = '\0';
-    }
-    add_to_list(&state->label_list, buffer_label, state->nb_elem);
-
-    sprintf(buffer_filename, "/sys/class/hwmon/hwmon%d/temp%d_input", id_rep, i);
-    state->fid_list = realloc(state->fid_list, (state->nb_elem+1)*sizeof(int));
-    state->fid_list[state->nb_elem] = open(buffer_filename, O_RDONLY);
-
-    state->nb_elem++;
-    // printf("%s : %s\n", buffer_label, buffer_filename);
-  }
-  
-  key++;
-}
-
-unsigned int init_temperature(char *args, void** ptr) {
-  struct temperature_t *state = malloc(sizeof(struct temperature_t));
-  state->nb_elem = 0;
-  state->label_list = NULL;
-  state->fid_list = NULL;
-  
-  char base_name[] = "/sys/class/hwmon/hwmon%d/name";
-  static char name[512];
-  static char buffer[512];
-
-  int i = 0;
-  sprintf(name, base_name, i);
-  while(get_string(name, buffer, 8) != -1) {
-    if (strcmp(buffer, "coretemp")==0)
-      add_temperature_sensor(i, state);
-
-    i++;
-    sprintf(name, base_name, i);
-  }
-  *ptr = (void*) state;
-  return state->nb_elem;
-}
-
-unsigned int get_temperature(uint64_t* results, void* ptr) {
-  struct temperature_t *state = (struct temperature_t*)ptr;
-  static char buffer[512];
-  for(int i=0; i<state->nb_elem; i++) {
-    pread(state->fid_list[i], buffer, 100, 0);
-    results[i] = strtoull(buffer, NULL, 10);
-  }
-  return state->nb_elem;
-}
-
-void clean_temperature(void* ptr) {
-  struct temperature_t *state = (struct temperature_t*)ptr;
-  
-  for(int i=0; i<state->nb_elem; i++) {
-    free(state->label_list[i]);
-    close(state->fid_list[i]);
-  }
-  free(state->label_list);
-  free(state->fid_list);
-  free(state);
-}
-
-void label_temperature(char**labels, void* ptr) {
-  struct temperature_t *state = (struct temperature_t*)ptr;
-  for(int i=0; i<state->nb_elem; i++)
-    labels[i] = state->label_list[i];
-}
diff --git a/tests/amd_rapl.c b/tests/amd_rapl.c
new file mode 100644
index 0000000000000000000000000000000000000000..7c05ab5826954a7f4d7127c9fa3b18896af31e86
--- /dev/null
+++ b/tests/amd_rapl.c
@@ -0,0 +1,349 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+*******************************************************/
+
+#include "small_test.h"
+#include "../src/amd_rapl.c"
+
+TFUNCTION(test_raw_to_microjoule, {
+    uint64_t raw = 0;
+    uint64_t unit = 0;
+    uint64_t result = 0;
+    uint64_t expected = 0;
+
+    // Test 1:
+    // -- Setup
+    raw = 100;
+    unit = 1000;
+    expected = 390625;
+    // -- Run
+    result = raw_to_microjoule(raw, unit);
+    // -- Verification
+    TEST_UINT64_T(&result, &expected);
+
+    // TEST 2:
+    // -- Setup
+    raw = 200;
+    unit = 1;
+    expected = 100000000;
+    // -- Run
+    result = raw_to_microjoule(raw, unit);
+    // -- Verification
+    TEST_UINT64_T(&result, &expected);
+
+    // TEST 3:
+    // -- Setup
+    raw = 500;
+    unit = 2;
+    expected = 125000000;
+    // -- Run
+    result = raw_to_microjoule(raw, unit);
+    // -- Verification
+    TEST_UINT64_T(&result, &expected);
+
+    // TEST 4:
+    // -- Setup
+    raw = 1000;
+    unit = 3;
+    expected = 125000000;
+    // -- Run
+    result = raw_to_microjoule(raw, unit);
+    // -- Verification
+    TEST_UINT64_T(&result, &expected);
+
+    // TEST 5:
+    // -- Setup
+    raw = 10000;
+    unit = 4;
+    expected = 625000000;
+    // -- Run
+    result = raw_to_microjoule(raw, unit);
+    // -- Verification
+    TEST_UINT64_T(&result, &expected);
+})
+
+TFUNCTION(test_get_name, {
+    size_t cpu_id = 0;
+    char *result = NULL;
+    char expected[100];
+
+    // TEST 1:
+    // -- Setup
+    cpu_id = 0;
+    strcpy(expected, "core0");
+    // -- Run
+    result = get_name(cpu_id);
+    // -- Verification
+    TEST_STR(result, expected);
+    free(result);
+
+    // TEST 2:
+    // -- Setup
+    cpu_id = 10000;
+    strcpy(expected, "core10000");
+    // -- Run
+    result = get_name(cpu_id);
+    // -- Verification
+    TEST_STR(result, expected);
+    free(result);
+})
+
+#define NONE 0
+#define DUMMY_SENSOR(__sensor, __cpu_id, __name) \
+do {                                             \
+  __sensor = (CpuSensor) {                    \
+    .cpu_id = __cpu_id,                          \
+    .package_id = NONE,                          \
+    .core_id = NONE,                             \
+    .name = __name,                              \
+    .fd = NONE,                                  \
+    .energy_units = NONE,                        \
+    .core_energy = NONE,                         \
+  };                                             \
+} while(0);
+
+#define DUMMY_RAPL(__rapl, __sensors, __sensors_count) \
+do {                                                   \
+    __rapl = (AmdRapl) {                           \
+        .sensors = __sensors,                          \
+        .sensor_count = __sensors_count                \
+    };                                                 \
+} while(0);
+
+TFUNCTION(test_label_amd_rapl, {
+    CpuSensor sensors[100];
+    AmdRapl rapl;
+    char *results[100];
+    char expecteds[10][100];
+    uint64_t nb = 0;
+
+    // Test 1:
+    // -- Setup
+    nb = 1;
+    DUMMY_SENSOR(sensors[0], 0, "core0");
+    DUMMY_RAPL(rapl, sensors, nb);
+    strcpy(expecteds[0], "core0");
+    // -- Run
+    label_amd_rapl(results, (void *) &rapl);
+    // -- Verification
+    for(unsigned int i = 0; i < nb; i++)
+    {
+        TEST_STR(results[i], expecteds[i]);
+    }
+
+    // Test 2:
+    // -- Setup
+    nb = 4;
+    DUMMY_SENSOR(sensors[0], 0, "core0");
+    DUMMY_SENSOR(sensors[1], 1, "core1");
+    DUMMY_SENSOR(sensors[2], 2, "core2");
+    DUMMY_SENSOR(sensors[3], 3, "core3");
+    DUMMY_RAPL(rapl, sensors, nb);
+    strcpy(expecteds[0], "core0");
+    strcpy(expecteds[1], "core1");
+    strcpy(expecteds[2], "core2");
+    strcpy(expecteds[3], "core3");
+    // -- Run
+    label_amd_rapl(results, (void *) &rapl);
+    // -- Verification
+    for(unsigned int i = 0; i < nb; i++)
+    {
+        TEST_STR(results[i], expecteds[i]);
+    }
+    // Test 3:
+    // -- Setup
+    nb = 4;
+    DUMMY_SENSOR(sensors[0], 0, "core0");
+    DUMMY_SENSOR(sensors[1], 3, "core3");
+    DUMMY_SENSOR(sensors[2], 1, "core1");
+    DUMMY_SENSOR(sensors[3], 2, "core2");
+    DUMMY_RAPL(rapl, sensors, nb);
+    strcpy(expecteds[0], "core0");
+    strcpy(expecteds[1], "core3");
+    strcpy(expecteds[2], "core1");
+    strcpy(expecteds[3], "core2");
+    // -- Run
+    label_amd_rapl(results, (void *) &rapl);
+    // -- Verification
+    for(unsigned int i = 0; i < nb; i++)
+    {
+        TEST_STR(results[i], expecteds[i]);
+    }
+})
+
+
+#define DUMMY_CPUINFO(__sensor, __cpu_id, __package_id, __core_id) \
+do {                                                               \
+    __sensor = (CpuSensor) {                                    \
+        .cpu_id = __cpu_id,                                        \
+        .package_id = __package_id,                                \
+        .core_id = __core_id,                                      \
+        .name = NULL,                                              \
+        .fd = NONE,                                                \
+        .energy_units = NONE,                                      \
+        .core_energy = NONE                                        \
+    };                                                             \
+} while (0);
+
+TFUNCTION(test_is_duplicate, {
+    static const unsigned int nb_core = 4;
+    static const unsigned int nb_package = 2;
+    static const unsigned int max_cpu = 10;
+
+    unsigned char map[nb_core][nb_package];
+    CpuSensor cpu_information[max_cpu];
+    unsigned int results[max_cpu];
+    unsigned int expecteds[max_cpu];
+
+    // -- Setup
+    memset(map, NONE, sizeof(map));
+    memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu);
+    DUMMY_CPUINFO(cpu_information[0], 0, 1, 1);
+    expecteds[0] = 1;
+    expecteds[1] = 0;
+    // -- Run
+    results[0] = is_duplicate(&cpu_information[0], nb_core, nb_package,map);
+    results[1] = is_duplicate(&cpu_information[0], nb_core, nb_package,map);
+    // -- Verification
+    TEST_BOOL(&results[0], &expecteds[0]);
+    TEST_BOOL(&results[1], &expecteds[1]);
+
+    // -- Setup
+    memset(map, NONE, sizeof(map));
+    memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu);
+    DUMMY_CPUINFO(cpu_information[0], 0, 1, 1);
+    DUMMY_CPUINFO(cpu_information[1], 0, 1, 1);
+    expecteds[0] = 1;
+    expecteds[1] = 0;
+    // -- Run
+    results[0] = is_duplicate(&cpu_information[0], nb_core, nb_package, map);
+    results[1] = is_duplicate(&cpu_information[1], nb_core, nb_package, map);
+    // -- Verification
+    TEST_BOOL(&results[0], &expecteds[0]);
+    TEST_BOOL(&results[1], &expecteds[1]);
+
+    // -- Setup
+    memset(map, NONE, sizeof(map));
+    memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu);
+    DUMMY_CPUINFO(cpu_information[0], 0, 1, 1);
+    DUMMY_CPUINFO(cpu_information[1], 0, 0, 0);
+    expecteds[0] = 1;
+    expecteds[1] = 1;
+    // -- Run
+    results[0] = is_duplicate(&cpu_information[0], nb_core, nb_package, map);
+    results[1] = is_duplicate(&cpu_information[1], nb_core, nb_package, map);
+    // -- Verification
+    TEST_BOOL(&results[0], &expecteds[0]);
+    TEST_BOOL(&results[1], &expecteds[1]);
+
+    // -- Setup
+    memset(map, NONE, sizeof(map));
+    memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu);
+    DUMMY_CPUINFO(cpu_information[0], 0, 1, 1);
+    DUMMY_CPUINFO(cpu_information[1], 0, 1, 0);
+    expecteds[0] = 1;
+    expecteds[1] = 1;
+    // -- Run
+    results[0] = is_duplicate(&cpu_information[0], nb_core, nb_package, map);
+    results[1] = is_duplicate(&cpu_information[1], nb_core, nb_package, map);
+    // -- Verification
+    TEST_BOOL(&results[0], &expecteds[0]);
+    TEST_BOOL(&results[1], &expecteds[1]);
+
+    // -- Setup
+    memset(map, NONE, sizeof(map));
+    memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu);
+    DUMMY_CPUINFO(cpu_information[0], 0, 1, 1);
+    DUMMY_CPUINFO(cpu_information[1], 0, 0, 1);
+    expecteds[0] = 1;
+    expecteds[1] = 1;
+    // -- Run
+    results[0] = is_duplicate(&cpu_information[0], nb_core, nb_package, map);
+    results[1] = is_duplicate(&cpu_information[1], nb_core, nb_package, map);
+    // -- Verification
+    TEST_BOOL(&results[0], &expecteds[0]);
+    TEST_BOOL(&results[1], &expecteds[1]);
+
+    // -- Setup
+    memset(map, NONE, sizeof(map));
+    memset(cpu_information,NONE, sizeof(CpuSensor) * max_cpu);
+    DUMMY_CPUINFO(cpu_information[0], 0, 0, 0);
+    DUMMY_CPUINFO(cpu_information[1], 0, 0, 1);
+    DUMMY_CPUINFO(cpu_information[2], 0, 1, 0);
+    DUMMY_CPUINFO(cpu_information[3], 0, 1, 1);
+    DUMMY_CPUINFO(cpu_information[4], 0, 0, 0);
+    DUMMY_CPUINFO(cpu_information[5], 0, 0, 1);
+    DUMMY_CPUINFO(cpu_information[6], 0, 1, 0);
+    DUMMY_CPUINFO(cpu_information[7], 0, 1, 1);
+    memset(expecteds, 1, sizeof(unsigned int) * 4);
+    memset(&expecteds[4], 0, sizeof(unsigned int) * 4);
+    // -- Run
+    for (unsigned int i = 0; i < 8; i++)
+    {
+        results[i] = is_duplicate(&cpu_information[i], nb_core, nb_package, map );
+    }
+    // -- Verification
+    for(unsigned int i = 0; i < 8; i++)
+    {
+        TEST_BOOL(&results[i], &expecteds[i]);
+    }
+})
+
+
+TFILE_ENTRY_POINT(test_amd_rapl, {
+    CALL_TFUNCTION(test_raw_to_microjoule);
+    CALL_TFUNCTION(test_get_name);
+    CALL_TFUNCTION(test_label_amd_rapl);
+    CALL_TFUNCTION(test_is_duplicate);
+})
+
+#ifdef __TESTING__AMD__
+int main()
+{
+    static const unsigned int time = 10;
+    AmdRapl *rapl = NULL;
+    unsigned int count_cpu = init_amd_rapl(NULL, (void **) &rapl);
+    uint64_t results[count_cpu];
+    char *labels[count_cpu];
+
+    label_amd_rapl(labels, (void *) rapl);
+
+    for (unsigned int i = 0; i < rapl->sensor_count; ++i) {
+        printf("%s ", labels[i]);
+    }
+    printf("\n");
+
+    // -- Run
+
+    for (unsigned int i = 0; i < time; ++i) {
+        sleep(1);
+        get_amd_rapl(results, (void *)rapl);
+
+        for (unsigned int j = 0; j < rapl->sensor_count; ++j) {
+            printf("%ld ", results[j]);
+        }
+        printf("\n");
+    }
+
+    clean_amd_rapl(rapl);
+    return 0;
+}
+
+#endif
+
diff --git a/tests/info_reader.c b/tests/info_reader.c
new file mode 100644
index 0000000000000000000000000000000000000000..1e4f281f046adb9de795f945b69f38fb6f5b1f4d
--- /dev/null
+++ b/tests/info_reader.c
@@ -0,0 +1,340 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+*******************************************************/
+
+#include "small_test.h"
+#include "./../lib/info_reader.h"
+
+TFUNCTION(test_replace_first, {
+    // useful variables :
+    char result[100];
+    char expected[100];
+
+    // -- Setup
+    strcpy(result, "This is my string");
+    strcpy(expected, "ThIs is my string");
+    // -- Run
+    replace_first(result, 'i', 'I');
+    // -- Verification
+    TEST_STR(result, expected);
+
+    // -- Setup
+    strcpy(result, "This is my string");
+    strcpy(expected, "This is my string");
+    // -- Run
+    replace_first(result, 'x', 'X');
+    // -- Verification
+    TEST_STR(result, expected);
+
+    // -- Setup
+    strcpy(result, "This is my string");
+    strcpy(expected, "This_is my string");
+    // -- Run
+    replace_first(result, ' ', '_');
+    // -- Verification
+    TEST_STR(result, expected);
+
+    // -- Setup
+    strcpy(result, "This is my string");
+    strcpy(expected, "This_is my string");
+    // -- Run
+    replace_first(result, ' ', '_');
+    // -- Verification
+    TEST_STR(result, expected);
+
+    // -- Setup
+    strcpy(result, "This is my string");
+    strcpy(expected, "This is my string");
+    // -- Run
+    replace_first(result, 'T', 'T');
+    // -- Verification
+    TEST_STR(result, expected);
+})
+
+TFUNCTION(test_split_on_delimiter, {
+    // Useful variables
+    char string[100];
+    char *result_key;
+    char *result_value;
+    char expected_key[100];
+    char expected_value[100];
+
+    // Setup
+    strcpy(string, "key:value");
+    strcpy(expected_key, "key");
+    strcpy(expected_value, "value");
+    // Run
+    split_on_delimiter(string, ":", &result_key, &result_value);
+    // Verification
+    TEST_STR(result_key, expected_key);
+    TEST_STR(result_value, expected_value);
+
+    // Setup
+    strcpy(string, "key: value");
+    strcpy(expected_key, "key");
+    strcpy(expected_value, " value");
+    // Run
+    split_on_delimiter(string, ":", &result_key, &result_value);
+    // Verification
+    TEST_STR(result_key, expected_key);
+    TEST_STR(result_value, expected_value);
+
+    // Setup
+    strcpy(string, "key:value");
+    strcpy(expected_key, "key");
+    strcpy(expected_value, "value");
+    replace_first(string, ':', ' ');
+    // Run
+    split_on_delimiter(string, " ", &result_key, &result_value);
+    // Verification
+    TEST_STR(result_key, expected_key);
+    TEST_STR(result_value, expected_value);
+
+    // Setup
+    strcpy(string, "");
+    // Run
+    split_on_delimiter(string, ":", &result_key, &result_value);
+    // Verification
+    TEST_STR(result_key, NULL);
+    TEST_STR(result_value, NULL);
+
+    // Setup
+    strcpy(string, "key:value:extra");
+    strcpy(expected_key, "key");
+    strcpy(expected_value, "value:extra");
+    // Run
+    split_on_delimiter(string, ":", &result_key, &result_value);
+    // Verification
+    TEST_STR(result_key, expected_key);
+    TEST_STR(result_value, expected_value);
+
+    // Setup
+    strcpy(string, "key: value :extra");
+    strcpy(expected_key, "key");
+    strcpy(expected_value, " value :extra");
+    // Run
+    split_on_delimiter(string, ":", &result_key, &result_value);
+    // Verification
+    TEST_STR(result_key, expected_key);
+    TEST_STR(result_value, expected_value);
+})
+
+TFUNCTION(test_start_with, {
+    char *prefix = NULL;
+    char *string = NULL;
+    bool result = false;
+    bool _true = true;
+    bool _false = false;
+
+    prefix = "Hello";
+    string = "Hello World";
+    result = start_with(prefix, string);
+    TEST_BOOL(&result, &_true);
+
+    prefix = "Goodbye";
+    string = "Hello World";
+    result = start_with(prefix, string);
+    TEST_BOOL(&result, &_false);
+
+    prefix = "Hello World";
+    string = "Hello";
+    result = start_with(prefix, string);
+    TEST_BOOL(&result, &_false);
+
+    prefix = "Hello";
+    string = "Hello";
+    result = start_with(prefix, string);
+    TEST_BOOL(&result, &_true);
+
+    prefix = NULL;
+    string = "Hello World";
+    result = start_with(prefix, string);
+    TEST_BOOL(&result, &_false);
+})
+
+#define NONE 0
+#define INIT_KEYFINDER(__key_finder, __key, __delimiter, __copy, __set)  \
+  do {                                                                   \
+    __key_finder = (KeyFinder) {                                         \
+      .key = __key,                                                      \
+      .delimiter = __delimiter,                                          \
+      .copy = __copy,                                                    \
+      .set = __set                                                       \
+    };                                                                   \
+  } while (0);
+
+#define DUMMY_KEYFINDER(__key_finder, __key, __delimiter) \
+  INIT_KEYFINDER(__key_finder, __key, __delimiter, NONE, NONE)    \
+
+#define INIT_PARSER(__parser, __storage, __nb_stored, __capacity,     \
+                    __storage_struct_size, __keys, __nb_keys, __file) \
+do {                                                                  \
+    __parser = (Parser) {                                             \
+      .storage = __storage,                                           \
+      .nb_stored = __nb_stored,                                       \
+      .capacity = __capacity,                                         \
+      .storage_struct_size = __storage_struct_size,                   \
+      .keys = __keys,                                                 \
+      .nb_keys = __nb_keys,                                           \
+      .file = __file                                                  \
+    };                                                                \
+  } while (0);
+
+
+#define DUMMY_PARSER(__parser, __keys, __nb_keys) \
+  INIT_PARSER(__parser, NONE, NONE, NONE, NONE, __keys, __nb_keys, NONE)
+
+
+TFUNCTION(test_match, {
+    // useful variables :
+    bool _true = true;
+    bool _false = false;
+    KeyFinder keys[10];
+    char line[100];
+    Parser parser;
+    bool result;
+    KeyFinder *found_key_finder = NULL;
+    char *raw_value = NULL;
+
+    // Test 1:
+    // -- Setup
+    DUMMY_KEYFINDER(keys[0], "key", ": ");
+    DUMMY_PARSER(parser, keys, 1);
+    strcpy(line, "key: value");
+    found_key_finder = NULL;
+    raw_value = NULL;
+    // -- Run
+    result = match(&parser, line, &found_key_finder, &raw_value);
+    // -- Verification
+    TEST_BOOL(&result, &_true);
+    TEST_PTR(found_key_finder, &keys[0]);
+    TEST_STR(raw_value, "value");
+
+    // Test 2:
+    // -- Setup
+    DUMMY_KEYFINDER(keys[0], "key", ": ");
+    DUMMY_PARSER(parser, keys, 1)
+    strcpy(line, "not a key: value");
+    found_key_finder = NULL;
+    raw_value = NULL;
+    // -- Run
+    result = match(&parser, line, &found_key_finder, &raw_value);
+    // -- Verification
+    TEST_BOOL(&result, &_false);
+    TEST_PTR(found_key_finder, NULL);
+    TEST_STR(raw_value, NULL);
+
+    // Test 3:
+    // -- Setup
+    DUMMY_KEYFINDER(keys[0],"key", ": ");
+    DUMMY_PARSER(parser, keys, 1);
+    strcpy(line, "key:value");
+    found_key_finder = NULL;
+    raw_value = NULL;
+    // -- Run
+    result = match(&parser, line, &found_key_finder, &raw_value);
+    // -- Verification
+    TEST_BOOL(&result, &_false);
+    TEST_PTR(found_key_finder, NULL);
+    TEST_STR(raw_value, NULL);
+
+    // Test 4:
+    // -- Setup
+    DUMMY_KEYFINDER(keys[0], "key", ": ");
+    DUMMY_KEYFINDER(keys[1], "second_key", ": ");
+    DUMMY_PARSER(parser, keys, 2);
+    strcpy(line, "second_key: value");
+    found_key_finder = NULL;
+    raw_value = NULL;
+    // -- Run
+    result = match(&parser, line, &found_key_finder, &raw_value);
+    // -- Verification
+    TEST_BOOL(&result, &_true);
+    TEST_PTR(found_key_finder, &keys[1]);
+    TEST_STR(raw_value, "value");
+
+    // Test 5:
+    // -- Setup
+    DUMMY_KEYFINDER(keys[0], "key", ": ");
+    DUMMY_PARSER(parser, keys, 1);
+    strcpy(line, "");
+    found_key_finder = NULL;
+    raw_value = NULL;
+    // -- Run
+    result = match(&parser, line, &found_key_finder, &raw_value);
+    TEST_BOOL(&result, &_false);
+    TEST_PTR(found_key_finder, NULL);
+    TEST_STR(raw_value, NULL);
+})
+
+
+#define __NB_KEYS 4
+
+typedef struct {
+    int values[__NB_KEYS];
+} IntArray;
+
+GenericPointer __test_file_int_allocator(char *s)
+{
+    unsigned int value = atoi(s);
+    return (GenericPointer) value;
+}
+
+void __test_file_set_int(GenericPointer storage, GenericPointer data)
+{
+    IntArray *array = (IntArray *) storage;
+    int i = (int) data;
+    array->values[i] = i;
+}
+
+TFUNCTION(test_dummy_file, {
+    KeyFinder keys[__NB_KEYS];
+    INIT_KEYFINDER(keys[0], "int0", " : ", __test_file_int_allocator, __test_file_set_int);
+    INIT_KEYFINDER(keys[1], "int1", " ", __test_file_int_allocator, __test_file_set_int);
+    INIT_KEYFINDER(keys[2], "int2", " -> ", __test_file_int_allocator, __test_file_set_int);
+    INIT_KEYFINDER(keys[3], "int3", "--", __test_file_int_allocator, __test_file_set_int);
+
+    IntArray results;
+    IntArray expected;
+
+    expected.values[0] = 0;
+    expected.values[1] = 1;
+    expected.values[2] = 2;
+    expected.values[3] = 3;
+
+    Parser parser;
+    FILE *file = fopen("./tests/info_reader_test.txt", "r");
+    INIT_PARSER(parser, (GenericPointer) &results, 0, 1, sizeof(IntArray), keys, __NB_KEYS, file);
+    parse(&parser);
+
+    for (unsigned int i = 0; i < __NB_KEYS; i++)
+    {
+        TEST_INT(&(results.values[i]), &(expected.values[i]));
+    }
+    fclose(file);
+})
+
+TFILE_ENTRY_POINT(test_info_reader, {
+    CALL_TFUNCTION(test_replace_first);
+    CALL_TFUNCTION(test_split_on_delimiter);
+    CALL_TFUNCTION(test_start_with);
+    CALL_TFUNCTION(test_match);
+    CALL_TFUNCTION(test_dummy_file);
+})
+
diff --git a/tests/info_reader_test.txt b/tests/info_reader_test.txt
new file mode 100644
index 0000000000000000000000000000000000000000..17d4ee43fd318faf156932d60dad44116ca6e503
--- /dev/null
+++ b/tests/info_reader_test.txt
@@ -0,0 +1,9 @@
+noise:
+int0 : 0
+noise--
+int1 1
+int2 -> 2
+noise->
+int3--3
+noise----->>ss<<
+noiseee
diff --git a/counters.h b/tests/main.c
similarity index 67%
rename from counters.h
rename to tests/main.c
index 751557ab5b308c0eda13c61f077d5bdff6519cd1..e90e8dd69863fcc269d561c07df43dc49a2437ec 100644
--- a/counters.h
+++ b/tests/main.c
@@ -1,5 +1,5 @@
 /*******************************************************
- Copyright (C) 2018-2019 Georges Da Costa <georges.da-costa@irit.fr>
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
 
     This file is part of Mojitos.
 
@@ -16,11 +16,16 @@
     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*);
+#include "util.c"
+#include "amd_rapl.c"
+#include "info_reader.c"
+#include "memory.c"
 
-void show_all_counters();
+TMAIN({
+    CALL_TFUNCTION(test_util);
+    CALL_TFUNCTION(test_amd_rapl);
+    CALL_TFUNCTION(test_info_reader);
+    CALL_TFUNCTION(test_memory);
+})
diff --git a/tests/memory.c b/tests/memory.c
new file mode 100644
index 0000000000000000000000000000000000000000..78efd28146558909dc0a940bfa2efce57a102b3d
--- /dev/null
+++ b/tests/memory.c
@@ -0,0 +1,78 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+*******************************************************/
+
+#include "small_test.h"
+#include "../src/memory.c"
+
+// In order to verify the integrity.
+TFUNCTION(test_labels, {
+    // If it fails update the tests
+    int tested_count = 10;
+    int expected_count = MEMORY_COUNT;
+    TEST_INT(&tested_count, &expected_count);
+
+    const char *result = NULL;
+    char *expected = NULL;
+
+    expected = "totalram";
+    result =  memory_labels[TOTALRAM];
+    TEST_STR(result, expected);
+
+    expected = "freeram";
+    result = memory_labels[FREERAM];
+    TEST_STR(result, expected);
+
+    expected = "sharedram";
+    result = memory_labels[SHAREDRAM];
+    TEST_STR(result, expected);
+
+    expected = "bufferram";
+    result = memory_labels[BUFFERRAM];
+    TEST_STR(result, expected);
+
+    expected = "totalswap";
+    result = memory_labels[TOTALSWAP];
+    TEST_STR(result, expected);
+
+    expected = "freeswap";
+    result = memory_labels[FREESWAP];
+    TEST_STR(result, expected);
+
+    expected = "procs";
+    result = memory_labels[PROCS];
+    TEST_STR(result, expected);
+
+    expected = "totalhigh";
+    result = memory_labels[TOTALHIGH];
+    TEST_STR(result, expected);
+
+    expected = "freehigh";
+    result = memory_labels[FREEHIGH];
+    TEST_STR(result, expected);
+
+    expected = "mem_unit";
+    result = memory_labels[MEM_UNIT];
+    TEST_STR(result, expected);
+})
+
+TFILE_ENTRY_POINT(test_memory, {
+    CALL_TFUNCTION(test_labels);
+})
+
diff --git a/tests/small_test.h b/tests/small_test.h
new file mode 100644
index 0000000000000000000000000000000000000000..7e8c11e4e12b92ac97106dc392873a3496977a40
--- /dev/null
+++ b/tests/small_test.h
@@ -0,0 +1,393 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+*******************************************************/
+
+#ifndef __SMALL_TEST_H
+#define __SMALL_TEST_H
+
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "../src/util.h"
+
+#define FMT_BUFFER_SIZE 1000
+
+// ---------------------------API_INTERFACE
+/**
+ * @def TMAIN(__code)
+ *
+ * @brief Define the entry point for the tests.
+ * It initialises any useful variables and acts as the main one.
+ *
+ * @param __code The code that contains the calls to the test functions.
+ */
+
+#define TMAIN(__code)                            \
+  int main()                                     \
+{                                                \
+  unsigned int __indentation_level = 0;          \
+  INDENTED_PRINT("%s:%s\n", __FILE__, __func__); \
+  unsigned int __error_counter__ = 0;            \
+  do __code while (0);                           \
+  DEFERRED_ERROR(__error_counter__);             \
+  return __error_counter__;                      \
+}
+
+/**
+ * @def TFILE_ENTRY_POINT(__filename, __code)
+ *
+ * @brief Define the entry point of a test file.
+ * This macro is used to define the entry point of a test file.
+ * It defines a function with the specified __filename that contains the test code specified in code.
+ *
+ * When the function is called, it initialises the test file using the INIT_TEST_FILE macro,
+ * declares an integer variable __error_counter__ to keep track of any errors encountered during the tests,
+ * executes the test code in a do-while loop, and then checks for any deferred errors using the DEFERRED_ERROR macro.
+ * The function returns the value of __error_counter__,
+ * which indicates the number of errors encountered during the tests.
+ *
+ * @param __filename The name of the function to be used as an entry point for the test file.
+ * @param __code The test code to be executed in the function.
+ */
+#define TFILE_ENTRY_POINT(__filename, __code)       \
+  int __filename (unsigned int __indentation_level) \
+{                                                   \
+  INIT_TEST_FILE();                                 \
+  int __error_counter__ = 0;                        \
+  do __code while(0);                               \
+  DEFERRED_ERROR(__error_counter__);                \
+  return __error_counter__;                         \
+}
+
+/**
+ * @def TFUNCTION(__function_name, __code)
+ *
+ * @brief Define a test function within a test file.
+ * This macro is used to define a test function within a test file.
+ * It defines a function with the given __function_name containing the test code specified in __code.
+ *
+ * When the function is called, it initialises the test function using the INIT_TEST_FUNCTION macro,
+ * declares an integer variable __error_counter__ to keep track of any errors encountered during the tests,
+ * executes the test code in a do-while loop, and then checks for any deferred errors using the DEFERRED_ERROR macro.
+ * The function returns the value of __error_counter__, which indicates the number of errors encountered during the tests.
+ *
+ * @param __function_name The name of the test function.
+ * @param __code The test code to be executed in the function.
+ */
+#define TFUNCTION(__function_name, __code)              \
+  int __function_name(unsigned int __indentation_level) \
+{                                                       \
+  INIT_TEST_FUNCTION();                                 \
+  int __error_counter__ = 0;                            \
+  do __code while(0);                                   \
+  DEFERRED_ERROR(__error_counter__);                    \
+  return __error_counter__;                             \
+}
+
+/**
+ * @def CALL_TFUNCTION(__function_name)
+ *
+ * @brief Call a test function within a test file.
+ * This macro is used to call a test function within a test file.
+ * It calls the function specified by __function_name and adds the return value to the __error_counter__ variable.
+ * This allows multiple test functions to be executed and their error count to be accumulated.
+ *
+ * @param __function_name The name of the test function to be called.
+ */
+#define CALL_TFUNCTION(__function_name) \
+  do {__error_counter__ += __function_name(__indentation_level + 1);} while(0)
+
+/**
+ * @def TEST_STR(__result, __expected)
+ *
+ * @brief Test strings
+ * This macro is used to test strings. It takes two arguments: `__result`, which is the string to test, and `__expected`,
+ * which is the expected value of the string.
+ *
+ * @param __result the string to test.
+ * @param __expected the expected value of the string.
+ *
+ * @code
+ * TEST_STR("Hello", "Hello");
+ * @endcode
+ */
+#define TEST_STR(__result, __expected) \
+    do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, &str_interface);} while(0)
+
+/**
+ * @def TEST_BOOL(__result, __expected)
+ *
+ * @brief Test bools
+ * This macro is used to test bools. It takes two arguments: `__result`, which is the bool to test, and `__expected`,
+ * which is the expected value of the bool.
+ *
+ * @param __result the pointer to bool to test.
+ * @param __expected the pointer to the expected value of the bool.
+ *
+ * @code
+ * bool x = true;
+ * bool y = true;
+ * TEST_BOOL(&x, &y);
+ * @endcode
+ */
+#define TEST_BOOL(__result, __expected) \
+    do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, &bool_interface);} while (0)
+
+/**
+ * @def TEST_INT(__result, __expected)
+ *
+ * @brief Test ints
+ * This macro is used to test ints. It takes two arguments: `__result`, which is the int to be test, and `__expected`,
+ * which is the expected value of the int.
+ *
+ * @param __result the pointer to int to test.
+ * @param __expected the pointer to expected value of the int.
+ *
+ * @code
+ * int x = 1;
+ * int y = 1;
+ * TEST_INT(&x, &y)
+ * @endcode
+ */
+#define TEST_INT(__result, __expected) \
+    do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, &int_interface);} while (0)
+
+/**
+ * @def TEST_PTR(__result, __expected)
+ *
+ * @brief Test pointers
+ * This macro is used to test pointers. It takes two arguments: `__result`, which is the pointer to test, and `__expected`,
+ * which is the expected value of the pointer.
+ *
+ * @param __result the pointer to test.
+ * @param __expected the expected value of the pointer.
+ *
+ * @code
+ * int x = 5;
+ * int *ptr = &x;
+ * TEST_PTR(ptr, &x);
+ * @endcode
+ */
+#define TEST_PTR(__result, __expected) \
+	do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, &ptr_interface);} while(0)
+
+
+/**
+ * @def TEST_UINT64_T(__result, __expected)
+ *
+ * @brief Test 64-bit unsigned integers
+ * This macro is used to test 64-bit unsigned integers. It takes two arguments: `__result`,
+ * which is the integer to test, and `__expected`, which is the expected value of the integer.
+ *
+ * @param __result the pointer to integer to test.
+ * @param __expected the pointer to expected value of the integer.
+ *
+ * @code
+ * uint64_t x = 5;
+ * uint64_t y = 5;
+ * TEST_UINT64_T(&x, &y);
+ * @endcode
+ */
+#define TEST_UINT64_T(__result, __expected) \
+    do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, &u64_interface);} while(0)
+
+/**
+ * @def TEST_INTERFACE(__result, __expected, __interface)
+ *
+ * @brief Define a macro on a usertype with the given __interface.
+ * This macro is used by the user to define a new test macro on a usertype.
+ *
+ * @param __result
+ * @param __expected
+ * @param __interface the interface of the usertype.
+ *
+ * @code
+ * TestInterface usertype_interface = {.compare = ..., .format = ...};
+ * #define TEST_USERTYPE(__result, __expected) \
+ * TEST_INTERFACE(__result, __expected, &usertype_interface)
+ * @endcode
+ */
+#define TEST_INTERFACE(__result, __expected, __interface) \
+    do {__error_counter__ += test(__FILE__, __LINE__, __indentation_level, __result, __expected, __interface);} while(0)
+
+
+
+
+// ------------------------------------CODE
+// These functions should not be in use, only the previous macros should be in use.
+
+#define INDENTED_PRINT(__fmt, ...)                          \
+  do {                                                      \
+    for(unsigned int i = 0; i < __indentation_level; i++) { \
+      printf("|    ");                                      \
+    }                                                       \
+    printf(__fmt, ##__VA_ARGS__);                           \
+  } while(0)
+
+
+#define INIT_TEST_FILE() \
+  INDENTED_PRINT("%s:%s\n", __FILE__, __func__)
+
+#define INIT_TEST_FUNCTION() \
+  INDENTED_PRINT("%s()\n", __func__);
+
+#define DEFERRED_ERROR(nb_error) \
+    INDENTED_PRINT("|_Deferred Error : %d\n",nb_error);
+
+typedef int (Comparator) (const void *, const void *);
+typedef char *(Formatter) (char[FMT_BUFFER_SIZE], const void *);
+
+typedef struct {
+    Comparator *compare;
+    Formatter *format;
+} TestInterface;
+
+// ---------------------------TEST FUNCTION
+
+int test(char *file, int line, unsigned int __indentation_level, const void *result, const void *expected, const TestInterface *interface)
+{
+    __indentation_level += 1;
+    static char buffer_result[FMT_BUFFER_SIZE];
+    static char buffer_expected[FMT_BUFFER_SIZE];
+    int is_equal = interface->compare(result, expected);
+
+    char *fmt_expected = interface->format(buffer_expected, expected);
+    char *fmt_result = interface->format(buffer_result, result);
+    if  (!is_equal) {
+        INDENTED_PRINT("%s:%d: failed, expected <%s>, got <%s>\n", file, line, fmt_expected, fmt_result);
+    }
+    return !is_equal;
+}
+
+// ------------------------------INTERFACES
+
+// -- str_interface
+int str_compare(const void *ptr1, const void *ptr2)
+{
+    char *str1 = (char *) ptr1;
+    char *str2 = (char *) ptr2;
+
+    if (str1 == NULL && str2 == NULL) {
+        return 1;
+    } else if (str1 == NULL || str2 == NULL) {
+        return 0;
+    } else {
+        return (strcmp(str1, str2) == 0);
+    }
+}
+
+char *str_format(char buffer[FMT_BUFFER_SIZE], const void *ptr)
+{
+    UNUSED(buffer);
+    static char *str_null = "NULL";
+    char *str = (char *) ptr;
+    return str ? str : str_null;
+}
+
+static const TestInterface str_interface = {
+    .compare = str_compare,
+    .format = str_format
+};
+
+// -- bool_interface
+
+int bool_compare(const void *ptr1, const void *ptr2)
+{
+    bool *bool1 = (bool *) ptr1;
+    bool *bool2 = (bool *) ptr2;
+    return *bool1 == *bool2;
+}
+
+char *bool_format(char buffer[FMT_BUFFER_SIZE], const void *ptr)
+{
+    UNUSED(buffer);
+    bool *_bool = (bool *) ptr;
+    return *_bool ? "True" : "False";
+}
+
+static const TestInterface bool_interface = {
+    .compare = bool_compare,
+    .format = bool_format
+};
+
+// -- int_interface
+
+int int_compare(const void *ptr1, const void *ptr2)
+{
+    int *int1 = (int *) ptr1;
+    int *int2 = (int *) ptr2;
+    return *int1 == *int2;
+}
+
+char *int_format(char buffer[FMT_BUFFER_SIZE], const void *ptr)
+{
+    int *_int = (int *) ptr;
+    snprintf(buffer, FMT_BUFFER_SIZE, "%d", *_int);
+    return buffer;
+}
+
+static const TestInterface int_interface = {
+    .compare = int_compare,
+    .format = int_format
+};
+
+// -- ptr_interface
+
+int ptr_compare(const void *ptr1, const void *ptr2)
+{
+    return ptr1 == ptr2;
+}
+
+char *ptr_format(char buffer[FMT_BUFFER_SIZE], const void *ptr)
+{
+    snprintf(buffer, FMT_BUFFER_SIZE, "%p", ptr);
+    return buffer;
+}
+
+static const TestInterface ptr_interface = {
+    .compare = ptr_compare,
+    .format = ptr_format
+};
+
+// -- u64_interface
+
+int u64_compare(const void *ptr1, const void *ptr2)
+{
+    uint64_t *v1 = (uint64_t *) ptr1;
+    uint64_t *v2 = (uint64_t *) ptr2;
+    return *v1 == *v2;
+}
+
+char *u64_format(char buffer[FMT_BUFFER_SIZE], const void *ptr)
+{
+    uint64_t *v = (uint64_t *) ptr;
+    snprintf(buffer, FMT_BUFFER_SIZE, "%"PRIu64"", *v);
+    return buffer;
+}
+
+static const TestInterface u64_interface = {
+    .compare = u64_compare,
+    .format = u64_format
+};
+
+#endif
+
diff --git a/tests/util.c b/tests/util.c
new file mode 100644
index 0000000000000000000000000000000000000000..ff979b943368f40dec153c955377dedf3d2cb3f6
--- /dev/null
+++ b/tests/util.c
@@ -0,0 +1,126 @@
+/*******************************************************
+ Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+    This file is part of Mojitos.
+
+    Mojitos is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Mojitos is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with MojitO/S.  If not, see <https://www.gnu.org/licenses/>.
+
+*******************************************************/
+
+#include "../src/util.h"
+#include "small_test.h"
+
+
+TFUNCTION(test_modulo_substraction, {
+    uint64_t lhs = 0;
+    uint64_t rhs = 0;
+    uint64_t result = 0;
+    uint64_t expected = 0;
+
+    // Test 1:
+    // -- Setup
+    lhs = 10;
+    rhs = 10;
+    expected = 0;
+    // -- Run
+    result = modulo_substraction(lhs, rhs);
+    // -- Verification
+    TEST_UINT64_T(&result, &expected);
+
+    // Test 2:
+    // -- Setup
+    lhs = UINT64_MAX;
+    rhs = 0;
+    expected = UINT64_MAX;
+    // -- Run
+    result = modulo_substraction(lhs, rhs);
+    // -- Verification
+    TEST_UINT64_T(&result, &expected);
+
+    // Test 3:
+    // -- Setup
+    lhs = 0;
+    rhs = UINT64_MAX;
+    expected = 1;
+    // -- Run
+    result = modulo_substraction(lhs, rhs);
+    // -- Verification
+    TEST_UINT64_T(&result, &expected);
+
+    // Test 4:
+    // -- Setup
+    lhs = 10;
+    rhs = 20;
+    expected = UINT64_MAX - 9;
+    // -- Run
+    result = modulo_substraction(lhs, rhs);
+    // -- Verification
+    TEST_UINT64_T(&result, &expected);
+
+    // Test 5:
+    // -- Setup
+    lhs = 1000;
+    rhs = 1000;
+    expected = 0;
+    result = modulo_substraction(lhs, rhs);
+    TEST_UINT64_T(&result, &expected);
+})
+
+TFUNCTION(test_max, {
+    int expected = 0;
+    int result = 0;
+
+    expected = 10;
+    result = MAX(expected, 9);
+    TEST_INT(&result, &expected);
+
+    expected = -15;
+    result = MAX(expected, -16);
+    TEST_INT(&result, &expected);
+
+    expected = 0;
+    result = MAX(expected, -1);
+    TEST_INT(&result, &expected);
+
+    expected = 1;
+    result = MAX(expected, 0);
+    TEST_INT(&result, &expected);
+})
+
+TFUNCTION(test_min, {
+    int expected = 0;
+    int result = 0;
+
+    expected = 9;
+    result = MIN(expected, 10);
+    TEST_INT(&result, &expected);
+
+    expected = -16;
+    result = MIN(expected, -15);
+    TEST_INT(&result, &expected);
+
+    expected = -1;
+    result = MIN(expected, 0);
+    TEST_INT(&result, &expected);
+
+    expected = 0;
+    result = MIN(expected, 1);
+    TEST_INT(&result, &expected);
+})
+
+TFILE_ENTRY_POINT(test_util, {
+    CALL_TFUNCTION(test_modulo_substraction);
+    CALL_TFUNCTION(test_max);
+    CALL_TFUNCTION(test_min);
+})
diff --git a/tools/update-readme-usage.sh b/tools/update-readme-usage.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2c1950ec6ad9f5e2681e968b251d80f8c335969d
--- /dev/null
+++ b/tools/update-readme-usage.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+# SPDX-License-Identifier: GPL-3.0-or-later
+# Copyright (C) 2023-2023 Georges Da Costa <georges.da-costa@irit.fr>
+
+die() { yell "$*"; exit 111; }
+try() { "$@" || die "cannot $*"; }
+yell() { echo "$0: $*" >&2; }
+echo() { printf '%s\n' "$*"; }
+
+try ./configure.sh --all
+try make mojitos
+usage=$(
+	./bin/mojitos |
+		awk '
+			/^SENSORS/ {
+				$0 = ""
+				printf "```\n"
+				printf "\n"
+				printf "The following is an exhaustive list of all the sensors (it is very likely\n"
+				printf "that one will not have all the sensors activated in his build):\n"
+				printf "```bash\n"
+				printf "SENSORS:"
+			}
+			{ print }
+		'
+)
+[ -n "$usage" ] || die 'empty usage. cannot continue.'
+
+try awk -v "usage=$usage" '
+	/^Usage/ {
+		print usage
+		del = 1
+	}
+	{
+		if (del == 1 || del == 2) {
+			if (match($0, "^```")) {
+				del++
+			}
+		} else if (del == 3) {
+			if (match($0, "^```")) {
+				del = 0
+				print $0
+			}
+		} else {
+			print $0
+		}
+	}
+' README.md > README.tmp
+try mv README.tmp README.md