用 Python 拓展 GDB(四)

2021-09-19 04:50:51 字數 3305 閱讀 3036

歡迎來到《用python拓展gdb》的最後一篇。第一篇結尾,我提到了通用語言相對於領域特定語言的一項優勢,即在處理資料上更加靈活。其實通用語言還有著另一樣優勢,領域特定語言只能侷限在宿主程式中使用,而通用語言則無此限制。對於通用語言來說,gdb暴露的介面不過是又乙個庫而已。

在本篇中,我們會把python當作一門「膠水語言」,a面是gdb的介面,b面是乙個終端介面的程式。姑且把這個終端介面程式稱之為gti(gdb's terminal inte***ce)吧。我們會實現從gdb到gti的單向資料傳輸。每當gdb觸發斷點時,就在gti上自動輸出各項相關資訊。這兩者間的通訊使用udp協議。換言之,接下來要完成的是乙個位於gdb內部udp客戶端,和監聽指定埠的帶終端介面的udp服務端。

gdb端功能如下:

每當斷點被觸發時,通過gdb介面獲取info breakpointsinfo args,以及info locals三者的值

把上述三者的值轉換成json格式

通過udp協議傳送到埠9876

功能要求看上去很多,不過實現成**其實也就二三十行:

import json

import socket

import gdb

host = 'localhost'

port = 9876

sock = socket.socket(socket.af_inet, socket.sock_dgram)

sock.connect((host, port))

def send_data(event):

cur = event.breakpoints[0].location

if cur is none:

cur = event.breakpoints[0].expr

local_vars = gdb.execute('info locals', to_string=true)

args = gdb.execute('info args', to_string=true)

bps = gdb.execute('info breakpoints', to_string=true)

data =

data = json.dumps(data)

sock.send(bytes(data, 'utf-8'))

gdb.events.stop.connect(send_data)

在此之前,需要設定乙個監聽9876埠的服務端,不然客戶端這邊就建立不了連線。執行nc -l 9876作為服務端的mock,暫時只需觀察下傳送過來的資料是否正確。

寫乙個自動化指令碼,讓gdb設定若干斷點並執行,連續執行多次continue。你應該可以觀察到接連有資料顯示在nc的輸出中:

$ nc -l 9876

{"locals": "pointers = ...

gti 端功能如下:

監聽埠9876

每當收到資料報時,提取出json格式的資料

根據收到的資料,重繪當前介面

在繪製終端介面時,我用的是自帶的curses模組。在監聽埠方面,我用的是python3.4之後才有的async模組。當然蘿蔔白菜,各有所愛,大可改用你自己喜歡的庫。

#!/usr/bin/env python3

import asyncio

import curses

import json

def main():

loop = asyncio.get_event_loop()

# 1. 監聽埠9876

server = loop.create_datagram_endpoint(

gtiprotocol, local_addr=('127.0.0.1', 9876))

try:

loop.run_until_complete(server)

loop.run_forever()

except keyboardinterrupt:

pass

finally:

curses.endwin()

class gtiprotocol(asyncio.protocol):

def __init__(self):

self.ui = textpad()

def datagram_received(self, byte, _):

"2. 將收到的資料從byte轉成json"

data = byte.decode()

data = json.loads(data)

self.ui.display(data)

class textpad:

def __init__(self):

self.pad = curses.initscr()

curses.start_color()

def _addstr(self, text):

self.pad.addstr(text, curses.a_bold)

def display(self, data):

"3. 根據給定的資料重繪介面"

try:

self.pad.erase()

self._addstr('current: %s\n\n' % data['current'])

for key, value in data.items():

if key != 'current':

self._addstr('%s:\n' % key)

self._addstr(value)

self._addstr('\n')

self.pad.refresh()

except curses.error:

pass

main()

現在可以用./gti.py來替換掉nc -l 9876,再重新執行gdb。你應該能看到,每當有新的斷點觸發時,./gti.py就會應用新的資料繪製介面。

順便一提,使用curses模組純粹是為了方便示範。curses提供的介面過於底層,許多細節方面都需要自己去摳。如果真的要開發實際可用的終端介面程式,建議使用諸如urwid這樣的第三方包。

如上面的例子所示,我們成功地用python實現了內嵌於gdb的客戶端。該客戶端可以向外界暴露出gdb除錯時的資訊。依據同樣的思路,我們也可以在gdb內實現內嵌的服務端,這樣外界就能動態修改gdb除錯的方式。當然,這一切離不開python這把「瑞士軍刀」。

用gdb檢視記憶體

用gdb檢視記憶體 格式 x nfu 說明 x 是 examine 的縮寫 n表示要顯示的記憶體單元的個數 f表示顯示方式,可取如下值 x 按十六進製制格式顯示變數。d 按十進位制格式顯示變數。u 按十進位制格式顯示無符號整型。o 按八進位制格式顯示變數。t 按二進位制格式顯示變數。a 按十六進製制...

用gdb檢視記憶體

格式 x nfu 引數說明 x 是 examine 的縮寫 n表示要顯示的記憶體單元的個數 f表示顯示方式,可取如下值 x 按十六進製制格式顯示變數。d 按十進位制格式顯示變數。u 按十進位制格式顯示無符號整型。o 按八進位制格式顯示變數。t 按二進位制格式顯示變數。a 按十六進製制格式顯示變數。i...

gdb斷點(四)刪除

斷點的刪除與斷點的設定同樣的重要。刪除斷點的命令有兩個 delete 用法 delete breakpoints num range.delete可刪除單個斷點,也可刪除乙個斷點的集合,這個集合用連續的斷點號來描述。例如 delete 5 delete 1 10 clear 用法 clear 刪除所...