From d0f69eb90a2b3b2055fd893729032bd124acc44e Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 14 Sep 2026 14:42:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(phase2):=20=E7=83=AD=E7=82=B9=E6=8F=92?= =?UTF-8?q?=E6=A1=A9=E6=97=B6=E5=BC=BA=E5=88=B6=E5=A4=B1=E6=95=88=20includ?= =?UTF-8?q?e=20=E6=A8=A1=E5=9D=97=E7=9A=84=E6=97=A7=E7=9B=AE=E6=A0=87?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit include 模块同样会被插桩(hotspot_sites_file 经 worker 透传),但 _compile_include_py_files 对预编译缓存 precompiled_obj 与 output 缓存 obj_path 存在即跳过,且没有 hotspot_on 强制重译分支,导致复用上一次未插桩的 .obj,使 include 内热点模块(MemBuddy.alloc、ast.tokens、ast.base、llvmlite 窥孔等)的计数/计时 IR 静默缺失、其耗时被计入调用者 self。 启用热点时先删除这两类旧 .obj(沿用 App 主循环同一写法:用未缓存的 os.path.isfile 判定,避免把已存在写入只写存在性缓存后被删除导致后续判定读到过期值而漏编),删除失败仅告警不中断、不静默吞错。 include 路径本就每次无条件重译 .ll(无跳过逻辑),故只需处理 .obj;.dso.ll/.stub.ll/.text.ll/.vpi/.deps.json 经核对无同类隐患。未启用热点时行为逐字节不变。 --- lib/Projectrans/Phase2Translator.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/Projectrans/Phase2Translator.py b/lib/Projectrans/Phase2Translator.py index ef7bfce..bcf3913 100644 --- a/lib/Projectrans/Phase2Translator.py +++ b/lib/Projectrans/Phase2Translator.py @@ -2715,6 +2715,12 @@ class Phase2Translator: sorted_files, file_deps = self._sort_include_files_by_deps(self.extra_py_files) _vlog().info(f"\n[includes 编译] 找到 {len(sorted_files)} 个 Python 源文件") + # --hotspot-sites:include 模块同样被插桩(hotspot_sites_file 经 worker 透传), + # 故启用时必须让旧 .obj 失效并强制重译,否则会复用上一次未插桩的目标文件, + # 使 include 模块的热点计数/计时 IR 静默缺失。未启用时 hotspot_on=False, + # 本函数行为与改动前逐字节一致(零影响)。 + hotspot_on: bool = bool(self.hotspot_sites_file) + # 构建 include 文件的模块 SHA1 映射(预计算所有文件,构建完整映射) include_ModuleSha1Map = self._build_current_ModuleSha1Map(None) need_translate = [] # [(src_path, sha1, module_name, ll_path, obj_path, precompiled_obj)] @@ -2753,6 +2759,21 @@ class Phase2Translator: self._collect_inline_symbols(src_path) continue + # --hotspot-sites:include 的 .py 未变时下面两处会因旧 .obj 存在而直接跳过, + # 复用上一次可能未插桩的目标文件。启用热点时先让这两类旧 .obj 一并失效, + # 使其必然走入 need_translate 重新翻译并嵌入热点计数/计时 IR。 + # 用 os.path.isfile 而非 _CachedIsFile:避免把"已存在"写入存在性缓存后又被 + # 本处删除,使下面两处判定读到过期缓存而漏编(与 App 主循环同一写法)。 + if hotspot_on: + for _stale_obj in (precompiled_obj, obj_path): + if _stale_obj and os.path.isfile(_stale_obj): + try: + os.remove(_stale_obj) + except OSError as e: + # 不中断构建,但真实错误(如权限不足)必须打印告警,不静默吞掉 + _vlog().warning( + f"Phase2Translator: 删除热点插桩 include 模块的旧目标文件失败: {_stale_obj}: {e}") + # 优先检查 includes.binary 预编译缓存(跨项目共享) if not self.force_recompile_includes and precompiled_obj and _CachedIsFile(precompiled_obj): _vlog().info(f" 跳过(预编译): {module_name}.py -> {os.path.basename(precompiled_obj)}")