Windows Phone App的dump文件实例分析- System.ExecutionEngineException
前言
在开始这篇文章之前我们先来讲讲如何从高度优化的Release版的Dump中找到正确的异常上下文地址,并手动恢复异常发生的第一现场。
1. 什么是异常上下文
简单来说,在windows体系的操作系统里面,每个线程都有自己的线程上下文来保存需要的信息,其中包括当前寄存器的值。我们这里需要找到的异常上下文就是当异常发生的时候,异常分发器帮我们保存的当前寄存器的值(也叫寄存器上下文),利用这些信息结合汇编代码的分析,我们就可以进一步找到发生异常的原因。关于寄存器上下文结构体的定义我们可以在winnt.h找到,这里就不一一解释了。
2. Windbg 中的“.cxr + Context Address”命令可以帮助我们恢复异常发生时候寄存器的一手资料,那么我们只需要找到这个上下文的地址就可以交由这个命令来处理了。
3. 如何找到保存上下文的地址?
当我们使用windbg打开dump file的时候,我们都会看到类似下面的调用堆栈模式:
这个正式异常分发器在帮助我们保存上下文,我们看看RtlDispatchException的函数声明,这里使用”x”命令:
大家已经发现了,这个函数的第二个参数正是我们要找的上下文地址。
4. 有了这个上下文地址,我们就可以使用”.cxr ”命令进行恢复了。这里第二个参数是00c2e168, 执行”.cxr 00c2e168”命令。
5. 这个值真的对吗?恢复了异常发生时的上下文,让我们再看看调用堆栈的变化
这个堆栈和寄存器的的值看起来太诡异了,这不禁让我们对上面的地址产生了怀疑!
6. 想想看,这个dump文件来自于Release版本的App,编译器会在生成release版的可执行文件中进行大量的优化,这种情况下的函数调用会最大程度地使用我们的寄存器来传递参数,而不是靠@esp+0x?来进行。想到了这里,让我们再次回到RtlDispatchException去查看一下。
7. 使用”.frame + index”跳转到调用堆栈中指定的栈帧,并使用”dv”命令查看本地变量
这次我们清晰的看到了寄存器r6和r9里面的内容,正是代码优化的结果。编译器优化了代码使用r6, r9两个寄存器来代替“栈操作”进行了传参。
8. 再次使用”.cxr” 和”k”,这次我们就看到了真正的异常发生第一现场
原来是ldr指令企图加载一个空地址引发了遭难。
现在我们已经学会了如何在高度优化的release版本中分辨出正确的上下文地址,接下来我们继续查找程序奔溃的原因。
定位异常上下文
1. 我们找到了真正的异常上下文,先看来看一下这个调用堆栈。
2. 让我们来看看FontFileReference::ReleaseFragment到底干了些什么,使用命令”uf 模块名字!函数名字“来反编译这个函数。
3. 再次查看这个函数对应的栈帧里面的内容来辅助我们理解上面的汇编代码,”.frame + frame index”
4. 使用命令”dt 模块名字!类名字”来查看FontFileReference的内存布局
分析和推测
1. 从调用堆栈可以看到,下面的语句触发了异常,这里的指令正在执行加载寄存器的操作,load r3寄存器里面存的地址,但是r3=0,我们不禁要问,r3为什么会是0,而这个r3里面又应该是什么呢?
2. 重点分析寄存器,r3。这里给大家分享个看汇编指令的心得,其实我们并不用每次都一行一行的来分析,简单的方法就是找到关键的指令!在这个case里面,我把下面的指令列为关键指令:
为什么要把这几行列为关键指令呢?这是因为这里出现了”r0, #0x10”和blx。这里的”r0, #0x10”是”r0 + 0x10“的意思,”blx” 是调用子函数的意思。再结合FontFileReference这个类的结构,我们可以合理的猜测到r0里面的地址应该指向了this指针,那么r0 + 0x10就是FontFileReference这个类里的成员变量”stream_”。
我们已经知道了r0里面的是this指针,r3里面的是指向类型为ComPtr<IDWriteFontFileStream>的COM对象stream_,那么接下来r3+0x10又是什么呢,关于IDWriteFontFileStream的定义可以在dwrite.h中查到也可以使用”dt /v”显示出来。
我们知道从IUnknown继承下来的COM接口前三个函数一定是QueryInterfacy,Release和AddRef,那么r3+0x10就是ReleaseFileFragment这个函数了。布局大致如下:
在这里需要解释一下为什么我们要进行“猜测”而不是直接调用”dt this”命令来直接查看当前的这个FontFileReference对象呢?因为这个dump是release版本产生的,很多的信息都已经无效了:
3. 经过上面的分析我们就知道了这段汇编代码的主要工作就是调用” stream_->ReleaseFileFragement”。对比”!analyze –v” 命令给出的信息可以验证我们的猜测并得出结论,正是 stream_这个变量为空导致了异常。再来看看这个线程堆里面还有什么有用的信息:
分析和定位我们的源代码
栈里面残留的信息也把问题指向了Font,FontFamily相关的内容,从native的callstack中我们没法定位到我们的代码,这里使用”!CLRStack”命令来看看我们CLR的代码栈里面的调用信息:
红线的代码就是我们自己的代码了,好了打开代码查看一下:
从这段代码看我们在动态创建TextBlock的时候确实没有指定FontFamily这个属性。程序员在开发代码和测试的时候这个异常并没有出现,从我们下载下来的excel文件来看,这个异常只出现在WP8.0平台,30天内导致的闪退累计次数是4000次。这个次数相比我们APP的活跃度来说非常小。所以我们可以推断,这个bug应该是系统的问题,很可能是stream_这个COM对象的ref count增减操作在没有指定FontFamily的一些特殊情况下发生的,它导致了stream_被提前释放掉了。既然是系统的问题,那我们能做什么呢?首先,我们可以指定一个FontFamily给控件TextBlock;其次,在CreateWideTileBackgroundImage这个函数里面的Render调用上添加try catch来捕获这个“System.ExecutionEngineException“。
小结
虽然有些时候问题是由系统引起的,但是通过分析我们还是可以采取一定的措施在我们的代码里面来处理的。
Windows Phone App的dump文件实例分析- System.ExecutionEngineException的更多相关文章
- Windows Phone App的dump文件实例分析-Stack Overflow
前言 这篇文章我们一起来分析一个从Windows Phone Dev Center上下载下来的dump file.首先按照我上一篇的步骤设置好我们的Windbg,并按住Ctrl +D打开dumpfil ...
- Windows Phone App的dump 文件分析
前言 我们在发布了自己的App以后,Windows Phone的Error Report机制会帮助我们收集程序的崩溃信息并发送到微软的服务器上,这可以辅助开发者提高App的稳定性. 那么如何利用这些d ...
- HTTP的上传文件实例分析
这个是http文件传输的一种格式,当时不知道这种格式,废弃. HTTP的上传文件实例分析 由于论坛不支持Word写文章发帖. 首先就是附件发送怎么搞,这个必须解决.论坛是php的.我用Chrome类浏 ...
- 干货分享丨jvm系列:dump文件深度分析
摘要:java内存dump是jvm运行时内存的一份快照,利用它可以分析是否存在内存浪费,可以检查内存管理是否合理,当发生OOM的时候,可以找出问题的原因.那么dump文件的内容是什么样的呢? JVM ...
- linux core dump 文件 gdb分析
core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump. (linux中如果内存越界会收到SIG ...
- windows程序崩溃生成dump文件
第一种: 通过任务管理器:这种适用在程序挂了(crash)的时候进程还未退出,比如我运行程序,出现了下面的错: 此时打开任务管理器,右击相应进程,点击"Create Dump File“: ...
- Windows Store App 用户库文件分组
在Windows应用商店应用程序中浏览用户库中的文件时,可以将文件或者文件夹分组显示,以便于进行分类浏览,这类似于音乐库中的文件可以按照艺术家名称.创建日期或者评级等多种方式进行分类.本节内容将会介绍 ...
- Windows Store App 用户库文件操作
(1)获取用户库位置 如果想要通过应用程序在用户库中创建文件,首先需要获得用户库中指定的位置,例如图片库.文档库等.这里值得注意的是,在获取用户库的位置之前,必须在Windows应用商店项目的清单文件 ...
- Windows Store App 用户库文件夹操作
上面介绍了与用户库文件有关的操作,包括创建.读写等,下面将介绍与用户库文件夹相关的操作. 与文件操作一样,想要对用户库文件夹进行操作,需要首先获取用户库的相应位置,获取的方法上面已经介绍过了,这里不再 ...
随机推荐
- python课程第二周重点记录
python课程第二周重点记录 1.元组的元素不可被修改,元组的元素的元素可以被修改(字典在元组中,字典的值可以被修改) 2.个人感觉方便做加密解密 3.一些方法的使用 sb = "name ...
- 使用phar上线你的代码包
在我前一阵子写的一篇文章<新版 SegmentFault 重构之系统架构>中,很多人对其中提到的利用phar上线代码比较感兴趣,我就在这边跟大家分享下我目前的做法. 哪些项目适合phar打 ...
- Beyond Compare 2
Beyond Compare 2 确实很好用,差异行不交叉,自动留出空白,比windiff要清楚.
- 未定义标识符string
“未定义标识符string” 解决方法: 头文件加上 #include <iostream>using namespace std; string是标准库的,要加std::string, ...
- grep笔记
grep "match_text" file1 file2 file3 ... #grep可以对多个文件进行过滤 --color ...
- javascript里的继承
js里面继承的方式, 1. 类式继承,通过构造函数的继承 function extend(subClass, superClass){ var F = function(){}; F.prototyp ...
- (Python )格式化输出、文件操作、json
本节学习Python的格式化输出,文件操作以及json的简单用法 1.格式化输出 将非字符串类型转换成字符串,可以使用函数:str() 或者repr() ,(这两个函数的区别目前我还没搞懂,求解答) ...
- 如果因特网中的所有链路都提供可靠的交付服务,TCP可靠传输服务是多余的吗?
IP协议因为是无连接的, 所以其传输是不可靠的.虽然链路保证了数据包在端到端的传输中不发生差错,但是它不能保证IP数据包是按照正确的书需到达最终的目的地.IP数据包可以使用不同的路由通过网络,到达接收 ...
- 11.APP打包成ipa文件,然后利用Application Loader 上架
第一步:保证已经完成了证书,Bundle Identifier 和描述文件的配置(未完成参考http://www.jianshu.com/p/391f6102b4fb) 第二步:打开要上传的项目,选择 ...
- js 正则表达式
//判断字符串是否为数字 function checkRate(input) { var re = /^[0-9]+.?[0-9]*$/; if (!re.test(input.rate.value) ...