下面小編就為大家帶來一篇在linux與windows上獲取當前堆棧信息的方法。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
在編寫穩定可靠的軟件服務時經常用到輸出堆棧信息,以便用戶/開發者獲取準確的運行信息。常用在日志輸出,錯誤報告,異常檢測。
在linux有比較簡便的函數獲取堆棧信息:
#include?<stdio.h> #include?<execinfo.h> #include?<signal.h> #include?<stdlib.h> #include?<unistd.h> void?handler(int?sig)?{ ?void?*array[5]; ?size_t?size; ?//?get?void*'s?for?all?entries?on?the?stack ?size?=?backtrace(array,?5); ?//?print?out?all?the?frames?to?stderr ?fprintf(stderr,?"Error:?signal?%d:n",?sig); ?char**?msgs?=?backtrace_symbols(array,?size); ?for(int?i=1;i<size void int><p>以上代碼從參考的stack<a href="http://www.php.cn/wiki/414.html" target="_blank">錯誤報告</a>中稍作修改而來。核心就是backtrace與backtrace_symbols兩個函數。</p> <p>windows下推薦用StackWalker這個開源代碼,支持X86,AMD64,IA64。</p> <p>如果你需要一個最簡的代碼,那么下面是我抽取出來的代碼,明顯比Linux要復雜一些。(Win的很多功能實現起來要復雜一些,當然也有很多功能實現要比Linux簡單很多。)</p> <p>我會做一些講解,在后面。</p> <p class="jb51code"><br></p> <pre class="brush:php;toolbar:false">#include?"stdafx.h" #include?<windows.h> #include?<iostream> #include?<dbghelp.h> #include?<tlhelp32.h> using?namespace?std; HANDLE?ph; void?baz() { ?int*?v?=?0; ?*v?=?0; } void?bar() { ?baz(); } void?foo(){ ?try?{ ??bar(); ?} ?except(EXCEPTION_EXECUTE_HANDLER)?{ ??auto?sire?=?SymInitialize(ph,?0,?FALSE); ??sire?=?SymSetOptions(SymGetOptions()?|?SYMOPT_LOAD_LINES?|?SYMOPT_FAIL_CRITICAL_ERRORS); ??CONTEXT?ctx?=?{?0?}; ??ctx.ContextFlags?=?CONTEXT_FULL; ??RtlCaptureContext(&ctx); ??STACKFRAME64?sf?=?{?0?}; ?#ifdef?_M_IX86?//?ignore?IA64 ??auto?imageType?=?IMAGE_FILE_MACHINE_I386; ??sf.AddrPC.Offset?=?ctx.Eip; ??sf.AddrPC.Mode?=?AddrModeFlat; ??sf.AddrFrame.Offset?=?ctx.Ebp; ??sf.AddrFrame.Mode?=?AddrModeFlat; ??sf.AddrStack.Offset?=?ctx.Esp; ??sf.AddrStack.Mode?=?AddrModeFlat; ?#elif?_M_X64 ??auto?imageType?=?IMAGE_FILE_MACHINE_AMD64; ??sf.AddrPC.Offset?=?ctx.Rip; ??sf.AddrPC.Mode?=?AddrModeFlat; ??sf.AddrFrame.Offset?=?ctx.Rsp; ??sf.AddrFrame.Mode?=?AddrModeFlat; ??sf.AddrStack.Offset?=?ctx.Rsp; ??sf.AddrStack.Mode?=?AddrModeFlat; ?#endif ??MODULEENTRY32?me; ??auto?snap?=?CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,?GetCurrentProcessId()); ??auto?info?=?Module32First(snap,?&me); ??while?(info)?{ ???auto?dw?=?SymLoadModule64(ph,?0,?me.szExePath,?me.szModule,?(DWORD64)me.modBaseAddr,?me.modBaseSize); ???if?(!Module32Next(snap,?&me))break; ??} ??CloseHandle(snap); ??auto?thread?=?GetCurrentThread(); ??PIMAGEHLP_SYMBOL64?sym?=?(IMAGEHLP_SYMBOL64?*)malloc(sizeof(IMAGEHLP_SYMBOL64)?+?100); ??if?(!sym) ???return; ??memset(sym,?0,?sizeof(IMAGEHLP_SYMBOL64)?+?100); ??sym->SizeOfStruct?=?sizeof(IMAGEHLP_SYMBOL64); ??sym->MaxNameLength?=?100; ??IMAGEHLP_LINE64?line?=?{?0?}; ??line.SizeOfStruct?=?sizeof(line); ??for?(;;)?{ ???auto?result?=?StackWalk(imageType,?ph,?thread,?&sf,?&ctx,?0,?SymFunctionTableAccess64,?SymGetModuleBase64,?0); ???if?(result)?{ ????DWORD64?offset?=?0; ????DWORD?offset_for_line?=?0; ????CHAR?und_fullname[100]; ????if?(sf.AddrPC.Offset?!=?0)?{ ?????if?(SymGetSymFromAddr64(ph,?sf.AddrPC.Offset,?&offset,?sym))?{ ??????UnDecorateSymbolName(sym->Name,?und_fullname,?100,?UNDNAME_COMPLETE); ??????cout?<p>編譯請鏈接dbghelp.lib</p> <p>核心就是StackWalk與SymGetSymFromAddr64,SymGetLineFromAddr64。</p> <p><span style="background-color: #ccffcc"><strong>StackWalk用于獲取下一層堆棧。</strong></span></p> <p><span style="background-color: #ccffcc"><strong>SymGetSymFromAddr64用于獲取當前函數名。</strong></span></p> <p><span style="background-color: #ccffcc"><strong>SymGetLineFromAddr64用于獲取函數所在文件及行號。</strong></span></p> <p>為了這三個函數正常工作,還要初始化符號相關功能(SymInitialize),取得當前線程描述表(RtlCaptureContext),加載用到的模塊(SymLoadModule64)。</p> <p>用到了<dbghelp.h><tlhelp32.h>這兩個頭文件。</tlhelp32.h></dbghelp.h></p> <p>上面代碼執行后會在控制臺輸出堆棧信息。</p></tlhelp32.h></dbghelp.h></iostream></windows.h>
? 版權聲明
文章版權歸作者所有,未經允許請勿轉載。
THE END