fix(tpv): 修 CreateProcessA 重定向未启动外部命令,并把捕获输出移到 error 之前
主因:_run_cmd_wait 置 STARTF_USESTDHANDLES 时只设了 hStdOutput/hStdError,hStdInput 仍是 memset 后的 NULL(0);Windows 要求三个句柄都有效且可继承,故 CreateProcessA 直接返回 0(表现为 exit=4294967295 即 -1,且 .llc.log 恒空、一次也没有 llc 输出)。日志文件改以 GENERIC_WRITE|GENERIC_READ 打开并令 hStdInput 同指该文件(llc 不读 stdin),三态句柄均不泄漏;无重定向时不置 USESTDHANDLES、退回继承控制台的行为不变。 另在 CreateProcessA 失败处立即取 w32.win32base.GetLastError 并以 VLogger.warning 打印错误码+命令+重定向文件路径(warning 而非 error:error 会 sys.exit,而此处运行在 llc worker 线程内)。 同时修正一处使功能完全失效的顺序问题:_log_captured_output 原先排在 VLogger.error 之后,而 error 内部 sys.exit(1),故捕获到的 llc stderr|stdout 永远不会打印(这正是只看到一行 llc 编译失败、.llc.log 留空的原因)。串行 compile_ll_to_obj 与并行 run_llc_parallel 两处均改为先打印捕获输出、再报错。
This commit is contained in:
1 parent
82f16127c0
commit
05d5bfbf37
1 file changed
+26
-6
@@ -435,8 +435,10 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s
|
||||
viperlib.snprintf(log_path, len(compile_input) + 16, "%s.llc.log", compile_input)
|
||||
sys_ret: int = _run_cmd_wait(cmd, log_path)
|
||||
if sys_ret != 0:
|
||||
VLogger.error(f"编译失败 (module={module_name})", "LLC")
|
||||
# 必须先打捕获输出再报错:VLogger.error 内部 sys.exit(1),其后的语句永不执行
|
||||
# (否则 llc 的 stderr|stdout 永远打印不出来)
|
||||
_log_captured_output(log_path, module_name, cmd, sys_ret, "LLC")
|
||||
VLogger.error(f"编译失败 (module={module_name})", "LLC")
|
||||
return 1
|
||||
w32.win32file.DeleteFileA(log_path)
|
||||
return 0
|
||||
@@ -449,7 +451,9 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s
|
||||
# - LlcTask 及命令串一律 stdlib.malloc:主池 mb 会被 reset_to(mark2)
|
||||
# 回收,线程运行期间字符串悬空 → 概率崩溃
|
||||
# - worker 只调 _run_cmd_wait(内部仅 stdlib.malloc/CreateProcessA/
|
||||
# CreateFileA/CloseHandle),不做任何池分配(MemBuddy 分配非线程安全)
|
||||
# CreateFileA/CloseHandle;CreateProcessA 失败时加一条 VLogger.warning
|
||||
# 诊断,只走 CRT printf/控制台着色),不做任何池分配(MemBuddy 分配
|
||||
# 非线程安全)
|
||||
# - 依赖 CreateProcessA + Win32 句柄接口的多线程安全性
|
||||
# ============================================================
|
||||
# llc 并行最大线程数(参考 TPC Phase2Translator n_workers=os.cpu_count(),本机 16 核)
|
||||
@@ -597,7 +601,10 @@ def _run_cmd_wait(cmd: str, log_path: str = None) -> int:
|
||||
# 全仓 20+ 处同样写法)。NULL 与 INVALID 都不参与关闭,故关闭处需双重守卫。
|
||||
log_fh: w32.win32base.HANDLE = None
|
||||
if log_path is not None:
|
||||
log_fh = w32.win32file.CreateFileA(log_path, w32.win32file.GENERIC_WRITE,
|
||||
# GENERIC_READ 一并打开:见下方 hStdInput 说明(置 USESTDHANDLES 后三个
|
||||
# std 句柄都要有效;llc 不读 stdin,读到空文件无副作用)
|
||||
log_fh = w32.win32file.CreateFileA(log_path,
|
||||
w32.win32file.GENERIC_WRITE | w32.win32file.GENERIC_READ,
|
||||
w32.win32file.FILE_SHARE_READ,
|
||||
t.CPtr(c.Addr(sa)),
|
||||
w32.win32file.CREATE_ALWAYS,
|
||||
@@ -605,12 +612,24 @@ def _run_cmd_wait(cmd: str, log_path: str = None) -> int:
|
||||
t.CPtr(0))
|
||||
if log_fh != w32.win32base.INVALID_HANDLE_VALUE:
|
||||
si.dwFlags = win32process.STARTF_USESTDHANDLES
|
||||
# 三个 std 句柄必须都是有效句柄:Windows 对 hStdInput 同样做复制/校验,
|
||||
# hStdInput 保持 memset 后的 NULL(0) 会让 CreateProcessA 直接失败返回 0
|
||||
# → 外部命令根本没被启动(表现为 exit=-1 且捕获文件恒为空)。llc 不读
|
||||
# stdin,故 stdin 也指向该日志文件(已带 sa.bInheritHandle=1,可继承)。
|
||||
si.hStdInput = log_fh
|
||||
si.hStdOutput = log_fh
|
||||
si.hStdError = log_fh
|
||||
# create_flags=0:无重定向时子进程继承父控制台;有重定向时 stdio 走文件
|
||||
ok: int = win32process.CreateProcessA(
|
||||
t.CPtr(0), cmd_line, t.CPtr(0), t.CPtr(0), 1, 0,
|
||||
t.CPtr(0), t.CPtr(0), t.CPtr(c.Addr(si)), t.CPtr(c.Addr(pi)))
|
||||
# CreateProcessA 失败诊断:GetLastError 必须在返回后立即取(此后 stdlib.free/
|
||||
# CloseHandle/SetConsoleTextAttribute 等都会覆盖它)。用 warning 不用 error——
|
||||
# VLogger.error 会 sys.exit(1),而本函数可能在 worker 线程里执行;warning 只走
|
||||
# CRT printf + 控制台着色,不做 MemBuddy 池分配,满足 worker 线程安全约束。
|
||||
if ok == 0:
|
||||
cp_err: uint32 = w32.win32base.GetLastError()
|
||||
VLogger.warning(f"CreateProcessA 失败 (GetLastError={cp_err}) 外部命令未启动: 重定向文件={(log_path if log_path is not None else "无")} cmd={cmd}", "LLC")
|
||||
stdlib.free(cmd_line)
|
||||
# 子进程已持有文件句柄副本,父侧立即关闭,避免句柄泄漏
|
||||
# 双重守卫:NULL(未重定向) 与 INVALID(打开失败) 都不关闭
|
||||
@@ -707,11 +726,12 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
|
||||
tsk2: LlcTask | t.CPtr = (LlcTask | t.CPtr)(t.CVoid(
|
||||
uint64(tasks) + sizet(batch_start + j) * slot_size, t.CPtr))
|
||||
if tsk2 is not None and tsk2.Result != 0:
|
||||
# 失败:打印模块 sha1 + 退出码 + 完整命令,再读重定向文件打印
|
||||
# llc 的 stderr|stdout(.ll 路径即命令末参数)
|
||||
VLogger.error(f"llc 编译失败: {(tsk2.ModuleSha1 if tsk2.ModuleSha1 is not None else "?")} (exit={tsk2.ExitCode} cmd={(tsk2.LlcCmd if tsk2.LlcCmd is not None else "?")})", "LLC")
|
||||
# 失败:先读重定向文件打印 llc 的 stderr|stdout,再打模块 sha1 + 退出码
|
||||
# + 完整命令(.ll 路径即命令末参数)。顺序不可颠倒:VLogger.error 内部
|
||||
# sys.exit(1),其后的语句永不执行。
|
||||
if tsk2.ModuleSha1 is not None and tsk2.LlcCmd is not None:
|
||||
_log_captured_output(tsk2.LogPath, tsk2.ModuleSha1, tsk2.LlcCmd, tsk2.ExitCode, "LLC")
|
||||
VLogger.error(f"llc 编译失败: {(tsk2.ModuleSha1 if tsk2.ModuleSha1 is not None else "?")} (exit={tsk2.ExitCode} cmd={(tsk2.LlcCmd if tsk2.LlcCmd is not None else "?")})", "LLC")
|
||||
ret = 1
|
||||
else:
|
||||
# 成功:丢弃捕获文件(成功路径零额外输出)
|
||||
|
||||
Reference in new issue
Block a user