diff --git a/src/extract_gpuinfo.c b/src/extract_gpuinfo.c index 57fe388..896b78f 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/interface.c b/src/interface.c index 6f38df5..63f402c 100644 --- a/src/interface.c +++ b/src/interface.c @@ -2095,14 +2095,14 @@ bool show_information_messages(unsigned num_messages, const char **messages) { } void print_snapshot(struct list_head *devices, bool use_fahrenheit_option) { - gpuinfo_populate_static_infos(devices); - gpuinfo_refresh_dynamic_info(devices); 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"; @@ -2173,26 +2173,162 @@ void print_snapshot(struct list_head *devices, bool use_fahrenheit_option) { 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); + 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); + printf("%s\"%s\": null,\n", indent_level_four, mem_used_field); // 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_eight); + 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); diff --git a/src/nvtop.c b/src/nvtop.c index c473936..6795444 100644 --- a/src/nvtop.c +++ b/src/nvtop.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -74,7 +75,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 +94,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 +114,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 +178,9 @@ int main(int argc, char **argv) { case 's': show_snapshot = true; break; + case 'l': + loop_snapshot = true; + break; case ':': case '?': switch (optopt) { @@ -226,8 +233,34 @@ int main(int argc, char **argv) { return EXIT_SUCCESS; } - if (show_snapshot) { - print_snapshot(&monitoredGpus, use_fahrenheit_option); + 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); + gpuinfo_shutdown_info_extraction(&monitoredGpus); return EXIT_SUCCESS; }