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