Bug 报告:手机跟pc同步任务中包含图片时必定触发的崩溃问题

Bug 报告:手机跟pc同步任务中包含图片时必定触发的崩溃问题

环境

  • 产品:Qoder CN Desktop(Qoder-CN App)0.5.5,Electron 43.8.4
  • 系统:Fedora Linux 44 Workstation(x86_64,Wayland + Xwayland,App 以 ozone-platform=x11 运行)
  • 触发场景:另一设备通过"远程控制"连接本机,触发会话历史同步

现象

  1. 远程设备连接后开始同步两个本地会话的历史消息(batch=50,每批 100–160KB)
  2. 同步子进程 Qoder Remote Control Sync(node.mojom.NodeService)反复异常退出并自动重启,形成崩溃循环:
    • 昨日日志 main.1.log 中相关记录 279 次
    • 当日 main.log 中 16 次,集中在 11:10–11:11(UTC+8)
  3. 11:11:31(UTC+8)主进程日志完全停止(renderer 停在 10:34),UI 无响应,持续 8 小时以上
  4. 卡死的主进程不响应 SIGTERM(systemctl --user stop 被吊住),仅 kill -9 可结束
  5. 重启后故障消失,0 错误运行正常

崩溃现场(coredumpctl)

  • Signal 11 (SEGV),崩溃线程 PC = 0x0(空指针)
  • 崩溃点位于原生图像库 sharp-linux-x64.node(+0x1835d),调用链经 Node 线程池(ThreadPoolWork/uv_work)
  • coredump:/var/lib/systemd/coredump/core.qoder-cn.1000.7de104dd....2199622.1793934686....zst(13M,保留可用)
Stack trace of thread 2199699:
 #0  0x0000000000000000 n/a
 #1  0x00007f5a71ada35d n/a (sharp-linux-x64.node + 0x1835d)
 #2  0x00007f5a71ae200c n/a (sharp-linux-x64.node + 0x2000c)
 #3  0x00007f5a71ad8eac n/a (sharp-linux-x64.node + 0x16eac)
 #4  node::ThreadPoolWork::ScheduleWork(...) [uv_work]

主进程日志关键摘录

[2026-11-06T03:11:16.097Z] [ERROR] [main] [App] Electron child process exited
  {"type":"Utility","reason":"crashed","exitCode":139,
   "serviceName":"node.mojom.NodeService","name":"Qoder Remote Control Sync"}
# 同类记录每 2~5 秒一条,同时 Remote Control 历史同步 body_batch 持续进行
[2026-11-06T03:11:31.381Z]  ← 最后一条日志,此后主进程静默 8h+

systemd 日志佐证(每 2~3 秒一次段错误):

11月 06 11:11:16 fedora systemd-coredump[...]: Process 2198631 (qoder-cn) terminated
  abnormally with signal 11/SEGV
11月 06 11:11:19 fedora systemd-coredump[...]: Process 2198872 (qoder-cn) ...
(连续多条)

日志目录

~/.config/com.qodercn.app.stable/logs/20261105-150951.663-2144850-6e36e84f/(含 main.log / main.1.log / renderer.log)

反馈的两个问题

  1. 崩溃本体:Remote Control Sync 在历史同步/图片处理路径上,sharp 原生库空指针段错误(怀疑与 Fedora 44 的 glibc/新环境兼容性亦有关)
  2. 容错设计:单个 utility 子进程崩溃循环最终导致主进程事件循环假死,且不响应 SIGTERM。建议:同步子进程崩溃应退避重试并隔离失败任务,主进程不应被拖死;恢复后历史同步应能断点续传

复现建议

远程连接一台含有大体量历史会话(本次同步了 16+ 批、800+ 条消息且含图片)的桌面端,观察 Remote Control Sync 是否段错误并复现主进程假死。


追加证据(2026-11-07 凌晨,升级 0.5.6 后)

升级:qoder-cn-0.5.6-1.x86_64(官方 rpm,SHA256 校验通过)——问题未修复,同样失败。

