- M7 launcher 重写 5 期(虚拟化宫格/meow_theme 单一权威/横幅动画/自定义 PIN 键盘) - OOM 通用化(cachegov 上收 SDK)、自动切歌根治、meowpanel jv_free 泄漏修复、Hello 滚动卡顿修复 - ncm 歌词 UI / 媒体通知封面 / 封面分辨率 640 - 5 bug 修复:ncm 后台化 UI 叠层+点击穿透、再进 ncm 桌面叠层、播放页新旧 UI 叠层抢刷脏区(meow_ui_gfx 恢复边沿整帧重画)、PIN 亮屏亮度跳(keys.c meow_panel_on 按 settings_brightness_clamped 恢复)、通知中心封面显歌单封面(cover.c save_cover_raw 加 is_song + 命中块守卫) - 四 App 统一重链新 meow_ui.a 51caee4a;M6.19=0.8.19 固件 2d2331f2 推卡自刷验收全绿
202 lines
7.0 KiB
C
202 lines
7.0 KiB
C
/* play/mem_watch.c — 边播边监控**取证**实现(接口见 mem_watch.h;agent 32bit 大文件 OOM 取证)。
|
||
*
|
||
* 归属演进(2026-09-02 定案,ARCHITECTURE_PRINCIPLES ③混合):**降载决策上收通用层**——
|
||
* A(DONTNEED 治理)+ C(/proc/meminfo Cached 监控)分别归 SDK meow_cachegov / meow_svc_perf;
|
||
* 本模块退化为「RssAnon 取证」:低频读 /proc/self/status(优先 RssAnon,缺失回退 VmRSS),
|
||
* 记录匿名内存峰值,**仅供日志**。不再驱动下载/预取降载(那由 meow_cachegov_download_paused
|
||
* / prefetch_paused 查询)。**绝不打断解码/播放**。
|
||
*
|
||
* 阈值滞后(仅用于取证 level 标注):
|
||
* kb >= CRITICAL → level=2;kb >= HIGH 且当前<2 → level=1;kb <= LOW → level=0;其它保持。
|
||
* 本模块自含:只依赖 pthread + meow_log(SDK 日志),不依赖 core/app 其它 TU。
|
||
*/
|
||
#include "mem_watch.h"
|
||
#include "../sdk/include/meow_ui_log.h"
|
||
|
||
#include <pthread.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
|
||
/* 默认阈值(设备缺省;宿主单测用 env/force 桩覆盖) */
|
||
#define MEM_WATCH_PERIOD_MS 500
|
||
#define MEM_WATCH_HIGH_KB (30 * 1024)
|
||
#define MEM_WATCH_LOW_KB (20 * 1024)
|
||
#define MEM_WATCH_CRITICAL_KB (40 * 1024)
|
||
|
||
struct mw_cfg {
|
||
int period_ms;
|
||
long high_kb;
|
||
long low_kb;
|
||
long critical_kb;
|
||
};
|
||
|
||
static struct mw_cfg g_cfg = {
|
||
.period_ms = MEM_WATCH_PERIOD_MS,
|
||
.high_kb = MEM_WATCH_HIGH_KB,
|
||
.low_kb = MEM_WATCH_LOW_KB,
|
||
.critical_kb = MEM_WATCH_CRITICAL_KB,
|
||
};
|
||
|
||
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
|
||
static pthread_t g_thr;
|
||
static int g_thr_on;
|
||
static volatile int g_quit;
|
||
static long g_rss_anon_kb; /* 最近一次采样(KB;-1=不可读) */
|
||
static long g_peak_song_kb; /* 本曲匿名峰值(song_begin 清零) */
|
||
static int g_level; /* NCM_MEM_LVL_*(仅取证日志) */
|
||
static long g_forced_kb = -1; /* 宿主桩 ≥0 时替代真读 */
|
||
|
||
/* env 覆盖阈值:非法值回落默认。 */
|
||
static int env_int(const char *k, int def)
|
||
{
|
||
const char *e = getenv(k);
|
||
if (!e || !e[0]) return def;
|
||
int v = atoi(e);
|
||
return v > 0 ? v : def;
|
||
}
|
||
static long env_long(const char *k, long def)
|
||
{
|
||
const char *e = getenv(k);
|
||
if (!e || !e[0]) return def;
|
||
long v = atol(e);
|
||
return v > 0 ? v : def;
|
||
}
|
||
|
||
/* 读匿名 RSS(KB)。优先 RssAnon;缺则回退 VmRSS;都无 → -1。 */
|
||
static long read_rss_kb(void)
|
||
{
|
||
pthread_mutex_lock(&g_mu);
|
||
long forced = g_forced_kb;
|
||
pthread_mutex_unlock(&g_mu);
|
||
if (forced >= 0) return forced;
|
||
|
||
FILE *fp = fopen("/proc/self/status", "r");
|
||
if (!fp) return -1;
|
||
char line[256];
|
||
long anon = -1, rss = -1;
|
||
while (fgets(line, sizeof line, fp))
|
||
if (!strncmp(line, "RssAnon:", 8)) anon = atol(line + 8);
|
||
else if (rss < 0 && !strncmp(line, "VmRSS:", 6)) rss = atol(line + 6);
|
||
fclose(fp);
|
||
return anon >= 0 ? anon : rss;
|
||
}
|
||
|
||
static const char *level_str(int lvl)
|
||
{
|
||
switch (lvl) {
|
||
case NCM_MEM_LVL_HIGH: return "high";
|
||
case NCM_MEM_LVL_CRITICAL: return "critical";
|
||
default: return "normal";
|
||
}
|
||
}
|
||
|
||
static void *watch_thread(void *arg)
|
||
{
|
||
(void)arg;
|
||
while (!g_quit) {
|
||
long kb = read_rss_kb();
|
||
int lvl;
|
||
pthread_mutex_lock(&g_mu);
|
||
g_rss_anon_kb = kb;
|
||
if (kb > g_peak_song_kb) g_peak_song_kb = kb;
|
||
long hi = g_cfg.high_kb, lo = g_cfg.low_kb, cr = g_cfg.critical_kb;
|
||
/* 滞后三档(仅取证 level 标注) */
|
||
if (kb >= cr) lvl = NCM_MEM_LVL_CRITICAL;
|
||
else if (kb >= hi) lvl = (g_level == NCM_MEM_LVL_CRITICAL) ? NCM_MEM_LVL_CRITICAL : NCM_MEM_LVL_HIGH;
|
||
else if (kb <= lo) lvl = NCM_MEM_LVL_NORMAL;
|
||
else lvl = g_level; /* 滞后带:保持 */
|
||
if (lvl != g_level) {
|
||
meow_log(MEOW_LOG_INFO,
|
||
"[mem] rss_anon=%ldKB peak=%ldKB level=%s (H=%ldK/L=%ldK/C=%ldK)",
|
||
kb, g_peak_song_kb, level_str(lvl), hi, lo, cr);
|
||
}
|
||
g_level = lvl;
|
||
pthread_mutex_unlock(&g_mu);
|
||
usleep((useconds_t)g_cfg.period_ms * 1000u);
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
int mem_watch_start(void)
|
||
{
|
||
pthread_mutex_lock(&g_mu);
|
||
if (g_thr_on) { pthread_mutex_unlock(&g_mu); return 0; }
|
||
/* 开关(默认开;NCM_MEM_WATCH_ENABLED=0/off 关闭监控,行为退化为旧版零侵入) */
|
||
const char *en = getenv("NCM_MEM_WATCH_ENABLED");
|
||
if (en && (!en[0] || !strcmp(en, "0") || !strcmp(en, "off") || !strcmp(en, "false"))) {
|
||
pthread_mutex_unlock(&g_mu);
|
||
meow_log(MEOW_LOG_INFO, "[mem] watch disabled (NCM_MEM_WATCH_ENABLED=%s)", en);
|
||
return 0;
|
||
}
|
||
/* 阈值 env 覆盖(宿主单测) */
|
||
g_cfg.period_ms = env_int("NCM_MEM_WATCH_PERIOD_MS", MEM_WATCH_PERIOD_MS);
|
||
g_cfg.high_kb = env_long("NCM_MEM_WATCH_HIGH_KB", MEM_WATCH_HIGH_KB);
|
||
g_cfg.low_kb = env_long("NCM_MEM_WATCH_LOW_KB", MEM_WATCH_LOW_KB);
|
||
g_cfg.critical_kb = env_long("NCM_MEM_WATCH_CRITICAL_KB", MEM_WATCH_CRITICAL_KB);
|
||
g_quit = 0;
|
||
pthread_attr_t a;
|
||
pthread_attr_init(&a);
|
||
pthread_attr_setstacksize(&a, (size_t)(128 * 1024)); /* 同 ncm_thread_create 128KB(内存瘦身,OOM 修复) */
|
||
int rc = pthread_create(&g_thr, &a, watch_thread, NULL);
|
||
pthread_attr_destroy(&a);
|
||
if (rc != 0) { pthread_mutex_unlock(&g_mu); return -1; }
|
||
g_thr_on = 1;
|
||
pthread_mutex_unlock(&g_mu);
|
||
meow_log(MEOW_LOG_INFO,
|
||
"[mem] watch started period=%dms H=%ldK L=%ldK C=%ldK (RssAnon 优先)",
|
||
g_cfg.period_ms, g_cfg.high_kb, g_cfg.low_kb, g_cfg.critical_kb);
|
||
return 0;
|
||
}
|
||
|
||
void mem_watch_stop(void)
|
||
{
|
||
g_quit = 1;
|
||
pthread_mutex_lock(&g_mu);
|
||
if (g_thr_on) { g_thr_on = 0; pthread_detach(g_thr); }
|
||
pthread_mutex_unlock(&g_mu);
|
||
}
|
||
|
||
int mem_watch_level(void)
|
||
{
|
||
pthread_mutex_lock(&g_mu);
|
||
int v = g_level;
|
||
pthread_mutex_unlock(&g_mu);
|
||
return v;
|
||
}
|
||
|
||
long mem_watch_rss_anon_kb(void)
|
||
{
|
||
pthread_mutex_lock(&g_mu);
|
||
long v = g_rss_anon_kb;
|
||
pthread_mutex_unlock(&g_mu);
|
||
return v;
|
||
}
|
||
|
||
void mem_watch_song_begin(void)
|
||
{
|
||
long kb = read_rss_kb(); /* 起播即采样当前内存,本曲峰值从当前起算 */
|
||
pthread_mutex_lock(&g_mu);
|
||
g_peak_song_kb = kb;
|
||
pthread_mutex_unlock(&g_mu);
|
||
meow_log(MEOW_LOG_INFO, "[mem] song begin rss_anon=%ldKB", kb);
|
||
}
|
||
|
||
/* 播完/切歌释放钩子之后调用:落本曲峰值 + 重置计数。 */
|
||
void mem_watch_song_end(void)
|
||
{
|
||
pthread_mutex_lock(&g_mu);
|
||
long peak = g_peak_song_kb;
|
||
g_peak_song_kb = g_rss_anon_kb;
|
||
pthread_mutex_unlock(&g_mu);
|
||
meow_log(MEOW_LOG_INFO, "[mem] song end peak_anon=%ldKB (released)", peak);
|
||
}
|
||
|
||
void mem_watch_set_forced_kb(long kb)
|
||
{
|
||
pthread_mutex_lock(&g_mu);
|
||
g_forced_kb = kb;
|
||
pthread_mutex_unlock(&g_mu);
|
||
}
|