崩溃时需用系统级机制捕获堆栈:Windows调用SetUnhandledExceptionFilter+MiniDumpWriteDump,Linux/macOS用sigaction捕获SIGSEGV等信号并调用backtrace(),禁用非异步信号安全函数,配合PDB或调试符号实现精准定位。

崩溃时如何捕获 Qt 程序的堆栈信息
Qt 本身不内置崩溃捕获机制,真正的崩溃(如段错误、访问违规)会绕过 Qt 事件循环,qInstallMessageHandler 和 QObject::installEventFilter 都无效。必须依赖操作系统级信号处理或第三方 crash handler。
Linux/macOS 下用 sigaction 捕获 SIGSEGV、SIGABRT 等信号;Windows 下用 SetUnhandledExceptionFilter。关键不是“Qt 怎么做”,而是“进程崩溃时怎么活下来一秒,把调用栈打出来”。
- 不要在信号处理函数里调用
qDebug()、malloc、std::string构造等非异步信号安全函数 - 推荐用
backtrace()+backtrace_symbols_fd()(Linux)或MiniDumpWriteDump()(Windows)这类轻量、信号安全的接口 - Qt 6.2+ 的
QApplication::setFallbackSessionManagementEnabled(false)与崩溃无关,别被误导
Qt 日志系统能捕获哪些“崩溃前兆”
qInstallMessageHandler 只能捕获 qFatal、qCritical 等 Qt 主动抛出的日志,比如 QMetaObject::activate: Receiver is null 或 QObject: Cannot create children for a parent that is in a different thread —— 这些不是崩溃,但大概率会在几秒内引发崩溃。
把这类日志重定向到文件并带上时间戳和线程 ID,比等真崩溃再抓堆栈更实用:
void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
QByteArray localMsg = msg.toLocal8Bit();
QString logLine = QString("[%1] %2 (%3:%4, %5)")
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"))
.arg(msg)
.arg(context.file ? context.file : "")
.arg(context.line)
.arg(QThread::currentThread()->objectName());
QFile file("qt_runtime.log");
file.open(QIODevice::WriteOnly | QIODevice::Append);
file.write(logLine.toUtf8() + "\n");
file.close();
}
- 务必在
main()最开头调用qInstallMessageHandler(myMessageHandler) - 避免在 handler 中使用
QFile以外的 Qt 类(如QDir、QJsonDocument),它们可能触发内部日志,造成递归 - 日志文件路径建议用绝对路径,相对路径在 daemon 或双击启动时容易写失败
Windows 上用 Qt 调用 MiniDumpWriteDump 的最小可行方案
Qt 不封装 Windows 崩溃转储,但你可以用原生 API。核心是:注册异常过滤器 → 在回调中调用 MiniDumpWriteDump → 保存为 .dmp 文件供 WinDbg 或 Visual Studio 分析。
注意三点:Qt 的 main() 函数默认是 WinMain 入口,需确保链接 dbghelp.lib;dump 文件需要符号文件(.pdb)才能解析;Qt Creator 默认不生成 pdb,要手动开启 CONFIG += force_debug_info。
- 在
.pro文件中添加:LIBS += -ldbghelp和CONFIG += force_debug_info - dump 文件名建议包含 PID 和时间戳,避免多实例覆盖:
QString dumpPath = QString("crash_%1_%2.dmp").arg(QCoreApplication::applicationPid()).arg(QDateTime::currentMSecsSinceEpoch()); - 不要尝试在 dump 后
qApp->quit()—— 此时进程已处于异常状态,强行调用 Qt API 可能导致二次崩溃
为什么 Qt 的 qFatal 不等于崩溃,但要当崩溃处理
qFatal 默认调用 abort(),这会触发 SIGABRT,进而进入你注册的信号处理器 —— 所以它其实是“可控崩溃”。很多团队误以为只要没 segfault 就安全,结果 qFatal 在 Release 模式下静默退出,连日志都没留下。
- 开发期务必把
QT_FATAL_WARNINGS=1加入环境变量,让所有qWarning升级为qFatal,提前暴露问题 - Release 包中不要禁用
qFatal输出:在qInstallMessageHandler中对QtFatalMsg类型单独处理,强制写磁盘并延时 500ms 再 abort - Qt Widgets 程序常见
qFatal("QWidget: Must construct a QApplication before a QWidget"),这种属于启动顺序错误,靠崩溃日志不如靠 CI 阶段的静态检查
真正难抓的是释放后使用(use-after-free)和竞态导致的随机崩溃 —— 它们往往不产生可复现的堆栈,这时候得靠 AddressSanitizer(Linux/macOS)或 Application Verifier(Windows),而不是依赖 Qt 层的日志机制。











