使用源代码行信息处理 C++ 异常

Posted

技术标签:

【中文标题】使用源代码行信息处理 C++ 异常【英文标题】:C++ exception handling with source code line info 【发布时间】:2010-01-05 15:20:42 【问题描述】:

有没有办法捕获访问冲突之类的异常并获取有关发生异常的行的信息?这对于调试目的非常有用,尤其是对于测试人员..

我的环境是 VS2008 上的 VC++ 的 Windows

【问题讨论】:

你是在什么样的环境下运行的?正在使用什么编译器? 抱歉回复晚了,我的环境是VS 2008和vc++ 【参考方案1】:

访问冲突不是 C++ 术语中的例外,因此答案通常为“否”。可以想象,您的特定实现可能具有将访问冲突转换为 C++ 异常的功能 - 您需要指定您正在使用的编译器和平台。

【讨论】:

在 Windows 中,这将是对 SetUnhandledExceptionFilter 的调用。 @George Edison - 你能提供一些详细信息吗?【参考方案2】:

如果您真的想记录有关从代码中抛出的 C++ 异常(AV 不是一个)的信息,您可以在异常类型的构造函数中使用宏 __FILE____LINE__

【讨论】:

只要确保在对构造函数的调用中使用这些宏,而不是在构造函数体本身中使用——否则你将始终得到相同的位置——就在构造函数体中! :) 哈哈 - 好点子。我曾经使用过一些 THROW 宏,它们会自动为我做这件事。 @Nemanja Trifunovic - 你能给出 FILE LINE 的最小样本吗? @whoi:未经测试。类 my_exception : public std::exception public: my_exception(const char* file, const char* line) log(file, line); ; ... throw my_exception(__FILE__, __LINE__);【参考方案3】:

如果您使用 __catch 处理程序捕获 SEH 异常,您可以在异常发生时访问线程上下文,然后使用 StackWalk64 在生成异常的位置转储调用堆栈。请注意,正如我在这里提到的:Printing the stack trace in C++ (MSVC)?StackWalker 由 Jochen Kalmbach [MVP VC++] 提供,并且可在 codeproject 上找到可能是最简单的方法。它包含了处理底层 StackWalk64 API 的所有细节。

或者,您可以使用我在此处提出的相同解决方案:How to catch divide-by-zero error in Visual Studio 2008 C++? 将 Windows 结构化异常转换为 C++ 异常,并在转换异常的位置捕获堆栈跟踪,如上所示。这会给你一个带有堆栈跟踪的 C++ 异常,就像你在 C# 或 Java 中一样。

【讨论】:

【参考方案4】:

我不确定为什么测试人员需要知道哪一行发生了异常。

不过,开发人员可能想知道。但更好的方法如下:

    在每个 PROGRAM 异常中包含有关类和方法的信息。这些是不应该发生的例外情况。

    这应该由记录您的异常的任何内容输出。您的程序确实会捕获并记录每个异常,不是吗?如果没有,它应该。

    确保您的方法足够小,以使以上信息足以轻松追踪错误。如果您还需要行信息,那么您的方法太大并且您的异常不够具体。

【讨论】:

【参考方案5】:

在 MSVC 中,您可以将调试器设置为在发生任何异常或访问冲突之类的事情时中断,请转到 debug->Exceptions 并选中相应的框。

请注意,访问冲突不是 C++ 异常,在不同的系统上会以不同的方式处理。在 Windows 中,您可以使用结构化异常处理程序来捕获它们。在 unixy 系统中,它通常会进行核心转储。您通常可以从中获取堆栈跟踪。

【讨论】:

【参考方案6】:

对于 msdev / Windows,在开发过程中,始终让您的链接器生成一个 MAP 文件。 当 Windows 抛出访问冲突时,它是您的代码(而不是库),您可以使用该地址来匹配 MAP 文件中的函数/数据,并在几行内获取违规者。至少,你会知道函数/方法。

【讨论】:

【参考方案7】:

这确实是一个编译器问题。

如果您想知道编译器是否可能提供,答案是肯定的。我知道多个 Ada 编译器为未处理的异常提供回溯,所以这显然是可能的。这包括基于 gcc 的 Gnat,因此如果 C++ 编译器对其异常使用任何相同的工具,那么应该已经支持这样做了。

【讨论】:

【参考方案8】:

在 unix 类型的系统上,访问冲突会生成 SEGV 或 BUS 信号。这通常会导致应用程序进行核心转储。您总是可以编写自己的信号处理程序。从那里您可能会在核心转储之前使用 glibc 生成堆栈转储。

核心转储通常会为您提供所有这些,以便在 gdb 中进行 wasy 分析。

【讨论】:

【参考方案9】:

我的回答仅适用于 Windows。你有很多选择:

无需更改代码 -

避免捕获异常(至少是您没有预料到的),让您的应用程序崩溃。配置好老博士。 watson (drwtsn32.exe) 创建故障转储,并在调试器中打开该转储。您将在那里获得确切的代码行。 Use adPlus(来自 windbg 安装)监控应用的运行情况,并在引发异常时创建转储。

在您的代码中,您可以 -

在引发结构化异常时轻松遍历堆栈。例如,请参阅Jochen Kalmbach's stack walker。 在抛出 C++ 异常时使用 this weird hack 遍历堆栈。

最后,关于这个问题,这里已经提出了很多问题。环顾四周,您会得到更多答案。

【讨论】:

【参考方案10】:

在我从事的一个项目中,使用的异常层次结构的根类在构造函数中捕获了一个调用堆栈,以防在捕获异常后稍后需要该信息进行调试。这是一个不错的主意,尽管如果您要捕获并“处理”异常,那么它究竟是什么时候被抛出的并不那么重要。

换句话说,如果您关心此信息(“谁”扔它的上下文),您可能不应该使用异常来报告这种情况,或者您可能不应该在第一名。未捕获的异常会导致崩溃,并在引发异常时为您提供崩溃转储。

【讨论】:

【参考方案11】:

在 Windows 上,C 运行时系统函数 _set_se_translator() 采用带有签名的简单静态函数

void f(int, EXCEPTION_POINTERS*)

您完全不需要哪个论点。在 f 的主体中,抛出你最喜欢的异常。在程序开头附近调用该函数。该文档对微软来说是合理的。

你可以用这个功能做各种额外的事情。

google breakpad 项目有很多好东西。其中包括使用构建符号和 debuginfo dll 将崩溃地址转换为文件、行和函数名称的方法。

【讨论】:

以上是关于使用源代码行信息处理 C++ 异常的主要内容,如果未能解决你的问题,请参考以下文章

java在线聊天项目1.0版 异常处理——开启多个客户端,关闭一个客户端后,在其他客户端中再发出信息会出现异常的处理

开发之统一异常处理

用于检索自定义消息而不是异常详细信息的 PLSQL 异常处理

异常处理

javaweb异常提示信息统一处理(使用springmvc,附源码)

异常处理