Repository navigation
Conversation
macOS 上首次 dlopen 一个新路径的 dylib 需要约 4.5~5.4 秒走代码签名验证
(amfid / syspolicyd IPC),而同一路径的后续加载只要约 0.2 毫秒。由于
GccJitter::Compile 每次都生成全新路径的临时 dylib,反复编译同一份 C 代码时
这笔固定开销成倍累积:单个 gtest 用例耗时 30 秒以上,全量测试在本地实际
无法跑完。CI 三个 workflow 都是直接跑 unit_tests 二进制且无超时保护,因此
问题一直只表现为"变慢"而非失败。
根因是生成的 C 代码里有两处随运行变化的进程级状态,导致同一输入编译出不同
字节,既破坏可复现构建,也让按内容缓存产物永远无法命中:
1. State 指针被硬编码进产物(static void * _S = (void *) <addr>;)。地址受
ASLR 影响。更严重的是产物一旦被跨 State 复用,_S 会指向别的 State。
2. 常量字符串 ID 被内联进产物(FlSetTableStrId(ops, 35471392768, ...))。该 ID
来自进程级自增计数器。
改为:
- _S 初值置 0,由宿主在加载产物后、调用 __fakelua_init 之前通过新导出的
__fakelua_set_state 注入。GCC 与 TCC 两条路径都做注入。
- 常量字符串 ID 不再内联,改为在 __fakelua_const_init 里经
FakeluaConstStrAlloc 注册后存入产物内的静态变量,代码引用变量。含 \0 或
非文本字节的字符串(如 string.charpattern)自动改用长度 + 字节数组形式,
避免 C 字符串字面量截断。依赖常量字符串 ID 的文件级全局变量改为零初始化
并在 __fakelua_const_init 里赋值,因为静态变量不是编译期常量、不能出现在
静态初始化器中。
- 新增按「生成的 C 代码 + 全部编译参数」摘要(xxhash-64)的产物缓存,命中时
复用同一路径从而命中 dyld 的路径缓存。写入走「临时文件 + rename」保证原子。
可通过 StateGCCConfig::enable_compile_cache = false 关闭。
- cache 命中与正常编译两条路径共用 GccJitter::RegisterAndInit,避免行为漂移。
缓存条目失效时删除并退回重新编译,不让缓存成为故障源。
效果(macOS,Release):
- closure 套件(11 tests):487 s → 热缓存 1.1 s
- 17 个套件 1012 个测试(含 jitter 差分):29 m 12 s → 94 s
- 副作用:exception 套件因编译去重从 36 s 降到 7.2 s
已知取舍:缓存目录 $TMPDIR/fakelua-jit-cache 不自动清理,需手动删除。刻意不放在
$TMPDIR/fakelua/ 下 —— 该目录会被测试 runtime.generate_tmp_filename_creates_dir
用 remove_all 清掉。
另外修正文档:README 里的 ctest --test-dir build 实际不生效(根 CTestTestfile
缺 subdirs("test")),已改为直接运行二进制并说明需开 -DFAKELUA_BUILD_TESTS=ON。
Owner
Author
|
回滚:改动破坏了 State 隔离性,且与既有设计意图冲突。 原因
我犯的错我只在 macOS 上验证了 1012 个测试全绿就提交了,而这个 bug 只在 Linux CI 上暴露(macOS 的缓存命中路径行为不同)。基线对比也不充分:中途我用 遗留
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
macOS 上单个 gtest 用例耗时 30 秒以上(应毫秒级),全量测试在本地实际跑不完。CI 三个 workflow 都是直接跑
unit_tests二进制且无超时保护,所以问题一直只表现为"变慢"而非失败。实测定位(4 个不同 dylib 路径):
gcc -O3 -dynamiclibdlopen某新路径 dylibdlopen同一路径每个新路径的首次加载都要付约 4.5–5.4 秒走 macOS 代码签名验证(amfid / syspolicyd IPC),差值达 25000 倍。
排查中排除了两个假设,避免后来者重走:
codesign -s -实测 6312/5106/5222 ms,-Wl,-adhoc_codesign4852/4753/4672 ms,都不比基线好。根因
生成的 C 代码里有两处随运行变化的进程级状态,导致同一输入编译出不同字节 —— 既破坏可复现构建,也让按内容缓存产物永远无法命中:
static void * _S = (void *) 0x1014b2aa0;,地址受 ASLR 影响。更严重的是产物一旦被跨 State 复用,_S会指向别的 State。FlSetTableStrId(ops, 35471392768, ...),该 ID 来自进程级自增计数器。改动
_S初值置 0,由宿主在加载产物后、调用__fakelua_init之前通过新导出的__fakelua_set_state注入。GCC 与 TCC 两条路径都注入。__fakelua_const_init里经FakeluaConstStrAlloc注册后存入产物内的静态变量,代码引用变量。\0或非文本字节的字符串(如string.charpattern里的[\0-\255])自动改用长度 + 字节数组形式,避免 C 字符串字面量截断。__fakelua_const_init里赋值 —— 静态变量不是编译期常量,不能出现在静态初始化器中。StateGCCConfig::enable_compile_cache = false关闭。GccJitter::RegisterAndInit,避免行为漂移。条目失效时删除并退回重新编译,不让缓存成为故障源。效果(macOS, Release)
closure套件(11 tests)副作用:
exception套件因编译去重从 36 s 降到 7.2 s。测试:1012 个测试全部通过(含 254 个
.lua差分语料驱动的 jitter 套件)。冷启动与热缓存各跑一遍,均全绿。已知取舍
$TMPDIR/fakelua-jit-cache不自动清理,需手动删除。刻意不放在$TMPDIR/fakelua/下 —— 该目录会被测试runtime.generate_tmp_filename_creates_dir用remove_all清掉。顺带修正文档
README里的ctest --test-dir build -V实际不生效 —— 根CTestTestfile.cmake缺subdirs("test"),执行结果是No tests were found!!!。已改为直接运行二进制,并说明需显式开-DFAKELUA_BUILD_TESTS=ON(默认为OFF)。