【问题标题】:Getting information about where c++ exceptions are thrown inside of catch block?获取有关c++异常在catch块内抛出位置的信息?
【发布时间】:2011-03-02 21:11:58
【问题描述】:

我有一个 C++ 应用程序,它将大部分代码包装在 try 块中。当我捕获异常时,我可以将用户返回到稳定状态,这很好。但我不再收到故障转储。我真的很想弄清楚异常发生在代码中的哪个位置,这样我就可以记录它并修复它。

能够在不停止应用程序的情况下获得转储将是理想的,但我不确定这是否可能。

有什么方法可以找出异常是从 catch 块中引发的吗?如果有用,我会在 windows xp 及更高版本上使用本机 msvc++。我的计划是简单地将崩溃记录到各个用户计算机上的文件中,然后在达到一定大小后上传崩溃日志。

【问题讨论】:

标签: c++ exception visual-c++ logging


【解决方案1】:

这可以通过使用 SEH(结构化异常处理)来实现。关键是 MSVC 通过 SEH 实现 C++ 异常。另一方面,纯 SEH 更加强大和灵活。

这就是你应该做的。而不是像这样使用纯 C++ try/catch 块:

try
{
    DoSomething();
} catch(MyExc& exc)
{
    // process the exception
}

你应该用 SEH 块包裹内部代码块DoSomething

void DoSomething()
{
    __try {
        DoSomethingInner();
    }
    __except (DumpExc(GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) {
        // never get there
    }
}

void DumpEx(EXCEPTION_POINTERS* pExc)
{
    // Call MiniDumpWriteDump to produce the needed dump file
}

也就是说,在 C++ try/catch 块中,我们放置了另一个原始 SEH 块,它只转储所有异常而不捕获它们。

有关使用 MiniDumpWriteDump 的示例,请参阅 here

【讨论】:

  • 如果你不关心可移植性,直接使用 SEH 是可以的。标准 C++ 异常更具可移植性(嗯,除了一些不支持异常的嵌入式系统编译器)。
  • 这是处理 C++ 异常的有效方法。如果您想获得有关操作系统异常的有用信息,例如堆栈溢出或访问冲突,您应该安排从另一个进程调用MiniDumpWriteDump
  • @George:目前在 C++ 中没有可移植的方法来获取堆栈跟踪。我似乎记得这是新的 C++0x 标准试图改进的一种情况。
  • 不一定。好吧,如果您谈论堆栈溢出,并且在堆栈展开之前处理此异常 - 您应该从另一个线程调用“MiniDumpWriteDump”(因为我们的线程几乎没有堆栈)。但是没有必要从另一个进程做事情。除非您将异常视为错误,并且不要依赖该过程
  • @23W:您是在谈论未处理的异常(这是崩溃而不是警告),还是第一次机会异常调试器消息?如果是后者,那可能是因为SendMessage 有自己的 SEH 处理程序,它处理该异常。
【解决方案2】:

可以设计您的例外以包括源文件名和行号。为此,您需要创建一个派生自std::exception 的类来包含信息。在下面的示例中,我的应用程序有一个异常库,包括my_exception。我还有一个traced_error,它是从我的应用程序级异常派生的模板异常类。 traced_error异常保存文件名&行号信息,调用应用级异常类的what()方法获取详细错误信息。

#include <cstdlib>
#include <string>
#include <stdexcept>
#include <iostream>
using namespace std;


template<class EX>
class traced_error : virtual public std::exception, virtual public EX
{
public:
    traced_error(const std::string& file, int line, const EX& ex)
    :   EX(ex),
        line_(line),
        file_(file)
    {       
    }

    const char* what() const
    {
        std::stringstream ss;
        static std::string msg;
        ss << "File: " << file_ << " Line: " << line_ << " Error: " << EX::what();
        msg = ss.str().c_str();
        return msg.c_str();
    }

    int line_;
    std::string file_;
};

template<class EX> traced_error<EX> make_traced_error(const std::string& file, int line, const EX& ex)
{
    return traced_error<EX>(file, line, ex);
}


class my_exception : virtual public std::exception
{
public:
    my_exception() {};

    const char* what() const
    {
        return "my_exception's what";
    }
};

#define throwx(EX) (throw make_traced_error(__FILE__,__LINE__,EX))


int main()
{
    try
    {
        throwx(my_exception());
    }
    catch( const std::exception& ex )
    {
        cout << ex.what();
    }
    return 0;
}

这个程序的输出是:

文件:.\main.cpp 行:57 错误: my_exception 是什么

您也可以重新设计它,以便应用程序级异常派生自traced_error,而不是相反,以防您希望捕获特定的应用程序级异常。在您的catch 中,您可以将错误记录到日志文件并使用MiniDumpWriteDump() 创建转储文件。

【讨论】:

    【解决方案3】:

    一个独立于编译器的技巧是将 throw 语句包装在一个函数中。该函数可以在抛出异常之前执行其他职责,例如记录到日志文件。它还可以方便地放置断点。如果您创建一个宏来调用该函数,您可以自动包含发生抛出的__FILE____LINE__

    【讨论】:

      【解决方案4】:

      您需要分析堆栈以找出异常的来源。对于 msvc,有一个名为 dbghelp.dll 的库可以帮助您注销异常。一般来说,我所做的是在使用正确的程序数据库(pdb 文件)旁边注销minidump file and use this to replay the issue。这适用于没有源代码或您不想向其提供 pdb 的客户系统。

      【讨论】:

        【解决方案5】:

        您可以使用MiniDumpWriteDump 函数编写转储。

        如果您使用的是 C++ 异常,那么您可以在抛出异常的任何地方简单地包含文件/行/函数信息(因此,如果您调用 std::exception.what(),您会看到它是文本)异常(使用 ____FUNCTION____、____FILE____ 和 ____LINE____ 宏)。

        如果您尝试捕获操作系统异常,那么让应用程序崩溃可能是更好的选择。

        【讨论】:

          猜你喜欢
          • 2012-09-21
          • 1970-01-01
          • 1970-01-01
          • 2014-06-19
          • 1970-01-01
          • 2023-03-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多