清空镜像库后的连锁发现(0.5.5 与 0.5.6 行为一致):

  1. 将 remote-control.sqlite 移走后,数据库准备流程在 handoff 阶段报 ENOENT——该流程不会从零创建库文件,只认已有文件
  2. 用空 WAL 模式 SQLite 文件顶位后,inspect/handoff/open 全部通过,死在 import 阶段:elapsed 1ms 即失败(failure_stage:"import",有 child_pid 但无任何日志/信号记录),cause 仍为 REMOTE_CONTROL_SCOPE_DATABASE_UNAVAILABLE,REMOTE_CONTROL_STORAGE_REPAIR_FAILED 同样出现。修复流程同样无法工作
  3. 主进程在无人操作时自发 SIGILL(signal 4)崩溃:00:42:51、00:53:06、01:04:44 各一次,堆栈全部落在 Electron 主二进制内。主机环境:CPU 品牌字符串为空(lscpu 显示 "06/55"),疑似虚拟机/特殊 CPU,flags 含 avx2/avx512f
  4. 该机器上 Qoder 的凭据存储在非 GNOME 会话环境启动时会退化为 basic_text 且 encryptionAvailable:false,此时拒绝保存凭据并清除已有条目,表现为"每次重启都掉登录"。以完整 GNOME 会话环境(含 XDG_CURRENT_DESKTOP=GNOME)启动则正常使用 gnome_libsecret

临时缓解措施(供其他用户参考):

  • 毒镜像库备份:remote-control.sqlite{,-shm,-wal}.bak-20261106-231930(清空后崩溃循环停止)
  • 崩溃循环期间主进程仅 kill -9 可结束;systemctl --user stop 会被吊死
  • 建议 Qoder:远程控制数据库准备流程应能自行建库(而不是 ENOENT 即弃);import 子进程秒死应在日志中输出原因;凭据存储退化时不应清除已有凭据

coredump 保留:/var/lib/systemd/coredump/(SIGILL 样本 3 份 + 早期 SIGSEGV 样本),日志目录 ~/.config/com.qodercn.app.stable/logs/(20261105-150951 系列为故障全程记录)。


根因定位(2026-11-07 12:20,已可脱离 App 单点复现)

一句话:App 自带的 @img/sharp-libvips-linux-x64 把 glib 2.86.1 静态内嵌在 libvips-cpp.so.8.17.3 里,而 Fedora 44 系统 glib 是 2.88.0;Electron 进程在加载 sharp 之前已经全局加载了系统 libgobject-2.0.so.0,于是 sharp-linux-x64.node 中未加符号版本的 g_object_unref 绑定到了系统 2.88 实现,被用来释放由 libvips 内置 2.86 glib 创建的 GObject → 两版 GObject 结构布局不一致 → 取到空的类方法指针并调用 → PC=0x0 SIGSEGV。任何一次图像处理必然崩溃,与会话内容、数据库、登录态无关。

证据链

  1. 脱离 App 复现(不需要手机、不需要远程控制):
    ELECTRON_RUN_AS_NODE=1 ELECTRON_NO_ASAR=1 /opt/Qoder-CN/qoder-cn -e \
      "require('.../node_modules/sharp')({create:{width:16,height:16,channels:3,background:'#123'}}).png().toBuffer()"
    → exit 139(resize / raw / metadata 全崩;DISABLE_SIMD、VIPS_CONCURRENCY=1、LD_PRELOAD 均无效)
    
  2. gdb 崩溃线程(libuv-worker):
    #0  0x0000000000000000 in ?? ()
    #1  0x7ffff79b656c in g_object_unref () from /lib64/libgobject-2.0.so.0     ← 系统 glib 2.88
    #2  0x7fff6567035d in sharp-linux-x64.node (+0x1835d)
    
  3. objdump 反汇编 sharp-linux-x64.node:0x18358 处即 call 12280 <g_object_unref@plt>,0x1835d 是其返回地址——崩溃点在 glib 引用计数释放路径上。
  4. 包体属性:
    • nm -D --defined-only libvips-cpp.so.8.17.3 → 自带 g_object_ref / g_object_unref(共 1778 个 g_* 符号),且 objdump -p 的 NEEDED 里没有 libgobject/libglib → glib 是静态内嵌的
    • nm -D --undefined-only sharp-linux-x64.node → U g_object_ref / U g_object_unref(无版本约束)
    • sharp.versions = vips 8.17.3, glib 2.86.1, sharp 0.34.5;rpm -q glib2 = glib2-2.88.0-1.fc44
    • sharp-linux-x64.node 用的是 RPATH(非 RUNPATH),所以 LD_LIBRARY_PATH 无法替换 libvips;LD_PRELOAD 预载内嵌 libvips 会把第二套 glib 也带进全局作用域,引发另一种崩溃(g_slice 跨 glib 混用)
  5. 对照组:同一份 libvips-cpp.so.8.17.3 用 python3 + ctypes 单独加载,vips_init 返回 0、建图/写 PNG 正常退出(exit 0,无段错误)→ libvips 本体没问题,问题只出现在"系统 glib 与内嵌 glib 混链"时。

