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