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

  1. 参考qt5.4修正m_bound栈边界跟栈基址的获取,重新编译 QtWebKit.sln(附件替换qt4.8.5后重新编译WebKit.sln工程即可)
  2. 二进制编辑器 直接找到对应代码二进制,修改数字大小(适合本地使用)
  3. 增大m_bound 栈边界固定值,重新编译QtWebkit.sln 工程(不推荐)

Release


项目属性-》链接器-》系统 修改堆栈保留大小(推荐2097152)

其他默认0


Release模式下QtWebKit不对堆栈使用做检查。一旦发生栈空间不够,直接崩溃。

Q&A

  1. 同样操作为什么debug模式必崩溃,但是release模式不会崩溃?

    答:因为QtWebkit StackBounds类负责做栈边界检查的时候,认为栈的大小固定在512kB,而主线程的默认栈 1MB,当主线程使用超过512kB的栈空间时,QtWebkit必崩,

    但在release模式下,QtWebkit不做栈检查,只要主线程使用栈不超过1MB,程序就不会崩溃。

  1. 为什么跟js做一些交互的时候,程序会崩溃?

    答:使用QWebView内核,与js交互都是通过我们项目中的 xxx::MainCall 完成分发的,MainCall中声明的各种数组消耗了大量的栈空间,

    目前来看已使用850kB左右,此时函数调用继续发生,堆栈进一步被消耗,当某些操作需要消耗大一点栈空间的时候,此时就会发生崩溃,而如果崩溃在

    Vs编译的库(不主动做栈检查,不主动产生中断),会友好提示 stackOverflow,崩溃在其他库(不主动做栈检查,不主动产生中断),就会显得莫名其妙了吧。

!!隐藏的问题,虽然扩大默认栈大小,可以解决问题,但是,改变默认栈大小带来的问题?

  1. 如果最后我们的工程生成的是 xxx.exe 以进程提供服务,那么我们设置的默认堆栈大小会起到作用。
  2. 如果我们工程生成的是 xxx.dll 或 xxx.ocx。我们的服务是被IE(其他进程)加载,主线程的堆栈是由加载进程决定的,我们工程设置的大堆栈将不起作用。(解决方法:修改IE默认堆栈大小字段,利用PE工具很方便)

总之,问题的根源在于,一个函数中大量使用堆栈资源,势必不是良好的程序设计风格,就目前及以后会出现的问题,提两点自己的建议

  1. 一个函数不要太长,应按照实际业务分发处理,多加些函数负责不同的操作;同时一个函数内部不要消耗太多的栈空间,这样有可能导致后的函数调用时,stackOverflow。
  2. 使用标准库容器来管理大量临时对象(容器对象在栈上分配空间,容器中的内容在堆上分配,堆的释放由标准库负责,有一定的可靠性).

附:手动修改QtWebKitd4.dll文件,改变QtWebkit 设置的固定栈大小。

下断点观察 m_bound的指令地址

指令地址 0x10EC3636  查看模块加载地址:0x10000000  则文件偏移地址 0x00EC3626

用二进制编辑器打开QtWebKit4d.dll (debug才需修改) 找到0x00EC3626 或直接搜内容 2D00000800

2D  00 00 08 00 对应汇编指令 sub eax 80000h     注意为小端字节序

保存即可。

替换QtWebkitd4.dll 断点查看

修改成功

时间: 2024-10-11 03:10:27

Qt4.8.5 QtWebKit QWebView 用户栈检查崩溃问题的思考的相关文章

qt4.8.5 qtwebkit 静态编译 版本

2013年就编译好了,qtwebkit是最不好编译的了,尤其是静态编译,这儿分享给大家 估计总有人会用得到... 静态库下载地址:http://yunpan.cn/cyyNqrApbVDwq  提取码 eb64 静态qtwebkitdemo:http://yunpan.cn/cyt9DtmPEkW6j  提取码 dd42 作者:混沌游侠 qq:414713276 博客地址:http://www.cnblogs.com/wallini/p/4222438.html 来自为知笔记(Wiz)

性能测试-7.事务、检查、关联、思考时间、集合点

