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 用户库文件夹操作
上面介绍了与用户库文件有关的操作,包括创建.读写等,下面将介绍与用户库文件夹相关的操作. 与文件操作一样,想要对用户库文件夹进行操作,需要首先获取用户库的相应位置,获取的方法上面已经介绍过了,这里不再 ...
随机推荐
- CSS布局(二)
本节内容:position.float.clear.浮动布局例子.百分比宽度 position CSS中的position属性设置元素的位置.属性值:static.relative.fixed.abs ...
- <停车卫> 产品需求说明书 version 2.0
<停车卫> 产品需求说明书 文档版本号: Version 2.0 文档编号: xxxx 文档密级: 归属部门/项目: 产品名: 停车卫 子系统名: 编写人: kina 编写日期: 2015 ...
- cocoapods specs 镜像
在使用cocoapods 进行update 或者 install的时候 每次回去更新获取的pod specs,每次速度都不是很理想,博主最近对github上的specs仓库进行了镜像,分别在gitca ...
- 关于swfupload,客户端中文乱码解决方案!
公司做了个邮箱系统,上传附件用到了swfupload控件,测试成功上线后hr找我说上传附件中文乱码. 奇怪了,就只有她的电脑出问题,我找了好几台电脑,虚拟机也跑了怎么就找不到问题. 后来网上查了好久, ...
- loop 循环次数
在汇编中可以使用 loop 段地址:偏移地址 并配合 cx 达到循环执行的目的,但是在一些资料中看到说,cx 是循环的次数,我觉得这是不对的. 比如下面这段代码的作用是使得最终的 ax 中的值为 3 ...
- 【转】scp 命令格式
SCP 命令 \ svn 删除所有的 .svn文件 find . -name .svn -type d -exec rm -fr {} \; linux之cp/scp命令+scp命令详解 名称 ...
- 断言(ASSERT)的用法
ASSERT ()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行.如果表达式不为0,则继续执行后面的语句.这个宏通常原来判 ...
- 利用JDBC连接MySQL并使用MySQL
driver为JDBC的驱动. url为数据库的地址. usrname和password分别为数据库的用户名和密码. Connection类用来连接MySQL. PreparedStatement类用 ...
- 【CronExpression表达式详解和案例】
1. cron表达式格式: {秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)} 2. cron表达式各占位符解释: {秒数} ==> 允许值范围: 0~59 ,不允许 ...
- CodeForces 742B Arpa’s obvious problem and Mehrdad’s terrible solution (暴力枚举)
题意:求定 n 个数,求有多少对数满足,ai^bi = x. 析:暴力枚举就行,n的复杂度. 代码如下: #pragma comment(linker, "/STACK:1024000000 ...