摘要:歡迎來到用拓展的最后一篇。對于通用語言來說,暴露的接口不過是又一個庫而已。這兩者間的通訊使用協(xié)議。該客戶端可以向外界暴露出調(diào)試時的信息。用拓展系列到此就結束了。
歡迎來到《用python拓展gdb》的最后一篇。第一篇結尾,我提到了通用語言相對于領域特定語言的一項優(yōu)勢,即在處理數(shù)據(jù)上更加靈活。其實通用語言還有著另一樣優(yōu)勢,領域特定語言只能局限在宿主程序中使用,而通用語言則無此限制。對于通用語言來說,gdb暴露的接口不過是又一個庫而已。
在本篇中,我們會把python當作一門“膠水語言”,A面是gdb的接口,B面是一個終端界面的程序。姑且把這個終端界面程序稱之為gti(gdb"s terminal interface)吧。我們會實現(xiàn)從gdb到gti的單向數(shù)據(jù)傳輸。每當gdb觸發(fā)斷點時,就在gti上自動輸出各項相關信息。這兩者間的通訊使用UDP協(xié)議。換言之,接下來要完成的是一個位于gdb內(nèi)部UDP客戶端,和監(jiān)聽指定端口的帶終端界面的UDP服務端。
gdb 端實現(xiàn)gdb端功能如下:
每當斷點被觸發(fā)時,通過gdb接口獲取info breakpoints和info args,以及info locals三者的值
把上述三者的值轉(zhuǎn)換成json格式
通過UDP協(xié)議發(fā)送到端口9876
功能要求看上去很多,不過實現(xiàn)成代碼其實也就二三十行:
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 = { "current": cur, "locals": local_vars, "args": args, "breakpoints": bps } data = json.dumps(data) SOCK.send(bytes(data, "utf-8")) gdb.events.stop.connect(send_data)
在此之前,需要設置一個監(jiān)聽9876端口的服務端,不然客戶端這邊就建立不了連接。運行nc -l 9876作為服務端的mock,暫時只需觀察下發(fā)送過來的數(shù)據(jù)是否正確。
寫一個自動化腳本,讓gdb設置若干斷點并運行,連續(xù)執(zhí)行多次continue。你應該可以觀察到接連有數(shù)據(jù)顯示在nc的輸出中:
$ nc -l 9876 {"locals": "pointers = ...gti 端實現(xiàn)
gti 端功能如下:
監(jiān)聽端口9876
每當收到數(shù)據(jù)包時,提取出json格式的數(shù)據(jù)
根據(jù)收到的數(shù)據(jù),重繪當前界面
在繪制終端界面時,我用的是自帶的curses模塊。在監(jiān)聽端口方面,我用的是python3.4之后才有的async模塊。當然蘿卜白菜,各有所愛,大可改用你自己喜歡的庫。
#!/usr/bin/env python3 import asyncio import curses import json def main(): loop = asyncio.get_event_loop() # 1. 監(jiān)聽端口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. 將收到的數(shù)據(jù)從byte轉(zhuǎn)成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. 根據(jù)給定的數(shù)據(jù)重繪界面" try: self.pad.erase() self._addstr("current: %s " % data["current"]) for key, value in data.items(): if key != "current": self._addstr("%s: " % key) self._addstr(value) self._addstr(" ") self.pad.refresh() except curses.error: pass main()
現(xiàn)在可以用./gti.py來替換掉nc -l 9876,再重新運行gdb。你應該能看到,每當有新的斷點觸發(fā)時,./gti.py就會應用新的數(shù)據(jù)繪制界面。
順便一提,使用curses模塊純粹是為了方便示范。curses提供的接口過于底層,許多細節(jié)方面都需要自己去摳。如果真的要開發(fā)實際可用的終端界面程序,建議使用諸如urwid這樣的第三方包。
小結如上面的例子所示,我們成功地用python實現(xiàn)了內(nèi)嵌于gdb的客戶端。該客戶端可以向外界暴露出gdb調(diào)試時的信息。依據(jù)同樣的思路,我們也可以在gdb內(nèi)實現(xiàn)內(nèi)嵌的服務端,這樣外界就能動態(tài)修改gdb調(diào)試的方式。當然,這一切離不開python這把“瑞士軍刀”。
《用python拓展gdb》系列到此就結束了。如果你正準備編寫一個拓展,希望本教程可以教會相關的知識。如果你是一位C/C++開發(fā)者,希望本教程能夠讓你的工具箱增添新道具。如果你是想了解更多關于gdb調(diào)試的信息,希望今后遇到相關問題時能想起編寫python拓展予以解決。
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/38011.html
摘要:在末尾,我提到了也可以用來實現(xiàn)拓展腳本。其中最為常用的是和。接受一個字符串作為表達式,并以的形式返回表達式求值的結果。當觸發(fā)斷點或收到信號時,就會調(diào)用事先注冊的回調(diào)函數(shù)。對應的,撤銷回調(diào)函數(shù)的接口是。本教程剩余部分會提及這一點。 之前寫的《GDB 自動化操作的技術》一文介紹了可在gdb內(nèi)部使用的DSL(領域特定語言)來自動化gdb的操作。借助該DSL,我們分別實現(xiàn)了一個名為mv的自定義...
摘要:歡迎來到用拓展的第三篇。它們必須以開頭,以此區(qū)別于來自于上下文的函數(shù)。提供的基類名為。不過有一個區(qū)別是,的方法通常會返回一個對象,表示調(diào)用該函數(shù)后的返回值。它不能像通常意義上的函數(shù)獨立使用,只能跟某個命令搭配。具體實現(xiàn)參見用拓展第一篇。 歡迎來到《用python拓展gdb》的第三篇。上一篇我們談到了pretty printer,一個需要python支持的特性。這一篇我們談論另一個需要p...
摘要:歡迎來到用拓展的第二篇。到目前為止,我們都是在用實現(xiàn)內(nèi)置領域特定語言也能實現(xiàn)的效果。這就是的全部要求了。構造函數(shù)接收一個表示被打印的的必選。在后被調(diào)用,可用于打印復雜的成員。能通過來自定義打印方式,無疑為的使用打開新的大門。 歡迎來到《用python拓展gdb》的第二篇。在上一篇,我們學習了gdb提供的常用python接口,并用python實現(xiàn)了自定義命令和調(diào)試腳本。 到目前為止,我們...
摘要:背景這幾天一直在查一個線上程序住的問題這個程序總是在運行分鐘后住通過以下的一些調(diào)試手段發(fā)現(xiàn)是打日志的時候因為滿被了日志是默認打到的無論日志級別而我這個程序是被另一個程序調(diào)起的父進程沒有接收子進程的導致了被打滿在調(diào)試的過程中用到了以下幾種調(diào)試 FROM http://kamushin.github.io/debug/python.html 背景 這幾天一直在查一個線上程序 hang 住的...
閱讀 1810·2021-08-13 15:06
閱讀 3100·2021-08-05 10:02
閱讀 3365·2019-08-30 15:55
閱讀 2378·2019-08-30 13:46
閱讀 2485·2019-08-30 13:01
閱讀 1323·2019-08-29 17:17
閱讀 2824·2019-08-29 15:27
閱讀 1431·2019-08-29 11:12