Windows 中崩溃进程的可预测退出代码

Posted

技术标签:

【中文标题】Windows 中崩溃进程的可预测退出代码【英文标题】:Predictable exit code of crashed process in Windows 【发布时间】:2015-08-11 06:31:45 【问题描述】:

对于Windows中正常退出的进程,该进程的退出码一般要么是main的返回值,要么是传递给std::exit的退出码。然后,%ERRORLEVEL% 可用于查询退出代码,并可用于确定程序是否正确执行,或者是否有一些异常输入/故障表明存在特定问题(特定于应用程序)。

但是,如果进程崩溃,我对退出代码感兴趣。举一个很简单的例子程序:

int main()

    int * a = nullptr;
    *a = 0xBAD;
    return 0;

当我编译它并在 Windows 中运行时,我在命令行上得到:

MyCrashProgram.exe -> crashes
echo %ERRORLEVEL%  -> -1073741819

退出代码始终是这个数字。这让我想到了几个问题:

退出代码-1073741819 是否可以根据无效写入崩溃以某种方式预测? 如果是这样,是否有某种方法可以根据退出代码确定崩溃的类型? 这是否会随着使用的编译器而改变(我使用的是 MSVC 2012)? 这是否会随着使用的 Windows 版本(我使用的是 Win10 TP)而改变? 这是否会随着架构而改变(例如 x64 - 我使用的是 Win32)?

注意,我对如何修改程序来捕获异常不感兴趣。我有兴趣对现有程序中可能发生的崩溃进行分类,我可能无法修改。

【问题讨论】:

看起来是标准的“STATUS_ACCESS_VIOLATION”错误。 请参阅 SDK 中的 ntstatus.h 了解有关状态码的更多信息。按照惯例,干净地退出的进程返回 0、1 或 Win32 错误代码(它们都是小的正数),因此设置了最高位的退出代码通常意味着状态代码,通常是因为未处理的异常。特别是0xCnnnnnnn 几乎总是一个状态码。 (但是,进程可以返回它喜欢的任何退出代码。) 【参考方案1】:

关于STATUS_ACCESS_VIOLATION 的评论将我带到GetExceptionCode 上的文档:

返回值标识异常的类型。下表列出了由于常见编程错误而可能发生的异常代码。这些值在 WinBase.h 和 WinNT.h 中定义。

EXCEPTION_ACCESS_VIOLATION 映射到下面列表中的STATUS_ACCESS_VIOLATION。列表中所有前缀为STATUS 的异常都直接定义为前缀为EXCEPTION 的异常代码。在RaiseException的文档之后,它解释了在异常发生时尝试调试异常的过程,最后一步是:

如果进程没有被调试,或者关联的调试器没有处理异常,系统会根据异常类型提供默认处理。对于大多数例外情况,默认操作是调用 ExitProcess 函数。

所以回答我的问题:

是的,退出代码是可预测的,它映射到EXCEPTION_STATUS_VIOLATION。 其他类型的错误会映射到其他常见的异常代码。但是,通过使用任意异常代码(未处理)调用 RaiseException,进程的退出代码可以是任何东西 退出代码取决于执行 Windows 版本或体系结构的 Windows SDK,而不是编译器。虽然理论上这可能会随着较新的 ​​Windows SDK 发生变化,但这不太可能实现向后兼容性。

【讨论】:

【参考方案2】:

这是一个相关的short blog post by Raymond Chen(强调我的):

进程退出代码没有标准。你可以通过任何你 想要退出进程,这就是 GetExitCodeProcess 将给出的 背部。内核不解释该值。如果你想要 代码 42 表示“发生了极其不可能的事情”然后 给你更多的力量。

但是,有一个约定,退出代码为零意味着 成功(尽管“成功”的构成由自行决定 程序作者的)和非零退出代码意味着失败 (同样,细节由程序员自行决定)。经常, 退出代码的值越高,表明故障类型越严重。 命令处理器 ERRORLEVEL 关键字是用这些设计的 记住约定。

在某些情况下,您的流程会处于如此糟糕的状态,以至于 组件将自行终止进程。为了 例如,如果一个进程无法找到它从中导入的 DLL,或者一个 那些 DLL 初始化失败,加载器将终止 进程并使用status code 作为进程退出代码。 我相信 当程序由于未处理的异常而崩溃时, 异常代码用作退出代码。

一位客户看到他们的程序崩溃,退出代码为 3,并且 无法弄清楚它来自哪里。他们从不使用那个出口 他们程序中的代码。最终,神奇数字3的来源 已确定:C 运行时中止函数终止进程 退出代码为 3

【讨论】:

【参考方案3】:

这绝不是一个全面的答案,而是一些提示,以便您继续前进。

我认为没有办法自动区分所有可能的崩溃原因。为此,您必须自己捕获错误并提供自己的退出代码

为了捕获所有可能的(可捕获的)错误,您必须同时设置异常和信号处理程序。 这是因为访问冲突是windows下的异常和linux下的信号(SIGSEV)。

有关 Windows 上不同类型错误的详细信息,请参阅此问题: Catching access violation exceptions

这是signal handling on linux的另一个帖子

【讨论】:

感谢您的回答 - 我更新了问题以声明我对修改程序不感兴趣,我只想对发生的崩溃进行分类。

以上是关于Windows 中崩溃进程的可预测退出代码的主要内容,如果未能解决你的问题,请参考以下文章

跟踪 Windows 进程退出代码

怎么用c++编写一个Windows服务程序来监控另一个程序,崩溃后重新启动。

Android Studio Emulator崩溃:进程以退出代码139结束(由信号11中断:SIGSEGV)

进程以退出代码 -1073741819 (0xC0000005) C++ clion 完成

Process.Responding检测进程退出,同时进程运行

Windows 上的 PyQt4 应用程序在退出时崩溃