From 0f7ba08ec4de3508f72b4f11115b2080aa1e41c8 Mon Sep 17 00:00:00 2001 From: Stephen Horvath Date: Sat, 7 Feb 2026 21:34:45 +1000 Subject: [PATCH 1/6] Add interval mode to snapshot --- include/nvtop/interface.h | 2 +- src/interface.c | 14 +++++++++++++- src/nvtop.c | 5 ++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/include/nvtop/interface.h b/include/nvtop/interface.h index 05cd5278..e3f8c097 100644 --- a/include/nvtop/interface.h +++ b/include/nvtop/interface.h @@ -55,6 +55,6 @@ int interface_update_interval(const struct nvtop_interface *interface); bool show_information_messages(unsigned num_messages, const char **messages); -void print_snapshot(struct list_head *devices, bool use_fahrenheit_option); +void print_snapshot(struct list_head *devices, bool use_fahrenheit_option, int interval); #endif // INTERFACE_H_ diff --git a/src/interface.c b/src/interface.c index 6f38df5f..0f61579c 100644 --- a/src/interface.c +++ b/src/interface.c @@ -2094,9 +2094,21 @@ bool show_information_messages(unsigned num_messages, const char **messages) { return dontShowAgainOption; } -void print_snapshot(struct list_head *devices, bool use_fahrenheit_option) { +void print_snapshot(struct list_head *devices, bool use_fahrenheit_option, int interval) { gpuinfo_populate_static_infos(devices); gpuinfo_refresh_dynamic_info(devices); + gpuinfo_refresh_processes(devices); + gpuinfo_utilisation_rate(devices); + gpuinfo_fix_dynamic_info_from_process_info(devices); + + if (interval > 0) { + usleep(interval * 1000); + gpuinfo_refresh_dynamic_info(devices); + gpuinfo_refresh_processes(devices); + gpuinfo_utilisation_rate(devices); + gpuinfo_fix_dynamic_info_from_process_info(devices); + } + struct gpu_info *device; printf("[\n"); diff --git a/src/nvtop.c b/src/nvtop.c index c473936d..d827b3c5 100644 --- a/src/nvtop.c +++ b/src/nvtop.c @@ -227,7 +227,10 @@ int main(int argc, char **argv) { } if (show_snapshot) { - print_snapshot(&monitoredGpus, use_fahrenheit_option); + int interval = 0; + if (update_interval_option_set) + interval = update_interval_option; + print_snapshot(&monitoredGpus, use_fahrenheit_option, interval); gpuinfo_shutdown_info_extraction(&monitoredGpus); return EXIT_SUCCESS; } From dc8345ba3cb29a7626c5b2dfcc6c88a565805602 Mon Sep 17 00:00:00 2001 From: Stephen Horvath Date: Sat, 7 Feb 2026 21:55:24 +1000 Subject: [PATCH 2/6] Add loop snapshot mode --- src/nvtop.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/nvtop.c b/src/nvtop.c index d827b3c5..7344184d 100644 --- a/src/nvtop.c +++ b/src/nvtop.c @@ -74,7 +74,8 @@ static const char helpstring[] = "Available options:\n" "(default 30s, negative = always on screen)\n" " -h --help : Print help and exit\n" " -s --snapshot : Output the current gpu stats without ncurses" - "(useful for scripting)\n"; + "(useful for scripting)\n" + " -l --loop : Output the current gpu stats without ncurses in a loop\n"; static const char versionString[] = "nvtop version " NVTOP_VERSION_STRING; @@ -92,10 +93,11 @@ static const struct option long_opts[] = { {.name = "no-processes", .has_arg = no_argument, .flag = NULL, .val = 'P'}, {.name = "reverse-abs", .has_arg = no_argument, .flag = NULL, .val = 'r'}, {.name = "snapshot", .has_arg = no_argument, .flag = NULL, .val = 's'}, + {.name = "loop", .has_arg = no_argument, .flag = NULL, .val = 'l'}, {0, 0, 0, 0}, }; -static const char opts[] = "hvd:c:CfE:pPris"; +static const char opts[] = "hvd:c:CfE:pPrisl"; int main(int argc, char **argv) { (void)setlocale(LC_CTYPE, ""); @@ -111,6 +113,7 @@ int main(int argc, char **argv) { bool encode_decode_timer_option_set = false; bool show_gpu_info_bar = false; bool show_snapshot = false; + bool loop_snapshot = false; double encode_decode_hide_time = -1.; char *custom_config_file_path = NULL; while (true) { @@ -174,6 +177,9 @@ int main(int argc, char **argv) { case 's': show_snapshot = true; break; + case 'l': + loop_snapshot = true; + break; case ':': case '?': switch (optopt) { @@ -235,6 +241,18 @@ int main(int argc, char **argv) { return EXIT_SUCCESS; } + if (loop_snapshot) { + int interval = 1000; + if (update_interval_option_set) + interval = update_interval_option; + while (!signal_exit) { + print_snapshot(&monitoredGpus, use_fahrenheit_option, 0); + usleep(interval * 1000); + } + gpuinfo_shutdown_info_extraction(&monitoredGpus); + return EXIT_SUCCESS; + } + unsigned numWarningMessages = 0; const char **warningMessages; get_info_messages(&monitoredGpus, &numWarningMessages, &warningMessages); From 1e6061f59b28a6cb4b6c52bd60851fdd00c51cd3 Mon Sep 17 00:00:00 2001 From: Stephen Horvath Date: Sat, 7 Feb 2026 22:02:27 +1000 Subject: [PATCH 3/6] Fix invalid JSON in snapshot --- src/interface.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/interface.c b/src/interface.c index 0f61579c..a7f1854a 100644 --- a/src/interface.c +++ b/src/interface.c @@ -2187,20 +2187,20 @@ void print_snapshot(struct list_head *devices, bool use_fahrenheit_option, int i // Memory Utilization if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, mem_util_rate)) - printf("%s\"%s\": \"%u%%\"\n", indent_level_four, mem_util_field, device->dynamic_info.mem_util_rate); + printf("%s\"%s\": \"%u%%\",\n", indent_level_four, mem_util_field, device->dynamic_info.mem_util_rate); else - printf("%s\"%s\": null\n", indent_level_four, mem_util_field); + printf("%s\"%s\": null,\n", indent_level_four, mem_util_field); // Memory Total if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, total_memory)) - printf("%s\"%s\": \"%llu\"\n", indent_level_four, mem_total_field, device->dynamic_info.total_memory); + printf("%s\"%s\": \"%llu\",\n", indent_level_four, mem_total_field, device->dynamic_info.total_memory); else - printf("%s\"%s\": null\n", indent_level_four, mem_total_field); + printf("%s\"%s\": null,\n", indent_level_four, mem_total_field); // Memory Used if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, used_memory)) - printf("%s\"%s\": \"%llu\"\n", indent_level_four, mem_used_field, device->dynamic_info.used_memory); + printf("%s\"%s\": \"%llu\",\n", indent_level_four, mem_used_field, device->dynamic_info.used_memory); else - printf("%s\"%s\": null\n", indent_level_four, mem_used_field); - // Memory Available + printf("%s\"%s\": null,\n", indent_level_four, mem_used_field); + // Memory Available (Notice: no comma at the end as it's the last field here) if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, free_memory)) printf("%s\"%s\": \"%llu\"\n", indent_level_four, mem_free_field, device->dynamic_info.free_memory); else From f3c52031c02d68e6f4ab332b721be0660733af78 Mon Sep 17 00:00:00 2001 From: Maxime Schmitt Date: Sun, 8 Feb 2026 12:23:35 +0100 Subject: [PATCH 4/6] Snapshot with delay by default --- src/extract_gpuinfo.c | 2 -- src/nvtop.c | 44 +++++++++++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/extract_gpuinfo.c b/src/extract_gpuinfo.c index 57fe3881..896b78fb 100644 --- a/src/extract_gpuinfo.c +++ b/src/extract_gpuinfo.c @@ -21,9 +21,7 @@ #include #include -#include #include -#include #include "nvtop/extract_gpuinfo.h" #include "nvtop/extract_gpuinfo_common.h" diff --git a/src/nvtop.c b/src/nvtop.c index 7344184d..67954448 100644 --- a/src/nvtop.c +++ b/src/nvtop.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -232,23 +233,34 @@ int main(int argc, char **argv) { return EXIT_SUCCESS; } - if (show_snapshot) { - int interval = 0; - if (update_interval_option_set) - interval = update_interval_option; - print_snapshot(&monitoredGpus, use_fahrenheit_option, interval); - gpuinfo_shutdown_info_extraction(&monitoredGpus); - return EXIT_SUCCESS; - } + if (show_snapshot || loop_snapshot) { + gpuinfo_populate_static_infos(&monitoredGpus); + + // Always do a refresh followed by a short sleep to have valid cycle based + // metrics + gpuinfo_refresh_dynamic_info(&monitoredGpus); + gpuinfo_refresh_processes(&monitoredGpus); + gpuinfo_utilisation_rate(&monitoredGpus); + // Default to 0.1 sec + if (!update_interval_option_set) + update_interval_option = 100; + + do { +#if _POSIX_C_SOURCE >= 199309L + struct timespec tv = {.tv_sec = update_interval_option / 1000, + .tv_nsec = (update_interval_option % 1000) * 1000000}; + nanosleep(&tv, &tv); +#else + int sec = update_interval_option / 1000; + sleep(sec > 0 ? sec : 1); +#endif + gpuinfo_refresh_dynamic_info(&monitoredGpus); + gpuinfo_refresh_processes(&monitoredGpus); + gpuinfo_utilisation_rate(&monitoredGpus); + gpuinfo_fix_dynamic_info_from_process_info(&monitoredGpus); + print_snapshot(&monitoredGpus, use_fahrenheit_option); + } while (loop_snapshot && !signal_exit); - if (loop_snapshot) { - int interval = 1000; - if (update_interval_option_set) - interval = update_interval_option; - while (!signal_exit) { - print_snapshot(&monitoredGpus, use_fahrenheit_option, 0); - usleep(interval * 1000); - } gpuinfo_shutdown_info_extraction(&monitoredGpus); return EXIT_SUCCESS; } From b6b693995df46e60fb38145936bb4426652efd03 Mon Sep 17 00:00:00 2001 From: Maxime Schmitt Date: Sun, 8 Feb 2026 12:40:50 +0100 Subject: [PATCH 5/6] Encode/decode usage and processes in snapshot Fixes: #336 --- include/nvtop/interface.h | 2 +- src/interface.c | 160 +++++++++++++++++++++++++++++++++----- 2 files changed, 143 insertions(+), 19 deletions(-) diff --git a/include/nvtop/interface.h b/include/nvtop/interface.h index e3f8c097..05cd5278 100644 --- a/include/nvtop/interface.h +++ b/include/nvtop/interface.h @@ -55,6 +55,6 @@ int interface_update_interval(const struct nvtop_interface *interface); bool show_information_messages(unsigned num_messages, const char **messages); -void print_snapshot(struct list_head *devices, bool use_fahrenheit_option, int interval); +void print_snapshot(struct list_head *devices, bool use_fahrenheit_option); #endif // INTERFACE_H_ diff --git a/src/interface.c b/src/interface.c index a7f1854a..440d3d6e 100644 --- a/src/interface.c +++ b/src/interface.c @@ -2094,27 +2094,15 @@ bool show_information_messages(unsigned num_messages, const char **messages) { return dontShowAgainOption; } -void print_snapshot(struct list_head *devices, bool use_fahrenheit_option, int interval) { - gpuinfo_populate_static_infos(devices); - gpuinfo_refresh_dynamic_info(devices); - gpuinfo_refresh_processes(devices); - gpuinfo_utilisation_rate(devices); - gpuinfo_fix_dynamic_info_from_process_info(devices); - - if (interval > 0) { - usleep(interval * 1000); - gpuinfo_refresh_dynamic_info(devices); - gpuinfo_refresh_processes(devices); - gpuinfo_utilisation_rate(devices); - gpuinfo_fix_dynamic_info_from_process_info(devices); - } - +void print_snapshot(struct list_head *devices, bool use_fahrenheit_option) { struct gpu_info *device; printf("[\n"); list_for_each_entry(device, devices, list) { const char *indent_level_two = " "; const char *indent_level_four = " "; + const char *indent_level_six = " "; + const char *indent_level_eight = " "; const char *device_name_field = "device_name"; const char *gpu_clock_field = "gpu_clock"; @@ -2185,6 +2173,26 @@ void print_snapshot(struct list_head *devices, bool use_fahrenheit_option, int i else printf("%s\"%s\": null,\n", indent_level_four, gpu_util_field); + // Encode / Decode + if (device->static_info.encode_decode_shared) { + printf("%s\"encode_decode\": ", indent_level_four); + if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, decoder_rate)) + printf("\"%u%%\",\n", device->dynamic_info.decoder_rate); + else + printf("null,\n"); + } else { + printf("%s\"encode\": ", indent_level_four); + if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, encoder_rate)) + printf("\"%u%%\",\n", device->dynamic_info.encoder_rate); + else + printf("null,\n"); + printf("%s\"decode\": ", indent_level_four); + if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, decoder_rate)) + printf("\"%u%%\",\n", device->dynamic_info.decoder_rate); + else + printf("null,\n"); + } + // Memory Utilization if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, mem_util_rate)) printf("%s\"%s\": \"%u%%\",\n", indent_level_four, mem_util_field, device->dynamic_info.mem_util_rate); @@ -2200,11 +2208,127 @@ void print_snapshot(struct list_head *devices, bool use_fahrenheit_option, int i printf("%s\"%s\": \"%llu\",\n", indent_level_four, mem_used_field, device->dynamic_info.used_memory); else printf("%s\"%s\": null,\n", indent_level_four, mem_used_field); - // Memory Available (Notice: no comma at the end as it's the last field here) + // Memory Available if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, free_memory)) - printf("%s\"%s\": \"%llu\"\n", indent_level_four, mem_free_field, device->dynamic_info.free_memory); + printf("%s\"%s\": \"%llu\",\n", indent_level_four, mem_free_field, device->dynamic_info.free_memory); else - printf("%s\"%s\": null\n", indent_level_four, mem_free_field); + printf("%s\"%s\": null,\n", indent_level_four, mem_free_field); + + // Processes + printf("%s\"processes\" : [\n", indent_level_four); + for (unsigned i = 0; i < device->processes_count; ++i) { + struct gpu_process *proc = &device->processes[i]; + printf("%s{\n", indent_level_six); + + // PID + printf("%s\"pid\": \"%d\",\n", indent_level_eight, proc->pid); + + printf("%s\"cmdline\": \"", indent_level_six); + for (char *li = proc->cmdline; *li != '\0'; li++) { + // We need to escape some characters for for json strings + if (*li == '\n') { + printf("\\n"); + continue; + } else if (*li == '\b') { + printf("\\b"); + continue; + } else if (*li == '\f') { + printf("\\f"); + continue; + } else if (*li == '\r') { + printf("\\r"); + continue; + } else if (*li == '\t') { + printf("\\t"); + continue; + } + // escaping backslash and quotes + if (*li == '\\' || *li == '"') + printf("\\"); + printf("%c", *li); + } + printf("\",\n"); + + printf("%s\"kind\": ", indent_level_eight); + if (proc->type != gpu_process_unknown) { + printf("\""); + switch (proc->type) { + case gpu_process_graphical: + printf("graphic"); + break; + case gpu_process_compute: + printf("compute"); + break; + case gpu_process_graphical_compute: + printf("graphic & compute"); + break; + default: + printf("N/A"); + break; + } + printf("\""); + } else { + printf("null"); + } + printf(",\n"); + + // GPU memory usage + printf("%s\"user\": ", indent_level_eight); + if (GPUINFO_PROCESS_FIELD_VALID(proc, user_name)) + printf("\"%s\",\n", proc->user_name); + else + printf("null,\n"); + + // GPU usage + printf("%s\"gpu_usage\": ", indent_level_eight); + if (GPUINFO_PROCESS_FIELD_VALID(proc, gpu_usage)) + printf("\"%u%%\",\n", proc->gpu_usage); + else + printf("null,\n"); + + // GPU memory usage + printf("%s\"gpu_mem_bytes_alloc\": ", indent_level_eight); + if (GPUINFO_PROCESS_FIELD_VALID(proc, gpu_memory_usage)) + printf("\"%llu\",\n", proc->gpu_memory_usage); + else + printf("null,\n"); + + // GPU memory usage + printf("%s\"gpu_mem_usage\": ", indent_level_eight); + if (GPUINFO_PROCESS_FIELD_VALID(proc, gpu_memory_percentage)) + printf("\"%u%%\",\n", proc->gpu_memory_percentage); + else + printf("null,\n"); + + // Encode usage + if (device->static_info.encode_decode_shared) { + // (Notice: no comma at the end as it's the last field here) + printf("%s\"encode_decode\": ", indent_level_eight); + if (GPUINFO_PROCESS_FIELD_VALID(proc, decode_usage)) + printf("\"%u%%\"\n", proc->decode_usage); + else + printf("null\n"); + } else { + printf("%s\"encode\": ", indent_level_eight); + if (GPUINFO_PROCESS_FIELD_VALID(proc, encode_usage)) + printf("\"%u%%\",\n", proc->encode_usage); + else + printf("null,\n"); + // (Notice: no comma at the end as it's the last field here) + printf("%s\"decode\": ", indent_level_eight); + if (GPUINFO_PROCESS_FIELD_VALID(proc, decode_usage)) + printf("\"%u%%\"\n", proc->decode_usage); + else + printf("null\n"); + } + + printf("%s}", indent_level_six); + if (i != device->processes_count - 1) + printf(","); + printf("\n"); + } + // (Notice: no comma at the end as it's the last field here) + printf("%s]\n", indent_level_four); if (device->list.next == devices) printf("%s}\n", indent_level_two); From 2c47bc17b54c6e76cdc020d78f49a6f260255568 Mon Sep 17 00:00:00 2001 From: Maxime Schmitt Date: Sun, 8 Feb 2026 13:56:03 +0100 Subject: [PATCH 6/6] Fix json cmdline indent --- src/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interface.c b/src/interface.c index 440d3d6e..63f402c7 100644 --- a/src/interface.c +++ b/src/interface.c @@ -2223,7 +2223,7 @@ void print_snapshot(struct list_head *devices, bool use_fahrenheit_option) { // PID printf("%s\"pid\": \"%d\",\n", indent_level_eight, proc->pid); - printf("%s\"cmdline\": \"", indent_level_six); + printf("%s\"cmdline\": \"", indent_level_eight); for (char *li = proc->cmdline; *li != '\0'; li++) { // We need to escape some characters for for json strings if (*li == '\n') {