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:
xXJSONDeruloXx committed 2026-09-28 12:28:45 -04:00
1 parent ab8542e4e5
commit 0cc3fe6bca
1 file changed
+63 -8
+63 -8
View File
@@ -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);
}