Qt4.8.5 QtWebKit QWebView 用户栈检查崩溃问题的思考
最近在项目中,发现在使用Qt4.8.5 提供的QWebView与网页交互的时候,
m_pWebView->page()->mainFrame()->evaluateJavaScript(tmp);
QtWebKitd4.dll模块偶尔会出现崩溃,如图

中断查看调用堆栈(加载QtWebkitd4.pdb 才可看到正确的堆栈信息)

最后停止在 QT StackBounds::checkConsistency。从堆栈类名跟函数名看出,可能是跟堆栈相关,尝试看看源文件,找到函数定义

函数很短,跟具体业务逻辑没什么关系,可以得出release模式函数直接返回,debug模式下,在栈上创建一个对象来获取此时栈指针大小,assert根据堆栈增长方向,
检查此时栈指针跟 m_origin 和m_bound的关系。 从名字推测是与栈基址跟栈边界比较。继续看代码,怎么给这两个赋值的。

在X86CPU配合MSVC编译器的平台下,栈基址 m_origin 通过FS寄存器中保存的 NT_TIB 线程信息块中得到当前线程的栈的基址,没有问题
那么栈边界呢m_bound ?
通过源码的注释,似乎想通过NT_TIB获得,但没这样做(后面验证,此方法得到的栈边界不可靠,只能获得已提交栈大小。qt5.4中提供新的方式获取,后面修改也是基于此)。
继续看看 QT是怎么做的。
转到函数定义:

QT把栈边界的大小固定为512kB,显然不适用所有平台,源码的注释也给出了说明 this code unsafely guesses stack sizes!,WINDOWS、WINCE等平台下,may be work wrong。
明知不可行但还是设置了固定值,而且是全平台通用的一个值,想想我们在以往的项目中是不是也做过类似的妥协呢?
可见实现Qt4.8.5时还是比较匆忙,并不是一个稳定的版本。
后面再看qt5.4时(中间哪个版本开始修改的,这个没有关注。。),全平台都是代码获取,感觉很可靠,至少是在window平台。(Qt团队效率还是可以的!)
问题原因大致定位了,但是是哪里导致栈空间被大量使用了呢?
猜测1:QtWebKit内部调用,消耗了大量栈空间。
验证: 1.1 新建一个工程,用QWebView加载网页 m_pWebView->load(QUrl("xxx"));
1.2 注册js调用对象 m_pWebView->page()->mainFrame()->addToJavaScriptWindowObject("servers", this);
1.3 声明接口供js调用
int JS2QT::MainCall(QString szCallOperate,QString szExternData)
{
….
m_pWebView->page()->mainFrame()->evaluateJavaScript(tmp);
}
1.4 在执行js之前查看堆栈ebp(栈基址) 与esp(栈顶指针)。 当前消耗 ebp-esp 才几k,属于正常现象。
1.5 执行js,正常。
猜测2:项目工程在进入QWebView调用之前=的调用链就已经消耗了大量栈空间。
验证: 在进入MainCall之前下断点,观察到进入MainCall之后, ebp-esp 瞬间消耗了 850KB以上的栈空间(默认1MB),
执行js,出现中断。850KB 早已超过512KB, debug模式下QtWebKit只要执行stackCheck,必然assert。
相同情况relese不会崩溃,正好验证了之前的源码在relese模式下不检查stack。
结果: 查看实际项目中MainCall的实现,该函数内部确实声明了大量的临时对象,消耗了大量的栈空间。
修改意见:
|
主工程(生成exe的工程)属性 |
QtWebKit |
|
|
Debug |
项目属性-》链接器-》系统 修改堆栈保留大小(推荐2097152) 其他默认0 |
|
|
Release |
项目属性-》链接器-》系统 修改堆栈保留大小(推荐2097152) 其他默认0 |
Release模式下QtWebKit不对堆栈使用做检查。一旦发生栈空间不够,直接崩溃。 |
Q&A
- 同样操作为什么debug模式必崩溃,但是release模式不会崩溃?
答:因为QtWebkit StackBounds类负责做栈边界检查的时候,认为栈的大小固定在512kB,而主线程的默认栈 1MB,当主线程使用超过512kB的栈空间时,QtWebkit必崩,
但在release模式下,QtWebkit不做栈检查,只要主线程使用栈不超过1MB,程序就不会崩溃。
- 为什么跟js做一些交互的时候,程序会崩溃?
答:使用QWebView内核,与js交互都是通过我们项目中的 xxx::MainCall 完成分发的,MainCall中声明的各种数组消耗了大量的栈空间,
目前来看已使用850kB左右,此时函数调用继续发生,堆栈进一步被消耗,当某些操作需要消耗大一点栈空间的时候,此时就会发生崩溃,而如果崩溃在
Vs编译的库(不主动做栈检查,不主动产生中断),会友好提示 stackOverflow,崩溃在其他库(不主动做栈检查,不主动产生中断),就会显得莫名其妙了吧。
!!隐藏的问题,虽然扩大默认栈大小,可以解决问题,但是,改变默认栈大小带来的问题?
- 如果最后我们的工程生成的是 xxx.exe 以进程提供服务,那么我们设置的默认堆栈大小会起到作用。
- 如果我们工程生成的是 xxx.dll 或 xxx.ocx。我们的服务是被IE(其他进程)加载,主线程的堆栈是由加载进程决定的,我们工程设置的大堆栈将不起作用。(解决方法:修改IE默认堆栈大小字段,利用PE工具很方便)
总之,问题的根源在于,一个函数中大量使用堆栈资源,势必不是良好的程序设计风格,就目前及以后会出现的问题,提两点自己的建议
- 一个函数不要太长,应按照实际业务分发处理,多加些函数负责不同的操作;同时一个函数内部不要消耗太多的栈空间,这样有可能导致后的函数调用时,stackOverflow。
- 使用标准库容器来管理大量临时对象(容器对象在栈上分配空间,容器中的内容在堆上分配,堆的释放由标准库负责,有一定的可靠性).
附:手动修改QtWebKitd4.dll文件,改变QtWebkit 设置的固定栈大小。
下断点观察 m_bound的指令地址

