• <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

            關(guān)鍵字:callstack, StackWalker

            簡介

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

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

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

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

            3, 隱藏具體API的實(shí)現(xiàn)

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

            5, 支持用戶提供的內(nèi)存只讀函數(shù)

            6, 編譯器支持VC5-VC8

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

            背景

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

            最新版本的dbghelp.dll可以和windbg一起下載(譯者注:windbgMS發(fā)布的一款調(diào)試工具,當(dāng)你下載并安裝的時(shí)候,相應(yīng)的安裝目錄下會(huì)有dbghelp.dll文件)。同時(shí)也包含了symsrv.dll文件,這個(gè)文件主要用來激活MS的公共符號(hào)服務(wù)(這個(gè)服務(wù)主要用來獲取系統(tǒng)文件的調(diào)試信息)。

            如何使用代碼

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

            代碼演示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窗口生成相應(yīng)的輸出如下:

            [...] (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

            你現(xiàn)在可以雙擊任意一行,VS會(huì)自動(dòng)的跳轉(zhuǎn)到你想到的文件并定位到具體行。

            定制你自己的輸出結(jié)構(gòu)

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

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

            獲得callstack的具體信息

            如果你想獲得關(guān)于callstack的具體信息(比如已加載的模塊,地址信息,以及錯(cuò)誤信息),你可以重載下面提供的相應(yīng)的方法。
            演示代碼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);
            };
            上述的方法會(huì)在callstack的生成過程中被調(diào)用。

            callstack的各種類別

            StackWalker的構(gòu)造函數(shù)中,如果你想針對(duì)具體的進(jìn)程生成callstack,那你需要傳入具體的進(jìn)程信息作為參數(shù),比如進(jìn)程ID和進(jìn)程句柄,請(qǐng)看下面的兩個(gè)構(gòu)造函數(shù)。

            演示代碼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
                 );
            };

            顯示一個(gè)異常的callstack

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

            演示代碼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()))
            {
            }

            本文要點(diǎn)

            上下文與callstack

            遍歷一個(gè)線程的callstack,你至少要知道以下兩點(diǎn):

            1, 當(dāng)前線程的上下文context

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

            2, Callstack

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

            初始化STACKFRAME64結(jié)構(gòu)

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

            如果STACKFRAME64的兩個(gè)成員AddrPCAddrFrame沒有被初始化就作為參數(shù)傳給StackWalk64的話,那么這個(gè)函數(shù)在第一次被調(diào)用的時(shí)候就會(huì)失敗。

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

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

            2, 現(xiàn)在的dbghelp版本,你應(yīng)該遵循下面的做法:

            a). 請(qǐng)使用StackWalk64

            b). 請(qǐng)把參數(shù)AddrPC設(shè)置成指向當(dāng)前指令指針,分別是EIP(x86),Rip(x64),stIIP(IA64)

            c). 請(qǐng)把AddrStack設(shè)置成指向當(dāng)前的SP指針,分別是ESP(x86),RSP(x64),IntSP(IA64)

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

            e). IA64的平臺(tái)下請(qǐng)把AddrStore設(shè)置成指向RsBSP。


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

            評(píng)論

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

            但是對(duì)于CPU來說,只有SP是必不可少的,F(xiàn)P是提供給編譯器用的,你可以取消FP的使用開關(guān)。<strong><a href="http://www.uggbootskaufende.com/">ugg boots tall</a></strong>  回復(fù)  更多評(píng)論   

            欧美一区二区久久精品| AA级片免费看视频久久| 久久综合伊人77777| 91精品国产91久久久久福利| 97精品国产97久久久久久免费 | 国产AV影片久久久久久| 97久久久精品综合88久久| 久久99精品久久只有精品| 人妻精品久久无码区| 久久亚洲天堂| 午夜精品久久久内射近拍高清| 亚洲人成无码久久电影网站| 久久久久久av无码免费看大片 | 观看 国产综合久久久久鬼色 欧美 亚洲 一区二区 | 久久精品成人一区二区三区| 久久精品国产亚洲7777| 久久伊人影视| 久久久久人妻一区二区三区| 看久久久久久a级毛片| 精品久久一区二区| 久久精品国产一区二区| 久久丫忘忧草产品| 69SEX久久精品国产麻豆| 99久久精品国产一区二区| 亚洲国产日韩欧美综合久久| 亚洲精品无码久久久久久| 久久综合久久综合久久综合| 免费一级欧美大片久久网 | 久久午夜夜伦鲁鲁片免费无码影视 | 国产伊人久久| 亚洲国产精品无码久久久不卡| 狠狠色丁香久久综合婷婷| 精品国产婷婷久久久| 午夜天堂精品久久久久| 国产精品美女久久久免费| 久久婷婷午色综合夜啪| 亚洲国产精品久久久久网站| 狠狠色丁香久久婷婷综合_中| 国产精品一区二区久久精品| 日韩影院久久| 久久香蕉一级毛片|