本章目录: 事务及事务状态 检查点 思考时间 集合点 关联 一.事务: 一个或多个业务操作的集合,协助统计业务的时间.TPS就是每秒钟所处理的事务数. 在要添加的函数前后插入事务开始和结束.运行后日志会显示事务的结果和运行时间. 事务=响应时间+传输时间+网络延迟时间 函数自身的时间也会有 二.检查点 检查点:预期值与实际值比较  实际值在所定义的函数下面语句的服务器的响应包里 检查点支持参数化,性能测试中,不建议做过,会消耗时间(对服务器没有负载) 三.思考时间 lr_think_time(1

QT4项目升级到QT5遇到的问题和解决方法

QT4升级到QT5改动: PC部分: [改QTDIR变量] 在工程根目录下找到.user文件, 如InnoTabPlugin.vcxproj.user 修改指向你的QT5根目录: <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Template|Win32'"> <QTDIR>C:\qt\Qt5.2.1\5.2.1\msvc2010</QTDIR> </PropertyGro

QT项目升级(QT4.6.3到QT5.2)时,遇到的问题和解决方法

QT4升级到QT5修改: PC部分: [改QTDIR变量] 在project根文件夹下找到.user文件, 如InnoTabPlugin.vcxproj.user 改动指向你的QT5根文件夹: <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Template|Win32'"> <QTDIR>C:\qt\Qt5.2.1\5.2.1\msvc2010</QTDIR> </Prop

QT4 升级到 QT5 升级总结

QT4 升级到 QT5 改动: PC部分: [改 QTDIR 变量] 在工程根目录下找到 .user 文件 ,  如 InnoTabPlugin.vcxproj.user 修改指向你的 QT5 根目录: <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Template|Win32'"> <QTDIR>C:\qt\Qt5.2.1\5.2.1\msvc2010</QTDIR> <

使用 PyQt 转换网页到 PDF(使用QtWebKit加载完毕后,打印整个窗口就行了,真简单!)

import sys try: from PyQt4 import QtWebKit from PyQt4.QtCore import QUrl from PyQt4.QtGui import QApplication, QPrinter except ImportError: from PySide import QtWebKit from PySide.QtCore import QUrl from PySide.QtGui import QApplication, QPrinter app

当您的应用程序崩溃时,您希望知道所有一切信息

为何应该关心崩溃报告 设想一个用户安装了一个移动应用程序的情况.由于抱有很高的预期,该应用程序被频繁地使用,或许在前几个星期一天使用好几次.应用程序创建的数据以及用户投入该应用程序中的精力在不断积累.然后,突然之间,应用程序崩溃了.用户的数据现在被损坏或丢失.尽管等待了好几天,该应用程序仍未更新,这些问题仍未得到解决.用户为该应用程序提供了一星评价和批评性评论,然后放弃了该应用程序.即使以后某个时刻提供了修复程序,用户也不可能返回再使用该应用程序. 作为应用程序供应商,您投入了数月时间来设计和开

正写代码,此处省略若干字,遂崩溃。

在Asp.Net工程里写Razor代码 某处有个循环读取级联对象生成表格行的操作 本能的要写一个方法用于回调 尝试使用Google Chrome搜索“Razor 内联函数”发现搜索框无法切换中文输入法并在已经切换好后经常性无故的失去焦点 为了找原因,搜索“Google Chrome 中文输入法” 发现有记录显示很久以前就有这种bug,并且google修复过: 尝试在Google Chrome 官方网站下载最新版本的(因为当前用的是已经百度提供的) 打开官网后点击下载,同意协议.当开始下载的时候浏

素材检查的一些思考

现在很多算法都使用了深度学习算法,这就带来一个问题:测试素材的需求量很大.这是一些AI测试与其他测试很重的一个区别.所以测试的工作就不仅仅是原来的一些测试,还包括素材的相关工具,具体来说有:素材的采集和整理,采集需求文档,素材清洗标准,素材check.今天就主要来谈谈关于素材检查的一些思考. 素材检查就是检查check采集的素材是否满足测试的要求.这就要求前期的采集需求文档要写的很明确,需求文档不能只是一些笼统的说说,要具体到采集哪些case,采集的数量要求,人数要求,每个人要做多少case.在