我以前写过一篇文章Windows应用程序是如何在国产系统上运行的,介绍了wine的运行机制。由于Windows不开源,对于Wine开发者而言,就是一黑盒子,要完美复制一份黑盒子系统,显然是难度很大,所以很多Windows应用程序在国产系统上运行,就是各种崩,比如下图这种:
遇到崩溃就要去定位,定位之前首先需要理解seh。理解seh
日志通道,对于分析程序崩溃、异常处理等底层问题至关重要。
seh
通道是什么?—— Windows的“安全网”seh
是 Structured Exception Handling (结构化异常处理) 的缩写。这是Windows操作系统提供的一套核心机制,用来处理程序运行中发生的“意外事件”(即异常),比如:
c0000005
页面错误就是最典型的)、执行非法指令等。RaiseException
)一个异常,来表示发生了某种错误状态。您可以把SEH想象成一个“安全网”。当程序在执行时“坠落”(发生异常),SEH机制会被激活,它会沿着函数调用栈(call stack)从里到外一层层地寻找能够处理这个特定异常的“异常处理器”(exception handler)。
try...catch
或 __try...__except
块),程序就会跳转到那里执行,有机会从错误中恢复。Wine的seh
日志通道,就是专门用来记录Wine在模拟这套复杂的SEH机制时,所有关键活动的日志。
seh
日志里有什么?当您开启seh
通道后,您会看到Wine在处理异常时的详细步骤,例如:
NtRaiseException
)。seh
日志?—— 使用 WINEDEBUG
环境变量和所有其他调试通道一样,我们使用WINEDEBUG
环境变量来控制seh
日志的输出。
WINEDEBUG
的基本语法是 [class][+/-]channel
:
class
:日志级别,可以是 err
(错误), warn
(警告), fixme
(待办), trace
(追踪)。不写默认为 warn
。+/-
:+
表示开启该通道,-
表示关闭。channel
:您感兴趣的通道名,比如 seh
, heap
, dll
, relay
等。操作方法:
seh
的错误和警告信息 (推荐入门):
WINEDEBUG=seh wine your_program.exe
或者更明确地写:
WINEDEBUG=warn+seh wine your_program.exe
这会显示所有与SEH相关的 warn
和 err
级别的日志。seh
的所有活动 (信息量巨大):如果你想看到最详细的SEH处理流程,可以使用 trace
级别。
WINEDEBUG=trace+seh wine your_program.exe
警告:trace
级别会产生海量的输出,可能会淹没掉关键信息。通常只在已经把问题范围缩小后,才用它来精细分析。WINEDEBUG=warn+seh,warn+dll wine your_program.exe
您之前的日志里有这样一行:019c:err:seh:KiUserCallbackDispatcher ignoring exception
019c
: 线程ID。err
: 日志级别是“错误”。seh
: 通道名是seh
。KiUserCallbackDispatcher ignoring exception
: 日志内容,意思是“KiUserCallbackDispatcher这个函数忽略了一个异常”。这行日志本身就是seh
通道产生的。它告诉我们,程序内部发生了一个异常,SEH机制被激活了,但是找到的某个处理器决定“忽略”这个异常,让程序继续往下跑。这种“忽略”的行为有时是正常的(程序在自我修复),但反复、大量地出现,通常意味着程序正处在一个非常不稳定的状态,在崩溃的边缘疯狂试探。
如果您想更深入地了解这个被忽略的异常到底是什么,就可以使用 trace+seh
来运行:
# 确保在纯32位环境下
export WINEARCH=win32
export WINEPREFIX=~/.wine32-chrome
# 开启seh的追踪日志,并将所有输出重定向到文件
WINEDEBUG=trace+seh wine "C:\Program Files\Google\Chrome\Application\chrome.exe" --no-sandbox &> seh_trace_log.txt
然后,在 seh_trace_log.txt
文件里搜索 ignoring exception
,查看它前面发生了什么,比如是哪个异常码(ExceptionCode)被触发了。这可能会给您提供新的线索。
总之,seh
通道是您调试程序崩溃、死锁和各种“玄学”问题的强大武器。学会使用它,您就能更深入地理解程序的底层行为了。