工欲善其事,必先利其器。排查 erlang 系統問題時,肯定希望能有一個像 unix top 一樣的工具,entop 就是這麼個東東。
---------- 我是三月份發版本天天加班的分隔線 -----------
(以下内容翻譯自 entop 的 readme.md 檔案)
entop
如同 unix 中 top 一樣的 erlang 節點資訊檢視工具。
簡介
entop 是用來展示遠端 erlang 節點運作資訊的工具,其資訊顯示的方式類似于 unix 中的 top 指令。
若要保證 entop 的正常運作,在 pre-r15 環境下,需要使用 cecho 0.3.0 版本;在 r15 或更高版本的環境下需要 cecho 0.4.0 版本。
編譯
清理和編譯可以分别運作如下指令
注意:如果你遇到和 cecho 依賴相關的問題,可以手動建立符号連結到 deps/ 下的 cecho (如果你的 cecho 放在其他目錄也可以進行類似操作),或者運作 ./rebar get-deps 以下載下傳最新版本。當通過 rebar 擷取到最新版本後,不要忘記重新編譯整個應用。
用法
若想成功運作 entop ,首先要確定 erlang 已經安裝到你的系統之中,并且 cecho 庫所在路徑被 erlang code path 所包含。 項目中預設提供的啟動腳本(entop)假定了其在 entop 應用根目錄下被執行,如果這與你的實際情況不符,請自行調整腳本的相應路徑,或者直接確定 entop 的 ebin/ 目錄包含在 erlang code path 之中。詳情請參考啟動腳本具體内容。
entop 的運作示例
使用者接口
entop 的接口允許使用者定制化,是以本節描述的接口均為“内置”接口。
表頭資訊
第一行 主要展示了節點的靜态資訊,例如節點名、作業系統類型、指定的 erl flag 、目前所運作的 erlang 版本資訊。
第二行 展示了(目标節點所在機器的)本地時間、目标節點已持續運作的時間(格式為 days:hours:minutes:seconds)、運作 entop 的節點與目标節點之間的網絡延遲情況(即 net_adm:ping() 成功互動所需花費的時間)
第三行 展示了系統中每個程序的具體資訊、程序的總數、運作隊列中的程序數量(由排程器進行排程的待運作程序數量)、reductions per interval (rpi) 值(自從上一次 called the node 後系統已經 reduction 的次數)、以及每個程序占用的記憶體量。
第四行 展示了系統記憶體使用量、atom 記憶體占用量(目前使用量/總體配置設定量)、binary 記憶體占用量、code 記憶體占用量,以及 ets 記憶體占用量。
第五行 為空白,目前作為預留。
第六行 為和行内容展示相關的資訊,例如資訊擷取時間間隔、資訊展示排序方式,以及擷取相關資訊所耗費的時間。
在 entop 運作狀态下可以使用的控制指令:
[1-n]: 根據指定列編号進行輸出内容排序。第一列編号為 1 ,其他列按順序增加。
r: 在升序排序和降序排序之間進行切換。
q: 從 entop 中退出傳回 shell 指令行。
ctrl-c: 等價于 'q' 指令。
'<' 和 '>': 将目前排序列左移或者右移(注意:次數為小于和大于号,非箭頭)
按照 readme.md 中的說明 “entop 的正常運作在 pre-r15 情況下需要 cecho 0.3.0 的支援,在 r15 或更高版本的情況下需要 cecho 0.4.0 的支援” 做了如下配置變更。
通過 entop 連接配接到 rabbitmq 程序進行檢視
通過挂起回到前台,檢視 entop 相關程序運作情況,并強殺
可以看到,按照上面的操作,我們失敗了,排查錯誤的原因,我 檢視了 cecho 的代碼。
在 cecho_srv.erl 中
檢視手冊,針對 erl_ddll/load/2 有如下說明
---------- 我是三月份發版本天天加班的分隔線 -----------
(以下内容翻譯自 kernel-2.15.2)
load(path, name) -> ok | {error, errordesc}
types:
path = path()
name = driver()
errordesc = term()
加載并連結名為 name 的動态 driver 。path 為包含該 driver 的目錄。name 指定的對象必須為共享對象或動态連結庫。
若兩個 driver 具有不同的 path 參數(即在不同路徑下),則無法通過相同的 name 進行加載。
name 的值對應 path 目錄下的動态加載對象檔案,但是去除了擴充名(例如,移除了 .so 字尾)。
在 driver 初始化函數中指定的 driver 名字方式,在很大程度上,與指定對應了 .beam 檔案的 erlang 子產品名一樣。
如果對 driver 執行了解除安裝動作,但由于 port 仍舊處于 open 狀态,故此 driver 實際上仍舊存在,此時若調用 load/2 ,則會停止針對 driver 的解除安裝行為,使得該 driver 得以保留(隻要 path 沒有發生過變更),并會傳回 ok 。
如果确實打算重新加載對象代碼(driver),則可以使用 reload/2 或者底層的接口 try_load/3 進行操作。
針對不同的場景下的加載/解除安裝行為描述請參考具體說明。
如果超過一個程序想要使用相同的 path 加載一個已經加載過的 driver ,或者如果相同的程序想要加載同一個 driver 多次,該函數調用都會傳回 ok 。
模拟器會跟蹤 load/2 被調用的次數,以便在相同數量的 unload/2 被調用後才真正解除安裝該 driver 。
如此,才能保證一個應用安全的加載一個 driver ,無論該 driver 是在多 erlang 程序間共享,還是在多 erlang 應用間共享。同樣能保證 driver 的安全解除安裝,而不會對系統的其他部分産生影響。
以相同的 name 但不同的 path 加載多個 driver 是不允許的;
注意:
需要注意的是,path 參數的值是按字面量解析的,是以針對統一 driver 的多次加載都需要指定具有相同字面量的 path 字元串,即使不同的路徑表達均指向相同的檔案系統目錄也不行(比如使用相對路徑或連結的情況)。
函數執行成功後傳回 ok ;函數執行失敗後傳回 {error, errordesc} ,其中 errordesc 為 opaque term ,可以通過 format_error/1 翻譯成人可讀的格式。
若希望對錯誤處理有更多控制,則需要使用 try_load/3 接口。
該函數會在入口參數不符合要求的情況下,抛出 badarg 異常。
根據上述資訊,檢視源碼目錄 priv 下,會在編譯 cecho 後生成的 cecho.so 檔案
我擦,果然其中沒有定義 scrollok 符号……erlang 果不欺我~~~
ok ,變回原始配置再再做一次挑戰...
整個過程下來,沒發現有啥具體差別啊!!如何破?!
在沒有其他思路的請款下,就讓我們簡單粗暴一點吧,直接拉下來 master 和 0.4.0 兩個版本的代碼進行比較~~
結果很明顯,隻有 rebar.config 中的不同才是問題關鍵。
這條配置資訊從字面上就可以了解,ldflags 是用來設定 link 選項的,是以上面是指定了對 ncurses 庫的連結依賴。
回頭再看依賴 cecho.so 的庫依賴關系(之前少看了該資訊,5555...)
而在引用 0.4.0 版本的 cecho 時,資訊如下
果然存在差别!這也就解釋了為何 scrollok 符号在兩次結果中雖然都是 u 狀态,但基于 master 的編譯卻可用的原因,因為 scrollok 符号在 libncurses.so.5 中~~
手動在 0.4.0 版本的 cecho 的 rebar.config 檔案中添加 {port_envs, [{"ldflags", "$ldflags -lncurses"}]}. 後,重新編譯運作,一切正常~~