Merge pull request #315 from xiaohuirong/master

Add support for the Raspberry Pi VideoCore GPU.
This commit is contained in:
Maxime Schmitt
2024-11-03 14:15:58 +01:00
committed by GitHub
5 changed files with 583 additions and 2 deletions

View File

@@ -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)

View File

@@ -9,8 +9,8 @@ accelerators. It can handle multiple GPUs and print information about them in a
htop-familiar way.
Currently supported vendors are AMD (Linux amdgpu driver), Apple (limited M1 &
M2 support), Huawei (Ascend), Intel (Linux i915 or Xe driver), NVIDIA (Linux
proprietary divers), Qualcomm Adreno (Linux MSM driver).
M2 support), Huawei (Ascend), Intel (Linux i915/Xe drivers), NVIDIA (Linux
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)
@@ -132,6 +133,14 @@ 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.
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
-----

View File

@@ -100,6 +100,11 @@ if(INTEL_SUPPORT)
target_sources(nvtop PRIVATE extract_gpuinfo_intel.c)
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)
target_sources(nvtop PRIVATE extract_gpuinfo_apple.m)
target_link_libraries(nvtop PRIVATE

270
src/extract_gpuinfo_v3d.c Normal file
View File

@@ -0,0 +1,270 @@
/*
*
* Copyright (C) 2022 Hoream Xiao <horeamx@gmail.com>
*
* 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
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "nvtop/device_discovery.h"
#include "nvtop/extract_gpuinfo_common.h"
#include "nvtop/extract_processinfo_fdinfo.h"
#include "nvtop/time.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <uthash.h>
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)
#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_process_info_cache_valid_count };
struct __attribute__((__packed__)) unique_cache_id {
pid_t pid;
};
struct v3d_process_info_cache {
struct unique_cache_id client_id;
uint64_t engine_render;
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;
int mb;
int card_id;
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);
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};
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;
set_pid_usage_gpuinfo(process_info);
nvtop_time current_time;
nvtop_get_current_time(&current_time);
process_info->type |= gpu_process_graphical;
struct v3d_process_info_cache *cache_entry;
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) &&
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));
}
} else {
cache_entry = calloc(1, sizeof(*cache_entry));
if (!cache_entry)
goto parse_fdinfo_exit;
cache_entry->client_id.pid = process_info->pid;
}
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);
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, 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, "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->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)++;
}
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, devname, 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);
if (gpu_info->mb >= 0)
set_init_max_memory(gpu_info->mb);
}
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);
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);
}
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);
}

View File

@@ -0,0 +1,295 @@
/*
*
* Copyright (C) 2022 Hoream Xiao <horeamx@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "nvtop/extract_gpuinfo_common.h"
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
/*
* 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
#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_memory_bytes = 128 << 20;
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);
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) {
int ret_val = ioctl(mb, IOCTL_MBOX_PROPERTY, buf);
if (ret_val < 0) {
printf("ioctl_set_msg failed:%d\n", ret_val);
}
return ret_val;
}
int mbox_open(void) {
int mb;
// open a char device file used for communicating with kernel mbox driver
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 mb;
}
void mbox_close(int mb) { close(mb); }
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);
// 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 response
memcpy(p + i, command, len + 1);
i += MAX_STRING >> 2;
p[i++] = 0x00000000; // end tag
p[0] = i * sizeof *p; // actual size
mbox_property(mb, p);
result[0] = 0;
size_t available_space = result_len - strlen(result) - 1;
strncat(result, (const char *)(p + 6), available_space);
return p[5];
}
void set_init_max_memory(int mb) {
char result[MAX_STRING] = {};
int ret = gencmd(mb, get_mem_gpu, result, sizeof result);
if (!ret) {
if (sscanf(result, "gpu=%luM", &max_gpu_memory_bytes) == 1) {
max_gpu_memory_bytes <<= 20;
}
}
}
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, measure_clock_h264, result, sizeof result);
if (!ret) {
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) {
float temperature = 0;
char result[MAX_STRING] = {};
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);
}
}
}
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, 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);
}
}
}
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_memory_gpuinfo(struct gpuinfo_dynamic_info *dynamic_info) {
FILE *fp = fopen(bo_stats_file, "rb");
if (fp == NULL) {
return;
}
char line[256];
uint64_t allocated_bo_size_kb = 0;
while (fgets(line, sizeof(line), fp)) {
if (sscanf(line, "allocated bo size (kb): %lu", &allocated_bo_size_kb) == 1) {
break;
}
}
fclose(fp);
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_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));
}
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 jobs, active;
uint64_t timestamp, elapsed, runtime;
while (getline(&buf, &res, fp) > 0) {
if (sscanf(buf, "timestamp;%lu;", &timestamp) == 1) {
elapsed = timestamp - last_timestamp;
last_timestamp = timestamp;
} 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;
}
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) {
return;
}
char *buf = NULL;
size_t res = 0;
unsigned jobs, active;
pid_t tid;
uint64_t runtime;
uint64_t timestamp;
while (getline(&buf, &res, fp) > 0) {
if (sscanf(buf, "timestamp;%lu;", &timestamp) == 1) {
} else if (sscanf(strchr(buf, ';'), ";%u;%u;%lu;%u;", &tid, &jobs, &runtime, &active) == 4) {
if (!strncmp(buf, "v3d_render", 10)) {
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);
return;
}