踩坑记录
踩坑记录 Windows 踩坑 1. 多模块加载冲突(error 487) 现象: 第二个模块 返回 error 487(ERROR INVALID ADDRESS)。 原因: 多个模块的 PE 文件 相同(Unity 构建默认都是 ),多次 共用同一进程句柄时产生地址冲突。 修复: 通过 单例解决——所有模块共享一个 上下文,每个模块以其 崩溃时实际基址(crash base) 作为加载地址,保…
踩坑记录
Windows 踩坑
1. 多模块加载冲突(error 487)
现象: 第二个模块 SymLoadModuleEx 返回 error 487(ERROR_INVALID_ADDRESS)。
原因: 多个模块的 PE 文件 ImageBase 相同(Unity 构建默认都是 0x180000000),多次 SymInitialize 共用同一进程句柄时产生地址冲突。
修复: stack_sym.py 通过 DbgHelpSession 单例解决——所有模块共享一个 SymInitialize 上下文,每个模块以其**崩溃时实际基址(crash_base)**作为加载地址,保证唯一不冲突。
2. 加固 DLL 导致 SymLoadModuleEx 失败
现象: 传入加固后的 GameAssembly.dll,SymLoadModuleEx 失败。
原因: 加固工具修改了 PE 头,dbghelp 无法正常解析。
解决: 使用构建流程中加固之前产出的原始 DLL(文件名可能为 GameAssembly_1.dll 或构建中间目录中的版本)。
3. Sentry 显示 missing_symbol 不等于 PDB 无符号
Sentry symbolicator 对 non-dev Unity PDB 有处理 bug,即使 PDB 已上传且 debug_status=found,仍显示 missing_symbol。遇到此情况直接用本地工具 stack_sym.py 符号化即可。
结论: has_debug_info: false 出现在 PE 文件(.dll)上是正常的——debug info 在 PDB 里,不在 DLL 里。
4. SymFromAddr 在 Win10/11 失效(error 87)
Windows 新版 dbghelp.dll 的 SymFromAddr 对进程外地址做虚拟内存检查,返回 error 87(ERROR_INVALID_PARAMETER)。
绕过方案: 不用 SymFromAddr,改用 SymEnumSymbols 枚举全部符号 + bisect 二分查找最近前驱符号。stack_sym.py 已内置此方案。
5. stack_sym.py 加载 PDB 报 error 18
现象: SymLoadModuleEx 失败: error 18(ERROR_NO_MORE_FILES)。
原因: 没有传对应的 DLL 文件(或 DLL 路径不存在),工具找不到基址信息。
解决: --pdb 和 --dll 必须成对传入,缺一不可。若未显式传 --dll,工具会在 PDB 同目录自动查找(find_dll_near_pdb()),确保同目录有对应的 .dll 文件。
Android 踩坑
1. libunity.so 符号文件选择
Unity AndroidPlayer 目录下有三个版本:
| 文件 | 大小 | 符号信息 | 能否用于符号化 |
|---|---|---|---|
Libs/arm64-v8a/libunity.so |
~24MB | 无符号(发布用裁剪版) | 不能 |
Symbols/arm64-v8a/libunity.sym.so |
~10MB | 精简符号表(仅函数名) | 可以,但无行号 |
Symbols/arm64-v8a/libunity.so |
~296MB | 完整 DWARF 信息 | 优先使用 |
2. addr2line 偏移计算
addr2line 需要的是相对于 ELF 加载基址的偏移量,不是运行时绝对地址。
公式:offset = instructionAddr - image_addr,其中 image_addr 从 Sentry debugmeta 读取。
3. JSON 解析失败(JSONDecodeError)
Android 崩溃事件体积大(包含大量系统库 debugmeta),直接用管道 | python -c 解析容易因编码问题失败。
解决: 始终先 curl -o 保存到文件,再用 errors='replace' 打开解析。
4. libil2cpp.so 符号文件
libil2cpp.so 的符号文件依赖项目构建产出,不在 Unity Editor 目录中。需要从构建流程的中间产物获取。
配置路径:config.paths.android_symbols.libil2cpp(默认 ${symbol_store}/libil2cpp.dbg.so,即 F:/PDB/libil2cpp.dbg.so)。
5. ACE/TPShell 加固导致 debug_meta 缺失
现象: Android native crash 事件中 debug_meta 完全为空(0 个 debug images),所有帧的 imageAddr 为空,Sentry 无法自动符号化。
原因: 项目使用 ACE/TPShell 加固,tpshell-config.xml 配置了 <EncSo>libil2cpp.so</EncSo>,加密了 libil2cpp.so 的 ELF 头。运行时 sentry-native 的信号处理器通过 /proc/self/maps 遍历加载的 .so 并解析 ELF header 获取 build-id 和段信息,加密后的 ELF 头无法解析,整个 debug images 收集流程中断,连 libunity.so 等未加密模块的 debug image 也一并丢失。
解决: 使用**路径 B(基址推算)**手动符号化。详见 skill.md Phase 2 附录 Android 路径 B。
6. 无 image_addr 时推算基址
现象: 需要符号化但事件中没有 image_addr。
方法:
- 解析 .so 的 ELF program headers,找到 text 段(PT_LOAD + PF_X)的
vaddr和memsz - 从事件帧地址取
addr_min和addr_max - 计算
base_range = [addr_max - text_end, addr_min - text_start] - 在范围内按 4MB → 2MB → 1MB 对齐枚举候选基址
- 用 addr2line 验证第一个候选是否返回有效函数名
实测案例(2026-03-25,issue #1851):
- libil2cpp.dbg.so 651MB,text segment vaddr=0x1a9e694 memsz=0x2c0788c
- 24 帧地址范围 0x7bac42b224 ~ 0x7baea18f64
- 推算基址 0x7baa800000(4MB 对齐),addr2line 验证通过
- 符号化成功但函数调用链无逻辑关联(ART StackVisitor 内部崩溃,栈数据损坏)
7. 符号化成功但栈数据损坏
现象: addr2line 返回真实函数名,但函数之间完全不相关(如 ValueTuple_Equals → OnValidateInput_Invoke → ComputeShortestPathFromNodes)。
原因: 某些 native crash(尤其 Android ART 运行时崩溃如 art::StackVisitor::GetDexPc)发生时,栈已损坏或 unwinder 读到了无效返回地址。符号化出的函数名是"离该地址最近的函数入口",不代表真实调用关系。
处理: 在分析报告中注明"栈数据损坏,符号化结果仅供参考",结合 breadcrumbs 和 tags 分析崩溃上下文。
Windows 开发环境踩坑
5. python3 命令返回 exit code 49
现象: python3 -c "..." 返回 exit code 49,无输出。
原因: Windows 11 自带 python3.exe 是 Microsoft Store 的 stub(位于 WindowsApps/),不是真实 Python。
解决: 始终用 python(不带 3),它指向实际安装的 Python(如 Python313/python.exe)。
6. $TEMP 路径在 bash 和 python 之间不一致
现象: curl -o "$TEMP/file.json" 写入成功,但 python 中 open('/tmp/file.json') 找不到文件。
原因: Git Bash 中 $TEMP=/tmp,curl(基于 MSYS)将其映射到 Windows 的 %TEMP%(C:/Users/<user>/AppData/Local/Temp/)。但 Python 是原生 Windows 程序,不识别 /tmp。
解决: Python 中使用 Windows 绝对路径:C:/Users/<user>/AppData/Local/Temp/file.json。或在 bash 中用 cygpath -w "$TEMP" 获取 Windows 路径。
7. jq 未安装
现象: jq: command not found。
原因: Windows 环境默认不带 jq。
解决: 不依赖 jq,用 python -c "import json; ..." 替代所有 JSON 解析。skill 文档已更新。
8. breadcrumb message 字段为 None
现象: c.get('message','')[:200] 抛出 TypeError: 'NoneType' object is not subscriptable。
原因: Sentry breadcrumb 的 message 字段值可能是 JSON null(Python None),dict.get('message', '') 在 key 存在但值为 None 时返回 None 而非默认值 ''。
解决: 使用 (c.get('message') or '')[:200]。
通用踩坑
纯系统库崩溃无需游戏侧符号
堆栈全为 libc.so / libaaudio.so / d3d11.dll / nvwgf2umx.dll 等系统/驱动模块时,游戏代码不在调用链中,无需符号化。直接分析调用链语义即可得出结论(驱动 bug、系统兼容性问题等)。