feat(watchdog): M52 资源压力可观测 + multi_font 日志去刷屏 + audiod bind 失败即退
- watchdog(新 wd_resguard.h + meow_watchdog.c):每 10s 采 /proc/meminfo (MemFree/MemAvailable)、进程总数、单父进程子进程数前 5 名;过阈值时向 MeowOS/logs 打 RESGUARD 告警,带等级/命中规则/进程名+PID 名单。零 fork、零堆分配、 不发任何信号(只报警)。阈值依据 = 真机实测 56872kB / 空闲 100 进程 / maxch=11, 推导写在 wd_resguard.h 文件头;日志只在等级跃迁或同态每 5 分钟复述。 - launcherd:multi_font 那行「每 2s 无条件打一条」改为只在配置签名跃迁时打 (新 mf_logsig.h 纯函数 + test_mf_logsig 宿主单测,接进 build_launcher.sh test)。 真机 6603 行日志里该行占 5297 行(80%)。 - audiod:io bind 失败不再只记一行就 return(旧行为让主线程 watchdog 空转 720s、 客户端全 ECONNREFUSED),改为明确报错 + g_stop 干净退出;仅动 io_thread 一个 hunk。
This commit is contained in:
1 parent
f3d7b386b2
commit
166b32d0e5
8 files changed
+755
-4
No files matched your search
@@ -49,6 +49,14 @@ run_tests() {
|
||||
rc=1
|
||||
fi
|
||||
|
||||
# M52:多字体日志跃迁签名(纯头文件逻辑,无设备/无 launcher 依赖)
|
||||
echo "== host test: test_mf_logsig =="
|
||||
if "$HOSTCC" -I. -Wall -Wextra test_mf_logsig.c -o "$TMPD/test_mf_logsig"; then
|
||||
"$TMPD/test_mf_logsig" || rc=1
|
||||
else
|
||||
rc=1
|
||||
fi
|
||||
|
||||
if [ $rc -eq 0 ]; then echo "== ALL TESTS PASS =="; else echo "!! TESTS FAILED"; fi
|
||||
return $rc
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* font_theme.c — MeowOS launcher 拆分模块(LAUNCHER_SPLIT_PLAN 拆分)。*/
|
||||
/* 本文件由拆分工具从 meow_launcherd.c 按职责搬移;逻辑与原文 0-diff(仅 static 可见性按跨模块需求调整)。*/
|
||||
#include "meow_launcher_internal.h"
|
||||
#include "mf_logsig.h" /* M52:多字体日志只在配置签名跃迁时打(纯函数,宿主单测) */
|
||||
|
||||
/* M2.6 字重结构 + M2.8 三级定案(FONT_SYSTEM):卡内字体 =
|
||||
* <卡根>/MeowOS/fonts/<family>/<weight>/(两级,向后兼容)或
|
||||
@@ -282,6 +283,10 @@ fallback:
|
||||
* meow_font_config_multifont 统一处理)。仅当全局字库已装载(mfont_ready / slot0)时生效;
|
||||
* 关或未装载 → 单字体(on=0),零额外开销。调用时机:启动 meow_font_ensure() 之后,以及
|
||||
* 设置页变更 display.multi_font / font.class_* / font.file。 */
|
||||
/* M52:上一次打过日志的配置签名(含 valid 位:签名本身可能恰好是 0,不能拿 0 当「没打过」)。 */
|
||||
static uint64_t mfont_log_sig;
|
||||
static int mfont_log_sig_valid;
|
||||
|
||||
void meow_font_reload_multifont(void)
|
||||
{
|
||||
static const char *cls_keys[MEOW_FCLS_COUNT] = {
|
||||
@@ -318,7 +323,17 @@ void meow_font_reload_multifont(void)
|
||||
}
|
||||
}
|
||||
int distinct = 0, truncated = 0;
|
||||
if (meow_font_config_multifont(on, gpng, gidx, cfg, &distinct, &truncated) != 0)
|
||||
int rc = meow_font_config_multifont(on, gpng, gidx, cfg, &distinct, &truncated);
|
||||
/* M52(日志刷屏):本函数被主循环 hotplug 轮询每 2s 调一次,配置绝大多数周期不变。
|
||||
* 旧实现无条件打 "multi_font: enabled..."(真机 6603 行日志里 5297 行 = 80%),
|
||||
* 现在只在「配置签名跃迁」时打 —— 首次、global 字体变、任一类字体变、on 变、
|
||||
* distinct/truncated 变。签名相同 = 行为相同 = 无新信息,静默。 */
|
||||
const char *csig[MEOW_FCLS_COUNT];
|
||||
for (int c = 0; c < MEOW_FCLS_COUNT; c++) csig[c] = cfg[c].png;
|
||||
uint64_t sig = mf_logsig(on, gpng, csig, MEOW_FCLS_COUNT, rc == 0 ? distinct : -1, truncated);
|
||||
/* 无变化(签名相同)= 行为相同 = 无新信息:静默,配置本身已生效。 */
|
||||
if (!mf_logsig_changed(&mfont_log_sig, &mfont_log_sig_valid, sig)) return;
|
||||
if (rc != 0)
|
||||
logmsg("multi_font: config failed -> single-font");
|
||||
else if (truncated)
|
||||
logmsg("multi_font: WARN >%d 套不同字体(会增内存),已按上限截断", distinct);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/* mf_logsig.h — 多字体配置的「日志跃迁签名」(纯函数,宿主可单测)。
|
||||
*
|
||||
* 为什么需要它(M52):meow_font_reload_multifont() 由 launcher 主循环的 hotplug 轮询
|
||||
* 每 2s 调一次(main_menu.c),而多字体配置在绝大多数周期里毫无变化。旧实现无条件打
|
||||
* `multi_font: enabled, %d distinct fonts (global=%s)`,真机 meow_20261111_154540.log
|
||||
* 6603 行里 5297 行是它(80%,~291KB/406KB),把事故现场唯一的信号淹没了。
|
||||
*
|
||||
* 修法不是删日志(首次启用/用户改字体必须留痕),而是把它绑到**配置签名**上:签名变了
|
||||
* 才打。签名覆盖所有能改变多字体行为的输入 ——
|
||||
* on(display.multi_font)、全局字体 png(font.file → 生效档)、6 个 font.class_* 的
|
||||
* 解析后绝对路径、distinct(实际驻留套数)、truncated(超预算截断)。
|
||||
* 因此「字体集合或 global font 变化」必然换签名,纯粹的时间流逝不会。
|
||||
*/
|
||||
#ifndef MF_LOGSIG_H
|
||||
#define MF_LOGSIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MF_LOGSIG_SEED 14695981039346656037ULL /* FNV-1a 64 offset basis */
|
||||
#define MF_LOGSIG_PRIME 1099511628211ULL
|
||||
|
||||
/* 把一段字符串折进签名。字段之间补分隔符,防 ("ab","c") 与 ("a","bc") 撞同一值。
|
||||
* NULL 与 "" 视为同一(配置项为空 = 该类别用全局字体)。 */
|
||||
static inline uint64_t mf_logsig_mix(uint64_t h, const char *s)
|
||||
{
|
||||
if (!s) s = "";
|
||||
while (*s) {
|
||||
h ^= (uint64_t)(unsigned char)*s++;
|
||||
h *= MF_LOGSIG_PRIME;
|
||||
}
|
||||
h ^= 0x1f; /* 字段分隔(0x1f = US,不会出现在路径里) */
|
||||
h *= MF_LOGSIG_PRIME;
|
||||
return h;
|
||||
}
|
||||
|
||||
/* 多字体配置签名。ncls = 类别数(MEOW_FCLS_COUNT=6),cpng[c] 可为 NULL。
|
||||
* distinct < 0 表示 config 失败(此时 distinct 无意义,用 -1 编码「失败」)。 */
|
||||
static inline uint64_t mf_logsig(int on, const char *gpng,
|
||||
const char *const *cpng, int ncls,
|
||||
int distinct, int truncated)
|
||||
{
|
||||
char num[24];
|
||||
uint64_t h = MF_LOGSIG_SEED;
|
||||
snprintf(num, sizeof num, "%d", on ? 1 : 0);
|
||||
h = mf_logsig_mix(h, num);
|
||||
h = mf_logsig_mix(h, gpng);
|
||||
for (int c = 0; c < ncls; c++)
|
||||
h = mf_logsig_mix(h, (cpng && cpng[c]) ? cpng[c] : "");
|
||||
snprintf(num, sizeof num, "%d:%d", distinct, truncated ? 1 : 0);
|
||||
h = mf_logsig_mix(h, num);
|
||||
return h;
|
||||
}
|
||||
|
||||
/* 日志门控:首次(*valid=0)或签名变化 → 返回 1 并记下;同一签名重复调用 → 0。
|
||||
* 调用方持有 *last / *valid(文件级静态),因此这就是「只在跃迁时打日志」的全部判定。 */
|
||||
static inline int mf_logsig_changed(uint64_t *last, int *valid, uint64_t sig)
|
||||
{
|
||||
if (*valid && sig == *last) return 0;
|
||||
*last = sig;
|
||||
*valid = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* MF_LOGSIG_H */
|
||||
@@ -0,0 +1,102 @@
|
||||
/* test_mf_logsig.c — 宿主单测:多字体「日志跃迁签名」与门控(M52 刷屏整改)。
|
||||
*
|
||||
* 背景:meow_font_reload_multifont() 被 launcher 主循环每 2s 调一次,旧实现每次无条件
|
||||
* 打 `multi_font: enabled, ...`(真机 5297/6603 行 = 80%)。整改后只在配置签名变化时打。
|
||||
* 本测试守两件事:
|
||||
* (1) 签名对**所有**能改变行为的输入敏感(on / global / 各类别 / distinct / truncated);
|
||||
* (2) 输入不变时签名恒定 —— 即 60s 空转(30 次调用)只产生 1 条日志。
|
||||
*
|
||||
* 编译运行:gcc -I. -Wall -Wextra test_mf_logsig.c -o test_mf_logsig && ./test_mf_logsig
|
||||
* (不依赖 launcher 其它模块 / 不依赖设备;纯头文件逻辑)
|
||||
*/
|
||||
#include "mf_logsig.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NCLS 6
|
||||
|
||||
static int g_pass = 0, g_fail = 0;
|
||||
|
||||
static void check(const char *name, int got, int want)
|
||||
{
|
||||
if (got == want) { g_pass++; printf("PASS %s\n", name); }
|
||||
else { g_fail++; printf("FAIL %s: got %d want %d\n", name, got, want); }
|
||||
}
|
||||
|
||||
/* 真机 settings.conf 的多字体形态(2026-11-11):
|
||||
* display.multi_font = 1
|
||||
* font.file = MeowOS/fonts/ztest/regular/font.png → global = ztest
|
||||
* font.class_num / font.class_latin = MeowOS/fonts/google_sans/regular/font.png
|
||||
* → distinct = 2(ztest + google_sans) */
|
||||
#define G_ZTEST "/usr/data/mnt/sd_0/MeowOS/fonts/ztest/regular/font.png"
|
||||
#define C_GOOGLE "/usr/data/mnt/sd_0/MeowOS/fonts/google_sans/regular/font.png"
|
||||
#define C_MISANS "/usr/data/mnt/sd_0/MeowOS/fonts/misans/regular/font.png"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* ---- 1. 真机形态:同输入 → 同签名(1000 次不变,这就是「空转不打日志」的根基)---- */
|
||||
const char *base[NCLS] = { NULL, NULL, C_GOOGLE, C_GOOGLE, NULL, NULL };
|
||||
uint64_t s0 = mf_logsig(1, G_ZTEST, base, NCLS, 2, 0);
|
||||
int stable = 1;
|
||||
for (int i = 0; i < 1000; i++)
|
||||
if (mf_logsig(1, G_ZTEST, base, NCLS, 2, 0) != s0) stable = 0;
|
||||
check("same config -> same sig (1000x)", stable, 1);
|
||||
|
||||
/* ---- 2. 每一条输入变化都必须换签名 ---- */
|
||||
check("on 1->0 changes sig", mf_logsig(0, G_ZTEST, base, NCLS, 2, 0) != s0, 1);
|
||||
check("global font changes sig",
|
||||
mf_logsig(1, "/usr/data/mnt/sd_0/MeowOS/fonts/default/regular/font.png",
|
||||
base, NCLS, 2, 0) != s0, 1);
|
||||
{
|
||||
const char *c[NCLS] = { NULL, NULL, C_MISANS, C_GOOGLE, NULL, NULL };
|
||||
check("class_num changes sig", mf_logsig(1, G_ZTEST, c, NCLS, 2, 0) != s0, 1);
|
||||
}
|
||||
check("distinct 2->1 changes sig", mf_logsig(1, G_ZTEST, base, NCLS, 1, 0) != s0, 1);
|
||||
check("truncated 0->1 changes sig", mf_logsig(1, G_ZTEST, base, NCLS, 2, 1) != s0, 1);
|
||||
check("config-fail (-1) changes sig", mf_logsig(1, G_ZTEST, base, NCLS, -1, 0) != s0, 1);
|
||||
|
||||
/* ---- 3. 空配置项形态:NULL 与 "" 等价(settings 空值 = 该类回退全局)---- */
|
||||
{
|
||||
const char *a[NCLS] = { NULL, NULL, NULL, NULL, NULL, NULL };
|
||||
const char *b[NCLS] = { "", "", "", "", "", "" };
|
||||
check("NULL == \"\"", mf_logsig(1, G_ZTEST, a, NCLS, 1, 0) ==
|
||||
mf_logsig(1, G_ZTEST, b, NCLS, 1, 0), 1);
|
||||
check("null cpng array tolerated", mf_logsig(1, G_ZTEST, NULL, NCLS, 1, 0) > 0, 1);
|
||||
}
|
||||
|
||||
/* ---- 4. 字段边界不串味:("ab","c") 与 ("a","bc") 不能撞 ---- */
|
||||
{
|
||||
const char *x[NCLS] = { "ab", "c", NULL, NULL, NULL, NULL };
|
||||
const char *y[NCLS] = { "a", "bc", NULL, NULL, NULL, NULL };
|
||||
check("field boundary no collision",
|
||||
mf_logsig(1, G_ZTEST, x, NCLS, 2, 0) != mf_logsig(1, G_ZTEST, y, NCLS, 2, 0), 1);
|
||||
}
|
||||
|
||||
/* ---- 5. 门控:60s 空转(2s 一次 × 30)只打 1 条;改配置再打 1 条;改回来再打 1 条 ---- */
|
||||
{
|
||||
uint64_t last = 0;
|
||||
int valid = 0, emits = 0;
|
||||
for (int i = 0; i < 30; i++) { /* 首轮 + 29 轮空转 */
|
||||
uint64_t sig = mf_logsig(1, G_ZTEST, base, NCLS, 2, 0);
|
||||
if (mf_logsig_changed(&last, &valid, sig)) emits++;
|
||||
}
|
||||
check("60s idle @2s -> exactly 1 log line", emits, 1);
|
||||
{
|
||||
const char *c[NCLS] = { NULL, NULL, C_MISANS, C_GOOGLE, NULL, NULL };
|
||||
uint64_t sig = mf_logsig(1, G_ZTEST, c, NCLS, 2, 0);
|
||||
if (mf_logsig_changed(&last, &valid, sig)) emits++;
|
||||
}
|
||||
check("class change -> 2nd log line", emits, 2);
|
||||
for (int i = 0; i < 30; i++) { /* 再空转 60s:不应再增长 */
|
||||
uint64_t sig = mf_logsig(1, G_ZTEST, base, NCLS, 2, 0);
|
||||
if (mf_logsig_changed(&last, &valid, sig)) emits++;
|
||||
}
|
||||
check("back to original -> 3rd line, then silent", emits, 3);
|
||||
/* 真实行数:旧实现 60s = 30 行(每 2s 一条),新实现 60s = 0 行(稳定后) */
|
||||
printf(" (旧实现 60s 空转 = 30 行;新实现稳态 = 0 行,本用例全程 %d 行)\n", emits);
|
||||
}
|
||||
|
||||
printf("\n%d pass, %d fail\n", g_pass, g_fail);
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
Reference in new issue
Block a user