简体   繁体   English

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

[英]Predictable exit code of crashed process in Windows

For a process exiting normally in Windows, the exit code of the process is generally either the return value from main , or the exit code passed to std::exit . 对于在Windows中正常退出的进程,进程的退出代码通常是main的返回值,或者是传递给std::exit的退出代码。 %ERRORLEVEL% can then be used to query the exit code, and that can be used to determine whether the program executed either correctly, or there were some exceptional inputs/failures that indicate a particular problem (application specific). 然后可以使用%ERRORLEVEL%来查询退出代码,并且可以用于确定程序是否正确执行,或者是否存在一些指示特定问题的特殊输入/故障(特定于应用程序)。

However, I'm interested in the exit code if the process crashes. 但是,如果进程崩溃,我对退出代码感兴趣。 Take a very simple example program: 举一个非常简单的示例程序:

int main()
{
    int * a = nullptr;
    *a = 0xBAD;
    return 0;
}

When I compile this and run in Windows, on the command line I get: 当我编译并在Windows中运行时,在命令行上我得到:

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

The exit code is consistently this number. 退出代码始终为此数字。 Which leads me to several questions: 这引出了几个问题:

  • Was the exit code -1073741819 somehow predicable, based on the invalid write crash? 基于无效的写入崩溃,退出代码-1073741819是否可以预测?
  • If so, is there some way to determine the type of crash, based on the exit code? 如果是这样,有没有办法根据退出代码确定崩溃的类型?
  • Does this change with the compiler used (I used MSVC 2012)? 这是否随使用的编译器而改变(我使用的是MSVC 2012)?
  • Does this change with the version of Windows being used (I used Win10 TP)? 这是否随使用的Windows版本而改变(我使用的是Win10 TP)?
  • Does this change with the architecture (eg. x64 - I used Win32)? 这是否随体系结构而变化(例如x64 - 我使用的是Win32)?

Note, I am not interested in how to modify the program to catch the exception. 注意,我对如何修改程序以捕获异常不感兴趣。 I am interested in classifying crashes that can happen in existing programs, that I may not be able to modify. 我有兴趣对可能在现有程序中发生的崩溃进行分类,我可能无法对其进行修改。

The comment about STATUS_ACCESS_VIOLATION , led me to the documentation on GetExceptionCode : 关于STATUS_ACCESS_VIOLATION的评论让我看到了关于GetExceptionCode的文档:

The return value identifies the type of exception. 返回值标识异常的类型。 The following table identifies the exception codes that can occur due to common programming errors. 下表列出了由于常见编程错误而可能发生的异常代码。 These values are defined in WinBase.h and WinNT.h. 这些值在WinBase.h和WinNT.h中定义。

EXCEPTION_ACCESS_VIOLATION maps to STATUS_ACCESS_VIOLATION in the list that follows. EXCEPTION_ACCESS_VIOLATION映射到STATUS_ACCESS_VIOLATION列表中的STATUS_ACCESS_VIOLATION All exceptions in the list prefixed with STATUS are directly defined to exception codes prefixed with EXCEPTION . 前缀为STATUS的列表中的所有异常都直接定义为以EXCEPTION为前缀的异常代码。 Following the documentation to RaiseException , it explains the process of attempting to debug the exception when it occurs, the final step being: 根据RaiseException的文档,它解释了在异常发生时尝试调试异常的过程,最后一步是:

If the process is not being debugged, or if the associated debugger does not handle the exception, the system provides default handling based on the exception type. 如果未调试进程,或者关联的调试器未处理异常,则系统会根据异常类型提供缺省处理。 For most exceptions, the default action is to call the ExitProcess function. 对于大多数例外,默认操作是调用ExitProcess函数。

