使用IDEA進行erlang的斷點調試

簡介

IDEA可是說是目前對erlang語言支持最好的IDE了,包括文件跳轉,代碼提示等已經相當成熟。但是因爲缺少官方維護,erlang的debug功能還是相對簡陋的,包括當前最新版本的erlang插件,對debug功能的支持也不能算完整。

erlang官方也提供了debug功能,雖然易用性上還是偏弱,但是功能還是相對比較完整。每次重啓的時候必須重新載入環境,斷點之類的配置都是在環境中。如果調試需要重啓節點,就會很麻煩。

下文的所有使用都是在擴展後的插件基礎上,現在官方的IDEA的erlang插件debug功能還需要完善,我自己補全了一部分,包括多進程調試,debug驗算窗的代碼提示。支持條件斷點等。
github地址
編譯好的插件
插件是在IDEA2019.3上編譯的,應該是需要2019.3以上的版本才能使用

Erlang的debug基礎

erlang的debug對外接口基本都由int模塊提供,和其他語言最大的不同在於,erlang調試的最小單位是模塊,如果要在某個模塊中進行斷點調試,則首先必須調用int:i 或者int:ni將模塊進行標記,沒有標記的模塊,即使標記了斷點,也是不會生效。斷點的跳轉也依賴於標記,如果在A模塊中觸發斷點,斷點處執行B模塊的函數,但是B模塊沒有被標記的情況下,下一步是不能跳轉到B模塊的。

還有一個重要的不同是,erlang是典型的多進程語言,意味着斷點可能被多個進程同時觸發,erlang的設計是觸發斷點的進程會阻塞在執行處,直到手動將斷點繼續,而無論繼續,下一步,都是以單個進程爲單位。意味着如果有10個進程觸發了斷點,則必須繼續10次才行。

IDEA提供的基礎debug

IDEA的erlang插件提供的debug大體上可以分爲兩種:

  • 本地debug,就是Erlang Application提供的debug和eunit
    在這裏插入圖片描述
    缺點的話比較明顯的就是沒有提供命令行輸入,如果要在erlang shell執行命令,只能remsh目標節點進行輸入,當然前提是debug的節點本身加了-name參數
    debug重新執行的時候,原始節點也會順帶重啓

  • RmoteDebug,debug邏輯運行在單獨的節點,停止debug邏輯不會影響目標節點
    在這裏插入圖片描述
    如標題所說,remote的debug邏輯是運行在一個單獨的節點上,debug節點會調用net_kernet和目標節點相連,之後調用int:ni標記需要debug的模塊,ni會在所有已經相連的節點上進行debug標記。

需要注意的是node name要填完整的包括@後的ip,cookie是連接目標節點使用的cookie,和node args的-setcookie不衝突,至於interpret scope,是我單獨加的,因爲遠程調試之前默認是interpret所有已知module,遠程調試的時候會比較慢,而且沒有必要,這裏添加了選項,默認是隻interpret有斷點的模塊。但是要注意的是,如果斷點邏輯在不同文件之間跳轉,則要跳轉的文件必須被interpret了才能跳轉到目標文件

斷點,調用棧基礎使用

在這裏插入圖片描述
斷點觸發時的界面如上,Debug欄會顯示當前調試的進程,和進程的調用棧,右側顯示當前棧層的變量
左側點擊不同的調用棧層級,可以進行切換,下拉進程列表可以切換當前調試的進程。
可以在A進程進行N次下一步之後,切換到B進程進行下一步,之後再切換回A進程。但是要注意debug的邏輯可能會改變程序正常執行的標準順序,調試的時候還是需要注意。

變量演算

和傳統調試一樣,erlang的debug也支持變量的驗算,並且當執行層級是堆棧頂層時,調用是在debug的目標進程進行響應的,比如調用self,返回的就是我們正在調試的進程的pid。

這個邏輯會非常的有用,可能以前我們會在一個gen_server裏編寫一個handle_info處理入參是一個MFA,之後調試的時候通過發送消息讓目標進程執行指定邏輯。這種算是後臺接口,放到生產環境中會比較危險,有了debug就可以直接在運行進程中執行邏輯調用,不需要提前編寫後臺接口。

但是,如上面所說,當選擇的執行層級不是調用棧棧頂時,erlang本身是不允許進行變量演算的,只提供當前層級的變量查看,這也可以理解,邏輯已經走到一個未來狀態了,再進行一個過去狀態的驗算也是沒有意義的。但是有時可能會有調用一些無狀態的函數來格式化當前變量的需求,因此,額外提供了本地驗算功能,驗算邏輯是通過rpc:call目標節點完成,不能保證執行邏輯的主體是debug的進程,當時可以確保執行點是目標node,返回的結果也會做額外的標記。

帶條件的斷點

erlang對外只提供的了一個死板的帶條件斷點,int:test_at_break(Module, Line, Function), Function的定義如下

Optionally an associated condition. A condition is a tuple {Module,Name}. When the breakpoint is reached, Module:Name(Bindings) is called. If it evaluates to true, execution stops. If it evaluates to false, the breakpoint is ignored. Bindings contains the current variable bindings. To retrieve the value for a specified variable, use get_binding.

也就是必須提供一個單參函數,通過函數返回值判斷是否要進入斷點,入參只有當時的Bindings。因此只能在添加條件的時候動態生成代碼:

會生成代碼:
在這裏插入圖片描述
表達式的名字轉換成atom作爲函數名,然後進行邏輯驗算,生成代碼的路徑可以在shell的啓動參數裏找到

最佳實踐

推薦的是使用ErlangConsole來運行服務器實例,然後再使用remoteDebug來debug實例節點,IDEA的Console比起werl的功能更爲強大,包括全模塊提示,record提示,自動加入代碼路徑等。

remote即使停止了debug也不會影響原有node,而且如果測試環境和本機在同一內網,還可以實現遠程debug測試節點。

其他

fork的分支不光改動了debug部分, 還對case分支下的變量提示,rebar項目的導入都進行了修復,對rebar3的支持更友善。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章