前言

在开始这篇文章之前我们先来讲讲如何从高度优化的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的更多相关文章

  1. Windows Phone App的dump文件实例分析-Stack Overflow

    前言 这篇文章我们一起来分析一个从Windows Phone Dev Center上下载下来的dump file.首先按照我上一篇的步骤设置好我们的Windbg,并按住Ctrl +D打开dumpfil ...

  2. Windows Phone App的dump 文件分析

    前言 我们在发布了自己的App以后,Windows Phone的Error Report机制会帮助我们收集程序的崩溃信息并发送到微软的服务器上,这可以辅助开发者提高App的稳定性. 那么如何利用这些d ...

  3. HTTP的上传文件实例分析

    这个是http文件传输的一种格式,当时不知道这种格式,废弃. HTTP的上传文件实例分析 由于论坛不支持Word写文章发帖. 首先就是附件发送怎么搞,这个必须解决.论坛是php的.我用Chrome类浏 ...

  4. 干货分享丨jvm系列:dump文件深度分析

    摘要:java内存dump是jvm运行时内存的一份快照,利用它可以分析是否存在内存浪费,可以检查内存管理是否合理,当发生OOM的时候,可以找出问题的原因.那么dump文件的内容是什么样的呢? JVM ...

  5. linux core dump 文件 gdb分析

    core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump. (linux中如果内存越界会收到SIG ...

  6. windows程序崩溃生成dump文件

    第一种: 通过任务管理器:这种适用在程序挂了(crash)的时候进程还未退出,比如我运行程序,出现了下面的错: 此时打开任务管理器,右击相应进程,点击"Create Dump File“: ...

  7. Windows Store App 用户库文件分组

    在Windows应用商店应用程序中浏览用户库中的文件时,可以将文件或者文件夹分组显示,以便于进行分类浏览,这类似于音乐库中的文件可以按照艺术家名称.创建日期或者评级等多种方式进行分类.本节内容将会介绍 ...

  8. Windows Store App 用户库文件操作

    (1)获取用户库位置 如果想要通过应用程序在用户库中创建文件,首先需要获得用户库中指定的位置,例如图片库.文档库等.这里值得注意的是,在获取用户库的位置之前,必须在Windows应用商店项目的清单文件 ...

  9. Windows Store App 用户库文件夹操作

    上面介绍了与用户库文件有关的操作,包括创建.读写等,下面将介绍与用户库文件夹相关的操作. 与文件操作一样,想要对用户库文件夹进行操作,需要首先获取用户库的相应位置,获取的方法上面已经介绍过了,这里不再 ...

随机推荐

  1. 三维模型2.5D轮廓提取及遮挡部分的剔除

    轮廓提取相对容易,只需在2.5D渲染视角下,导出模型的顶点坐标以及基于视角的消隐后的三角形面,将三角面投影后合并就可得到轮廓,轮廓坐标基于2.5d图的基准坐标换算就得到.提取轮廓的在我另外一篇文章中有 ...

  2. 【java】:多表关联、排序

    SELECT * FROM (SELECT coll.collection_id, coll.report_id, coll.new_id, news.title,news.abstract_txt, ...

  3. CSS布局技巧 -- 各种居中

    多行垂直居中 废话少说,直接上例子!!! display:table Html代码: <div class="wrapper"> <div class=" ...

  4. android蓝牙打印机

    您还未登录!|登录|注册|帮助 首页 业界 移动 云计算 研发 论坛 博客 下载 更多 reality_jie的专栏 编程的过程是一种微妙的享受       目录视图 摘要视图 订阅 CSDN2013 ...

  5. NSNotification --关于通知

    一. 通知(NSNotification)的发送(Post)和注册(add)都必须借助于通知中心(NSNotificationCenter) 发送通知: NSNotificationCenter *c ...

  6. Ubuntu16.04下面配置java环境变量

    我在ubuntu 16.04下面配置java环境变量的时候,开始在网上查信息的时候,没太注意ubuntu的版本,结果在.bashrc下面设置,在.profile下面设置,都不成功, 后面才想起来搜索u ...

  7. shell下批量重命名svn文件的方法

    shell下批量重命名svn文件的方法 目标: 将svn目录下所有文件重命名 , 原文件前缀为 ucc_ , 批量改为 xmd_ 用tree看下当前svn目录 ucc_1.c ucc_1.h ucc_ ...

  8. IIS 部署 node.js ---- 基础安装部署

    一些可能有用的相关文章: https://blogs.msdn.microsoft.com/scott_hanselman/2011/11/28/window-iisnode-js/ http://b ...

  9. linux 第一题 计算题

    #!/bin/bash echo "输入第一个数字" read A b= ]] do && [[ ${A} != *[!]* ]] then echo " ...

  10. FMDB浅析

    一.FMDB介绍 FMDB是一种第三方的开源库,FMDB就是对SQLite的API进行了封装,加上了面向对象的思想,让我们不必使用繁琐的C语言API函数,比起直接操作SQLite更加方便. FMDB优 ...