So to answer my questions: 所以回答我的问题:

  • Yes, the exit code was predicatble, it maps to EXCEPTION_STATUS_VIOLATION . 是的,退出代码是可预测的,它映射到EXCEPTION_STATUS_VIOLATION
  • Other types of errors would map to other common exception codes. 其他类型的错误将映射到其他常见的异常代码。 However, with a call to RaiseException with an arbitrary exception code (which was unhandled), the exit code of the process could be anything 但是,通过使用任意异常代码(未处理)调用RaiseException,进程的退出代码可以是任何内容
  • The exit code is dependent on the Windows SDK, not the compiler, executing Windows version or architecture. 退出代码取决于Windows SDK,而不是编译器,执行Windows版本或体系结构。 Although this could theoretically change with newer Windows SDKs, that is highly unlikely for backwards compatibility. 虽然理论上这可以随着更新的Windows SDK而改变,但这对于向后兼容性来说是极不可能的。

Here is a related short blog post by Raymond Chen (emphasis mine): 这是Raymond Chen (强调我的)相关的短篇博文

There is no standard for process exit codes. 流程退出代码没有标准。 You can pass anything you want to ExitProcess, and that's what GetExitCodeProcess will give back. 您可以将任何想要的内容传递给ExitProcess,这就是GetExitCodeProcess将返回的内容。 The kernel does no interpretation of the value. 内核没有解释这个值。 If youw want code 42 to mean "Something infinitely improbable has occurred" then more power to you. 如果你想要代码42意味着“发生了无限不可能的事情”那么你就会有更多的权力。

There is a convention, however, that an exit code of zero means success (though what constitutes "success" is left to the discretion of the author of the program) and a nonzero exit code means failure (again, with details left to the discretion of the programmer). 然而,有一个约定,退出代码为零意味着成功(虽然什么构成“成功”由程序作者自行决定)和非零退出代码意味着失败(再次,细节留给自行决定)程序员)。 Often, higher values for the exit code indicate more severe types of failure. 通常,退出代码的较高值表示更严重的故障类型。 The command processor ERRORLEVEL keyword was designed with these convention in mind. 命令处理器ERRORLEVEL关键字的设计考虑了这些约定。

There are cases where your process will get in such a bad state that a component will take it upon itself to terminate the process. 在某些情况下,您的进程将处于如此糟糕的状态,以至于组件将自行终止该进程。 For example, if a process cannot locate the DLLs it imports from, or one of those DLLs fails to initialize, the loader will terminate the process and use the status code as the process exit code. 例如,如果进程无法找到它导入的DLL,或者其中一个DLL无法初始化, 则加载程序将终止该进程并使用状态代码作为进程退出代码。 I believe that when a program crashes due to an unhandled exception, the exception code is used as the exit code. 我相信当程序因未处理的异常而崩溃时,异常代码将用作退出代码。

A customer was seeing their program crash with an exit code of 3 and couldn't figure out where it was coming from. 一位客户看到他们的程序崩溃,退出代码为3,无法弄清楚它的来源。 They never use that exit code in their program. 他们从不在程序中使用退出代码。 Eventually, the source of the magic number 3 was identified: The C runtime abort function terminates the process with exit code 3 . 最终,识别出幻数3的来源: C运行时中止函数使用退出代码3终止进程

This is by no mean a comprehensive answer but rather a few hints so that you can move forward. 这绝不是一个全面的答案,而是一些提示,以便您可以继续前进。

I think there is no way to distinguish all the possible cause of crash automatically. 我认为没有办法自动区分所有可能的崩溃原因。 To do that you would have to catch the error yourself and provide your own exit code 要做到这一点,您必须自己捕获错误并提供自己的退出代码

In order to catch all the possible (catchable) errors, you have to setup both exception and signal handlers. 为了捕获所有可能的(可捕获的)错误,您必须设置异常和信号处理程序。 This is because access violations are exceptions under windows and signal (SIGSEV) under linux. 这是因为访问冲突是Linux下的窗口和信号(SIGSEV)下的异常。

See this question for the fine details about the different kinds of errors on windows: Catching access violation exceptions 有关Windows上不同类型错误的详细信息,请参阅此问题: 捕获访问冲突异常

Here is another thread for signal handling on linux 这是另一个在linux上进行信号处理的线程

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM