Files
MeowOS-For-R3P2/patch_v2/ncm_app/app/app.c
T
AskaEth bcc016e50e fix(ncm): 播放页歌词解析栈溢出(SIGSEGV)修复 —— lyric_parse 栈上 227KB 临时数组超 128KB 线程栈
根因(真机 sig=11,RA 落 lyric_parse / pc 在 lib):
app/logic/lyric.c lyric_parse() 在栈上定义
  long ttmp[NCM_LYRIC_LINES=600]; char txt[600][192]; char tl[600][192]
  = ~227KB 栈帧,远超 ncm_thread_create 统一设的 NCM_THREAD_STACK=128KB。
播放页 enter → player_manager_load_lyric → lyric_worker(128KB 线程栈) → lyric_parse → 栈溢出 SIGSEGV。
崩溃仅在「播放页前台 + 拉取歌词成功 + 解析」时触发,故与播放进度无关、autoplay 仅后台起播不崩。

修法:排序临时数组移到堆(按 s_n 计,≤600 行,峰值 ~230KB 瞬态),栈帧回到 KB 级。

复现/定位(/dev 调试 + 真机):
- 真机通过 amd 恢复路径:/tmp/ncm_autoplay 起播缓存曲,app_on_tick 于 UI 线程调 player_play_song
  打开播放页(复现用户点曲路径;autoplay_ui_consume 承接)。原崩溃:pid sig=11 dur=3683ms;
  修复后:连续两次 play:start 无崩溃、60s+ 存活、无 crash_report。
- app.c 增 async-signal-safe SIGSEGV/SIGBUS/SIGILL 处理器(MIPS mcontext_t.pc + gregs ra/sp/fp
  → stderr + crash_report.txt),供 addr2line/链接布局定位。

验证:stream/player_flow/window/search/cache_evict sim 全 PASS;test_ncm_core host + qemu-mipsel
PASS;build_ncm.sh -Wall -Wextra 0 warning;ncm_host NCM_PAGE=player/home 离屏渲染 exit=0。
2026-09-02 09:53:34 +08:00

