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


    前言

    这篇文章我们一起来分析一个从Windows Phone Dev Center上下载下来的dump file。首先按照我上一篇的步骤设置好我们的Windbg,并按住Ctrl +D打开dumpfile。可以看到下面的界面:

    clip_image002

    分析一个dump file可以分解为4个步骤,第一步是信息收集,第二步是定位异常上下文,第三步分析和推理出现问题的原因,第四步分析和定位我们的源代码并进行修复和验证。

    信息收集

    我们可以使用一些命令浏览一下这个dump file对应的系统版本和一些模块的信息辅助我们后面的分析。

    1. version命令,查看系统版本号

    clip_image004

    2. lm命令,显示当前加载的模块。

    clip_image005

    可以使用lmv命令查看所有模块的详细信息,如果想看某一模块的详细信息,需要使用参数m,比如查看System_Data_Linq_ni.dll的详细信息,”lmv mSystem_Data_Linq_ni”

    clip_image006

    定位异常上下文

    当异常发生的时候,寄存器的上下文会被异常分发器保存在栈上。我们可以通过一些方法找到并恢复发生异常时候的上下文,从上下文中找到我们需要的信息,这里使用”!analyze -v”命令。

    1. 首先确认发生异常的线程。有时候发生异常的线程不止一个,我们在使用 “!analyze –v”之前需要确认发生异常的线程。使用”~* kvb”命令来查看所有线程的调用堆栈。”~*“ 命令是枚举所有的线程,”kvb“命令是列出线程的调用堆栈。

    clip_image008

    可以发现所有的线程都是等待状态,只有线程0不是,从线程0的callstack可以看出来线程0就是我们要找的发生异常的那个线程,。

    2. 切换到发生异常的线程,”~0 s”

    clip_image009

    3. 使用“!analyze –v”,这个扩展命令来帮助我们找到发生异常时候的上下文,并显示当时的调用堆栈,有些情况下给出的调用堆栈并不是发生异常的第一现场,遇到这种情况我们需要进一步分析。

    clip_image011

    clip_image013

    分析和推测

    在上面的显示中,我们发现一个很有意思的托管调用堆栈,里面反复出现了”System.Diagnostics.StackTrace..ctor()+0x12”。看起来StackTrack这个类型的对象在构造的时候调用了自己的成员函数GetStackFramesInternal,而这个函数又去构造了新的StackTrack的对象,如此反复以至于发生了循环调用而导致栈被耗尽,这里并没有给出与我们的代码相关的调用,看起来很像一个.net framework的bug。那么为什么会发生这样的调用呢?让我们继续进行分析,看看是哪里引发了这个调用。

    为了找到更多的线索,我们可以进一步查看发生异常的线程栈里都保留了什么,我们可以通过”!teb”命令来查看当前线程的属性,并找到栈的基址和大小,有了栈的基址和大小,我们就可以查看里面的内容了。

    1. 查看当前线程的属性,”!teb”命令

    clip_image014

    2. ”dps + 地址范围”命令可以让我们查看栈里面保留的信息。

    clip_image015

    跳过这些无效的内容,我们继续往后查看。

    clip_image017

    红线的模块和函数正是我们App中的代码,我们可以做一个大胆的推理在这里。我们的函数DecrementPendingAndFinishIfNecessary调用了Logger.Info函数,这个函数使用了系统的StackTrace.CaptureStackTrace来获取当前的调用堆栈。那么为什么这个函数StackTrace.CaptureStackTrace又会去构造它自己的对象呢?让我们打开我们程序的源代码进一步分析。

    分析和定位我们的源代码

    打开我们的代码并找到Logger.Info的实现,红色的代码正是验证了我们上面的推理。在一些极端的形况下,StackTrace会创建失败并扔出异常,这个异常恰好被后面的catch块捕获再次调用了Logger的函数,而这个函数会再次创建StackTrace类型的对象,继续触发异常导致了反复的调用。

    private static void WriteLine(Level level, string message)
    {
        try
        {
            if (0 == message.Length)
            {
                return;
            }
            StackTrace st = new StackTrace(); // 1. 这里exception
            string name = st.GetFrame(2).GetMethod().Name;
            string prefix = string.Format("[{0}]@{1}", level, name);
            message = prefix + "-" + message;
        }
        catch (Exception e)
        {
            Logger.Fatal("Faild in WriteLog,message:" + e.Message); // 2. 然后执行这里
        }
    }
    
    public static void Fatal(string message)
    {
        WriteLine(Level.Fatal, message); //3. 这里继续执行1, 1 继续exception
    }

     

    知道了原因,我们就可以修改代码来修复了,最简单的方法就是先去掉catch里面的调用。

    后续问题

    那么为什么系统的StackTrack的GetStackFramesInternal会失败呢?感兴趣的同学可以尝试反编译命令来查看里面的细节。

    分享代码,改变世界!

  • 相关阅读:
    【林】Ubuntu下安装和设置 OpenSSH Server
    吐吐槽
    【不定时推荐】这些年读过的书第一本--《一个人的朝圣》
    source insight 和keil 编辑对齐
    WeifenLuo DockContent停靠窗口的大小设置
    指针无法保存值
    php 练习基础
    php 写日志
    数据库设计——字段类型设计
    .net用TreeView实现CheckBoxes全选 反选 子节选中 传值
  • 原文地址:https://www.cnblogs.com/ms-uap/p/4220928.html
Copyright © 2020-2023  润新知