diff --git a/patch_v2/ncm_app/app/app.c b/patch_v2/ncm_app/app/app.c index dd01947..6c1c349 100644 --- a/patch_v2/ncm_app/app/app.c +++ b/patch_v2/ncm_app/app/app.c @@ -9,6 +9,11 @@ */ #include "ncm_app_internal.h" #include +#include +#include +#include +#include +#include ncm_app_state_t g_st; ncm_play_run_t g_play = { .mu = PTHREAD_MUTEX_INITIALIZER, @@ -22,6 +27,53 @@ static meow_view *g_login_view, *g_home_view, *g_pl_view, *g_plr_view, *g_set_vi 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 平铺回调 → 当前页 */ /* ============================================================ */ @@ -89,6 +141,11 @@ 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; } @@ -110,6 +167,7 @@ int main(void) 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(); diff --git a/patch_v2/ncm_app/app/logic/appstate.c b/patch_v2/ncm_app/app/logic/appstate.c index 92e4747..196b042 100644 --- a/patch_v2/ncm_app/app/logic/appstate.c +++ b/patch_v2/ncm_app/app/logic/appstate.c @@ -644,6 +644,22 @@ int appstate_current_playing(void) #define AUTOPLAY_TRIGGER "/tmp/ncm_autoplay" #define AUTOPLAY_SCAN_PL 3 +/* 真机崩溃复现扩展:autoplay 在后台线程起播后,把「打开播放页」动作交给 UI 线程 + * (meow_view_page_push/view_enter 非线程安全,UI 栈只能 UI 线程驱动)。worker 写 + * g_autoplay_* 标志,app_on_tick 每 tick 用 autoplay_ui_consume() 取走并调 player_play_song, + * 从而完整复现用户「点曲 → 播放页渲染(封面/歌词/动画/进度)」路径。仅 /tmp/ncm_autoplay 存在时激活。 */ +static volatile int g_autoplay_goto_player = 0; +static int g_autoplay_play_idx = -1; + +int autoplay_ui_consume(void) /* UI 线程取走「已缓存起播」标志,返回曲目 idx(无则 -1) */ +{ + if (!g_autoplay_goto_player) return -1; + g_autoplay_goto_player = 0; + int i = g_autoplay_play_idx; + g_autoplay_play_idx = -1; + return i; +} + /* 播放单个歌单首页中第一首已缓存曲目。命中播放返回 1,否则 0。 */ static int autoplay_try_playlist(int pl) { @@ -666,7 +682,9 @@ static int autoplay_try_playlist(int pl) g_st.active_playlist = pl; meow_log(MEOW_LOG_INFO, "autoplay: playlist[%d] song[%d] id=%s cached -> play", pl, i, s.id); - player_manager_play(i); + /* 交给 UI 线程打开播放页(模拟用户点曲播放),避免后台线程调 UI 栈 */ + g_autoplay_play_idx = i; + g_autoplay_goto_player = 1; return 1; } } diff --git a/patch_v2/ncm_app/app/logic/lyric.c b/patch_v2/ncm_app/app/logic/lyric.c index a4129dc..0f92ce6 100644 --- a/patch_v2/ncm_app/app/logic/lyric.c +++ b/patch_v2/ncm_app/app/logic/lyric.c @@ -119,27 +119,33 @@ int lyric_parse(const char *lrc, const char *tlrc) { pthread_mutex_lock(&g_lyric_mu); lyric_clear_nolock(); - int *idx = NULL; int result = 0; lrc_parse(lrc, 0); lrc_parse(tlrc, 1); - /* 按时间排序(用索引数组) */ - idx = (int *)malloc(sizeof(int) * (size_t)(s_n ? s_n : 1)); - if (idx) { - for (int i = 0; i < s_n; i++) idx[i] = i; - qsort(idx, (size_t)s_n, sizeof(int), cmp_lines); - long ttmp[NCM_LYRIC_LINES]; - char txt[NCM_LYRIC_LINES][LRC_MAX_TEXT]; - char tl[NCM_LYRIC_LINES][LRC_MAX_TEXT]; - memcpy(ttmp, s_t, sizeof(s_t)); - memcpy(txt, s_txt, sizeof(s_txt)); - memcpy(tl, s_tl, sizeof(s_tl)); - for (int i = 0; i < s_n; i++) { - s_t[i] = ttmp[idx[i]]; - memcpy(s_txt[i], txt[idx[i]], LRC_MAX_TEXT); - memcpy(s_tl[i], tl[idx[i]], LRC_MAX_TEXT); + /* 按时间排序(用索引数组)。临时数组放**堆**上:`char [600][192]`×2 + `long[600]` + * 若放栈会占 ~227KB,超过任何线程的 128KB 栈(ncm_thread_create NCM_THREAD_STACK) + * → lyric_worker 起播解析时栈溢出 SIGSEGV(真机 sig=11,RA 落在 lyric_parse)。 + * 堆上按 s_n 计(≤600 行),峰值 ~230KB 瞬态、用后即 free。 */ + if (s_n > 0) { + int *idx = (int *)malloc(sizeof(int) * (size_t)s_n); + long *ttmp = (long *)malloc(sizeof(long) * (size_t)s_n); + char (*txt)[LRC_MAX_TEXT] = + (char (*)[LRC_MAX_TEXT])malloc((size_t)LRC_MAX_TEXT * (size_t)s_n); + char (*tl)[LRC_MAX_TEXT] = + (char (*)[LRC_MAX_TEXT])malloc((size_t)LRC_MAX_TEXT * (size_t)s_n); + if (idx && ttmp && txt && tl) { + for (int i = 0; i < s_n; i++) idx[i] = i; + qsort(idx, (size_t)s_n, sizeof(int), cmp_lines); + memcpy(ttmp, s_t, sizeof(long) * (size_t)s_n); + memcpy(txt, s_txt, (size_t)LRC_MAX_TEXT * (size_t)s_n); + memcpy(tl, s_tl, (size_t)LRC_MAX_TEXT * (size_t)s_n); + for (int i = 0; i < s_n; i++) { + s_t[i] = ttmp[idx[i]]; + memcpy(s_txt[i], txt[idx[i]], LRC_MAX_TEXT); + memcpy(s_tl[i], tl[idx[i]], LRC_MAX_TEXT); + } } - free(idx); + free(idx); free(ttmp); free(txt); free(tl); /* 任一 malloc 失败:保留未排序(不崩) */ } result = s_n; pthread_mutex_unlock(&g_lyric_mu); diff --git a/patch_v2/ncm_app/app/ncm_app_internal.h b/patch_v2/ncm_app/app/ncm_app_internal.h index 527bbd8..ce8dbac 100644 --- a/patch_v2/ncm_app/app/ncm_app_internal.h +++ b/patch_v2/ncm_app/app/ncm_app_internal.h @@ -431,6 +431,7 @@ void appstate_apply_playlist_cover(int idx); void appstate_set_playing_song(int idx, const char *cover_url); int appstate_current_playing(void); /* 返回当前播放歌曲下标(UI) */ void autoplay_maybe(void); /* 真机联调 hook(/tmp/ncm_autoplay 触发,自动播已缓存曲目,一次性) */ +int autoplay_ui_consume(void); /* UI 线程取走 autoplay 起播标志,返回曲目 idx(无则 -1) */ /* lyric */ int lyric_parse(const char *lrc, const char *tlrc); /* 填充 g_lyric_* */ diff --git a/patch_v2/ncm_app/ncm b/patch_v2/ncm_app/ncm index ecd94e9..7934403 100755 Binary files a/patch_v2/ncm_app/ncm and b/patch_v2/ncm_app/ncm differ diff --git a/patch_v2/ncm_app/ncm_host b/patch_v2/ncm_app/ncm_host index 3bb7c70..970a34d 100755 Binary files a/patch_v2/ncm_app/ncm_host and b/patch_v2/ncm_app/ncm_host differ