跨平台PHP调试器设计及使用方法——探索和设计
? ? ? ? 在《跨平臺PHP調試器設計及使用方法——立項》一文中,我確定了使用xdebug作為調試器插件部分的基礎組件。xdebug提供了一個遠程調試的功能(相關資料可以詳見https://xdebug.org/docs/remote),我們這個項目便是基于這個功能實現的。(轉載請指明出于breaksoftware的csdn博客)
? ? ? ? 遠程調試是基于網絡傳輸方式進行交互的一種調試方式,那么其必定有服務端和客戶端兩部分組成。這兒的服務端和客戶端都是相對的,因為一個客戶端可能在和服務器通信后就變成了一個服務端,而服務端則在一次通信后就變成了客戶端。xdebug在這個模型中屬于服務端,因為它是嵌入到PHP執行器內部,影響PHP執行流程的部分,這些核心功能肯定是作為服務端的一部分而存在。同時它也應該有接收和響應請求的功能。
? ? ? ? xdebug提供了兩種連接的方式,一種是固定地址單線連接。一種是未知地址多線連接。
? ? ? ? 我們先看下固定地址單線連接,它的執行流程如下圖
?
- 嵌入在PHP執行程序中的Xdebug開啟了一個80端口
- 控制調試過程的IDE發起一次HTTP的調試請求
- Xdebug根據配置項中的remote_host和remote_port字段(也就是IDE所在機器的IP和IDE開放的端口),向IDE發起連接請求
- IDE和Xdebug建立連接,相互通信
- Xdebug應答2過程中的HTTP請求
? ? ? ? 上述方式存在一個問題,就是要在Xdebug里配置好IDE的IP和PORT,這樣就只能有一臺機器上的IDE可以與該Xdebug通信。為了可以支持多用戶同時連接方式,Xdebug還提供了一種針對未知IP連接的方式,我們先看下流程圖:
- 嵌入在PHP執行程序中的Xdebug開啟一個80端口
- 控制調試過程的IDE發起一次HTTP的調試請求
- Xdebug的配置項中藥配置remote_connect_back為1或者on,還要配置remote_port。Xdebug根據2中的請求解析出遠端IDE的IP,然后通過該IP和remote_port發起一次連接請求
- IDE和Xdebug建立連接,相互通信
- Xdebug應答2過程中的HTTP請求
? ? ? ? 面對這兩種方式,我們需要如何選擇呢?首先我們看一個問題,如果配置過netbeans和Xdebug連接的朋友,肯定記得netbeans中要配置代碼FTP地址。因為作為IDE需要能查看到遠程機器上這些要被執行的文件(因為要展現給用戶看執行到哪兒了,哪兒要下斷點等)。雖然xdebug的source命令可以獲取當前執行文件的內容,而對于一款調試器來說,我們往往需要很多尚未發生的內容。所以IDE要能訪問遠程文件是必要的。
? ? ? ? 但是這一步驟,也將影響用戶配置調試器的進度。因為為了調試,我還要給遠程機器開啟一個FTP服務,還要配置服務對應的本地地址,這些似乎都和我們要進行調試的行為無關。所以為了解決這個問題,我們索性在調試器中不放開編輯源碼的功能。同時我們將IDE和Xdebug放在同一臺機器上,這樣IDE可以讀取本地的PHP執行的文件,這樣也就不用開啟FTP服務了。于是,我們就選擇固定IP單線連接的方式。
? ? ? ? 這兒需要指出的是,我們在配置remote_host時肯定不能寫死一個IP。因為我們代碼和配置隨時會被拷貝到其他環境,所以寫死一些值將嚴重影響其適用性,于是我們可以使用localhost來代替固定IP
| xdebug.remote_enable=On xdebug.remote_handler=dbgp? xdebug.remote_host=localhost xdebug.remote_port=9000? |
? ? ? ? 通信方式解決后,我們便需要關注通信協議的問題。xdebug使用的是一個叫dpgp的協議,其協議文檔見https://xdebug.org/docs-dbgp.php。
? ? ? ? 這份協議文檔雖然比較長,但是還算簡單。作為行為請求的發起方,需要向Xdebug發送command -a value -b value……這種類型的請求內容,而Xdebug會返回一個XML內容。對于這種看似不對稱的請求類型造成的原因,文檔中解釋是說XML內容生成是容易的,但是解析卻需要其他的庫。然而作者明顯不想引入這些并不太重要的第三方庫。其實我覺得這種請求方式挺好的,它非常像我們使用的其他輸入式調試器,比如windbg。
? ? ? ? 接下來我們看下調試的過程
- IDE獲取Xdebug支持的一些屬性(不同版本的Xdebug支持不同的功能,所以IDE要先探知它的支持什么不支持什么)
- IDE設置一些Xdebug屬性、斷點等信息
- Xdebug讓代碼運行起來,直到遇到斷點或者運行結束
- 如果遇到斷點,IDE可以向Xdebug詢問一些變量值,堆棧信息,或者修改一些變量值等
? ? ? ? 我們再看下Xdebug文檔中一段比較類似人通話的過程,它也展現了整個調試過程的樣貌
| IDE: ?feature_get supports_async DBG: ?yes IDE: ?stdin redirect DBG: ?ok IDE: ?stderr redirect DBG: ?ok IDE: ?run DBG: ?stdin data... DBG: ?stdin data... DBG: ?reached breakpoint, done running IDE: ?give me some variables DBG: ?ok, here they are IDE: ?evaluate this expression DBG: ?stderr data... DBG: ?ok, done IDE: ?run IDE: ?break DBG: ?ok, breaking DBG: ?at breakpoint, done running IDE: ?stop DBG: ?good bye |
? ? ? ? 雖然看似整個協議非常簡單,但是我并沒打算去實現一套發送請求并解析XML返回結果的庫。我看了一下其他軟件的Xdebug通信庫基本上都是調用了一套名叫pydbgp的Python實現庫,我也準備使用它。有時候還是非常必要站在巨人的肩膀上去實現一些事。
? ? ? ? pydbgd的官方地址是http://jaredforsyth.com/pydbgp/。我們只需要取用下載包中的bin和dbgp兩個目錄下文件。因為這塊的資料非常少,所以研究使用這套庫也花費了我一定的時間。而且這套庫實現中也存在不少缺陷,我也總是在不停探索和打patch中前行。好在經過一段努力,終于把它和Xdebug打通了。下面我展示一段pydbgp和Xdebug的交互過程
? ? ? ? 第1部分是告訴IDE,調試用的IDE-KEY是什么,要監聽哪個端口的。因為我是以netbeans的調試作為模板,所以我的IDE-KEY也是Netbeans和Xdebug交互的IDE-Key:netbeans-xdebug。當然這個值可以改成別的,但是要和xdebug的配置文件的idekey值一樣
xdebug.idekey="netbeans-xdebug"
? ? ? ? 然后我啟動了監聽本地9000端口。這個9000端口號也不是隨便設置的,也要和Xdebug配置文件中的remote_port值一樣
xdebug.remote_port=9000
? ? ? ? 此時我們可以在網頁中發起一次請求,用于觸發php執行。這種觸發行為分為兩種,我會在之后做介紹。
? ? ? ? 網頁此時一直處在等待狀態,這表示Xdebug已經把PHP的執行過程給中斷了。于是我們可以進行下步操作。
? ? ? ? 我們執行sessions指令,用于查看目前有哪些連接已經建立過了。如上圖,pydbgp返回了連接信息。當然這個展現不是Xdebug的原始數據——原始數據是XML的。
? ? ? ? 知道連接號后,我們使用select指令進入特定的連接。之后使用status查看調試的狀態。第一次status執行后,表示調試器處在開始狀態,這種狀態是一種中斷狀態,它還沒進入PHP代碼層。我們執行“步過”——step over操作一次,這個時候PHP執行便進入代碼了。使用stack_get指令查看當前的調用堆棧信息,這些信息中包括了棧號、文件路徑、函數名。如果我們不關心之后的執行,就直接調用run指令,讓程序跑到底。這個時候調用status命令,可以看到調試器處在stopping的狀態。此時再run一下,本次調試就徹底結束了。結束后,我們使用quit指令退出當前調試。如果還有別的調試請求過來,則可以再調用sessions查看連接號,重復上述的調試過程。上圖中4是這個過程的一個體現。
? ? ? ? 如果不想進行調試了,則可以調用quit退出整個pydbgp。
? ? ? ? pydbgp幫助我們連接Xdebug,轉發命令,解析返回的XML結果。但是它只提供了標準輸入方式的請求接口,我們沒法像直接調用API一樣調用這些接口。而且我也無意于將這套接口改成API的形式。于是我決定采用父子進程通信的方式,父進程是我們的業務邏輯,子進程是pydbgp,父子進程通過重定向輸入輸出來進行通信。這是我最初的想法,但是最后重定向的方案也被否掉了,因為python在不同平臺上(windows和linux)對這種方式存在兼容問題。這也是迫使我在項目后期老老實實使用標準socket去通信,還好我之前代碼耦合做的比較好,替代方案很快就用上了。這兒感慨下,一些事情想時總是美好的樣子,而現實往往是曲折的樣子。當然隨著閱歷的增多,想像也會越來越靠譜,這就是經驗的作用。
? ? ? ? Xdebug給我們提供了很多調試的基礎功能。但是作為調試器,我們應該在這些基礎之上開發出更多組合性的功能,這樣可以幫助使用者更快的發現問題。所以我們還需要對這些功能進行一些高階封裝組合,這些內容我們會在之后介紹。還有就是有些功能可能不是需要調試器提供的,比如日志文件監控,所以這塊也將是我們調試器的一些輔助功能。于是我們調試器的結構是這樣的
?
總結
以上是生活随笔為你收集整理的跨平台PHP调试器设计及使用方法——探索和设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 跨平台PHP调试器设计及使用方法——立项
- 下一篇: 跨平台PHP调试器设计及使用方法——通信