mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 19:27:09 -04:00
mangoapp: GPU memory from KGSL
mangoapp's VRAM figure on a Qualcomm driver comes only from the gpu_mem_total trace event. The tracefs stand-in's event list now declares that event as Android kernels do, and reading the stand-in's events answers with one record carrying the total KGSL reports in /sys/class/kgsl/kgsl/page_alloc, which every Adreno under Android has and the app can read.
This commit is contained in:
1 parent
ab8542e4e5
commit
0cc3fe6bca
1 file changed
+63
-8
@@ -7,20 +7,51 @@
|
||||
* Android mounts tracefs for root and the readtracefs group only, so it always fails here.
|
||||
*
|
||||
* The real calls run first. Only when they fail is the instance placed in an empty directory of
|
||||
* the session's own, where it exists but has no events: the overlay runs, and the one reading it
|
||||
* cannot take shows no GPU memory.
|
||||
* the session's own, and reading its events answers with one gpu_mem_total record carrying the
|
||||
* total KGSL - the GPU driver of every Adreno under Android - reports to the app.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define STANDIN_DIR "/tmp/droiddeck-tracefs"
|
||||
#define KGSL_TOTAL "/sys/class/kgsl/kgsl/page_alloc"
|
||||
|
||||
struct tracefs_instance;
|
||||
struct tep_handle;
|
||||
struct tep_event;
|
||||
|
||||
/* The head of libtraceevent 1.8's struct tep_record (the version tools/mangoapp/packages.txt pins). */
|
||||
struct tep_record {
|
||||
unsigned long long ts, offset;
|
||||
long long missed_events;
|
||||
int record_size, size;
|
||||
void *data;
|
||||
int cpu, ref_count, locked;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/* The event as Android kernels declare it (trace/events/gpu_mem.h). */
|
||||
static const char gpu_mem_total_format[] =
|
||||
"name: gpu_mem_total\n"
|
||||
"ID: 1\n"
|
||||
"format:\n"
|
||||
"\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
|
||||
"\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
|
||||
"\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;\tsigned:0;\n"
|
||||
"\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
|
||||
"\n"
|
||||
"\tfield:uint32_t gpu_id;\toffset:8;\tsize:4;\tsigned:0;\n"
|
||||
"\tfield:uint32_t pid;\toffset:12;\tsize:4;\tsigned:0;\n"
|
||||
"\tfield:uint64_t size;\toffset:16;\tsize:8;\tsigned:0;\n"
|
||||
"\n"
|
||||
"print fmt: \"gpu_id=%u pid=%u size=%llu\", REC->gpu_id, REC->pid, REC->size\n";
|
||||
|
||||
static struct tracefs_instance *standin;
|
||||
|
||||
struct tracefs_instance *tracefs_instance_create(const char *name) {
|
||||
struct tracefs_instance *(*real)(const char *) = dlsym(RTLD_NEXT, "tracefs_instance_create");
|
||||
@@ -33,14 +64,38 @@ struct tracefs_instance *tracefs_instance_create(const char *name) {
|
||||
mkdir(STANDIN_DIR "/instances", 0700);
|
||||
snprintf(path, sizeof(path), STANDIN_DIR "/instances/%s", name);
|
||||
mkdir(path, 0700);
|
||||
return alloc(STANDIN_DIR, name);
|
||||
return standin = alloc(STANDIN_DIR, name);
|
||||
}
|
||||
|
||||
/* The stand-in has no event formats to read, so it gets an empty handle rather than none. */
|
||||
struct tep_handle *tracefs_local_events(const char *tracing_dir) {
|
||||
struct tep_handle *(*real)(const char *) = dlsym(RTLD_NEXT, "tracefs_local_events");
|
||||
struct tep_handle *tep = real ? real(tracing_dir) : NULL;
|
||||
if (tep || !tracing_dir || strncmp(tracing_dir, STANDIN_DIR "/", sizeof(STANDIN_DIR))) return tep;
|
||||
if (!tracing_dir || strncmp(tracing_dir, STANDIN_DIR "/", sizeof(STANDIN_DIR))) {
|
||||
struct tep_handle *(*real)(const char *) = dlsym(RTLD_NEXT, "tracefs_local_events");
|
||||
return real ? real(tracing_dir) : NULL;
|
||||
}
|
||||
struct tep_handle *(*tep_alloc)(void) = dlsym(RTLD_DEFAULT, "tep_alloc");
|
||||
return tep_alloc ? tep_alloc() : NULL;
|
||||
int (*parse)(struct tep_handle *, const char *, unsigned long, const char *) = dlsym(RTLD_DEFAULT, "tep_parse_event");
|
||||
struct tep_handle *tep = tep_alloc ? tep_alloc() : NULL;
|
||||
if (tep && parse) parse(tep, gpu_mem_total_format, sizeof(gpu_mem_total_format) - 1, "gpu_mem");
|
||||
return tep;
|
||||
}
|
||||
|
||||
int tracefs_iterate_raw_events(struct tep_handle *tep, struct tracefs_instance *instance, void *cpus, int cpu_size,
|
||||
int (*callback)(struct tep_event *, struct tep_record *, int, void *), void *context) {
|
||||
if (!standin || instance != standin) {
|
||||
int (*real)(struct tep_handle *, struct tracefs_instance *, void *, int,
|
||||
int (*)(struct tep_event *, struct tep_record *, int, void *), void *) =
|
||||
dlsym(RTLD_NEXT, "tracefs_iterate_raw_events");
|
||||
return real ? real(tep, instance, cpus, cpu_size, callback, context) : -1;
|
||||
}
|
||||
struct tep_event *(*find)(struct tep_handle *, int) = dlsym(RTLD_DEFAULT, "tep_find_event");
|
||||
struct tep_event *event = find && tep ? find(tep, 1) : NULL;
|
||||
unsigned long long total;
|
||||
FILE *f = fopen(KGSL_TOTAL, "r");
|
||||
int have = f && fscanf(f, "%llu", &total) == 1;
|
||||
if (f) fclose(f);
|
||||
if (!event || !have || !callback) return -1;
|
||||
struct { uint16_t type; uint8_t flags, preempt; int32_t common_pid; uint32_t gpu_id, pid; uint64_t size; } data = {
|
||||
.type = 1, .size = total};
|
||||
struct tep_record record = {.record_size = sizeof(data), .size = sizeof(data), .data = &data};
|
||||
return callback(event, &record, 0, context);
|
||||
}
|
||||
Reference in new issue
Block a user