/* * test_mem_trace.c — MeowOS 字库加载内存驻留量测(宿主导出单测)。 * * 目的:精确量化 meow_font_load / meow_font_unload / meow_font_reload_tier: * (1) load 过程中堆峰值(含 PNG 解码中间:文件缓冲 + idat + 原始扫描行 + 打包面); * (2) load 后稳态驻留(plane + glyphs); * (3) unload 后 live 归零(free 真正归还、无碎片驻留)。 * * 机制:-Wl,--wrap=malloc/free/calloc/realloc 把 meow_font.c 的分配纳入统计。 * __wrap_* 用「指针→字节数」记账表精确统计 live / peak(无论 libc 是否支持 usable_size)。 * * 用法: * gcc -I. -O0 -g test_mem_trace.c meow_font.c -lz \ * -Wl,--wrap=malloc -Wl,--wrap=free -Wl,--wrap=calloc -Wl,--wrap=realloc \ * -o test_mem_trace * ./test_mem_trace * 退出码 0=全过;非 0=某断言失败。 */ #define _GNU_SOURCE #include "meow_font.h" #include #include #include #include #include /* ---------------- 指针→大小 记账表 ---------------- */ #define TREESZ 4096 static struct { void *p; size_t sz; } g_tab[TREESZ]; static size_t g_live = 0, g_peak = 0; static int g_tracking = 0; static void note_add(void *p, size_t sz) { if (!g_tracking || !p) return; g_live += sz; if (g_live > g_peak) g_peak = g_live; for (int i = 0; i < TREESZ; i++) if (!g_tab[i].p) { g_tab[i].p = p; g_tab[i].sz = sz; return; } } static void note_sub(void *p) { if (!g_tracking || !p) return; for (int i = 0; i < TREESZ; i++) { if (g_tab[i].p == p) { g_live -= g_tab[i].sz; g_tab[i].p = NULL; g_tab[i].sz = 0; return; } } } static size_t note_size(void *p) { if (!p) return 0; for (int i = 0; i < TREESZ; i++) if (g_tab[i].p == p) return g_tab[i].sz; return 0; } void *__real_malloc(size_t n); void *__real_calloc(size_t n, size_t s); void *__real_realloc(void *p, size_t n); void __real_free(void *p); void *__wrap_malloc(size_t n) { void *p = __real_malloc(n); if (p) note_add(p, n); return p; } void *__wrap_calloc(size_t n, size_t s) { void *p = __real_calloc(n, s); if (p) note_add(p, n * s); return p; } void *__wrap_realloc(void *p, size_t n) { if (!p) { void *np = __real_realloc(NULL, n); if (np) note_add(np, n); return np; } size_t old = note_size(p); void *np = __real_realloc(p, n); if (np) { note_sub(p); note_add(np, n); } else { /* realloc 失败:原指针仍有效,表项保留 */ } (void)old; return np; } void __wrap_free(void *p) { if (p) note_sub(p); __real_free(p); } static void reset_peak(void) { g_peak = g_live; } static int g_fail = 0; #define CHECK(cond, msg) do { \ if (!(cond)) { g_fail++; fprintf(stderr, "FAIL: %s (live=%zu peak=%zu)\n", msg, g_live, g_peak); } \ else printf("ok : %s\n", msg); \ } while (0) static double MiB(size_t b) { return (double)b / 1048576.0; } static double KiB(size_t b) { return (double)b / 1024.0; } int main(int argc, char **argv) { if (argc < 3) { fprintf(stderr, "usage: %s \n", argv[0]); return 2; } const char *png = argv[1], *idx = argv[2]; g_tracking = 1; reset_peak(); printf("== meow_font 内存量测(精确指针记账)==\n"); printf("png=%s idx=%s\n", png, idx); int r = meow_font_load(png, idx); CHECK(r == 0, "meow_font_load ok"); printf(" >> load 后稳态驻留 live = %.2f MiB (%.2f KiB)\n", MiB(g_live), KiB(g_live)); printf(" >> load 期间堆峰值 peak = %.2f MiB (%.2f KiB)\n", MiB(g_peak), KiB(g_peak)); static uint16_t fb[480 * 320]; meow_font_set_fb(480, 320, 480 * 2); printf(" drawtext smoke...\n"); meow_font_drawtext(fb, 0, 0, "中文测试 Abc 123,", 0xFFFF, 28); meow_font_drawtext(fb, 0, 60, "试渲染第20号 face 中文", 0xF800, 20); printf(" drawtext 后 live = %.2f MiB\n", MiB(g_live)); /* 反复 load/unload 验证无泄漏累增(每次 load 后的 live 应一致,unload 后归零) */ size_t live_after_load[3]; for (int k = 0; k < 3; k++) { meow_font_unload(); if (g_live != 0) { printf(" [iter %d] unload 后 live=%zu (非0! -> leak)\n", k, g_live); } meow_font_load(png, idx); live_after_load[k] = g_live; } printf(" 三次 load 后 live = %.2f / %.2f / %.2f MiB\n", MiB(live_after_load[0]), MiB(live_after_load[1]), MiB(live_after_load[2])); CHECK(live_after_load[0] == live_after_load[1] && live_after_load[1] == live_after_load[2], "三轮回 load/unload 稳定(无泄漏累增)"); meow_font_unload(); printf(" >> unload 后 live = %.2f MiB (峰值 %.2f MiB)\n", MiB(g_live), MiB(g_peak)); CHECK(g_live == 0, "unload 后 live 归零(free 真正归还)"); printf("\nRESULT: %s\n", g_fail ? "FAIL" : "PASS"); return g_fail ? 1 : 0; }