与现象的对应

  • Qoder Remote Control Sync(node.mojom.NodeService)是唯一走 sharp 图像路径的子进程 → 只有它反复 139,每 ~2.2s 一轮(崩溃→重启→立刻再崩)
  • 崩溃集中在 历史同步阶段 stage:"body_batch",会话 9177eb55 的批次 13/14/17/18/19/20 均在途中崩断,attempt 以 complete:false, canceled 收尾 → 历史同步永不收敛,rc_history_sync.attempts 累到 1111
  • 主进程被崩溃风暴拖住后静默假死、不响应 SIGTERM(11-06 现象)

建议修法(任选其一)

  1. 让 sharp 只对应一套 glib:改用动态链接系统 glib 的 libvips 构建,或把内嵌 glib 升到与主流发行版一致的 ABI;或在加载 .node 前用 RTLD_DEEPBIND/预绑定确保 g_object_unref 落在 libvips 自带实现上。
  2. 子进程与图像路径解耦:远程控制历史/资源同步不应在共享 Electron 运行时里直接调用 sharp;单独 utility 进程 + 崩溃退避 + 失败任务隔离(当前无退避,2.2s 一次无限循环)。
  3. 升级打包:sharp ≥ 0.34.6 / libvips 8.18 重新出包,并在 Fedora 44(glib 2.88)加一条 CI 冒烟:跑一次 resize 断言退出码 0。

本机可自行验证(无需源码)

ldd 全部干净、integrity_check 全部 ok,用户侧无法通过删库/清缓存绕过——只能等打包修正;因此本机已改用 qoderclicn remote-control(该二进制 ldd 不含 libgobject,不会形成同一套混链)。


补充(2026-11-07 13:55):谁调用 sharp、什么图像、以及三条自行修复路线的实测否决

1. 调用方已定位(不是 App 主包,而是随包的 Agent SDK worker)

  • Qoder Remote Control Sync 入口 out/main/remoteControlProcess.js 内 0 次 出现 sharp;它 import"@qoder-ai/qoder-cn-agent-sdk"
  • 真正的调用方:resources/app.asar.unpacked/node_modules/@qoder-ai/qoder-cn-agent-sdk/dist/_worker/qoder-worker-runtime.obf.mjs(runtime-info.json: version 1.1.79-hotfix.18,built 2026-11-05T15:39:18Z,copiedPackages 明确包含 @img/sharp-linux-x64 / @img/sharp-libvips-linux-x64)
  • 该文件内含完整的"喂给模型前的图像归一化"流程:sharp(buf).metadata() → 超过 3932160 B 或 2000 px 就 .resize(g,c,{fit:"inside",withoutEnlargement:true}) → .png({compressionLevel:9,palette:true,colors:64}) 或按 quality [80,60,40,20] 逐级降质 .jpeg() → .toBuffer();失败回退再 .resize(400,400).jpeg({quality:20})。日志串 Image resize: WxH → WxH、Fallback compression failed

