我目前正在为我的OSS应用程序添加异常和异常处理。从一开始就一直是一个总体想法,但是我想找到一个很好的异常框架,老实说,请理解C ++例外处理惯例,并在开始使用它们之前要好一些。我在C#/。网,Python和其他使用异常的语言方面有很多经验。我对这个想法并不陌生(但远非主人)。

在C#和Python中,当发生未经治疗的例外时,用户会得到一个不错的堆栈跟踪,一般而言 很有用 无价调试信息。如果您正在研究OSS应用程序,则将用户粘贴到问题报告中是...好吧,请说我发现没有这个信息很难生活。对于此C ++项目,我得到了“应用程序崩溃”,或者是从更多知情的用户中获得的,“我做了X,Y和Z,然后崩溃了”。但是我也希望调试信息!

我已经(而且很困难)使我的和平是因为我永远不会看到跨平台和跨编译器获得C ++异常堆栈跟踪的方式,但我知道我可以获取功能名称和其他功能相关信息。

现在,我想要我的例外。我在用着 boost ::异常, ,他们的很好 Diagnostic_information 可以打印出(未直的)功能名称,文件,行和最重要的其他例外特定信息的ThingamaJig,程序员添加了该异常。

自然,我会尽可能地处理代码中的例外,但是我并不是很天真地认为我不会让一对夫妇溜走(当然,当然是无意的)。

所以我想做的是将我的主要入口点包装在一个 try 用a catch 这创建了一个特殊的对话框,该对话框通知用户在应用程序中发生了错误,当用户单击“更多”或“调试信息”或其他任何内容时,会显示更详细的信息。这将包含诊断信息的字符串。然后,我可以指示用户将此信息粘贴到问题报告中。

但是,一种na乱的肠胃感觉告诉我,将所有内容包裹在尝试块中是一个非常糟糕的主意。我要做的是愚蠢的吗?如果是(即使不是),那么有什么更好的方法可以实现我想要的东西?

有帮助吗?

解决方案

将所有代码包装成一个 try/catch 块是A-OK。例如,它不会减慢其内部任何内容的执行。实际上,我所有的程序都具有此框架(代码类似):

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;
    }
}

其他提示

将try/catch块放入main()是可以的,这不会引起任何问题。无论如何,该程序死于一个未经治疗的例外。不过,您对获得最重要的堆栈痕迹的追求根本无济于事。当捕获块捕获异常时,该信息是奇闻趣事。

捕获C ++例外也不会很有帮助。该程序死于STD ::异常的例外情况很苗条。虽然可能发生。 C/C ++应用程序中的可能性更大,因为硬件例外,访问性是numero uno。将这些捕获需要__try和__的关键字在您的main()方法中。同样,几乎没有上下文,您基本上只有一个异常代码。 AV还告诉您哪个确切的内存位置导致例外。

这不仅是跨平台问题,而且您无法在任何平台上获得良好的堆栈跟踪。没有可靠的方法来散步,有太多的优化(例如FramePointer遗漏)使这一旅程成为危险的旅程。这是C/C ++的方式:使其尽可能快,不知道它在炸毁时发生了什么。

您需要做的是以C/C ++方式调试此类问题。您需要创建一个小型群落。它大致类似于旧的“核心转储”,这是在例外发生时的过程图像的快照。那时,您实际上得到了核心的完整转储。如今取得了进展,这是“迷你”,有些必要,因为完整的核心转储将接近2千兆字节。实际上,它可以很好地诊断程序状态。

在Windows上,首先要调用SetunHandLeDexceptionFilter(),您可以提供一个回调功能指针,指向当您的程序死于未手持异常时将运行的函数。任何例外,C ++以及SEH。您的下一个资源是dbghelp.dll,可在Windows下载的调试工具中获得。它具有一个名为MinidumpWritedump()的入口点,它会创建一个小型固定点。

一旦获得MinidumpWritedump()创建的文件,您就会变得非常金色。您可以将.dmp文件加载到Visual Studio中,几乎就像是一个项目一样。按F5和VS磨碎一段时间,试图加载该过程中加载的DLL的.pdb文件。您需要设置符号服务器,那就是 非常 重要的是要获得良好的堆栈痕迹。如果一切正常,您将在抛出异常的确切位置获得“调试中断”。带有堆栈跟踪。

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

  • 使用构建服务器创建二进制文件。它需要将调试符号(.pdb文件)推到符号服务器上,以便在调试微型设备时很容易获得。
  • 配置调试器,以便找到所有模块的调试符号。您可以从Microsoft获得Windows的调试符号,代码的符号需要来自上面提到的符号服务器。
  • 编写代码以捕获未经处理的异常并创建微型固定。我提到了setunhandledexceptionfilter(),但是创建小型设备的代码不应在崩溃的程序中。它可以成功地编写小型木材的几率相当苗条,该程序的状态尚未确定。最好的办法是运行一个“警卫”过程,以保持命名的静音。您的异常过滤器可以设置静音,后卫可以创建微型固定。
  • 为小型设备创建一种方法,可以将客户的计算机从客户的计算机转移到您的计算机上。我们将亚马逊的S3服务用于合理的速度。
  • 将小型处理程序连接到您的调试数据库中。我们使用JIRA,它具有一个网络服务,使我们能够针对具有相同“签名”的早期崩溃数据库验证崩溃桶。当它是唯一的,或者没有足够的命中,我们会要求崩溃管理器代码将小型设备上传到亚马逊并创建错误数据库条目。

好吧,这就是我为我工作的公司所做的。效果很好,它将崩溃量的频率从数千种减少到了几十个。给开源FFDShow组件的创建者的个人信息:我热情地讨厌您。但是您不再崩溃了我们的应用程序!遇难者。

不,这不是愚蠢的。这是一个非常好的主意,当然,直到您遇到一个未经治疗的例外之前,它实际上无需花费任何费用。

请注意,已经有一个例外处理程序包裹您的线程,该线程由OS提供(我认为是C-Runtime的另一个)。您可能需要将某些例外转移到这些处理程序中才能获得正确的行为。在某些架构中,访问错误的数据由异常处理程序处理。所以你可能想特殊案例 EXCEPTION_DATATYPE_MISALIGNMENT 并让它传递到更高级别的异常处理程序。

我包括寄存器,应用程序版本和构建号,异常类型和堆栈转储,其中包括可以地址为代码地址的“十六进制”值注释的十六进制。确保包括EXE的版本编号和构建编号/日期。

您也可以使用 VirtualQuery 很容易地将堆栈值变成“ ModulEname+Offset”。而且,与.map文件结合使用,通常会准确地告诉您崩溃的位置。

我发现我可以训练Beta测试人员很容易发送文本,但是在早期,我得到的是错误对话框而不是文本的图片。我认为这是因为很多用户不知道您可以右键单击任何编辑控件,以获取带有“选择全部”和“复制”的菜单。如果我要再做一次,我会添加一个将文本复制到剪贴板的按钮,以便可以轻松地将其粘贴到电子邮件中。

如果您想遇到“发送错误报告”按钮的麻烦,甚至更好,只是给用户一种将文本传达到自己的电子邮件中的方法,并不会引起任何危险信号关于“我与他们分享什么信息?”

实际上,BOOST :: DIAMATIC_INFORMATION已专门用于“全局”捕获(...)块中,以显示有关异常的信息,这些信息不应达到它。但是,请注意,BOOST :: Diagnostic_information返回的字符串不是用户友好的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top