指令地址 0x10EC3636 查看模块加载地址:0x10000000 则文件偏移地址 0x00EC3626
用二进制编辑器打开QtWebKit4d.dll (debug才需修改) 找到0x00EC3626 或直接搜内容 2D00000800

2D 00 00 08 00 对应汇编指令 sub eax 80000h 注意为小端字节序
保存即可。
替换QtWebkitd4.dll 断点查看

修改成功
Qt4.8.5 QtWebKit QWebView 用户栈检查崩溃问题的思考的更多相关文章
- qt4.8.5 qtwebkit 静态编译 版本
2013年就编译好了,qtwebkit是最不好编译的了,尤其是静态编译,这儿分享给大家 估计总有人会用得到... 静态库下载地址:http://yunpan.cn/cyyNqrApbVDwq 提取码 ...
- Qt4 QWebView的使用例子
最近项目中使用QT4框架开发PC端软件,所以耐着性子学习了一下QT相关的东西. 下面是QT4中QWebView的使用方法,觉得蛮方便的. 我使用的开发环境是:Win7+Qt 4.8.5开发库+qtcr ...
- QT4项目升级到QT5遇到的问题和解决方法
QT4升级到QT5改动: PC部分: [改QTDIR变量] 在工程根目录下找到.user文件, 如InnoTabPlugin.vcxproj.user 修改指向你的QT5根目录: <Proper ...
- QT项目升级(QT4.6.3到QT5.2)时,遇到的问题和解决方法
QT4升级到QT5修改: PC部分: [改QTDIR变量] 在project根文件夹下找到.user文件, 如InnoTabPlugin.vcxproj.user 改动指向你的QT5根文件夹: < ...
- 【Qt开发】QT4 升级到 QT5 改动
QT4 升级到 QT5 改动: PC部分: [改 QTDIR 变量] 在工程根目录下找到 .user 文件 , 如 InnoTabPlugin.vcxproj.user 修改指向你的 QT5 根目录 ...
- 使用 PyQt 转换网页到 PDF(使用QtWebKit加载完毕后,打印整个窗口就行了,真简单!)
import sys try: from PyQt4 import QtWebKit from PyQt4.QtCore import QUrl from PyQt4.QtGui import QAp ...
- 如何使用Microsoft的驱动程序验证程序解释无法分析的崩溃转储文件
这篇文章解释了如何使用驱动程序验证工具来分析崩溃转储文件. 使用Microsoft驱动程序验证工具 如果您曾经使用Windows的调试工具来分析崩溃转储,那么毫无疑问,您已经使用WinDbg打开了一个 ...
- Python各种花式截图工具,截到你手软
前言: 最近,项目中遇到了一个关于实现通过给定URL,实现对网页屏幕进行截图的一个功能,前面代码中已经用python的第三方库实现了截图功能,但在上线以后出现了一些bug,所以就改bug的任务就落在了 ...
- Linux2.6.11版本:classic RCU的实现
转载自:http://www.wowotech.net/kernel_synchronization/linux2-6-11-RCU.html 一.前言 无论你愿意或者不愿意,linux kernel ...
随机推荐
- ADVDAV驱动
// ADV7179 register configuration array for PAL mode ADI_DEV_ACCESS_REGISTER ADV7179_Cfg[]={ {ADV717 ...
- excel2003出现“向程序发送命令时出现错误”解决方法
电脑已经预装了office2010,因为想要学习 <Excel 图标之道>,安装了Excel2003,刚刚安装好,打开出现“向程序发送命令时出现错误”,虽然点击确定就可以用了,可总是觉得不 ...
- 使用C#开发计划任务调度服务
在系统运维中常常需要定期去跑一些计划任务,比如扫描服务器监控其性能.检查SQL Server作业是否正常.监控MQ队列是否存在堵塞现象等.如果使用Windows计划任务调度,一来管理起来就比较松散,二 ...
- VBA_Excel_教程:Option,错误处理
Option Explicit '强制对模块内所有变量进行声明 Option Compare Text '字符串不区分大小写 '指定数组的第一个下标为1(只能指定0或1) Sub s1() On Er ...
- Eclipse 复制代码保留原格式
当代码中有折叠代码时,无法复制格式,觉得方法有2: 1.设置取消折叠 如图所示,取消勾选"Enable folding"即可,该方法一劳永逸,缺点是以后编码显示不够简洁. 2.点开 ...
- 自增长主键Id的另类设计
一.引言 在使用ORM框架时,一个表有一个主键是必须的,如果没有主键,就没有办法来唯一的更新一条记录.在Sql Server数据库和Mysql数据库设置自增长的主键是一件很轻松的事情,如果在Oracl ...
- Android自动化学习笔记之MonkeyRunner:用Eclipse执行MonkeyRunner脚本
Info: 初步学习,难免会有疏漏,以后我会不断修改补全,直到完美.转载请注明出处,谢谢. 2014-10-11: 初版 2014-10-16: 完善 ------------------------ ...
- Spark Streaming中动态Batch Size实现初探
本期内容 : BatchDuration与 Process Time 动态Batch Size Spark Streaming中有很多算子,是否每一个算子都是预期中的类似线性规律的时间消耗呢? 例如: ...
- 简单递推 HDU-2108
要成为一个ACMer,就是要不断学习,不断刷题...最近写了一些递推,发现递推规律还是挺明显的,最简单的斐波那契函数(爬楼梯问题),这个大家应该都会,看一点稍微进阶了一点的,不是简单的v[i] = v ...
- 1219 spring3 项目总结
Spring3 项目总结 列志华 (组长) http://www.cnblogs.com/liezhihua/ 团队guihub https://github.com/LWHTF/OrderingFo ...