• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            focus on linux, c/c++, lua

            Walking the callstack [譯文 part1]

            Walking the callstack

            作者:Jochen Kalmbach

            翻譯:Hefe

            原文出處:www.codeproject.com

            關鍵字:callstack, StackWalker

            簡介

            有些情況下,我們需要顯示當前線程的callstack,或是顯示其他我們感興趣的進程或線程的callstack,為此,我專門寫了這篇文章闡述如何獲得callstack

            我寫這篇文章的主要目的如下:

            1, 提供一些簡單的接口來生成callstack

            2, 基于CPP的特性提供一些方法來用于重載

            3, 隱藏具體API的實現

            4, Callstack信息默認輸出在debug模式窗口(可以自己定制輸出方式)

            5, 支持用戶提供的內存只讀函數

            6, 編譯器支持VC5-VC8

            7, 提供最便利的callstack生成方案

            背景

            目前MS已經提供API(StackWalker64)用來遍歷callstack。從win9x/w2k開始,這個接口就被包含在dbghelp.dll的庫中(NT上,取而代之的是imagehelp.dll),只是這個接口(StackWalk64)w2k之后被改名字了,在w2k之前叫StackWalk,沒有尾巴的64。這個工程只支持最新的Xxx64接口,如果你想在比較舊的平臺上運行,你可以去下載支持相關的平臺dll

            最新版本的dbghelp.dll可以和windbg一起下載(譯者注:windbgMS發布的一款調試工具,當你下載并安裝的時候,相應的安裝目錄下會有dbghelp.dll文件)。同時也包含了symsrv.dll文件,這個文件主要用來激活MS的公共符號服務(這個服務主要用來獲取系統文件的調試信息)

            如何使用代碼

            StackWalker這個類的使用非常簡單。比如:如果你想獲得當前線程的callstack,你只需要初始化一個StackWalk的實例,然后調用ShowCallStack即可。(譯者注:一般我們需要繼承StackWalker這個類,然后聲明并初始化這個子類的實例)。

            代碼演示1

            #include <windows.h>
            #include "StackWalker.h"
             
            void Func5() { StackWalker sw; sw.ShowCallstack(); }
            void Func4() { Func5(); }
            void Func3() { Func4(); }
            void Func2() { Func3(); }
            void Func1() { Func2(); }
             
            int main()
            {
              Func1();
              return 0;
            }

            debug-output窗口生成相應的輸出如下:

            [...] (output stripped)
            d:\privat\Articles\stackwalker\stackwalker.cpp (736): StackWalker::ShowCallstack
            d:\privat\Articles\stackwalker\main.cpp (4): Func5
            d:\privat\Articles\stackwalker\main.cpp (5): Func4
            d:\privat\Articles\stackwalker\main.cpp (6): Func3
            d:\privat\Articles\stackwalker\main.cpp (7): Func2
            d:\privat\Articles\stackwalker\main.cpp (8): Func1
            d:\privat\Articles\stackwalker\main.cpp (13): main
            f:\vs70builds\3077\vc\crtbld\crt\src\crt0.c (259): mainCRTStartup
            77E614C7 (kernel32): (filename not available): _BaseProcessStart@4

            你現在可以雙擊任意一行,VS會自動的跳轉到你想到的文件并定位到具體行。

            定制你自己的輸出結構

            如果你想直接把callstack輸出到文件或是使用其他的輸出結構(譯者注:比如英雄島項目中就是ITrace*),你只需要繼承StackWalker類即可。你有兩種選擇來實現自己的輸出結構:1,重寫OnOutput方法。2,重寫所有的OnXXX函數。當然從OO的思想來說,第一種方法是推薦的,符合KISS的原則。

            演示代碼2
            class MyStackWalker : public StackWalker
            {
            public:
              MyStackWalker() : StackWalker() {}
            protected:
              virtual void OnOutput(LPCSTR szText)
                 { printf(szText); StackWalker::OnOutput(szText); }
            };

            獲得callstack的具體信息

            如果你想獲得關于callstack的具體信息(比如已加載的模塊,地址信息,以及錯誤信息),你可以重載下面提供的相應的方法。
            演示代碼3
            class StackWalker
            {
            protected:
              virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
              virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size,
                 DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
              virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
              virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
            };
            上述的方法會在callstack的生成過程中被調用。

            callstack的各種類別

            StackWalker的構造函數中,如果你想針對具體的進程生成callstack,那你需要傳入具體的進程信息作為參數,比如進程ID和進程句柄,請看下面的兩個構造函數。

            演示代碼4

            class StackWalker
            {
            public:
              StackWalker(
                 int options = OptionsAll,
                 LPCSTR szSymPath = NULL,
                 DWORD dwProcessId = GetCurrentProcessId(),
                 HANDLE hProcess = GetCurrentProcess()
                 );
              // Just for other processes with
              // default-values for options and symPath
              StackWalker(
                 DWORD dwProcessId,
                 HANDLE hProcess
                 );
            };

            真正遍歷callstack的方法也就是下面的ShowCallstack()

            演示代碼5

            class StackWalker
            {
            public:
              BOOL ShowCallstack(
                 HANDLE hThread = GetCurrentThread(),
                 CONTEXT *context = NULL,
                 PReadProcessMemoryRoutine readMemoryFunction = NULL,
                 LPVOID pUserData = NULL
                 );
            };

            顯示一個異常的callstack

            利用這個StackWalker你同樣可以獲得一個異常句柄的callstack。你只需要寫一個異常過濾器即可。

            演示代碼6

            // The exception filter function:
            LONG WINAPI ExpFilter(EXCEPTION_POINTERS* pExp, DWORD dwExpCode)
            {
              StackWalker sw;
              sw.ShowCallstack(GetCurrentThread(), pExp->ContextRecord);
              return EXCEPTION_EXECUTE_HANDLER;
            }
             
            // This is how to catch an exception:
            __try
            {
              // do some ugly stuff...
            }
            __except (ExpFilter(GetExceptionInformation(), GetExceptionCode()))
            {
            }

            本文要點

            上下文與callstack

            遍歷一個線程的callstack,你至少要知道以下兩點:

            1, 當前線程的上下文context

            線程的上下文主要是用來獲取當前IP指針(Instruction Pointer指令指針)SP(Stack Pointer)指針的值,有時候也用來獲取FP(Frame Pointer)指針的值。簡而言之,SPFP指針的區別在于:SP指針指向最近一次的堆棧地址,FP主要用來指向當前函數的地址,你可以參考以下的文檔來了解更多(Difference Between Stack Pointer and Frame Pointer.)。但是對于CPU來說,只有SP是必不可少的,FP是提供給編譯器用的,你可以取消FP的使用開關。

            2, Callstack

            Callstack其實就是一塊內存區域,它包含了調用者的所有的數據內容和地址信息。這些數據內容必須用來獲取callstack(譯者注:感覺這句話有點多余了,為了尊重作者,我還是保留了)。最重要的是:在完成stack-walking之前,這些數據內容必須保持不變。這也就是為什么在獲取有效callstack的時候,當前線程必須要被掛起的原因。如果你想遍歷當前線程的stack,那么你也就不能改變callstack的指針內容,也就是在上下文中聲明的寄存器指針內容。

            初始化STACKFRAME64結構

            為了能利用StackWalk64來成功的遍歷callstack,我們必須用有意義的值來初始化STACKFRAME64。在STACKFRAME64的文檔中,有一小段要點描述如下:

            如果STACKFRAME64的兩個成員AddrPCAddrFrame沒有被初始化就作為參數傳給StackWalk64的話,那么這個函數在第一次被調用的時候就會失敗。

            根據這篇文檔所述,大多數的程序只需要初始化AddrPCAddrFrame這兩個參數,而且這種方式在dbghelp.dll最新版本v5.6.3.7發布之前一直都是正確的。但是,現在你除了要初始化這兩個參數之外,還要初始化AddrStack這個參數。在發現一些麻煩和問題后,我和dbghelp開發小組討論了一下,并得到了如下的答案(2005-08-02,我的觀點是斜體文字)

            1, 在所有的平臺下,AddrStack都要被設置成指向stack pointer(也就是ESP)。你當然可以公布AddsStack應該被設置,甚至你可以說最新版本的dbghelp必須要求這么做。

            2, 現在的dbghelp版本,你應該遵循下面的做法:

            a). 請使用StackWalk64

            b). 請把參數AddrPC設置成指向當前指令指針,分別是EIP(x86)Rip(x64),stIIP(IA64)

            c). 請把AddrStack設置成指向當前的SP指針,分別是ESP(x86)RSP(x64),IntSP(IA64)

            d). 如果當前的Frame Pointer是有意義的,請把AddrFrame設置成指向當前的Frame Pointer,分別是EBP(x86)RBP(x64)[作者的斜體字部分:當時在VC2005B2的環境下,該寄存器無法使用,取而代之的是Rdi]RsBSP(IA64)StackWalk64會在沒有必要的情況忽略這個參數的值。

            e). IA64的平臺下請把AddrStore設置成指向RsBSP


            posted on 2010-10-20 10:24 zuhd 閱讀(798) 評論(1)  編輯 收藏 引用 所屬分類: c/c++

            評論

            # re: Walking the callstack [譯文 part1] 2010-10-28 16:57 ugg boots buy

            但是對于CPU來說,只有SP是必不可少的,FP是提供給編譯器用的,你可以取消FP的使用開關。<strong><a href="http://www.uggbootskaufende.com/">ugg boots tall</a></strong>  回復  更多評論   

            国内精品人妻无码久久久影院导航| 精品久久8x国产免费观看| 26uuu久久五月天| 久久93精品国产91久久综合| 久久久久人妻一区精品| 色婷婷综合久久久久中文字幕 | 亚洲国产精品无码久久久秋霞2| 久久精品aⅴ无码中文字字幕重口| 国产精品久久久天天影视| 久久乐国产综合亚洲精品| 国内精品久久久久影院优| 三级韩国一区久久二区综合 | 欧美粉嫩小泬久久久久久久 | 国产美女久久精品香蕉69| 国产午夜精品久久久久九九| 久久久国产打桩机| 欧美无乱码久久久免费午夜一区二区三区中文字幕 | 人人妻久久人人澡人人爽人人精品| 久久精品水蜜桃av综合天堂 | 久久国产精品无码HDAV| 欧美国产成人久久精品| 成人午夜精品久久久久久久小说| 久久人人爽人人爽人人片AV不 | 国产99久久久久久免费看| 久久九九精品99国产精品| 97久久国产露脸精品国产| 欧美午夜精品久久久久久浪潮| 老司机国内精品久久久久| 99久久超碰中文字幕伊人| 婷婷久久香蕉五月综合加勒比| 亚洲精品99久久久久中文字幕 | 久久久www免费人成精品| 少妇久久久久久被弄到高潮| 精品久久人人爽天天玩人人妻| 99久久夜色精品国产网站| 久久99国产精品久久久| 嫩草影院久久国产精品| 国产精品久久久久久久久久免费| 国产精品九九久久免费视频 | 久久久91精品国产一区二区三区| 久久久久久九九99精品|