向用户显示异常调试信息

Luc*_*cas 6 c++ debugging boost exception debug-information

我正在努力为我的OSS应用程序添加异常和异常处理.例外从一开始就是一般的想法,但是我想找到一个好的异常框架,并且在开始使用它们之前,要完全理解C++异常处理约定和习惯用法.我在使用C#/ .Net,Python和其他使用异常的语言方面有很多经验.我对这个想法并不陌生(但远非主人).

在C#和Python中,当发生未处理的异常时,用户会获得一个很好的堆栈跟踪,并且通常会获得许多非常有用的无价调试信息.如果你正在开发一个OSS应用程序,让用户将这些信息粘贴到问题报告中......好吧,我只是说我发现没有它就很难生存.对于这个C++项目,我得到"应用程序崩溃",或者来自更明智的用户,"我做了X,Y和Z,然后它崩溃了".但我也想要调试信息!

我已经(并且非常困难)让我感到安心,因为我永远不会看到跨平台和交叉编译的方式获得C++异常堆栈跟踪,但我知道我可以获得函数名称和其他相关信息.

现在我想要那些未经处理的例外情况.我正在使用boost :: exception,他们有这个非常好的diagnostic_information thingamajig,它可以打印出(unmangled)函数名,文件,行,最重要的是,程序员添加到该异常的其他异常特定信息.

当然,我会尽可能地处理代码中的异常,但我认为我不会让一对夫妇溜走(当然是无意的).

所以我想做的是将我的主入口点包装在一个try块中catch,创建一个特殊的对话框,通知用户应用程序中发生了错误,当用户单击"更多"或"调试"时会显示更详细的信息信息"或其他什么.这将包含来自diagnostic_information的字符串.然后,我可以指示用户将此信息粘贴到问题报告中.

但是一种唠叨的直觉感觉告诉我,将一切都包装在一个试块中是一个非常糟糕的主意.我将要做什么愚蠢的事情?如果是(即使不是),有什么更好的方法来实现我想要的?

Han*_*ant 33

在main()中放置一个try/catch块是可以的,它不会导致任何问题.无论如何,该程序已经死于未处理的异常.在你寻求获得最重要的堆栈跟踪时,它根本没有帮助.当catch块捕获异常时,该信息是奇闻趣事.

捕获C++异常也不是很有用.程序死于从std :: exception派生的异常的几率非常小.虽然它可能发生.在C/C++应用程序中更有可能是由于硬件异常导致死亡,AccessViolation是数字uno.捕获它们需要main()方法中的__try和__except关键字.同样,很少有上下文可用,你基本上只有一个异常代码.AV还会告诉您哪个确切的内存位置导致异常.

这不仅仅是一个跨平台的问题,你无法在任何平台上获得良好的堆栈跟踪.没有可靠的方法来进行堆叠,有太多的优化(如framepointer遗漏)使这成为一个危险的旅程.它是C/C++方式:尽可能快地使它,不知道它爆炸时发生了什么.

你需要做的是用C/C++方式调试这些问题.您需要创建一个minidump.它大致类似于旧的"核心转储",即异常发生时过程映像的快照.那时候,你实际上已经完全转储了核心.已经取得了进展,现在它是"迷你",有点必要,因为一个完整的核心转储需要接近2千兆字节.它实际上很适合诊断程序状态.

在Windows上,通过调用SetUnhandledExceptionFilter()开始,您提供一个回调函数指针,该函数将在程序在未处理的异常上死亡时运行.任何异常,C++以及SEH.您的下一个资源是dbghelp.dll,可在Windows调试工具下载中找到.它有一个名为MiniDumpWriteDump()的入口点,它创建一个minidump.

一旦你获得了由MiniDumpWriteDump()创建的文件,你就会变得非常精彩.您可以在Visual Studio中加载.dmp文件,几乎就像它是一个项目.按F5和VS研磨掉一段时间,试图为进程中加载​​的DLL加载.pdb文件.您需要设置符号服务器,这对于获得良好的堆栈跟踪非常重要.如果一切正常,您将在抛出异常的确切位置获得"调试中断".使用堆栈跟踪.

要使这项工作顺利进行,您需要做的事情:

  • 使用构建服务器创建二进制文件.它需要将调试符号(.pdb文件)推送到符号服务器,以便在调试minidump时可以随时使用它们.
  • 配置调试器,以便它可以找到所有模块的调试符号.您可以从Microsoft获取Windows的调试符号,代码的符号需要来自上面提到的符号服务器.
  • 编写代码来捕获未处理的异常并创建minidump.我提到了SetUnhandledExceptionFilter(),但是创建minidump的代码不应该在崩溃的程序中.它能成功编写minidump的可能性相当小,程序的状态尚未确定.最好的办法是运行一个"守护"过程,密切关注一个命名的互斥锁.您的异常过滤器可以设置互斥锁,后卫可以创建minidump.
  • 为minidump创建一种从客户端机器转移到您的机器的方法.我们使用亚马逊的S3服务,以合理的价格使用太字节.
  • 将minidump处理程序连接到调试数据库.我们使用Jira,它有一个Web服务,允许我们使用相同的"签名"来验证崩溃桶与早期崩溃的数据库.如果它是唯一的,或者没有足够的命中,我们会要求崩溃管理器代码将minidump上传到Amazon并创建bug数据库条目.

嗯,这就是我为我工作的公司所做的.工作得非常好,它将碰撞斗频率从数千减少到数十.给开源ffdshow组件创建者的个人信息:我热爱你.但是你不再崩溃了我们的应用程序!坏蛋.


GMa*_*ckG 3

将所有代码包装在一个try/catch块中是可以的。例如,它不会减慢其中任何内容的执行速度。事实上,我所有的程序都有(代码类似)这个框架:

int execute(int pArgc, char *pArgv[])
{
    // do stuff
}

int main(int pArgc, char *pArgv[])
{
    // maybe setup some debug stuff,
    // like splitting cerr to log.txt

    try
    {
        return execute(pArgc, pArgv);
    }
    catch (const std::exception& e)
    {
        std::cerr << "Unhandled exception:\n" << e.what() << std::endl;
        // or other methods of displaying an error

        return EXIT_FAILURE;
    }
    catch (...)
    {
        std::cerr << "Unknown exception!" << std::endl;

        return EXIT_FAILURE;
    }
}
Run Code Online (Sandbox Code Playgroud)