2. 触发崩溃的图像(已具名,非猜测)

  • 全机仅 1 个会话含图:~/.qoder-cn/projects/-run-user-1000-gvfs-smb-share-server-ds920plus-local-share-homes-gomain-Drive-Forex/9177eb55-d0ed-4e96-a095-9600842e668f.jsonl(26 MB,110 个 type:"image" 块,去重后 55 个 PNG 对象)
  • 内容是 NAS(SMB/gvfs)上的行情图表:docs/路二体检_2026-11-02/route3_*.png(conditional / filters / full_census / parent_phase / period_map / phase_universal / straddle_dist / straddle_m15 / unbiased)、路三规律表/law_map.png、路三相位_汇总/route3_2x2.png|role_flip.png、路四战术/seq_round5.png,以及 /tmp/pb.png|pb2.png|pb5.png|um.png|wc.png;上传时间 2026-11-02 12:35 → 11-04 16:19,落到 qoder-cn-vl-private.oss-cn-beijing.aliyuncs.com/cloud/.../*.png
  • 取其中一张真实图片走 App 自带 sharp:META 1200x780 png 之前的第一个原生调用即崩溃 → exit 139(同一路径的图像操作全部必崩,与图片本身内容无关)

3. 三条"用户可自行修复"路线实测否决

路线实测结果
换成动态链接系统 glib 的 libvips(dnf 侧 vips-8.18.0-6.fc44,即 /usr/lib64/libvips-cpp.so.42)段错误消失但功能全废:metadata 可用(META 1200x780 png),jpeg / png / png(palette) / raw / webp 一律 colourspace: parameter space not set(value "32" of type 'gint' is invalid for property 'space' of type 'VipsInterpretation')。8.18 改了 VipsInterpretation 取值,sharp 0.34.5 是按 8.17.3 编译的 → 该路线需同时升级 sharp,用户侧不可行
LD_PRELOAD 转发 shim(把 sharp 发出的 g_object_ref/unref 转给 libvips 内嵌实现,其余照旧)单元级可把 139 变成 exit 0,但部署不了:以 LD_PRELOAD=/tmp/shim3.so 重启 App 后,Electron 的 zygote / utility 子进程 /proc/<pid>/environ 中 LD_PRELOAD 命中数 0/11(只有 Node child_process 起的 qoderclicn 继承到)→ Chromium 对子进程环境做了净化,唯一会崩的进程恰好收不到
升级内嵌包 @img/sharp-libvips-linux-x64@1.3.4(glib 2.90.0 / vips 8.18.7)仍 exit 139(静态内嵌 glib 与系统 2.88 并存这一事实没变,换版本号不换链接方式就没用)

结论:本机无 root、无源码不可修;请打包侧按上面第 1 条建议出包(libvips 动态链接系统 glib 且 sharp 升到与该 libvips 匹配的版本),或对远程控制图像路径做进程隔离 + 退避。

展开
收起
狂人志有 2026-11-07 15:02:02 7 分享 版权
1 条回答
写回答
取消 提交回答
  • 这份Bug报告写得相当扎实,根因定位到sharp内嵌glib 2.86与Fedora 44系统glib 2.88的ABI冲突,证据链完整,连三条自修路线都实测否决了,开发团队拿到基本可以直接出补丁。

    核心问题就两个:一是打包时libvips静态链接了旧版glib,遇到新系统必崩;二是子进程崩溃没退避机制,2秒一轮无限重启把主进程拖死。建议优先让libvips动态链接系统glib并升级sharp版本匹配,同时给Remote Control Sync加崩溃退避和任务隔离,别让单个utility进程搞垮整个App。

    另外凭据存储在非GNOME环境退化后清已有条目这个体验问题也顺手修下,掉登录挺烦人的。

    官方详细解决方案:https://www.aliyun.com/product/lingma

    2026-11-07 15:20:35
    赞同 展开评论

Qoder CN 是 Qoder CN 系列中面向软件开发场景的核心子产品(原“通义灵码”),提供 IDE、JetBrains 插件、Visual Studio Code 插件等多种使用形态。Qoder CN 持续提供代码智能生成、智能问答、多文件修改、编程智能体等能力,并在模型选择、专家协作、任务规划等方向全面升级,为开发者带来高效、流畅的编码体验。同时面向企业客户提供企业标准版、企业专属版,具备企业级场景自定义、私域知识增强等能力,助力企业研发智能化升级。

热门讨论

热门文章

还有其他疑问?
咨询AI助理