【轉載】【GDB】GDB with Python
作者:薛定諤的喵
連結:https://zhuanlan.zhihu.com/p/152274203
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
你還在用GDB除錯程式嗎?
如果是,那麼我們是同道中人。但是你知道GDB有一個很強大的功能,Python scripting嘛?
如果是的,那麼恭喜你,你是一個大牛。
本文主要講述如何使用Python來提高你的gdb除錯技能, 讓你從繁重的重複的工作裡面掙脫出來呼吸新鮮空氣。
首先,第一件事,使用gdb7.x以上的版本,最好9.x的。因為Python的支援是從gdb7.0(2009年?)開始的。
進入正題
gdb本來就支援自定義指令碼輔助除錯,為什麼還要用Python指令碼呢?因為自定義指令碼的語法比較老,不如寫Python歡快。如果你喜歡用原來的自定義指令碼方法,那也是可以的。
藉助Python,你可以將難看的資料變得好看,
藉助Python,你可以將重複的工作變成一個命令,
藉助Python,你可以更快的除錯bug,
藉助Python,你可以裝逼,哈哈哈
……
將難看的資料變得好看
以下面的程式碼為例
#include <map> #include <iostream> #include <string> using namespace std; int main() { std::map<string, string> lm; lm["good"] = "heart"; // 檢視map 裡面內容 std::cout<<lm["good"]; }
當代碼執行到std<<cout時, 你想檢視map裡面的內容,如果沒有python和自定義的指令碼,print lm看到的是
$2 = {_M_t = { _M_impl = {<std::allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >> = {<__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >> = {<No data fields>}, <No data fields>}, <std::_Rb_tree_key_compare<std::less<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >> = { _M_key_compare = {<std::binary_function<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool>> = {<No data fields>}, <No data fields>}}, <std::_Rb_tree_header> = {_M_header = { _M_color = std::_S_red, _M_parent = 0x55555556eeb0, _M_left = 0x55555556eeb0, _M_right = 0x55555556eeb0}, _M_node_count = 1}, <No data fields>}}}
但是當你在gdb9.2裡面輸入print lm的時候,你看到的將是
(gdb) p lm $3 = std::map with 1 element = {["good"] = "heart"}
map裡面有什麼一清二楚。這是因為gdb9.x自帶了一系列標準庫的Python pretty priniter。 如果你使用的是gdb7.x,那麼你可以手動的匯入這些pretty printer實現同樣的效果。具體步驟如下:
- 下載pretty printer: svn co svn://http://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python
- 在gdb裡面輸入(將路徑改成你下載的路徑):
python import sys sys.path.insert(0, '/home/maude/gdb_printers/python') from libstdcxx.v6.printers import register_libstdcxx_printers register_libstdcxx_printers (None) end
這樣你就可以放心使用了~
詳細請看:
https://sourceware.org/gdb/wiki/STLSupport
https://codeyarns.com/2014/07/17/how-to-enable-pretty-printing-for-stl-in-gdb/
將重複的工作變成一個命令
比如在除錯的時候,你知道當前棧指向一個字串,但是你不知道具體在哪裡,你想遍歷這個棧將它找出來,那麼你可以藉助Python自定義一個命令"stackwalk",這個命令可以直接Python程式碼遍歷棧,將字串找出來。
##################################################### # Usage: to load this to gdb run: # (gdb) source ..../path/to/<script_file>.py import gdb class StackWalk(gdb.Command): def __init__(self): # This registers our class as "StackWalk" super(StackWalk, self).__init__("stackwalk", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): # When we call "StackWalk" from gdb, this is the method # that will be called. print("Hello from StackWalk!") # get the register rbp = gdb.parse_and_eval('$rbp') rsp = gdb.parse_and_eval('$rsp') ptr = rsp ppwc = gdb.lookup_type('wchar_t').pointer().pointer() while ptr < rbp: try: print('pointer is {}'.format(ptr)) print(gdb.execute('wc_print {}'.format(ptr.cast(ppwc).dereference()))) print('===') except: pass ptr += 8 # This registers our class to the gdb runtime at "source" time. StackWalk() 更快的除錯bug 當你除錯多執行緒的時候,你發現callstack 一堆,而且好多都是重複的,如果它們可以自動去重或者摺疊多好,這樣你只需要關注一小部分。好訊息!Python可以讓你用一個命令就可以輕鬆搞定。而且已經有人寫好了相應的程式碼,你只需要匯入即可。詳細介紹請看https://fy.blackhats.net.au/blog/html/2017/08/04/so_you_want_to_script_gdb_with_python.html # From https://fy.blackhats.net.au/blog/html/2017/08/04/so_you_want_to_script_gdb_with_python.html ##################################################### # # Usage: to load this to gdb run: # (gdb) source ..../path/to/debug_naughty.py # # To have this automatically load, you need to put the script # in a path related to your binary. If you make /usr/sbin/foo, # You can ship this script as: # /usr/share/gdb/auto-load/ <PATH TO BINARY> # /usr/share/gdb/auto-load/usr/sbin/foo # # This will trigger gdb to autoload the script when you start # to acces a core or the live binary from this location. # import gdb class StackFold(gdb.Command): def __init__(self): super(StackFold, self).__init__("stackfold", gdb.COMMAND_DATA) def invoke(self, arg, from_tty): # An inferior is the 'currently running applications'. In this case we only # have one. stack_maps = {} # This creates a dict where each element is keyed by backtrace. # Then each backtrace contains an array of "frames" # inferiors = gdb.inferiors() for inferior in inferiors: for thread in inferior.threads(): try: # Change to our threads context thread.switch() # Get the thread IDS (tpid, lwpid, tid) = thread.ptid gtid = thread.num # Take a human readable copy of the backtrace, we'll need this for display later. o = gdb.execute('bt', to_string=True) # Build the backtrace for comparison backtrace = [] gdb.newest_frame() cur_frame = gdb.selected_frame() while cur_frame is not None: if cur_frame.name() is not None: backtrace.append(cur_frame.name()) cur_frame = cur_frame.older() # Now we have a backtrace like ['pthread_cond_wait@@GLIBC_2.3.2', 'lazy_thread', 'start_thread', 'clone'] # dicts can't use lists as keys because they are non-hashable, so we turn this into a string. # Remember, C functions can't have spaces in them ... s_backtrace = ' '.join(backtrace) # Let's see if it exists in the stack_maps if s_backtrace not in stack_maps: stack_maps[s_backtrace] = [] # Now lets add this thread to the map. stack_maps[s_backtrace].append({'gtid': gtid, 'tpid' : tpid, 'bt': o} ) except Exception as e: print(e) # Now at this point we have a dict of traces, and each trace has a "list" of pids that match. Let's display them for smap in stack_maps: # Get our human readable form out. o = stack_maps[smap][0]['bt'] for t in stack_maps[smap]: # For each thread we recorded print("Thread %s (LWP %s))" % (t['gtid'], t['tpid'])) print(o) # This registers our class to the gdb runtime at "source" time. StackFold()
等等!還有好多,畢竟Python圖靈完備,只要GDB提供相應的API,你想要啥都能實現。
會了這些,你就可以向新手裝逼去了
References:
1 https://undo.io/resources/gdb-watchpoint/python-gdb/
2 https://codeyarns.com/2014/07/17/how-to-enable-pretty-printing-for-stl-in-gdb/