214 lines
9.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* app.c — dev.player.ncm 应用装配层:main + 组装 + 事件分发。
*
* 职责(对齐 hello_app/app.c):main 只做初始化/建页/跑事件循环/清理;把 meow_ui_run 的平铺输入
* 回调(key/tap/swipe/tick/touch)路由到当前活动页。页面切换统一走 meow_view_page_push/pop/replace。
* 播放线程(player_manager)+ 媒体会话 IPC 线程(ipc_media)在 main 起动、退出时停。
*
* 铁律(APPS.md §3.4.1):物理键禁映射——on_key 不处理任何键语义;返回=左缘左滑/返回钮/request_exit。
* 左缘返回:播放页→歌单→主页→退出(登录页→退出)。
*/
#include "ncm_app_internal.h"
#include <signal.h>
#include <ucontext.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
ncm_app_state_t g_st;
ncm_play_run_t g_play = { .mu = PTHREAD_MUTEX_INITIALIZER,
.state = NCM_PC_STOPPED, .cur_song = -1 };
volatile int g_app_quit = 0;
volatile int g_net_ready = 0;
int g_host_mock = 0;
static meow_view *g_login_view, *g_home_view, *g_pl_view, *g_plr_view, *g_set_view,
*g_search_view;
static void on_sigterm(int s) { (void)s; meow_ui_request_exit(); }
/* SIGSEGV/SIGBUS/SIGILL 定位处理器(真机复现调试用):**必须 async-signal-safe**(只 write+_exit,
* 手写十六进制格式化,不用 snprintf/sprintf——后者内部取锁,在信号上下文可能自锁死)。把
* si_addr + 中断点 pc + ra/sp/fp(MIPS O32:mcontext_t.pc / gregs[31 ra][29 sp][30 fp])
* 写 stderr(amsd 捕获)+ 固定文件,配合 nm/链接布局定位崩溃函数。 */
static void fatal_sig_report(int sig, siginfo_t *si, void *ctx)
{
ucontext_t *uc = (ucontext_t *)ctx;
unsigned long long v = 0;
unsigned char b[192];
int o = 0;
const char *tag = (sig == SIGSEGV) ? "SIGSEGV" : (sig == SIGBUS ? "SIGBUS" : "SIGILL");
#define FATAL_PUTS(s) do { const char *_p = (s); while (*_p) b[o++] = (unsigned char)*_p++; } while (0)
#define FATAL_HEX(x) do { unsigned long long _v = (unsigned long long)(x); \
int _sh; \
for (_sh = 28; _sh >= 0; _sh -= 4) b[o++] = \
(unsigned char)"0123456789abcdef"[(_v >> _sh) & 15]; } while (0)
#define FATAL_STR(h, x) do { FATAL_PUTS(h); FATAL_HEX(x); } while (0)
FATAL_PUTS("\nFATAL "); FATAL_PUTS(tag);
FATAL_PUTS(" sig="); FATAL_HEX(sig);
v = (unsigned long long)(uintptr_t)si->si_addr; FATAL_STR(" addr=", v);
v = uc->uc_mcontext.pc; FATAL_STR(" pc=", v);
v = uc->uc_mcontext.gregs[31]; FATAL_STR(" ra=", v);
v = uc->uc_mcontext.gregs[29]; FATAL_STR(" sp=", v);
v = uc->uc_mcontext.gregs[30]; FATAL_STR(" fp=", v);
FATAL_PUTS("\n");
#undef FATAL_PUTS
#undef FATAL_HEX
#undef FATAL_STR
(void)write(2, b, (size_t)o);
int fd = open("/usr/data/mnt/sd_0/MeowOS/logs/dev.player.ncm/crash_report.txt",
O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd >= 0) { (void)write(fd, b, (size_t)o); close(fd); }
_exit(128 + sig);
}
static void fatal_sig_install(void)
{
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_sigaction = fatal_sig_report;
sa.sa_flags = SA_SIGINFO; /* 不置 SA_NODEFER:防信号内重入 */
sigemptyset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
}
/* ============================================================ */
/* 事件分发:meow_ui_run 平铺回调 → 当前页 */
/* ============================================================ */
static void app_on_key(int keycode, int is_down)
{
(void)keycode; (void)is_down; /* 物理键系统语义;App 不映射(§3.4.1) */
}
static void app_on_tap(int x, int y)
{
if (login_is_active()) { login_on_tap(x, y); return; }
if (player_is_active()) { player_on_tap(x, y); return; }
if (settings_is_active()) { settings_on_tap(x, y); return; }
if (playlist_is_active()) { playlist_on_tap(x, y); return; }
if (search_is_active()) { search_on_tap(x, y); return; }
home_on_tap(x, y);
}
static void app_on_swipe(int dir, int dx, int dy)
{
(void)dx;
if (dir == MEOW_SWIPE_LEFT) {
/* 搜索历史模式:左滑删单条(§23);未消费才走上/下一层返回语义。 */
if (search_is_active() && search_swipe_left()) return;
if (player_is_active()) { meow_view_page_pop(); return; }
if (settings_is_active()) { meow_view_page_pop(); return; }
if (playlist_is_active()) { meow_view_page_pop(); return; }
if (search_is_active()) { meow_view_page_pop(); return; }
/* home / login = 根 → 退出 */
meow_log(MEOW_LOG_INFO, "exit:swipe");
meow_ui_request_exit();
return;
}
if (home_is_active()) { home_swipe(dir, dy); return; }
if (playlist_is_active()) { playlist_swipe(dir, dy); return; }
if (search_is_active()) { search_swipe(dir, dy); return; }
if (player_is_active()) { player_on_swipe(dir, dy); return; }
}
static void app_on_touch_down(int x, int y)
{
if (player_is_active()) player_touch_down(x, y); /* §22 中部横滑页切换跟踪 */
else if (home_is_active()) home_touch_down(x, y);
else if (playlist_is_active()) playlist_touch_down(x, y);
else if (settings_is_active()) settings_touch_down(x, y);
else if (search_is_active()) search_touch_down(x, y);
}
static void app_on_touch_move(int x, int y)
{
if (player_is_active()) player_touch_move(x, y);
else if (home_is_active()) home_touch_move(x, y);
else if (playlist_is_active()) playlist_touch_move(x, y);
else if (settings_is_active()) settings_touch_move(x, y);
else if (search_is_active()) search_touch_move(x, y);
}
static void app_on_swipe_progress(int x_start, int dx)
{
meow_ui_swipe_hint(x_start + dx);
}
static void app_on_tick(void)
{
uint64_t now = meow_ui_time_ms();
autoplay_maybe(); /* 真机联调 hook:/tmp/ncm_autoplay 存在则自动播已缓存曲目(一次性) */
/* autoplay 已在后台线程起播缓存曲 → 在此(UI 线程)打开播放页,复现用户点曲播放路径 */
{
int ai = autoplay_ui_consume();
if (ai >= 0) player_play_song(ai);
}
if (login_is_active()) { login_tick(now); return; }
if (player_is_active()) { player_tick(now); return; }
if (search_is_active()) { search_tick(now); return; }
if (home_is_active()) { home_tick(now); }
if (playlist_is_active()) { playlist_tick(now); }
chrome_tick();
}
/* ============================================================ */
/* main */
/* ============================================================ */
int main(void)
{
meow_log(MEOW_LOG_INFO, "start");
meow_ui_opts o = { APP_W, APP_H };
if (meow_ui_init(&o) != 0) { meow_log(MEOW_LOG_ERROR, "init failed"); return 1; }
/* 主题侦听:登记真实回调(旧代码 pass NULL 无效),系统明暗切换 → ncm_on_theme 实时换肤。
* 初始同步一次 g_dark(listener 只在变化时被调,初次状态需显式对齐)。 */
g_dark = (meow_theme_get_mode() == MEOW_THEME_DARK);
meow_theme_add_listener(ncm_on_theme);
signal(SIGTERM, on_sigterm);
fatal_sig_install(); /* SIGSEGV/SIGBUS/SIGILL 写崩溃地址定位(真机复现调试) */
/* 应用数据/核心库上下文(cookie/deviceId/cache/设置) */
appstate_init();
/* M2.82-联调修复:设备无系统 CA 库(真机 rc=-5 TLS 错误根因)——App 自带 Mozilla CA bundle,
* 显式指给 core HTTP 层(保持 verify=1,不降级为不校验) */
ncm_http_set_verify_default(1, "/usr/data/mnt/sd_0/MeowAPPs/dev.player.ncm/cacert.pem");
/* 播放 + 媒体会话线程(UI 不卡;无 launcher socket 时 ipc 优雅跳过) */
player_manager_start();
ipc_media_start();
g_net_ready = 1;
/* 建六页 view */
g_login_view = login_view_create();
g_home_view = home_view_create();
g_pl_view = playlist_view_create();
g_plr_view = player_view_create();
g_set_view = settings_view_create();
g_search_view = search_view_create();
/* 进入根页:已登录→home,否则→login */
meow_view_page_push(g_st.logged_in ? ACT_HOME : ACT_LOGIN);
meow_ui_events ev = {
app_on_key, app_on_tap, app_on_swipe, app_on_tick, app_on_swipe_progress,
app_on_touch_down, app_on_touch_move,
};
meow_ui_run(&ev);
/* 清理:停线程 → 释放页 */
g_app_quit = 1;
player_manager_stop_thread();
ipc_media_stop();
meow_view_free(g_set_view);
meow_view_free(g_plr_view);
meow_view_free(g_pl_view);
meow_view_free(g_home_view);
meow_view_free(g_login_view);
meow_view_free(g_search_view);
meow_ui_shutdown();
meow_log(MEOW_LOG_INFO, "exit");
return 0;
}