如何让 QT 应用程序写入崩溃日志

Posted

技术标签:

【中文标题】如何让 QT 应用程序写入崩溃日志【英文标题】:How to make QT Application write a crash log 【发布时间】:2011-08-08 06:52:51 【问题描述】:

我正在从事在 Qt、Windows 中开发的项目, 我使用 Qt Version 4.1.4 和 VC6 IDE 编译项目,产品的大部分使用都在 WindowsXp 上。

我经常收到来自测试人员和客户的崩溃报告,这些报告没有修复模式并且无法在我的开发环境中重现。

所以我得出的结论是,我需要从我的程序中编写崩溃日志,以便在程序崩溃时始终获得调用堆栈。

在 linux 中,我知道可以通过一些分段错误的信号处理来做到这一点。但是如何在 windows/C++ 中做同样的事情呢?

我尝试在 Google 上搜索以找到一些现成的解决方案,但没有任何运气。

也接受有关任何第三方工具/库(免费或许可)的建议,但请不要建议任何我必须修改模块中的每个函数才能获取日志的方法,因为项目很大,我不能买得起。

提前致谢。

更新。 我现在已将 StackWalker 附加到我的代码中(有点麻烦的 _MBCS 和 UNICODE),但至少设法附加了它。

我的主文件如下所示,

class MyStackWalker : public StackWalker
  
   FILE *sgLogFile;
   public:
   MyStackWalker() : StackWalker() 
   
     sgLogFile = fopen("ThisIsWhatIcallLog.log", "w");
   
   MyStackWalker(DWORD dwProcessId, HANDLE hProcess) : StackWalker(dwProcessId, hProcess) 
   
   
   virtual void OnOutput(LPCSTR szText) 
    
      printf(szText); 
      fprintf(sgLogFile, "%s",szText);
      fclose(sgLogFile);
      StackWalker::OnOutput(szText); 
   
;

LONG Win32FaultHandler(struct _EXCEPTION_POINTERS *  ExInfo)

    MyStackWalker sw;   
    sw.ShowCallstack();
    return EXCEPTION_EXECUTE_HANDLER;


void InstallFaultHandler()

    SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) Win32FaultHandler);

但由于找不到符号,我得到链接器错误,

main.obj : error LNK2001: unresolved external symbol "public: int __thiscall StackWalker::ShowCallstack(void *,struct _CONTEXT const *,int (__stdcall*)(void *,unsigned __int64,void *,int,unsigned long *,void *),void *)" (?ShowCallstack@StackWalker@@QAEHPAXPBU_CONTEXT@@P6GH0_K0HPAK0@Z0@Z)
main.obj : error LNK2001: unresolved external symbol "protected: virtual void __thiscall StackWalker::OnDbgHelpErr(char const *,int,unsigned __int64)(?OnDbgHelpErr@StackWalker@@MAEXPBDH_K@Z)
main.obj : error LNK2001: unresolved external symbol "protected: virtual void __thiscall StackWalker::OnLoadModule(char const *,char const *,unsigned __int64,int,int,char const *,char const *,unsigned __int64)" (?OnLoadModule@StackWalker@@MAEXPBD0_KHH001@Z)
main.obj : error LNK2001: unresolved external symbol "protected: virtual void __thiscall StackWalker::OnSymInit(char const *,int,char const *)" (?OnSymInit@StackWalker@@MAEXPBDH0@Z)
main.obj : error LNK2001: unresolved external symbol "public: __thiscall StackWalker::StackWalker(int,char const *,int,void *)" (??0StackWalker@@QAE@HPBDHPAX@Z)
release/Prog.exe : fatal error LNK1120: 5 unresolved externals
Error executing link.exe.

我已修改 StackWalker.cpp 文件以适应 UNICODE 支持,将“_tcscat_s”替换为“wcscat”。

你能看出来,这里出了什么问题吗?

【问题讨论】:

请记住,您的用户可能会将任何意外操作称为“崩溃”。这包括挂起、图形损坏,甚至数据错误。崩溃日志只会记录真正的崩溃。 您好 MSalters,感谢您的回复,是的,我明白,只有“真正的崩溃”是可以的,其余的我可以使用错误跟踪系统中用户提供的信息进行管理 【参考方案1】:

对于跨平台应用程序,Breakpad 可能是更好的解决方案。但是 Windows 的简单方法是通过 SetUnhandledExceptionFilter 设置未处理的异常处理程序,并在处理程序中使用 WriteMiniDumpEx 编写小型转储(带有您需要的配置)。

example

【讨论】:

你好,我尝试了一些基于 SetUnHandledExceptionFilter 的解决方案,但它们似乎没有多大帮助,Google breakpad 在获取崩溃日志方面似乎非常复杂(它要求设置服务器等,基本上是糟糕的文档)任何简单且易于使用的专门用于 Qt+Windows 的东西都会非常好。仍在寻找一个好的答案 添加了示例链接。有两件事让你检查。首先 - 您是否安装了 dbghelp.dll?它没有安装在旧的 MS 操作系统上,因此您必须将它与您的应用程序一起分发。其次 - 确保 QT 不处理未处理的异常(或者在这种情况下手动处理 QT 处理程序中的 TopLevelFilter(0) - 如果没有异常信息,您仍然会得到堆栈。【参考方案2】:

Google Breakpad 是一个看起来很有用的跨平台崩溃报告库。

另请参阅StackWalker,了解一些仅用于获取堆栈跟踪的 Windows 代码。

此外,通过覆盖 QCoreApplication::notify 来安装事件过滤器并捕获所有异常(包括异步异常,如堆栈溢出和 Windows 上的访问冲突)也是一个好主意。

【讨论】:

我会先尝试使用 Google breakpad,让我们期待最好的结果,否则我会选择 QCoreApplicaton::notify,...无论如何感谢您的回答 你好,我尝试了一些基于 SetUnHandledExceptionFilter 的解决方案,但它们似乎没有多大帮助,Google breakpad 在获取崩溃日志方面似乎非常复杂(它要求设置服务器等,基本上是糟糕的文档)任何简单且易于使用的专门用于 Qt+Windows 的东西都会非常好。仍在寻找一个好的答案 @maxchirag:我添加了一个指向 Windows 解决方案的链接,您应该能够将其集成到您的 Qt 应用程序中。 您好 Macke,感谢您的回复,我刚刚查看了 stackwalker,它似乎对调试更有帮助,但没有从客户端获取崩溃日志。 @maxchirag:只需将OutputDebugStringA(buffer); 替换为fprintf(crashlog_fp, buffer) 之类的东西,您就可以将跟踪存储到文件中。我在我们端使用我们的日志系统,然后弹出一个消息框,允许您在记事本中打开日志文件。然后,该文件可以轻松地从客户通过电子邮件或其他方式发送给您。

以上是关于如何让 QT 应用程序写入崩溃日志的主要内容,如果未能解决你的问题,请参考以下文章

使用 Qt 运行日志流送时 Python 崩溃

使用 Qt 运行日志流送时 Python 崩溃

Android应用崩溃后异常捕获并重启并写入日志

获取Android崩溃crash信息并写入日志发送邮件

qt 在不同线程中同时查询 MySQL = 崩溃

如何在开始时停止让我的应用程序崩溃