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
}
複製代碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章