Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/extract_gpuinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@

#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "nvtop/extract_gpuinfo.h"
#include "nvtop/extract_gpuinfo_common.h"
Expand Down
156 changes: 146 additions & 10 deletions src/interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
Expand Down
41 changes: 37 additions & 4 deletions src/nvtop.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <locale.h>
Expand Down Expand Up @@ -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;

Expand All @@ -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, "");
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down