From 8adf4babb2c30f2f99e80e69332433d65a9a43ba Mon Sep 17 00:00:00 2001 From: hoream Date: Mon, 26 Aug 2024 00:33:12 +0800 Subject: [PATCH 01/11] add basic support for raspberrypi gpu --- CMakeLists.txt | 2 + src/CMakeLists.txt | 4 + src/extract_gpuinfo_v3d.c | 350 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 356 insertions(+) create mode 100644 src/extract_gpuinfo_v3d.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b0e5679..142fdd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,7 @@ else() set(NVIDIA_SUPPORT_DEFAULT ON) set(AMDGPU_SUPPORT_DEFAULT ON) set(INTEL_SUPPORT_DEFAULT ON) + set(V3D_SUPPORT_DEFAULT ON) set(MSM_SUPPORT_DEFAULT ON) set(PANFROST_SUPPORT_DEFAULT ON) set(PANTHOR_SUPPORT_DEFAULT ON) @@ -97,6 +98,7 @@ option(APPLE_SUPPORT "Build support for Apple GPUs through Metal" ${APPLE_SUPPOR option(PANFROST_SUPPORT "Build support for Mali GPUs through panfrost driver" ${PANFROST_SUPPORT_DEFAULT}) option(PANTHOR_SUPPORT "Build support for Mali GPUs through panthor driver" ${PANTHOR_SUPPORT_DEFAULT}) option(ASCEND_SUPPORT "Build support for Ascend NPUs through Ascend DCMI" ${ASCEND_SUPPORT_DEFAULT}) +option(V3D_SUPPORT "Build support for Raspberrypi through v3d" ${V3D_SUPPORT_DEFAULT}) add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fde4b27..d648f6d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -100,6 +100,10 @@ if(INTEL_SUPPORT) target_sources(nvtop PRIVATE extract_gpuinfo_intel.c) endif() +if(V3D_SUPPORT) + target_sources(nvtop PRIVATE extract_gpuinfo_v3d.c) +endif() + if(APPLE_SUPPORT) target_sources(nvtop PRIVATE extract_gpuinfo_apple.m) target_link_libraries(nvtop PRIVATE diff --git a/src/extract_gpuinfo_v3d.c b/src/extract_gpuinfo_v3d.c new file mode 100644 index 0000000..67f23ea --- /dev/null +++ b/src/extract_gpuinfo_v3d.c @@ -0,0 +1,350 @@ +/* + * + * Copyright (C) 2022 Maxime Schmitt + * + * This file is part of Nvtop and adapted from igt-gpu-tools from v3d Corporation. + * + * Nvtop 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. + * + * Nvtop 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 nvtop. If not, see . + * + */ + +#include "nvtop/device_discovery.h" +#include "nvtop/extract_gpuinfo_common.h" +#include "nvtop/extract_processinfo_fdinfo.h" +#include "nvtop/time.h" + +#include +#include +#include +#include + +#define HASH_FIND_CLIENT(head, key_ptr, out_ptr) HASH_FIND(hh, head, key_ptr, sizeof(struct unique_cache_id), out_ptr) +#define HASH_ADD_CLIENT(head, in_ptr) HASH_ADD(hh, head, client_id, sizeof(struct unique_cache_id), in_ptr) + +#define SET_v3d_CACHE(cachePtr, field, value) SET_VALUE(cachePtr, field, value, v3d_cache_) +#define RESET_v3d_CACHE(cachePtr, field) INVALIDATE_VALUE(cachePtr, field, v3d_cache_) +#define v3d_CACHE_FIELD_VALID(cachePtr, field) VALUE_IS_VALID(cachePtr, field, v3d_cache_) + +enum v3d_process_info_cache_valid { + v3d_cache_engine_render_valid = 0, + v3d_cache_engine_copy_valid, + v3d_cache_engine_video_valid, + v3d_cache_engine_video_enhance_valid, + v3d_cache_process_info_cache_valid_count +}; + +struct __attribute__((__packed__)) unique_cache_id { + unsigned client_id; + pid_t pid; + char *pdev; +}; + +struct v3d_process_info_cache { + struct unique_cache_id client_id; + uint64_t engine_render; + uint64_t engine_copy; + uint64_t engine_video; + uint64_t engine_video_enhance; + nvtop_time last_measurement_tstamp; + unsigned char valid[(v3d_cache_process_info_cache_valid_count + CHAR_BIT - 1) / CHAR_BIT]; + UT_hash_handle hh; +}; + +struct gpu_info_v3d { + struct gpu_info base; + + unsigned long long last_timestamp, last_val[5]; + struct nvtop_device *card_device; + struct nvtop_device *driver_device; + struct v3d_process_info_cache *last_update_process_cache, *current_update_process_cache; // Cached processes info +}; + +static bool gpuinfo_v3d_init(void); +static void gpuinfo_v3d_shutdown(void); +static const char *gpuinfo_v3d_last_error_string(void); +static bool gpuinfo_v3d_get_device_handles(struct list_head *devices, unsigned *count); +static void gpuinfo_v3d_populate_static_info(struct gpu_info *_gpu_info); +static void gpuinfo_v3d_refresh_dynamic_info(struct gpu_info *_gpu_info); +static void gpuinfo_v3d_get_running_processes(struct gpu_info *_gpu_info); + +struct gpu_vendor gpu_vendor_v3d = { + .init = gpuinfo_v3d_init, + .shutdown = gpuinfo_v3d_shutdown, + .last_error_string = gpuinfo_v3d_last_error_string, + .get_device_handles = gpuinfo_v3d_get_device_handles, + .populate_static_info = gpuinfo_v3d_populate_static_info, + .refresh_dynamic_info = gpuinfo_v3d_refresh_dynamic_info, + .refresh_running_processes = gpuinfo_v3d_get_running_processes, + .name = "v3d", +}; + +unsigned v3d_gpu_count; +static struct gpu_info_v3d *gpu_infos; + +__attribute__((constructor)) static void init_extract_gpuinfo_v3d(void) { register_gpu_vendor(&gpu_vendor_v3d); } + +bool gpuinfo_v3d_init(void) { return true; } +void gpuinfo_v3d_shutdown(void) { + for (unsigned i = 0; i < v3d_gpu_count; ++i) { + struct gpu_info_v3d *current = &gpu_infos[i]; + nvtop_device_unref(current->card_device); + nvtop_device_unref(current->driver_device); + } +} + +const char *gpuinfo_v3d_last_error_string(void) { return "Err"; } + +static bool parse_drm_fdinfo_v3d(struct gpu_info *info, FILE *fdinfo_file, struct gpu_process *process_info) { + struct gpu_info_v3d *gpu_info = container_of(info, struct gpu_info_v3d, base); + + unsigned cid; + nvtop_time current_time; + nvtop_get_current_time(¤t_time); + + // The v3d driver does not expose compute engine metrics as of yet + process_info->type |= gpu_process_graphical; + + struct v3d_process_info_cache *cache_entry; + struct unique_cache_id ucid = {.client_id = cid, .pid = process_info->pid, .pdev = gpu_info->base.pdev}; + HASH_FIND_CLIENT(gpu_info->last_update_process_cache, &ucid, cache_entry); + if (cache_entry) { + uint64_t time_elapsed = nvtop_difftime_u64(cache_entry->last_measurement_tstamp, current_time); + HASH_DEL(gpu_info->last_update_process_cache, cache_entry); + if (GPUINFO_PROCESS_FIELD_VALID(process_info, gfx_engine_used) && + v3d_CACHE_FIELD_VALID(cache_entry, engine_render) && + // In some rare occasions, the gfx engine usage reported by the driver is lowering (might be a driver bug) + process_info->gfx_engine_used >= cache_entry->engine_render && + process_info->gfx_engine_used - cache_entry->engine_render <= time_elapsed) { + SET_GPUINFO_PROCESS( + process_info, gpu_usage, + busy_usage_from_time_usage_round(process_info->gfx_engine_used, cache_entry->engine_render, time_elapsed)); + } + if (GPUINFO_PROCESS_FIELD_VALID(process_info, dec_engine_used) && + v3d_CACHE_FIELD_VALID(cache_entry, engine_video) && + process_info->dec_engine_used >= cache_entry->engine_video && + process_info->dec_engine_used - cache_entry->engine_video <= time_elapsed) { + SET_GPUINFO_PROCESS( + process_info, decode_usage, + busy_usage_from_time_usage_round(process_info->dec_engine_used, cache_entry->engine_video, time_elapsed)); + } + if (GPUINFO_PROCESS_FIELD_VALID(process_info, enc_engine_used) && + v3d_CACHE_FIELD_VALID(cache_entry, engine_video_enhance) && + process_info->enc_engine_used >= cache_entry->engine_video_enhance && + process_info->enc_engine_used - cache_entry->engine_video_enhance <= time_elapsed) { + SET_GPUINFO_PROCESS(process_info, encode_usage, + busy_usage_from_time_usage_round(process_info->enc_engine_used, + cache_entry->engine_video_enhance, time_elapsed)); + } + } else { + cache_entry = calloc(1, sizeof(*cache_entry)); + if (!cache_entry) + goto parse_fdinfo_exit; + cache_entry->client_id.client_id = cid; + cache_entry->client_id.pid = process_info->pid; + cache_entry->client_id.pdev = gpu_info->base.pdev; + } + + RESET_ALL(cache_entry->valid); + if (GPUINFO_PROCESS_FIELD_VALID(process_info, gfx_engine_used)) + SET_v3d_CACHE(cache_entry, engine_render, process_info->gfx_engine_used); + if (GPUINFO_PROCESS_FIELD_VALID(process_info, dec_engine_used)) + SET_v3d_CACHE(cache_entry, engine_video, process_info->dec_engine_used); + if (GPUINFO_PROCESS_FIELD_VALID(process_info, enc_engine_used)) + SET_v3d_CACHE(cache_entry, engine_video_enhance, process_info->enc_engine_used); + + cache_entry->last_measurement_tstamp = current_time; + HASH_ADD_CLIENT(gpu_info->current_update_process_cache, cache_entry); + +parse_fdinfo_exit: + return true; +} + +static void add_v3d_cards(struct nvtop_device *dev, struct list_head *devices, unsigned *count) { + struct nvtop_device *parent; + if (nvtop_device_get_parent(dev, &parent) < 0) + return; + + const char *driver; + nvtop_device_get_driver(parent, &driver); + if (!strcmp(driver, "v3d")) + return; + + struct gpu_info_v3d *thisGPU = &gpu_infos[v3d_gpu_count++]; + thisGPU->base.vendor = &gpu_vendor_v3d; + thisGPU->card_device = nvtop_device_ref(dev); + thisGPU->driver_device = nvtop_device_ref(parent); + list_add_tail(&thisGPU->base.list, devices); + // Register a fdinfo callback for this GPU + processinfo_register_fdinfo_callback(parse_drm_fdinfo_v3d, &thisGPU->base); + thisGPU->last_timestamp = 0; + for (int i = 0; i < 5; i++) + thisGPU->last_val[i] = 0; + (*count)++; +} + +bool gpuinfo_v3d_get_device_handles(struct list_head *devices_list, unsigned *count) { + *count = 0; + nvtop_device_enumerator *enumerator; + if (nvtop_enumerator_new(&enumerator) < 0) + return false; + + if (nvtop_device_enumerator_add_match_subsystem(enumerator, "drm", true) < 0) + return false; + + if (nvtop_device_enumerator_add_match_property(enumerator, "DEVNAME", "/dev/dri/*") < 0) + return false; + + unsigned num_devices = 0; + for (nvtop_device *device = nvtop_enumerator_get_device_first(enumerator); device; + device = nvtop_enumerator_get_device_next(enumerator)) { + num_devices++; + } + + gpu_infos = calloc(num_devices, sizeof(*gpu_infos)); + if (!gpu_infos) + return false; + + for (nvtop_device *device = nvtop_enumerator_get_device_first(enumerator); device; + device = nvtop_enumerator_get_device_next(enumerator)) { + num_devices++; + const char *devname; + if (nvtop_device_get_devname(device, &devname) < 0) + continue; + if (strstr(devname, "/dev/dri/card")) { + add_v3d_cards(device, devices_list, count); + } + } + + nvtop_enumerator_unref(enumerator); + return true; +} + +void gpuinfo_v3d_populate_static_info(struct gpu_info *_gpu_info) { + struct gpu_info_v3d *gpu_info = container_of(_gpu_info, struct gpu_info_v3d, base); + struct gpuinfo_static_info *static_info = &gpu_info->base.static_info; + const char *dev_name = "VIDEO CORE"; + + static_info->integrated_graphics = true; + static_info->encode_decode_shared = false; + RESET_ALL(static_info->valid); + + snprintf(static_info->device_name, sizeof(static_info->device_name), "%s", dev_name); + SET_VALID(gpuinfo_device_name_valid, static_info->valid); +} + +static int get_vc_usage(struct gpu_info *_gpu_info) { + struct gpu_info_v3d *gpu_info = container_of(_gpu_info, struct gpu_info_v3d, base); + FILE *fp = fopen("/sys/kernel/debug/dri/0/gpu_usage", "rb"); + + char *buf = NULL; + size_t res = 0; + unsigned long jobs, active; + unsigned long long timestamp, elapsed, runtime; + float max, load[5]; + int i; + + while (getline(&buf, &res, fp) > 0) { + if (sscanf(buf, "timestamp;%lld;", ×tamp) == 1) { + // use the timestamp line to calculate time since last measurement + elapsed = timestamp - gpu_info->last_timestamp; + gpu_info->last_timestamp = timestamp; + } else if (sscanf(strchr(buf, ';'), ";%ld;%lld;%ld;", &jobs, &runtime, &active) == 3) { + // depending on which queue is in the line, calculate the percentage of time used since last measurement + // store the current time value for the next calculation + i = -1; + if (!strncmp(buf, "v3d_bin", 7)) + i = 0; + if (!strncmp(buf, "v3d_ren", 7)) + i = 1; + if (!strncmp(buf, "v3d_tfu", 7)) + i = 2; + if (!strncmp(buf, "v3d_csd", 7)) + i = 3; + if (!strncmp(buf, "v3d_cac", 7)) + i = 4; + + if (i != -1) { + if (gpu_info->last_val[i] == 0) + load[i] = 0.0; + else { + load[i] = runtime; + load[i] -= gpu_info->last_val[i]; + load[i] /= elapsed; + } + gpu_info->last_val[i] = runtime; + } + } + } + + free(buf); + fclose(fp); + + // calculate the max of the five queue values and store in the task array + max = 0.0; + for (i = 0; i < 5; i++) + if (load[i] > max) + max = load[i]; + + return (int)(max * 100); +} + +void gpuinfo_v3d_refresh_dynamic_info(struct gpu_info *_gpu_info) { + struct gpu_info_v3d *gpu_info = container_of(_gpu_info, struct gpu_info_v3d, base); + struct gpuinfo_dynamic_info *dynamic_info = &gpu_info->base.dynamic_info; + + RESET_ALL(dynamic_info->valid); + + nvtop_device *card_dev_copy; + const char *syspath; + nvtop_device_get_syspath(gpu_info->card_device, &syspath); + nvtop_device_new_from_syspath(&card_dev_copy, syspath); + + // GPU usage + int gpu_usage = get_vc_usage(_gpu_info); + SET_GPUINFO_DYNAMIC(dynamic_info, gpu_util_rate, gpu_usage); + + // GPU clock + const char *gt_cur_freq; + if (nvtop_device_get_sysattr_value(card_dev_copy, "gt_cur_freq_mhz", >_cur_freq) >= 0) { + unsigned val = strtoul(gt_cur_freq, NULL, 10); + SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed, val); + } + const char *gt_max_freq; + if (nvtop_device_get_sysattr_value(card_dev_copy, "gt_max_freq_mhz", >_max_freq) >= 0) { + unsigned val = strtoul(gt_max_freq, NULL, 10); + SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed_max, val); + } + + nvtop_device_unref(card_dev_copy); +} + +static void swap_process_cache_for_next_update(struct gpu_info_v3d *gpu_info) { + // Free old cache data and set the cache for the next update + if (gpu_info->last_update_process_cache) { + struct v3d_process_info_cache *cache_entry, *tmp; + HASH_ITER(hh, gpu_info->last_update_process_cache, cache_entry, tmp) { + HASH_DEL(gpu_info->last_update_process_cache, cache_entry); + free(cache_entry); + } + } + gpu_info->last_update_process_cache = gpu_info->current_update_process_cache; + gpu_info->current_update_process_cache = NULL; +} + +void gpuinfo_v3d_get_running_processes(struct gpu_info *_gpu_info) { + // For v3d, we register a fdinfo callback that will fill the gpu_process datastructure of the gpu_info structure + // for us. This avoids going through /proc multiple times per update for multiple GPUs. + struct gpu_info_v3d *gpu_info = container_of(_gpu_info, struct gpu_info_v3d, base); + swap_process_cache_for_next_update(gpu_info); +} From cb26cad4e2c8379524f3729eefeb0c40fd0cbbd6 Mon Sep 17 00:00:00 2001 From: hoream Date: Tue, 27 Aug 2024 00:14:30 +0800 Subject: [PATCH 02/11] add process gpu usage support for raspberrypi --- src/extract_gpuinfo_v3d.c | 138 ++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 79 deletions(-) diff --git a/src/extract_gpuinfo_v3d.c b/src/extract_gpuinfo_v3d.c index 67f23ea..755d41f 100644 --- a/src/extract_gpuinfo_v3d.c +++ b/src/extract_gpuinfo_v3d.c @@ -27,14 +27,15 @@ #include #include #include +#include #include #define HASH_FIND_CLIENT(head, key_ptr, out_ptr) HASH_FIND(hh, head, key_ptr, sizeof(struct unique_cache_id), out_ptr) #define HASH_ADD_CLIENT(head, in_ptr) HASH_ADD(hh, head, client_id, sizeof(struct unique_cache_id), in_ptr) -#define SET_v3d_CACHE(cachePtr, field, value) SET_VALUE(cachePtr, field, value, v3d_cache_) -#define RESET_v3d_CACHE(cachePtr, field) INVALIDATE_VALUE(cachePtr, field, v3d_cache_) -#define v3d_CACHE_FIELD_VALID(cachePtr, field) VALUE_IS_VALID(cachePtr, field, v3d_cache_) +#define SET_V3D_CACHE(cachePtr, field, value) SET_VALUE(cachePtr, field, value, v3d_cache_) +#define RESET_V3D_CACHE(cachePtr, field) INVALIDATE_VALUE(cachePtr, field, v3d_cache_) +#define V3D_CACHE_FIELD_VALID(cachePtr, field) VALUE_IS_VALID(cachePtr, field, v3d_cache_) enum v3d_process_info_cache_valid { v3d_cache_engine_render_valid = 0, @@ -45,9 +46,7 @@ enum v3d_process_info_cache_valid { }; struct __attribute__((__packed__)) unique_cache_id { - unsigned client_id; pid_t pid; - char *pdev; }; struct v3d_process_info_cache { @@ -64,7 +63,8 @@ struct v3d_process_info_cache { struct gpu_info_v3d { struct gpu_info base; - unsigned long long last_timestamp, last_val[5]; + uint64_t last_timestamp; + uint64_t last_runtime; struct nvtop_device *card_device; struct nvtop_device *driver_device; struct v3d_process_info_cache *last_update_process_cache, *current_update_process_cache; // Cached processes info @@ -105,63 +105,72 @@ void gpuinfo_v3d_shutdown(void) { const char *gpuinfo_v3d_last_error_string(void) { return "Err"; } +static void get_pid_usage(struct gpu_process *process_info) { + FILE *fp = fopen("/sys/kernel/debug/dri/0/gpu_pid_usage", "rb"); + + char *buf = NULL; + size_t res = 0; + unsigned long jobs, active; + pid_t pid; + uint64_t runtime; + uint64_t timestamp; + + while (getline(&buf, &res, fp) > 0) { + if (sscanf(buf, "timestamp;%ld;", ×tamp) == 1) { + } else if (sscanf(strchr(buf, ';'), ";%d;%ld;%ld;%ld;", &pid, &jobs, &runtime, &active) == 4) { + if (!strncmp(buf, "v3d_ren", 7) && (pid == process_info->pid || pid == process_info->pid + 10)) { + SET_GPUINFO_PROCESS(process_info, gfx_engine_used, runtime); + free(buf); + fclose(fp); + return; + } + } + } + + SET_GPUINFO_PROCESS(process_info, gfx_engine_used, 0); + free(buf); + fclose(fp); + return; +} + static bool parse_drm_fdinfo_v3d(struct gpu_info *info, FILE *fdinfo_file, struct gpu_process *process_info) { struct gpu_info_v3d *gpu_info = container_of(info, struct gpu_info_v3d, base); + struct unique_cache_id ucid = {.pid = process_info->pid}; - unsigned cid; + struct v3d_process_info_cache *added_cache_entry; + HASH_FIND_CLIENT(gpu_info->current_update_process_cache, &ucid, added_cache_entry); + if (added_cache_entry) + return false; + + get_pid_usage(process_info); nvtop_time current_time; nvtop_get_current_time(¤t_time); - // The v3d driver does not expose compute engine metrics as of yet process_info->type |= gpu_process_graphical; struct v3d_process_info_cache *cache_entry; - struct unique_cache_id ucid = {.client_id = cid, .pid = process_info->pid, .pdev = gpu_info->base.pdev}; HASH_FIND_CLIENT(gpu_info->last_update_process_cache, &ucid, cache_entry); if (cache_entry) { uint64_t time_elapsed = nvtop_difftime_u64(cache_entry->last_measurement_tstamp, current_time); HASH_DEL(gpu_info->last_update_process_cache, cache_entry); if (GPUINFO_PROCESS_FIELD_VALID(process_info, gfx_engine_used) && - v3d_CACHE_FIELD_VALID(cache_entry, engine_render) && - // In some rare occasions, the gfx engine usage reported by the driver is lowering (might be a driver bug) + V3D_CACHE_FIELD_VALID(cache_entry, engine_render) && process_info->gfx_engine_used >= cache_entry->engine_render && process_info->gfx_engine_used - cache_entry->engine_render <= time_elapsed) { SET_GPUINFO_PROCESS( process_info, gpu_usage, busy_usage_from_time_usage_round(process_info->gfx_engine_used, cache_entry->engine_render, time_elapsed)); } - if (GPUINFO_PROCESS_FIELD_VALID(process_info, dec_engine_used) && - v3d_CACHE_FIELD_VALID(cache_entry, engine_video) && - process_info->dec_engine_used >= cache_entry->engine_video && - process_info->dec_engine_used - cache_entry->engine_video <= time_elapsed) { - SET_GPUINFO_PROCESS( - process_info, decode_usage, - busy_usage_from_time_usage_round(process_info->dec_engine_used, cache_entry->engine_video, time_elapsed)); - } - if (GPUINFO_PROCESS_FIELD_VALID(process_info, enc_engine_used) && - v3d_CACHE_FIELD_VALID(cache_entry, engine_video_enhance) && - process_info->enc_engine_used >= cache_entry->engine_video_enhance && - process_info->enc_engine_used - cache_entry->engine_video_enhance <= time_elapsed) { - SET_GPUINFO_PROCESS(process_info, encode_usage, - busy_usage_from_time_usage_round(process_info->enc_engine_used, - cache_entry->engine_video_enhance, time_elapsed)); - } } else { cache_entry = calloc(1, sizeof(*cache_entry)); if (!cache_entry) goto parse_fdinfo_exit; - cache_entry->client_id.client_id = cid; cache_entry->client_id.pid = process_info->pid; - cache_entry->client_id.pdev = gpu_info->base.pdev; } RESET_ALL(cache_entry->valid); if (GPUINFO_PROCESS_FIELD_VALID(process_info, gfx_engine_used)) - SET_v3d_CACHE(cache_entry, engine_render, process_info->gfx_engine_used); - if (GPUINFO_PROCESS_FIELD_VALID(process_info, dec_engine_used)) - SET_v3d_CACHE(cache_entry, engine_video, process_info->dec_engine_used); - if (GPUINFO_PROCESS_FIELD_VALID(process_info, enc_engine_used)) - SET_v3d_CACHE(cache_entry, engine_video_enhance, process_info->enc_engine_used); + SET_V3D_CACHE(cache_entry, engine_render, process_info->gfx_engine_used); cache_entry->last_measurement_tstamp = current_time; HASH_ADD_CLIENT(gpu_info->current_update_process_cache, cache_entry); @@ -177,7 +186,7 @@ static void add_v3d_cards(struct nvtop_device *dev, struct list_head *devices, u const char *driver; nvtop_device_get_driver(parent, &driver); - if (!strcmp(driver, "v3d")) + if (strcmp(driver, "vc4-drm")) return; struct gpu_info_v3d *thisGPU = &gpu_infos[v3d_gpu_count++]; @@ -187,9 +196,9 @@ static void add_v3d_cards(struct nvtop_device *dev, struct list_head *devices, u list_add_tail(&thisGPU->base.list, devices); // Register a fdinfo callback for this GPU processinfo_register_fdinfo_callback(parse_drm_fdinfo_v3d, &thisGPU->base); + thisGPU->last_timestamp = 0; - for (int i = 0; i < 5; i++) - thisGPU->last_val[i] = 0; + thisGPU->last_runtime = 0; (*count)++; } @@ -243,60 +252,33 @@ void gpuinfo_v3d_populate_static_info(struct gpu_info *_gpu_info) { SET_VALID(gpuinfo_device_name_valid, static_info->valid); } -static int get_vc_usage(struct gpu_info *_gpu_info) { +static void set_sum_usage(struct gpu_info *_gpu_info) { struct gpu_info_v3d *gpu_info = container_of(_gpu_info, struct gpu_info_v3d, base); FILE *fp = fopen("/sys/kernel/debug/dri/0/gpu_usage", "rb"); char *buf = NULL; size_t res = 0; unsigned long jobs, active; - unsigned long long timestamp, elapsed, runtime; - float max, load[5]; - int i; + uint64_t timestamp, elapsed, runtime; while (getline(&buf, &res, fp) > 0) { - if (sscanf(buf, "timestamp;%lld;", ×tamp) == 1) { - // use the timestamp line to calculate time since last measurement + if (sscanf(buf, "timestamp;%ld;", ×tamp) == 1) { elapsed = timestamp - gpu_info->last_timestamp; gpu_info->last_timestamp = timestamp; - } else if (sscanf(strchr(buf, ';'), ";%ld;%lld;%ld;", &jobs, &runtime, &active) == 3) { - // depending on which queue is in the line, calculate the percentage of time used since last measurement - // store the current time value for the next calculation - i = -1; - if (!strncmp(buf, "v3d_bin", 7)) - i = 0; - if (!strncmp(buf, "v3d_ren", 7)) - i = 1; - if (!strncmp(buf, "v3d_tfu", 7)) - i = 2; - if (!strncmp(buf, "v3d_csd", 7)) - i = 3; - if (!strncmp(buf, "v3d_cac", 7)) - i = 4; - - if (i != -1) { - if (gpu_info->last_val[i] == 0) - load[i] = 0.0; - else { - load[i] = runtime; - load[i] -= gpu_info->last_val[i]; - load[i] /= elapsed; - } - gpu_info->last_val[i] = runtime; + } else if (sscanf(strchr(buf, ';'), ";%ld;%ld;%ld;", &jobs, &runtime, &active) == 3) { + if (!strncmp(buf, "v3d_ren", 7)) { + int usage = busy_usage_from_time_usage_round(runtime, gpu_info->last_runtime, elapsed); + gpu_info->last_runtime = runtime; + SET_GPUINFO_DYNAMIC(&(gpu_info->base.dynamic_info), gpu_util_rate, usage); + free(buf); + fclose(fp); + return; } } } free(buf); fclose(fp); - - // calculate the max of the five queue values and store in the task array - max = 0.0; - for (i = 0; i < 5; i++) - if (load[i] > max) - max = load[i]; - - return (int)(max * 100); } void gpuinfo_v3d_refresh_dynamic_info(struct gpu_info *_gpu_info) { @@ -310,10 +292,6 @@ void gpuinfo_v3d_refresh_dynamic_info(struct gpu_info *_gpu_info) { nvtop_device_get_syspath(gpu_info->card_device, &syspath); nvtop_device_new_from_syspath(&card_dev_copy, syspath); - // GPU usage - int gpu_usage = get_vc_usage(_gpu_info); - SET_GPUINFO_DYNAMIC(dynamic_info, gpu_util_rate, gpu_usage); - // GPU clock const char *gt_cur_freq; if (nvtop_device_get_sysattr_value(card_dev_copy, "gt_cur_freq_mhz", >_cur_freq) >= 0) { @@ -326,6 +304,8 @@ void gpuinfo_v3d_refresh_dynamic_info(struct gpu_info *_gpu_info) { SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed_max, val); } + set_sum_usage(_gpu_info); + nvtop_device_unref(card_dev_copy); } From 107005d71e4c6842bc5ffeaf0c6c9857852a8f11 Mon Sep 17 00:00:00 2001 From: hoream Date: Tue, 27 Aug 2024 16:37:01 +0800 Subject: [PATCH 03/11] move info read functions to utils and add some info from vcio device --- src/CMakeLists.txt | 1 + src/extract_gpuinfo_v3d.c | 76 ++---------- src/extract_gpuinfo_v3d_utils.c | 212 ++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+), 66 deletions(-) create mode 100644 src/extract_gpuinfo_v3d_utils.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d648f6d..169e112 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -102,6 +102,7 @@ endif() if(V3D_SUPPORT) target_sources(nvtop PRIVATE extract_gpuinfo_v3d.c) + target_sources(nvtop PRIVATE extract_gpuinfo_v3d_utils.c) endif() if(APPLE_SUPPORT) diff --git a/src/extract_gpuinfo_v3d.c b/src/extract_gpuinfo_v3d.c index 755d41f..52a41c2 100644 --- a/src/extract_gpuinfo_v3d.c +++ b/src/extract_gpuinfo_v3d.c @@ -1,8 +1,8 @@ /* * - * Copyright (C) 2022 Maxime Schmitt + * Copyright (C) 2022 Hoream Xiao * - * This file is part of Nvtop and adapted from igt-gpu-tools from v3d Corporation. + * This file is part of Nvtop. * * Nvtop is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,9 +27,13 @@ #include #include #include -#include #include +void set_gpuinfo_from_vcio(struct gpuinfo_dynamic_info *dynamic_info); +void set_mem_info(struct gpuinfo_dynamic_info *dynamic_info); +void set_sum_usage(struct gpuinfo_dynamic_info *dynamic_info); +void get_pid_usage(struct gpu_process *process_info); + #define HASH_FIND_CLIENT(head, key_ptr, out_ptr) HASH_FIND(hh, head, key_ptr, sizeof(struct unique_cache_id), out_ptr) #define HASH_ADD_CLIENT(head, in_ptr) HASH_ADD(hh, head, client_id, sizeof(struct unique_cache_id), in_ptr) @@ -63,8 +67,6 @@ struct v3d_process_info_cache { struct gpu_info_v3d { struct gpu_info base; - uint64_t last_timestamp; - uint64_t last_runtime; struct nvtop_device *card_device; struct nvtop_device *driver_device; struct v3d_process_info_cache *last_update_process_cache, *current_update_process_cache; // Cached processes info @@ -105,34 +107,6 @@ void gpuinfo_v3d_shutdown(void) { const char *gpuinfo_v3d_last_error_string(void) { return "Err"; } -static void get_pid_usage(struct gpu_process *process_info) { - FILE *fp = fopen("/sys/kernel/debug/dri/0/gpu_pid_usage", "rb"); - - char *buf = NULL; - size_t res = 0; - unsigned long jobs, active; - pid_t pid; - uint64_t runtime; - uint64_t timestamp; - - while (getline(&buf, &res, fp) > 0) { - if (sscanf(buf, "timestamp;%ld;", ×tamp) == 1) { - } else if (sscanf(strchr(buf, ';'), ";%d;%ld;%ld;%ld;", &pid, &jobs, &runtime, &active) == 4) { - if (!strncmp(buf, "v3d_ren", 7) && (pid == process_info->pid || pid == process_info->pid + 10)) { - SET_GPUINFO_PROCESS(process_info, gfx_engine_used, runtime); - free(buf); - fclose(fp); - return; - } - } - } - - SET_GPUINFO_PROCESS(process_info, gfx_engine_used, 0); - free(buf); - fclose(fp); - return; -} - static bool parse_drm_fdinfo_v3d(struct gpu_info *info, FILE *fdinfo_file, struct gpu_process *process_info) { struct gpu_info_v3d *gpu_info = container_of(info, struct gpu_info_v3d, base); struct unique_cache_id ucid = {.pid = process_info->pid}; @@ -197,8 +171,6 @@ static void add_v3d_cards(struct nvtop_device *dev, struct list_head *devices, u // Register a fdinfo callback for this GPU processinfo_register_fdinfo_callback(parse_drm_fdinfo_v3d, &thisGPU->base); - thisGPU->last_timestamp = 0; - thisGPU->last_runtime = 0; (*count)++; } @@ -252,35 +224,6 @@ void gpuinfo_v3d_populate_static_info(struct gpu_info *_gpu_info) { SET_VALID(gpuinfo_device_name_valid, static_info->valid); } -static void set_sum_usage(struct gpu_info *_gpu_info) { - struct gpu_info_v3d *gpu_info = container_of(_gpu_info, struct gpu_info_v3d, base); - FILE *fp = fopen("/sys/kernel/debug/dri/0/gpu_usage", "rb"); - - char *buf = NULL; - size_t res = 0; - unsigned long jobs, active; - uint64_t timestamp, elapsed, runtime; - - while (getline(&buf, &res, fp) > 0) { - if (sscanf(buf, "timestamp;%ld;", ×tamp) == 1) { - elapsed = timestamp - gpu_info->last_timestamp; - gpu_info->last_timestamp = timestamp; - } else if (sscanf(strchr(buf, ';'), ";%ld;%ld;%ld;", &jobs, &runtime, &active) == 3) { - if (!strncmp(buf, "v3d_ren", 7)) { - int usage = busy_usage_from_time_usage_round(runtime, gpu_info->last_runtime, elapsed); - gpu_info->last_runtime = runtime; - SET_GPUINFO_DYNAMIC(&(gpu_info->base.dynamic_info), gpu_util_rate, usage); - free(buf); - fclose(fp); - return; - } - } - } - - free(buf); - fclose(fp); -} - void gpuinfo_v3d_refresh_dynamic_info(struct gpu_info *_gpu_info) { struct gpu_info_v3d *gpu_info = container_of(_gpu_info, struct gpu_info_v3d, base); struct gpuinfo_dynamic_info *dynamic_info = &gpu_info->base.dynamic_info; @@ -304,8 +247,9 @@ void gpuinfo_v3d_refresh_dynamic_info(struct gpu_info *_gpu_info) { SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed_max, val); } - set_sum_usage(_gpu_info); - + set_sum_usage(dynamic_info); + set_mem_info(dynamic_info); + set_gpuinfo_from_vcio(dynamic_info); nvtop_device_unref(card_dev_copy); } diff --git a/src/extract_gpuinfo_v3d_utils.c b/src/extract_gpuinfo_v3d_utils.c new file mode 100644 index 0000000..cbd538a --- /dev/null +++ b/src/extract_gpuinfo_v3d_utils.c @@ -0,0 +1,212 @@ +/* + * + * Copyright (C) 2022 Hoream Xiao + * + * This file is part of Nvtop and adapted from the vcgencmd implementation. + * + * Nvtop 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. + * + * Nvtop 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 nvtop. If not, see . + * + */ + +#include "nvtop/extract_gpuinfo_common.h" +#include +#include +#include +#include +#include +#include + +/* + * use ioctl to send mbox property message + */ +#define DEVICE_FILE_NAME "/dev/vcio" +#define MAJOR_NUM 100 +#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) +#define MAX_STRING 1024 +#define GET_GENCMD_RESULT 0x00030080 +void set_gpuinfo_from_vcio(struct gpuinfo_dynamic_info *dynamic_info); +void set_mem_info(struct gpuinfo_dynamic_info *dynamic_info); +void set_sum_usage(struct gpuinfo_dynamic_info *dynamic_info); +void get_pid_usage(struct gpu_process *process_info); + +static uint64_t last_timestamp = 0; +static uint64_t last_runtime = 0; + +static int mbox_property(int file_desc, void *buf) { + int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); + + if (ret_val < 0) { + printf("ioctl_set_msg failed:%d\n", ret_val); + } + return ret_val; +} + +static int mbox_open(void) { + int file_desc; + + // open a char device file used for communicating with kernel mbox driver + file_desc = open(DEVICE_FILE_NAME, 0); + if (file_desc < 0) { + printf("Can't open device file: %s\n", DEVICE_FILE_NAME); + printf("Try creating a device file with: sudo mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM); + } + return file_desc; +} + +static void mbox_close(int file_desc) { close(file_desc); } + +static unsigned gencmd(int file_desc, const char *command, char *result, int result_len) { + int i = 0; + unsigned p[(MAX_STRING >> 2) + 7]; + int len = strlen(command); + // maximum length for command or response + if (len + 1 >= MAX_STRING) { + fprintf(stderr, "gencmd length too long : %d\n", len); + return -1; + } + p[i++] = 0; // size + p[i++] = 0x00000000; // process request + + p[i++] = GET_GENCMD_RESULT; // (the tag id) + p[i++] = MAX_STRING; // buffer_len + p[i++] = 0; // request_len (set to response length) + p[i++] = 0; // error repsonse + + memcpy(p + i, command, len + 1); + i += MAX_STRING >> 2; + + p[i++] = 0x00000000; // end tag + p[0] = i * sizeof *p; // actual size + + mbox_property(file_desc, p); + result[0] = 0; + strncat(result, (const char *)(p + 6), result_len); + + return p[5]; +} + +static void set_gpuinfo_temp(struct gpuinfo_dynamic_info *dynamic_info, int mb, const char *command) { + float temperature = 0; + char result[MAX_STRING] = {}; + + int ret = gencmd(mb, command, result, sizeof result); + if (!ret) { + if (sscanf(result, "temp=%f'C", &temperature) == 1) { + SET_GPUINFO_DYNAMIC(dynamic_info, gpu_temp, (unsigned)temperature); + } + } +} + +static void set_gpuinfo_clock(struct gpuinfo_dynamic_info *dynamic_info, int mb, const char *command) { + unsigned int clock = 0; + char result[MAX_STRING] = {}; + + int ret = gencmd(mb, command, result, sizeof result); + if (!ret) { + if (sscanf(result, "frequency(46)=%u", &clock) == 1) { + SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed, clock >> 20); + } + } +} + +void set_gpuinfo_from_vcio(struct gpuinfo_dynamic_info *dynamic_info) { + int mb = mbox_open(); + set_gpuinfo_temp(dynamic_info, mb, "measure_temp"); + set_gpuinfo_clock(dynamic_info, mb, "measure_clock v3d"); + mbox_close(mb); +} + +void set_mem_info(struct gpuinfo_dynamic_info *dynamic_info) { + FILE *file = fopen("/sys/kernel/debug/dri/0/bo_stats", "r"); + if (file == NULL) { + return; + } + + char line[256]; + unsigned long allocated_bo_size_kb = 0; + unsigned long max_mem_size = 128 << 20; + + while (fgets(line, sizeof(line), file)) { + if (sscanf(line, "allocated bo size (kb): %lu", &allocated_bo_size_kb) == 1) { + break; + } + } + + fclose(file); + + unsigned long allocated_bo_size_bytes = allocated_bo_size_kb << 10; + + if (allocated_bo_size_bytes >= max_mem_size) + max_mem_size = allocated_bo_size_bytes; + + SET_GPUINFO_DYNAMIC(dynamic_info, used_memory, allocated_bo_size_bytes); + SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, max_mem_size); + SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, (uint)(100.0 * allocated_bo_size_bytes / max_mem_size)); +} + +void set_sum_usage(struct gpuinfo_dynamic_info *dynamic_info) { + FILE *fp = fopen("/sys/kernel/debug/dri/0/gpu_usage", "rb"); + + char *buf = NULL; + size_t res = 0; + unsigned long jobs, active; + uint64_t timestamp, elapsed, runtime; + + while (getline(&buf, &res, fp) > 0) { + if (sscanf(buf, "timestamp;%ld;", ×tamp) == 1) { + elapsed = timestamp - last_timestamp; + last_timestamp = timestamp; + } else if (sscanf(strchr(buf, ';'), ";%ld;%ld;%ld;", &jobs, &runtime, &active) == 3) { + if (!strncmp(buf, "v3d_ren", 7)) { + int usage = busy_usage_from_time_usage_round(runtime, last_runtime, elapsed); + last_runtime = runtime; + SET_GPUINFO_DYNAMIC(dynamic_info, gpu_util_rate, usage); + free(buf); + fclose(fp); + return; + } + } + } + + free(buf); + fclose(fp); +} + +void get_pid_usage(struct gpu_process *process_info) { + FILE *fp = fopen("/sys/kernel/debug/dri/0/gpu_pid_usage", "rb"); + + char *buf = NULL; + size_t res = 0; + unsigned long jobs, active; + pid_t pid; + uint64_t runtime; + uint64_t timestamp; + + while (getline(&buf, &res, fp) > 0) { + if (sscanf(buf, "timestamp;%ld;", ×tamp) == 1) { + } else if (sscanf(strchr(buf, ';'), ";%d;%ld;%ld;%ld;", &pid, &jobs, &runtime, &active) == 4) { + if (!strncmp(buf, "v3d_ren", 7) && (pid == process_info->pid || pid == process_info->pid + 10)) { + SET_GPUINFO_PROCESS(process_info, gfx_engine_used, runtime); + free(buf); + fclose(fp); + return; + } + } + } + + SET_GPUINFO_PROCESS(process_info, gfx_engine_used, 0); + free(buf); + fclose(fp); + return; +} From 072e1dbbfb76f6fab3d13a784367b7e485fecbb0 Mon Sep 17 00:00:00 2001 From: hoream Date: Tue, 27 Aug 2024 19:14:51 +0800 Subject: [PATCH 04/11] add decode info for h264 decode --- src/extract_gpuinfo_v3d_utils.c | 39 ++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/extract_gpuinfo_v3d_utils.c b/src/extract_gpuinfo_v3d_utils.c index cbd538a..1dc6fcb 100644 --- a/src/extract_gpuinfo_v3d_utils.c +++ b/src/extract_gpuinfo_v3d_utils.c @@ -42,6 +42,7 @@ void get_pid_usage(struct gpu_process *process_info); static uint64_t last_timestamp = 0; static uint64_t last_runtime = 0; +static uint64_t max_gpu_mem = 0; static int mbox_property(int file_desc, void *buf) { int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); @@ -96,6 +97,29 @@ static unsigned gencmd(int file_desc, const char *command, char *result, int res return p[5]; } +static void set_gpuinfo_max_mem(int mb, const char *command) { + char result[MAX_STRING] = {}; + + int ret = gencmd(mb, command, result, sizeof result); + if (!ret) { + if (sscanf(result, "gpu=%luM", &max_gpu_mem) == 1) { + max_gpu_mem <<= 20; + } + } +} + +static void set_gpuinfo_decode(struct gpuinfo_dynamic_info *dynamic_info, int mb, const char *command) { + unsigned int decode_usage = 0; + char result[MAX_STRING] = {}; + + int ret = gencmd(mb, command, result, sizeof result); + if (!ret) { + if (sscanf(result, "frequency(28)=%u", &decode_usage) == 1) { + SET_GPUINFO_DYNAMIC(dynamic_info, decoder_rate, (unsigned)(100 * (decode_usage / 550006336.0))); + } + } +} + static void set_gpuinfo_temp(struct gpuinfo_dynamic_info *dynamic_info, int mb, const char *command) { float temperature = 0; char result[MAX_STRING] = {}; @@ -124,6 +148,8 @@ void set_gpuinfo_from_vcio(struct gpuinfo_dynamic_info *dynamic_info) { int mb = mbox_open(); set_gpuinfo_temp(dynamic_info, mb, "measure_temp"); set_gpuinfo_clock(dynamic_info, mb, "measure_clock v3d"); + set_gpuinfo_decode(dynamic_info, mb, "measure_clock h264"); + set_gpuinfo_max_mem(mb, "get_mem gpu"); mbox_close(mb); } @@ -135,7 +161,6 @@ void set_mem_info(struct gpuinfo_dynamic_info *dynamic_info) { char line[256]; unsigned long allocated_bo_size_kb = 0; - unsigned long max_mem_size = 128 << 20; while (fgets(line, sizeof(line), file)) { if (sscanf(line, "allocated bo size (kb): %lu", &allocated_bo_size_kb) == 1) { @@ -147,12 +172,14 @@ void set_mem_info(struct gpuinfo_dynamic_info *dynamic_info) { unsigned long allocated_bo_size_bytes = allocated_bo_size_kb << 10; - if (allocated_bo_size_bytes >= max_mem_size) - max_mem_size = allocated_bo_size_bytes; - SET_GPUINFO_DYNAMIC(dynamic_info, used_memory, allocated_bo_size_bytes); - SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, max_mem_size); - SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, (uint)(100.0 * allocated_bo_size_bytes / max_mem_size)); + if (allocated_bo_size_bytes >= max_gpu_mem) { + SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, allocated_bo_size_bytes); + SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, 100); + } else { + SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, max_gpu_mem); + SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, (uint)(100.0 * allocated_bo_size_bytes / max_gpu_mem)); + } } void set_sum_usage(struct gpuinfo_dynamic_info *dynamic_info) { From 834061428f37b79b2a85edd3b2784d9498489218 Mon Sep 17 00:00:00 2001 From: hoream Date: Tue, 27 Aug 2024 23:03:19 +0800 Subject: [PATCH 05/11] organize code --- src/extract_gpuinfo_v3d.c | 61 +++++------ src/extract_gpuinfo_v3d_utils.c | 174 ++++++++++++++++++-------------- 2 files changed, 128 insertions(+), 107 deletions(-) diff --git a/src/extract_gpuinfo_v3d.c b/src/extract_gpuinfo_v3d.c index 52a41c2..a2ec220 100644 --- a/src/extract_gpuinfo_v3d.c +++ b/src/extract_gpuinfo_v3d.c @@ -29,10 +29,14 @@ #include #include -void set_gpuinfo_from_vcio(struct gpuinfo_dynamic_info *dynamic_info); -void set_mem_info(struct gpuinfo_dynamic_info *dynamic_info); -void set_sum_usage(struct gpuinfo_dynamic_info *dynamic_info); -void get_pid_usage(struct gpu_process *process_info); +int mbox_open(void); +void mbox_close(int mb); +void set_debug_files(int card_id); +void set_gpuinfo_from_vcio(struct gpuinfo_dynamic_info *dynamic_info, int mb); +void set_memory_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info); +void set_usage_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info); +void set_pid_usage_gpuinfo(struct gpu_process *process_info); +void set_init_max_memory(int mb); #define HASH_FIND_CLIENT(head, key_ptr, out_ptr) HASH_FIND(hh, head, key_ptr, sizeof(struct unique_cache_id), out_ptr) #define HASH_ADD_CLIENT(head, in_ptr) HASH_ADD(hh, head, client_id, sizeof(struct unique_cache_id), in_ptr) @@ -41,13 +45,7 @@ void get_pid_usage(struct gpu_process *process_info); #define RESET_V3D_CACHE(cachePtr, field) INVALIDATE_VALUE(cachePtr, field, v3d_cache_) #define V3D_CACHE_FIELD_VALID(cachePtr, field) VALUE_IS_VALID(cachePtr, field, v3d_cache_) -enum v3d_process_info_cache_valid { - v3d_cache_engine_render_valid = 0, - v3d_cache_engine_copy_valid, - v3d_cache_engine_video_valid, - v3d_cache_engine_video_enhance_valid, - v3d_cache_process_info_cache_valid_count -}; +enum v3d_process_info_cache_valid { v3d_cache_engine_render_valid = 0, v3d_cache_process_info_cache_valid_count }; struct __attribute__((__packed__)) unique_cache_id { pid_t pid; @@ -56,9 +54,6 @@ struct __attribute__((__packed__)) unique_cache_id { struct v3d_process_info_cache { struct unique_cache_id client_id; uint64_t engine_render; - uint64_t engine_copy; - uint64_t engine_video; - uint64_t engine_video_enhance; nvtop_time last_measurement_tstamp; unsigned char valid[(v3d_cache_process_info_cache_valid_count + CHAR_BIT - 1) / CHAR_BIT]; UT_hash_handle hh; @@ -66,6 +61,8 @@ struct v3d_process_info_cache { struct gpu_info_v3d { struct gpu_info base; + int mb; + int card_id; struct nvtop_device *card_device; struct nvtop_device *driver_device; @@ -102,12 +99,16 @@ void gpuinfo_v3d_shutdown(void) { struct gpu_info_v3d *current = &gpu_infos[i]; nvtop_device_unref(current->card_device); nvtop_device_unref(current->driver_device); + if (current->mb >= 0) + mbox_close(current->mb); } } const char *gpuinfo_v3d_last_error_string(void) { return "Err"; } static bool parse_drm_fdinfo_v3d(struct gpu_info *info, FILE *fdinfo_file, struct gpu_process *process_info) { + if (!fdinfo_file) + return false; struct gpu_info_v3d *gpu_info = container_of(info, struct gpu_info_v3d, base); struct unique_cache_id ucid = {.pid = process_info->pid}; @@ -116,7 +117,7 @@ static bool parse_drm_fdinfo_v3d(struct gpu_info *info, FILE *fdinfo_file, struc if (added_cache_entry) return false; - get_pid_usage(process_info); + set_pid_usage_gpuinfo(process_info); nvtop_time current_time; nvtop_get_current_time(¤t_time); @@ -153,14 +154,14 @@ parse_fdinfo_exit: return true; } -static void add_v3d_cards(struct nvtop_device *dev, struct list_head *devices, unsigned *count) { +static void add_v3d_cards(struct nvtop_device *dev, const char *devname, struct list_head *devices, unsigned *count) { struct nvtop_device *parent; if (nvtop_device_get_parent(dev, &parent) < 0) return; const char *driver; nvtop_device_get_driver(parent, &driver); - if (strcmp(driver, "vc4-drm")) + if (strcmp(driver, "v3d")) return; struct gpu_info_v3d *thisGPU = &gpu_infos[v3d_gpu_count++]; @@ -170,6 +171,10 @@ static void add_v3d_cards(struct nvtop_device *dev, struct list_head *devices, u list_add_tail(&thisGPU->base.list, devices); // Register a fdinfo callback for this GPU processinfo_register_fdinfo_callback(parse_drm_fdinfo_v3d, &thisGPU->base); + thisGPU->mb = mbox_open(); + if (sscanf(devname, "/dev/dri/card%d", &thisGPU->card_id) != 1) + thisGPU->card_id = 0; + set_debug_files(thisGPU->card_id); (*count)++; } @@ -203,7 +208,7 @@ bool gpuinfo_v3d_get_device_handles(struct list_head *devices_list, unsigned *co if (nvtop_device_get_devname(device, &devname) < 0) continue; if (strstr(devname, "/dev/dri/card")) { - add_v3d_cards(device, devices_list, count); + add_v3d_cards(device, devname, devices_list, count); } } @@ -222,6 +227,7 @@ void gpuinfo_v3d_populate_static_info(struct gpu_info *_gpu_info) { snprintf(static_info->device_name, sizeof(static_info->device_name), "%s", dev_name); SET_VALID(gpuinfo_device_name_valid, static_info->valid); + set_init_max_memory(gpu_info->mb); } void gpuinfo_v3d_refresh_dynamic_info(struct gpu_info *_gpu_info) { @@ -235,21 +241,10 @@ void gpuinfo_v3d_refresh_dynamic_info(struct gpu_info *_gpu_info) { nvtop_device_get_syspath(gpu_info->card_device, &syspath); nvtop_device_new_from_syspath(&card_dev_copy, syspath); - // GPU clock - const char *gt_cur_freq; - if (nvtop_device_get_sysattr_value(card_dev_copy, "gt_cur_freq_mhz", >_cur_freq) >= 0) { - unsigned val = strtoul(gt_cur_freq, NULL, 10); - SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed, val); - } - const char *gt_max_freq; - if (nvtop_device_get_sysattr_value(card_dev_copy, "gt_max_freq_mhz", >_max_freq) >= 0) { - unsigned val = strtoul(gt_max_freq, NULL, 10); - SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed_max, val); - } - - set_sum_usage(dynamic_info); - set_mem_info(dynamic_info); - set_gpuinfo_from_vcio(dynamic_info); + set_usage_gpuinfo(dynamic_info); + set_memory_gpuinfo(dynamic_info); + if (gpu_info->mb >= 0) + set_gpuinfo_from_vcio(dynamic_info, gpu_info->mb); nvtop_device_unref(card_dev_copy); } diff --git a/src/extract_gpuinfo_v3d_utils.c b/src/extract_gpuinfo_v3d_utils.c index 1dc6fcb..d59ecce 100644 --- a/src/extract_gpuinfo_v3d_utils.c +++ b/src/extract_gpuinfo_v3d_utils.c @@ -35,17 +35,38 @@ #define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) #define MAX_STRING 1024 #define GET_GENCMD_RESULT 0x00030080 -void set_gpuinfo_from_vcio(struct gpuinfo_dynamic_info *dynamic_info); -void set_mem_info(struct gpuinfo_dynamic_info *dynamic_info); -void set_sum_usage(struct gpuinfo_dynamic_info *dynamic_info); -void get_pid_usage(struct gpu_process *process_info); +#define MAX_DECODER_FREQUENCE 550006336 + +int mbox_open(void); +void mbox_close(int mb); +void set_debug_files(int card_id); +void set_gpuinfo_from_vcio(struct gpuinfo_dynamic_info *dynamic_info, int mb); +void set_memory_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info); +void set_usage_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info); +void set_pid_usage_gpuinfo(struct gpu_process *process_info); +void set_init_max_memory(int mb); static uint64_t last_timestamp = 0; static uint64_t last_runtime = 0; -static uint64_t max_gpu_mem = 0; +static uint64_t max_gpu_memeory_bytes = 128 << 20; -static int mbox_property(int file_desc, void *buf) { - int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf); +static const char measure_temp[] = "measure_temp"; +static const char measure_clock_v3d[] = "measure_clock v3d"; +static const char measure_clock_h264[] = "measure_clock h264"; +static const char get_mem_gpu[] = "get_mem gpu"; + +static char gpu_usage_file[50]; +static char gpu_pid_usage_file[50]; +static char bo_stats_file[50]; + +void set_debug_files(int card_id) { + snprintf(gpu_usage_file, sizeof(gpu_usage_file), "/sys/kernel/debug/dri/%d/gpu_usage", card_id); + snprintf(gpu_pid_usage_file, sizeof(gpu_pid_usage_file), "/sys/kernel/debug/dri/%d/gpu_pid_usage", card_id); + snprintf(bo_stats_file, sizeof(bo_stats_file), "/sys/kernel/debug/dri/%d/bo_stats", card_id); +} + +static int mbox_property(int mb, void *buf) { + int ret_val = ioctl(mb, IOCTL_MBOX_PROPERTY, buf); if (ret_val < 0) { printf("ioctl_set_msg failed:%d\n", ret_val); @@ -53,21 +74,21 @@ static int mbox_property(int file_desc, void *buf) { return ret_val; } -static int mbox_open(void) { - int file_desc; +int mbox_open(void) { + int mb; // open a char device file used for communicating with kernel mbox driver - file_desc = open(DEVICE_FILE_NAME, 0); - if (file_desc < 0) { + mb = open(DEVICE_FILE_NAME, 0); + if (mb < 0) { printf("Can't open device file: %s\n", DEVICE_FILE_NAME); printf("Try creating a device file with: sudo mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM); } - return file_desc; + return mb; } -static void mbox_close(int file_desc) { close(file_desc); } +void mbox_close(int mb) { close(mb); } -static unsigned gencmd(int file_desc, const char *command, char *result, int result_len) { +static unsigned gencmd(int mb, const char *command, char *result, int result_len) { int i = 0; unsigned p[(MAX_STRING >> 2) + 7]; int len = strlen(command); @@ -90,41 +111,43 @@ static unsigned gencmd(int file_desc, const char *command, char *result, int res p[i++] = 0x00000000; // end tag p[0] = i * sizeof *p; // actual size - mbox_property(file_desc, p); + mbox_property(mb, p); result[0] = 0; strncat(result, (const char *)(p + 6), result_len); return p[5]; } -static void set_gpuinfo_max_mem(int mb, const char *command) { +void set_init_max_memory(int mb) { char result[MAX_STRING] = {}; - int ret = gencmd(mb, command, result, sizeof result); + int ret = gencmd(mb, get_mem_gpu, result, sizeof result); if (!ret) { - if (sscanf(result, "gpu=%luM", &max_gpu_mem) == 1) { - max_gpu_mem <<= 20; + if (sscanf(result, "gpu=%luM", &max_gpu_memeory_bytes) == 1) { + max_gpu_memeory_bytes <<= 20; } } } -static void set_gpuinfo_decode(struct gpuinfo_dynamic_info *dynamic_info, int mb, const char *command) { +static unsigned cal_percentage_usage(unsigned usage, unsigned all) { return (unsigned)(100.0 * usage / all + 0.5); } + +static void set_gpuinfo_decode(struct gpuinfo_dynamic_info *dynamic_info, int mb) { unsigned int decode_usage = 0; char result[MAX_STRING] = {}; - int ret = gencmd(mb, command, result, sizeof result); + int ret = gencmd(mb, measure_clock_h264, result, sizeof result); if (!ret) { - if (sscanf(result, "frequency(28)=%u", &decode_usage) == 1) { - SET_GPUINFO_DYNAMIC(dynamic_info, decoder_rate, (unsigned)(100 * (decode_usage / 550006336.0))); - } + if (sscanf(result, "frequency(28)=%u", &decode_usage) == 1) + // divide current frequency by max frequency; usage rate might not be accurate. + SET_GPUINFO_DYNAMIC(dynamic_info, decoder_rate, cal_percentage_usage(decode_usage, MAX_DECODER_FREQUENCE)); } } -static void set_gpuinfo_temp(struct gpuinfo_dynamic_info *dynamic_info, int mb, const char *command) { +static void set_gpuinfo_temp(struct gpuinfo_dynamic_info *dynamic_info, int mb) { float temperature = 0; char result[MAX_STRING] = {}; - int ret = gencmd(mb, command, result, sizeof result); + int ret = gencmd(mb, measure_temp, result, sizeof result); if (!ret) { if (sscanf(result, "temp=%f'C", &temperature) == 1) { SET_GPUINFO_DYNAMIC(dynamic_info, gpu_temp, (unsigned)temperature); @@ -132,11 +155,11 @@ static void set_gpuinfo_temp(struct gpuinfo_dynamic_info *dynamic_info, int mb, } } -static void set_gpuinfo_clock(struct gpuinfo_dynamic_info *dynamic_info, int mb, const char *command) { +static void set_gpuinfo_clock(struct gpuinfo_dynamic_info *dynamic_info, int mb) { unsigned int clock = 0; char result[MAX_STRING] = {}; - int ret = gencmd(mb, command, result, sizeof result); + int ret = gencmd(mb, measure_clock_v3d, result, sizeof result); if (!ret) { if (sscanf(result, "frequency(46)=%u", &clock) == 1) { SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed, clock >> 20); @@ -144,96 +167,99 @@ static void set_gpuinfo_clock(struct gpuinfo_dynamic_info *dynamic_info, int mb, } } -void set_gpuinfo_from_vcio(struct gpuinfo_dynamic_info *dynamic_info) { - int mb = mbox_open(); - set_gpuinfo_temp(dynamic_info, mb, "measure_temp"); - set_gpuinfo_clock(dynamic_info, mb, "measure_clock v3d"); - set_gpuinfo_decode(dynamic_info, mb, "measure_clock h264"); - set_gpuinfo_max_mem(mb, "get_mem gpu"); - mbox_close(mb); +void set_gpuinfo_from_vcio(struct gpuinfo_dynamic_info *dynamic_info, int mb) { + set_gpuinfo_temp(dynamic_info, mb); + set_gpuinfo_clock(dynamic_info, mb); + set_gpuinfo_decode(dynamic_info, mb); } -void set_mem_info(struct gpuinfo_dynamic_info *dynamic_info) { - FILE *file = fopen("/sys/kernel/debug/dri/0/bo_stats", "r"); - if (file == NULL) { +void set_memory_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info) { + FILE *fp = fopen(bo_stats_file, "rb"); + if (fp == NULL) { return; } char line[256]; - unsigned long allocated_bo_size_kb = 0; + uint64_t allocated_bo_size_kb = 0; - while (fgets(line, sizeof(line), file)) { + while (fgets(line, sizeof(line), fp)) { if (sscanf(line, "allocated bo size (kb): %lu", &allocated_bo_size_kb) == 1) { break; } } - fclose(file); + fclose(fp); - unsigned long allocated_bo_size_bytes = allocated_bo_size_kb << 10; + uint64_t allocated_bo_size_bytes = allocated_bo_size_kb << 10; SET_GPUINFO_DYNAMIC(dynamic_info, used_memory, allocated_bo_size_bytes); - if (allocated_bo_size_bytes >= max_gpu_mem) { - SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, allocated_bo_size_bytes); - SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, 100); - } else { - SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, max_gpu_mem); - SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, (uint)(100.0 * allocated_bo_size_bytes / max_gpu_mem)); - } + if (allocated_bo_size_bytes >= max_gpu_memeory_bytes) + max_gpu_memeory_bytes = allocated_bo_size_bytes; + SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, max_gpu_memeory_bytes); + SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, + cal_percentage_usage(allocated_bo_size_bytes, max_gpu_memeory_bytes)); } -void set_sum_usage(struct gpuinfo_dynamic_info *dynamic_info) { - FILE *fp = fopen("/sys/kernel/debug/dri/0/gpu_usage", "rb"); +void set_usage_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info) { + FILE *fp = fopen(gpu_usage_file, "rb"); + + if (fp == NULL) + return; char *buf = NULL; size_t res = 0; - unsigned long jobs, active; + unsigned jobs, active; uint64_t timestamp, elapsed, runtime; while (getline(&buf, &res, fp) > 0) { - if (sscanf(buf, "timestamp;%ld;", ×tamp) == 1) { + if (sscanf(buf, "timestamp;%lu;", ×tamp) == 1) { elapsed = timestamp - last_timestamp; last_timestamp = timestamp; - } else if (sscanf(strchr(buf, ';'), ";%ld;%ld;%ld;", &jobs, &runtime, &active) == 3) { - if (!strncmp(buf, "v3d_ren", 7)) { - int usage = busy_usage_from_time_usage_round(runtime, last_runtime, elapsed); - last_runtime = runtime; - SET_GPUINFO_DYNAMIC(dynamic_info, gpu_util_rate, usage); - free(buf); - fclose(fp); - return; - } + } else if (sscanf(strchr(buf, ';'), ";%u;%lu;%u;", &jobs, &runtime, &active) == 3) { + if (!strncmp(buf, "v3d_render", 10)) + break; } } - free(buf); fclose(fp); + int usage = busy_usage_from_time_usage_round(runtime, last_runtime, elapsed); + last_runtime = runtime; + SET_GPUINFO_DYNAMIC(dynamic_info, gpu_util_rate, usage); + return; } -void get_pid_usage(struct gpu_process *process_info) { - FILE *fp = fopen("/sys/kernel/debug/dri/0/gpu_pid_usage", "rb"); +void set_pid_usage_gpuinfo(struct gpu_process *process_info) { + FILE *fp = fopen(gpu_pid_usage_file, "rb"); + if (fp == NULL) { + return; + } char *buf = NULL; size_t res = 0; - unsigned long jobs, active; + unsigned jobs, active; pid_t pid; uint64_t runtime; uint64_t timestamp; + int min_pid_diff = 100; + uint64_t select_runtime; + while (getline(&buf, &res, fp) > 0) { - if (sscanf(buf, "timestamp;%ld;", ×tamp) == 1) { - } else if (sscanf(strchr(buf, ';'), ";%d;%ld;%ld;%ld;", &pid, &jobs, &runtime, &active) == 4) { - if (!strncmp(buf, "v3d_ren", 7) && (pid == process_info->pid || pid == process_info->pid + 10)) { - SET_GPUINFO_PROCESS(process_info, gfx_engine_used, runtime); - free(buf); - fclose(fp); - return; + if (sscanf(buf, "timestamp;%lu;", ×tamp) == 1) { + } else if (sscanf(strchr(buf, ';'), ";%u;%u;%lu;%u;", &pid, &jobs, &runtime, &active) == 4) { + if (!strncmp(buf, "v3d_render", 10)) { + // gpu_pid_usage_file report wrong pid, this is a workround. + int pid_diff = pid - process_info->pid; + if (pid_diff >= 0 && pid_diff < min_pid_diff) { + select_runtime = runtime; + min_pid_diff = pid_diff; + } } } } - SET_GPUINFO_PROCESS(process_info, gfx_engine_used, 0); free(buf); fclose(fp); + SET_GPUINFO_PROCESS(process_info, gfx_engine_used, select_runtime); return; } From 3ff3fa69ce958a7d2824efca7fe086153396263f Mon Sep 17 00:00:00 2001 From: hoream Date: Tue, 27 Aug 2024 23:24:50 +0800 Subject: [PATCH 06/11] print some debug messages --- src/extract_gpuinfo_v3d.c | 3 ++- src/extract_gpuinfo_v3d_utils.c | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/extract_gpuinfo_v3d.c b/src/extract_gpuinfo_v3d.c index a2ec220..6fb879b 100644 --- a/src/extract_gpuinfo_v3d.c +++ b/src/extract_gpuinfo_v3d.c @@ -227,7 +227,8 @@ void gpuinfo_v3d_populate_static_info(struct gpu_info *_gpu_info) { snprintf(static_info->device_name, sizeof(static_info->device_name), "%s", dev_name); SET_VALID(gpuinfo_device_name_valid, static_info->valid); - set_init_max_memory(gpu_info->mb); + if (gpu_info->mb >= 0) + set_init_max_memory(gpu_info->mb); } void gpuinfo_v3d_refresh_dynamic_info(struct gpu_info *_gpu_info) { diff --git a/src/extract_gpuinfo_v3d_utils.c b/src/extract_gpuinfo_v3d_utils.c index d59ecce..cb20fa7 100644 --- a/src/extract_gpuinfo_v3d_utils.c +++ b/src/extract_gpuinfo_v3d_utils.c @@ -61,8 +61,14 @@ static char bo_stats_file[50]; void set_debug_files(int card_id) { snprintf(gpu_usage_file, sizeof(gpu_usage_file), "/sys/kernel/debug/dri/%d/gpu_usage", card_id); + if (access(gpu_usage_file, F_OK)) + printf("%s is not available.\n", gpu_usage_file); snprintf(gpu_pid_usage_file, sizeof(gpu_pid_usage_file), "/sys/kernel/debug/dri/%d/gpu_pid_usage", card_id); + if (access(gpu_pid_usage_file, F_OK)) + printf("%s is not available.\n", gpu_pid_usage_file); snprintf(bo_stats_file, sizeof(bo_stats_file), "/sys/kernel/debug/dri/%d/bo_stats", card_id); + if (access(bo_stats_file, F_OK)) + printf("%s is not available.\n", bo_stats_file); } static int mbox_property(int mb, void *buf) { From 4e7379580294e6c8214a53ecdeac9827969945d9 Mon Sep 17 00:00:00 2001 From: hoream Date: Tue, 27 Aug 2024 23:33:28 +0800 Subject: [PATCH 07/11] fix strncat warning --- src/extract_gpuinfo_v3d_utils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/extract_gpuinfo_v3d_utils.c b/src/extract_gpuinfo_v3d_utils.c index cb20fa7..e6a811e 100644 --- a/src/extract_gpuinfo_v3d_utils.c +++ b/src/extract_gpuinfo_v3d_utils.c @@ -119,7 +119,9 @@ static unsigned gencmd(int mb, const char *command, char *result, int result_len mbox_property(mb, p); result[0] = 0; - strncat(result, (const char *)(p + 6), result_len); + + size_t available_space = result_len - strlen(result) - 1; + strncat(result, (const char *)(p + 6), available_space); return p[5]; } From f9957886ba9fc472bfe08911358830070a61fdce Mon Sep 17 00:00:00 2001 From: hoream Date: Wed, 28 Aug 2024 00:01:24 +0800 Subject: [PATCH 08/11] add the videocore support documentation --- README.markdown | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 4ecc6f7..ad60511 100644 --- a/README.markdown +++ b/README.markdown @@ -10,7 +10,7 @@ htop-familiar way. Currently supported vendors are AMD (Linux amdgpu driver), Apple (limited M1 & M2 support), Huawei (Ascend), Intel (Linux i915 driver), NVIDIA (Linux -proprietary divers), Qualcomm Adreno (Linux MSM driver). +proprietary divers), Qualcomm Adreno (Linux MSM driver), Broadcom VideoCore (Linux v3d driver). Because a picture is worth a thousand words: @@ -30,6 +30,7 @@ Table of Contents - [Adreno](#adreno) - [Apple](#apple) - [Ascend](#ascend) (only tested on 910B) + - [VideoCore](#videocore) - [Build](#build) - [Distribution Specific Installation Process](#distribution-specific-installation-process) - [Ubuntu / Debian](#ubuntu--debian) @@ -131,6 +132,16 @@ NVTOP supports Ascend (testing on Altas 800 (910B)) by DCMI API (version 6.0.0). Currently, the DCMI only supports limited APIs, missing PCIe generation, tx/rx throughput info, max power draw etc. +### VideoCore + +NVTOP supports VideoCore (testing on raspberrypi 4B). + +Supports GPU frequency, temperature, utilization, per-process utilization, GPU memory usage, and H264 decoding utilization. + +The pid in debugfs might be incorrect, so the per-process utilization might be inaccurate. + +On non-raspberry pi os, you need to use the `linux-rpi` kernel, ensure the presence of the `/dev/vcio` device, and have access permissions to the `/sys/kernel/debug` directory. + Build ----- From a387559596675356d0d3c549b3ccb4fd5585bee6 Mon Sep 17 00:00:00 2001 From: hoream Date: Wed, 28 Aug 2024 00:05:26 +0800 Subject: [PATCH 09/11] fix some typos --- src/extract_gpuinfo_v3d_utils.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/extract_gpuinfo_v3d_utils.c b/src/extract_gpuinfo_v3d_utils.c index e6a811e..0e53065 100644 --- a/src/extract_gpuinfo_v3d_utils.c +++ b/src/extract_gpuinfo_v3d_utils.c @@ -48,7 +48,7 @@ void set_init_max_memory(int mb); static uint64_t last_timestamp = 0; static uint64_t last_runtime = 0; -static uint64_t max_gpu_memeory_bytes = 128 << 20; +static uint64_t max_gpu_memory_bytes = 128 << 20; static const char measure_temp[] = "measure_temp"; static const char measure_clock_v3d[] = "measure_clock v3d"; @@ -109,7 +109,7 @@ static unsigned gencmd(int mb, const char *command, char *result, int result_len p[i++] = GET_GENCMD_RESULT; // (the tag id) p[i++] = MAX_STRING; // buffer_len p[i++] = 0; // request_len (set to response length) - p[i++] = 0; // error repsonse + p[i++] = 0; // error response memcpy(p + i, command, len + 1); i += MAX_STRING >> 2; @@ -131,8 +131,8 @@ void set_init_max_memory(int mb) { int ret = gencmd(mb, get_mem_gpu, result, sizeof result); if (!ret) { - if (sscanf(result, "gpu=%luM", &max_gpu_memeory_bytes) == 1) { - max_gpu_memeory_bytes <<= 20; + if (sscanf(result, "gpu=%luM", &max_gpu_memory_bytes) == 1) { + max_gpu_memory_bytes <<= 20; } } } @@ -201,11 +201,11 @@ void set_memory_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info) { uint64_t allocated_bo_size_bytes = allocated_bo_size_kb << 10; SET_GPUINFO_DYNAMIC(dynamic_info, used_memory, allocated_bo_size_bytes); - if (allocated_bo_size_bytes >= max_gpu_memeory_bytes) - max_gpu_memeory_bytes = allocated_bo_size_bytes; - SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, max_gpu_memeory_bytes); + if (allocated_bo_size_bytes >= max_gpu_memory_bytes) + max_gpu_memory_bytes = allocated_bo_size_bytes; + SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, max_gpu_memory_bytes); SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, - cal_percentage_usage(allocated_bo_size_bytes, max_gpu_memeory_bytes)); + cal_percentage_usage(allocated_bo_size_bytes, max_gpu_memory_bytes)); } void set_usage_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info) { @@ -256,7 +256,7 @@ void set_pid_usage_gpuinfo(struct gpu_process *process_info) { if (sscanf(buf, "timestamp;%lu;", ×tamp) == 1) { } else if (sscanf(strchr(buf, ';'), ";%u;%u;%lu;%u;", &pid, &jobs, &runtime, &active) == 4) { if (!strncmp(buf, "v3d_render", 10)) { - // gpu_pid_usage_file report wrong pid, this is a workround. + // gpu_pid_usage_file report wrong pid, this is a workaround. int pid_diff = pid - process_info->pid; if (pid_diff >= 0 && pid_diff < min_pid_diff) { select_runtime = runtime; From 081a1b0982d47d4dd017e88f7bc8709c63382429 Mon Sep 17 00:00:00 2001 From: hoream Date: Thu, 29 Aug 2024 20:29:45 +0800 Subject: [PATCH 10/11] convert tid to pgid to match the process's pid. --- src/extract_gpuinfo_v3d_utils.c | 48 ++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/extract_gpuinfo_v3d_utils.c b/src/extract_gpuinfo_v3d_utils.c index 0e53065..0b0ec5e 100644 --- a/src/extract_gpuinfo_v3d_utils.c +++ b/src/extract_gpuinfo_v3d_utils.c @@ -20,6 +20,7 @@ */ #include "nvtop/extract_gpuinfo_common.h" +#include #include #include #include @@ -204,8 +205,7 @@ void set_memory_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info) { if (allocated_bo_size_bytes >= max_gpu_memory_bytes) max_gpu_memory_bytes = allocated_bo_size_bytes; SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, max_gpu_memory_bytes); - SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, - cal_percentage_usage(allocated_bo_size_bytes, max_gpu_memory_bytes)); + SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, cal_percentage_usage(allocated_bo_size_bytes, max_gpu_memory_bytes)); } void set_usage_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info) { @@ -236,6 +236,31 @@ void set_usage_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info) { return; } +static pid_t get_tgid_from_tid(pid_t tid) { + char path[40]; + struct dirent *entry; + DIR *dp; + pid_t min_tid = INT_MAX; + + snprintf(path, sizeof(path), "/proc/%d/task/", tid); + + dp = opendir(path); + if (dp == NULL) { + return -1; + } + + while ((entry = readdir(dp)) != NULL) { + int current_tid = atoi(entry->d_name); + if (current_tid > 0 && current_tid < min_tid) { + min_tid = current_tid; + } + } + + closedir(dp); + + return min_tid; +} + void set_pid_usage_gpuinfo(struct gpu_process *process_info) { FILE *fp = fopen(gpu_pid_usage_file, "rb"); if (fp == NULL) { @@ -245,29 +270,26 @@ void set_pid_usage_gpuinfo(struct gpu_process *process_info) { char *buf = NULL; size_t res = 0; unsigned jobs, active; - pid_t pid; + pid_t tid; uint64_t runtime; uint64_t timestamp; - int min_pid_diff = 100; - uint64_t select_runtime; - while (getline(&buf, &res, fp) > 0) { if (sscanf(buf, "timestamp;%lu;", ×tamp) == 1) { - } else if (sscanf(strchr(buf, ';'), ";%u;%u;%lu;%u;", &pid, &jobs, &runtime, &active) == 4) { + } else if (sscanf(strchr(buf, ';'), ";%u;%u;%lu;%u;", &tid, &jobs, &runtime, &active) == 4) { if (!strncmp(buf, "v3d_render", 10)) { - // gpu_pid_usage_file report wrong pid, this is a workaround. - int pid_diff = pid - process_info->pid; - if (pid_diff >= 0 && pid_diff < min_pid_diff) { - select_runtime = runtime; - min_pid_diff = pid_diff; + if (get_tgid_from_tid(tid) == process_info->pid) { + SET_GPUINFO_PROCESS(process_info, gfx_engine_used, runtime); + free(buf); + fclose(fp); + return; } } } } + SET_GPUINFO_PROCESS(process_info, gfx_engine_used, 0); free(buf); fclose(fp); - SET_GPUINFO_PROCESS(process_info, gfx_engine_used, select_runtime); return; } From 41d2fe1f182ec2f1a562dd5cea1a16959714a037 Mon Sep 17 00:00:00 2001 From: hoream Date: Thu, 29 Aug 2024 20:31:13 +0800 Subject: [PATCH 11/11] update README --- README.markdown | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.markdown b/README.markdown index ad60511..95aa05d 100644 --- a/README.markdown +++ b/README.markdown @@ -138,8 +138,6 @@ NVTOP supports VideoCore (testing on raspberrypi 4B). Supports GPU frequency, temperature, utilization, per-process utilization, GPU memory usage, and H264 decoding utilization. -The pid in debugfs might be incorrect, so the per-process utilization might be inaccurate. - On non-raspberry pi os, you need to use the `linux-rpi` kernel, ensure the presence of the `/dev/vcio` device, and have access permissions to the `/sys/kernel/debug` directory. Build