摘要: 線上系統爲什麼常常出錯?數據庫爲什麼屢遭黑手?業務調用爲什麼頻頻失敗?連環異常堆棧案,到底是那次調用所爲? 數百臺服務器意外雪崩背後又隱藏着什麼?是軟件的扭曲仍是硬件的淪喪? 走進科學帶你瞭解Greys, Java線上問題診斷工具。css
線上系統爲什麼常常出錯?數據庫爲什麼屢遭黑手?業務調用爲什麼頻頻失敗?連環異常堆棧案,到底是那次調用所爲?
數百臺服務器意外雪崩背後又隱藏着什麼?是軟件的扭曲仍是硬件的淪喪?
走進科學帶你瞭解Greys, Java線上問題診斷工具。前端
![]() |
很早的時候,咱們使用BTrace排查問題,在感嘆BTrace的強大之餘,也曾好幾回將線上系統折騰掛掉。2012年淘寶的聚石寫了HouseMD,將經常使用的幾個Btrace腳本整合在一塊兒造成一個獨立風格的應用,但其核心代碼用的是Scala,咱們沒這方面的編程維護經驗,因此只好豔羨HouseMD的才思敏捷而沒法在其上增長功能。java
因而乎,Greys誕生了! |
PS:目前Greys僅支持Linux/Unix/Mac上的Java6+,Windows暫時沒法支持linux
Greys是一個JVM進程執行過程當中的異常診斷工具。 在不中斷程序執行的狀況下輕鬆完成JVM相關問題排查工做。nginx
和HouseMD同樣,Greys-Anatomy取名同名美劇「實習醫生格蕾」,目的是向前輩致敬。代碼編寫的時候參考了BTrace和HouseMD兩個前輩的思路。git
有時候忽然一個問題反饋上來,須要入參才能完成定位,但偏偏沒有任何日誌。回去加上從新部署,一杯咖啡時間過去了,是否是很崩潰?程序員
當你通過反覆這樣幾回折騰以後變得聰明瞭,在本身的代碼的全部入參和出參地方都加上debug
日誌,但此次問題彷佛暴露在別人的代碼中了...是否是很無奈?github
忽然遇到線上一個性能問題沒法肯定究竟是哪一個環節的耗時,只能反覆抓jstack
猜,還有沒有辦法能夠好好的過日子啦?web
遇到以上問題時,你就是咱們這類工具的目標客戶,此類工具能利用Java6的Instrumentation
特性,動態加強你所指定的類,獲取你想要到的信息。正則表達式
ClassLoader隔離
在設計和實現這款程序的時候,花費了很是多的精力在隔離目標類與Greys的ClassLoader隔離上。你能夠放心大膽的使用Greys,而不用擔憂Greys會干擾到現有業務代碼所使用的三方類庫。
運行時加載
要求目標JVM在JDK6+的基礎上,且當前執行人擁有與目標JVM相同權限。能夠作到不中斷當前JVM而動態進行加載、問題分析定位。
經常使用問題定位命令化
Greys與BTrace、HouseMD等同類軟件最大的不一樣在於,她擁有我多年來業務代碼疑難雜症定位的經常使用技術手段,並將這些排查思路和技巧命令化,將個人問題定位經驗Share給你們。
表達式支持
HouseMD相比BTrace最強大的地方就在於能快速指定攔截的類與方法,但卻沒法支持對觀察到的對象進行展開、條件過濾等操做。BTrace的腳本是本身所編寫,能夠實現此類功能。但編寫學習成本很高,且容易出錯。
表達式的引用能綜合這兩款軟件的特長同時彌補他們的不足。目前Greys所採用的是OGNL表達式。
多用戶同時訪問
遠程DEBUG最大的問題就在於,只容許一我的訪問DEBUG端口,並且一單斷點條件設置不當,頗有可能將其餘正常的業務請求攔下,影響其餘用戶的使用。
Greys採用的思路是作觀察者,其所設置的斷點不容許阻塞正常業務的流程,但你能夠觀察到斷點所攔截到的全部信息。
高性能
精心用ASM設計了字節碼加強,核心的數據結構用數組針對實際場景作裁剪優化。能夠放心的用在高負載有求下的JVM環境。
純Java編寫
Greys定位是專業的JVM的業務問題定位工具
,既然是JVM那咱們所面對的大部分就是Java程序員。我但願能Share本身在編寫軟件時候的全部技巧與思路,讓更多的Java程序員能參與開發或從中受益。
目前已經有很是多的熱心網友在給個人代碼挑錯,很是感謝這些朋友的支持!
Greys並不萬能,我也沒有計劃讓她能成爲萬能的問題定位工具。因此若是你在某些場合能用上更專業的工具,我會很是樂意推薦你使用。
性能環境下的性能損耗定位
性能分析須要有更專業的軟件,我本身經常使用的則是JProfiler(固然是付費的了)。Greys雖然性能損耗很小,但其分析的維度太少,因此只適合作簡單的性能損耗定位。當你用過專業的性能分析軟件以後,就會發現什麼叫專業!
開發環境下的遠程DEBUG
雖然Greys能取代部分的遠程DEBUG行爲,但畢竟沒不像DEBUG工具那樣能夠看到局部變量的值,並且可操做性上也沒有JVM下Eclipse/IDEA等優秀的IDE自帶的DEBUG工具這麼人性化操做。
線上環境大規模部署
與BTrace同樣,Greys獲取到的權限過高,若是線上大規模部署會遭受黑客的攻擊,而今天我爲了實現簡單是沒有作過多的鑑權控制。
JDK類庫分析
JDK的類庫存放在rt.jar
中,啓動時加載到BootstrapClassLoader
中(Hotspot-JVM),但因爲Greys也是用Java語言所編寫,因此自身也用到了這些基礎類庫,默認狀況下關閉了對這些類的加強。
固然,對於Spring、ibatis、Tomcat等三方類庫是能夠放心大膽使用的。
其它不適合場景
BTrace、HouseMD、Greys、JavOSize此類工具都會對Perm區、CodeCache(影響JIT)產生干擾,若是你的程序對這兩塊很是敏感,也請不要在這些場合下使用。
讓程序解決繁瑣的事情
命令行交互
由於不少場景下咱們都是用在遠程問題分析中(本地我就直接DEBUG了)。通常Java都會使用在Linux/BSD等類UNIX操做系統下。因此命令行是我最開始不二的選擇,也是目前支持最成熟的交互方案。
圖形界面交互
在2.x.x.x
版本中將會支持WEB方式訪問,HTTP採用websocket與後臺服務進行交互,預計過年以後能發佈上線。
查看已被JVM所加載的類、方法信息
方法執行監控
調用量,成功失敗率,響應時間
方法執行數據操做
入參、返回值、異常信息記錄與查看;支持動做回放
性能開銷渲染
跟蹤指定路徑中的方法調用軌跡、耗時
查看方法調用堆棧
monitor
、trace
等tt
命令能以時間維度紀錄下監控期內的每一次調用環境多人並行協做
基於C/S架構的任務模式甚至能讓多人同時遠程到同一進程上執行不一樣的指令、腳本,很是適合團隊一塊兒進行線上問題排查與跟蹤。Greys採用純Java編寫並留有良好的擴展,若是你有需求,只要你會Java,就能夠爲你本身編寫想要的功能。 Greys最有利的武器是他的表達式,能讓你在感覺到HouseMD集成功能便利的同時,也能發揮出自定義Btrace腳本的靈活。
1. 應用管理員擁有JVM進程權限,由他來首先在目標JVM上啓動Greys 2. 技術專家A和B平時沒有對應機器的權限,但只要網絡能訪問,他們能夠經過指定ip:port直接訪問目標機器的JVM進程,彷彿在本地通常
Greys支持在線安裝和本地安裝兩種安裝方案,安裝便可用,推薦使用在線安裝。
在線安裝(推薦)
請複製如下內容,並粘貼到命令行中。
curl -sLk http://ompc.oss.aliyuncs.com/greys/install.sh|sh
命令將會下載的啓動腳本文件greys.sh
到當前目錄,你能夠放在任何地方或加入到$PATH
中
本地安裝
在某些狀況下,目標服務器沒法訪問遠程阿里雲主機,此時你須要自行下載greys的安裝文件。
下載最新版本的GREYS
http://ompc.oss.aliyuncs.com/greys/release/greys-VERSION-bin.zip
最新的***VERSION***版本請參考主頁信息
解壓zip文件後,執行如下命令
cd greys sh ./install-local.sh
即完成本地安裝。
下載失敗
一般這樣的緣由你須要檢查你的網絡是否暢通,覈對是否能正確訪問這個網址
http://ompc.oss.aliyuncs.com/greys/greys.sh
downloading... download failed!
沒有權限
安裝腳本首先會將greys文件從阿里雲服務器上下載到當前執行腳本的目錄,因此你必需要擁有當前目錄的寫權限。
permission denied, target directory is not writable.
參數說明
./greys.sh <PID>[@IP:PORT]
啓動範例
若是不指定**IP**和**PORT**,默認是**127.0.0.1**和**3658**
./greys.sh 12345
等價於
./greys.sh 12345@127.0.0.1
等價於
./greys.sh 12356@127.0.0.1:3658
sudo支持
成熟的線上管理環境通常都不會直接開放JVM部署用戶權限給你,而是經過sudo-list來控制和監控用戶的越權操做。因爲greys.sh
腳本中會對當前用戶的環境變量產生感知,因此須要加上-H
參數
sudo -u admin -H ./greys.sh 12345
TELNET的支持
Greys支持經過telnet來訪問服務端,若是當你手頭的機器沒有安裝Greys的客戶端,你能夠簡單的經過telnet命令來進行訪問。
telnet 10.232.12.113 3658
固然了,telnet命令和Greys自帶的Console在使用友好度上仍是有必定的差距,不過解決應急之需沒有問題。
Greys是一個C/S架構的程序,因此當Client訪問到Server時,Server會維護一個session(會話),以及session的心跳、超時機制。事務(Tx)機制則是創建在session的基礎上,全部的命令交互都會建立一個事務,而且產生對應的隊列進行輸出緩衝。
事務伴隨着命令的生命週期而存在,命令分兩種:
當即返回
當即返回的命令定義是:敲下命令後Server端當即返回最終結果,後續無持續反饋信息,釋放Client對輸入的鎖定,從新開放讓用戶輸入信息,好比version
、sc
、sm
等。
等待停止
等待停止的命令則是須要用戶主動輸入Ctrl+D
完成的命令停止操做。命令執行後沒法當即返回最終結果,而是不斷的將中間產生的輸出源源不斷的輸出到客戶端中,這種命令好比stack
、monitor
等。
當session關閉時,全部掛在session的事務也會當即被關閉。
Greys相對於HouseMD、BTrace而言最靈活的地方就是在用表達式來靈活的支持不一樣的問題排查、分析場景。
表達式分兩種:條件表達式
與觀察表達式
條件表達式
條件表達式用在**使用表達式表達TRUE或FALSE**的場景,從1.6.0.6
版本開始,trace
、stack
、tt
、watch
命令都增長了條件表達式支持。
條件表達式將會使用greys內置的表達式解析引擎,識別OGNL語法。
特別指出的是,若是你書寫了一個錯誤的條件表達式,greys爲了兼容錯誤會解析爲FALSE。
如下是一些條件表達式使用的例子和預測結果
條件表達式 | 預測結果 | 解析結果說明 |
---|---|---|
1==1 | TRUE | 條件表達式爲真 |
true | TRUE | 條件表達式爲真 |
@@@ | FALSE | 非法的條件表達式 |
params==null | FALSE | 條件表達式爲假 |
false | FALSE | 條件表達式爲假 |
1!=1 | FALSE | 條件表達式爲假 |
觀察表達式
觀察表達式用在**使用表達式表達輸出內容**的場景,尤爲在watch
和tt
命令中,觀察表達式很是相當重要。
條件表達式將會使用greys內置的表達式解析引擎,識別OGNL語法,將表達式轉換爲待輸出的對象。
如下是一些觀察表達式使用的例子
字符串拼接
clazz.name+"."+method.name
數字運算
clazz.name.length()+method.name.length()
不管是匹配表達式也好、觀察表達式也罷,他們核心判斷變量都是圍繞着一個greys中的通用通知對象Advice
進行。
它的簡略代碼結構以下
public class Advice { private final ClassLoader loader; private final Class<?> clazz; private final GaMethod method; private final Object target; private final Object[] params; private final Object returnObj; private final Throwable throwExp; private final boolean isBefore; private final boolean isThrow; private final boolean isReturn; // getter/setter }
這裏列一個表格來講明不一樣變量的含義
變量名 | 變量解釋 |
---|---|
loader | 本次調用類所在的ClassLoader |
clazz | 本次調用類的Class引用 |
method | 本次調用方法反射引用 |
target | 本次調用類的實例 |
params | 本次調用參數列表,這是一個數組,若是方法是無參方法則爲空數組 |
returnObj | 本次調用返回的對象。當且僅當isReturn==true 成立時候有效,代表方法調用是以正常返回的方式結束。若是當前方法無返回值void ,則值爲null |
throwExp | 本次調用拋出的異常。當且僅當isThrow==true 成立時有效,代表方法調用是以拋出異常的方式結束。 |
isBefore | 輔助判斷標記,當前的通知節點有多是在方法一開始就通知,此時isBefore==true 成立,同時isThrow==false 和isReturn==false ,由於在方法剛開始時,還沒法肯定方法調用將會如何結束。 |
isThrow | 輔助判斷標記,當前的方法調用以拋異常的形式結束。 |
isReturn | 輔助判斷標記,當前的方法調用以正常返回的形式結束。 |
全部變量均可以在表達式中直接使用,若是在表達式中編寫了不符合OGNL腳本語法或者引入了不在表格中的變量,
對條件表達式
、檢索表達式
而言,則一概當成false
來處理
對觀察表達式
而言,則放棄當前方法調用的處理(不輸出)
JDK的類默認由BootstrapClassLoader負責加載,因爲Greys本身也適用了大量的JDK類,因此我不建議使用Greys直接對JDK相關類進行加強、代理。
默認而言,Greys會拒絕執行關於JDK類的操做命令。你需顯式用options
命令打開。
ga?>options unsafe true +--------+--------------+-------------+ | NAME | BEFORE-VALUE | AFTER-VALUE | +--------+--------------+-------------+ | unsafe | false | true | +--------+--------------+-------------+ Affect(row-cnt:1) cost in 4 ms. ga?>
一些命令須要對類、方法進行模式匹配過濾,從1.5.4.6
及其以後的版本以後,Greys默認支持通配符
匹配,目前僅支持*
和?
兩個通配符,正則表達式須要顯式指定-E
參數激活。
模式匹配舉例
原sc
命令的正則表達式匹配
sc com\.apache\.commons\.lang\.StringUtils
在1.5.4.6
及其以後的版本中將會默認使用通配符表達式
sc com.apache.commons.lang.StringUtils sc *lang.StringUtils
若想繼續使用正則表達式匹配,須要顯式-E
參數激活
sc -E com\.apache\.commons\.lang\.StringUtils sc -E com\..*StringUtils
支持模式匹配的命令
全部須要模式匹配的命令都支持參數-E
,他們分別是:sc
、sm
、stack
、monitor
、watch
、tt
、trace
命令 | 說明 |
---|---|
help | 查看命令的幫助文檔,每一個命令和參數都有很詳細的說明 |
sc | 查看JVM已加載的類信息 |
sm | 查看已加載的方法信息 |
monitor | 方法執行監控 |
trace | 渲染方法內部調用路徑,並輸出方法路徑上的每一個節點上耗時 |
ptrace | 強化版的trace 命令。經過指定渲染路徑,並可記錄下路徑中全部方法的入參、返值;與tt 命令聯動。 |
watch | 方法執行數據觀測 |
tt | 方法執行數據的時空隧道,記錄下指定方法每次調用的入參和返回信息,並能對這些不一樣的時間下調用進行觀測 |
stack | 輸出當前方法被調用的調用路徑 |
version | 輸出當前目標Java進程所加載的Greys版本號 |
quit | 退出greys客戶端 |
shutdown | 關閉greys服務端 |
reset | 重置加強類,將被greys加強過的類所有還原 |
session | 查看當前會話 |
jvm | 查看當前JVM的信息 |
help
命令會是你第一個在Greys中使用的命令,也會是從此使用最頻繁的命令之一,當你在使用的過程當中有任何不熟悉的疑問,請直接help
吧~
查看命令清單
進入Greys的歡迎界面後,全部命令均可以經過help
獲取幫助。此時你直接輸入一個help,Greys則會返回全部命令的大概用途介紹。
ga?>help +----------+----------------------------------------------------------------------------------+ | sc | Search all the classes loaded by JVM | +----------+----------------------------------------------------------------------------------+ | sm | Search the method of classes loaded by JVM | +----------+----------------------------------------------------------------------------------+ | monitor | Monitor the execution of specified Class and its method | +----------+----------------------------------------------------------------------------------+ | watch | Display the details of specified class and method | +----------+----------------------------------------------------------------------------------+ | tt | Time Tunnel | +----------+----------------------------------------------------------------------------------+ | stack | Display the stack trace of specified class and method | +----------+----------------------------------------------------------------------------------+ | ptrace | Display the detailed thread path stack of specified class and method | +----------+----------------------------------------------------------------------------------+ | trace | Display the detailed thread stack of specified class and method | +----------+----------------------------------------------------------------------------------+ | session | Display current session information | +----------+----------------------------------------------------------------------------------+ | quit | Quit Greys console | +----------+----------------------------------------------------------------------------------+ | version | Display Greys version | +----------+----------------------------------------------------------------------------------+ | jvm | Display the target JVM information | +----------+----------------------------------------------------------------------------------+ | reset | Reset all the enhanced classes | +----------+----------------------------------------------------------------------------------+ | shutdown | Shut down Greys server and exit the console | +----------+----------------------------------------------------------------------------------+ | help | Display Greys Help | +----------+----------------------------------------------------------------------------------+ Affect(row-cnt:1) cost in 9 ms. ga?>
嗯,囋囋,我知道個人英文翻譯很爛,就不用吐槽了。指望能有人能幫我從新打理英文的幫助界面和英文xwiki,小生感激涕零!
查看命令詳細幫助
help命令同時也支持對其餘命令的一個解釋說明,好比咱們鍵入help watch
,greys將會返回watch
命令的全部參數解釋、用法介紹等詳細信息。
ga?>help watch
+---------+----------------------------------------------------------------------------------+
| USAGE | -[bfesx:En:] class-pattern method-pattern express condition-express | | | Display the details of specified class and method | +---------+----------------------------------------------------------------------------------+ | OPTIONS | [b] | Watch before invocation | | | -----------------+-------------------------------------------------------------- | | | [f] | Watch after invocation | | | -----------------+-------------------------------------------------------------- | | | [e] | Watch after throw exception | | | -----------------+-------------------------------------------------------------- | | | [s] | Watch after successful invocation | | | -----------------+-------------------------------------------------------------- | | | [x:] | Expand level of object (0 by default) | | | -----------------+-------------------------------------------------------------- | | | [E] | Enable regular expression to match (wildcard matching by def | | | | ault) | | | -----------------+-------------------------------------------------------------- | | | [n:] | Threshold of execution times | | | -----------------+-------------------------------------------------------------- | | | class-pattern | Path and classname of Pattern Matching | | | -----------------+-------------------------------------------------------------- | | | method-pattern | Method of Pattern Matching | | | -----------------+-------------------------------------------------------------- | | | express | express, write by OGNL. | | | | | | | | FOR EXAMPLE params[0] | | | | params[0]+params[1] | | | | returnObj | | | | throwExp | | | | target | | | | clazz | | | | method | | | | | | | | THE STRUCTURE | | | | target : the object | | | | clazz : the object's class | | | | method : the constructor or method | | | | params[0..n] : the parameters of method | | | | returnObj : the returned object of method | | | | throwExp : the throw exception of method | | | | isReturn : the method ended by return | | | | isThrow : the method ended by throwing exception | | | -----------------+-------------------------------------------------------------- | | | condition-expre | Conditional expression by OGNL | | | ss | | | | | FOR EXAMPLE | | | | TRUE : 1==1 | | | | TRUE : true | | | | FALSE : false | | | | TRUE : params.length>=0 | | | | FALSE : 1==2 | | | | | | | | THE STRUCTURE | | | | target : the object | | | | clazz : the object's class | | | | method : the constructor or method | | | | params[0..n] : the parameters of method | | | | returnObj : the returned object of method | | | | throwExp : the throw exception of method | | | | isReturn : the method ended by return | | | | isThrow : the method ended by throwing exception | +---------+----------------------------------------------------------------------------------+ | EXAMPLE | watch -Eb org\.apache\.commons\.lang\.StringUtils isBlank params[0] | | | watch -b org.apache.commons.lang.StringUtils isBlank params[0] | | | watch -f org.apache.commons.lang.StringUtils isBlank returnObj | | | watch -bf *StringUtils isBlank params[0] | | | watch *StringUtils isBlank params[0] | | | watch *StringUtils isBlank params[0] params[0].length==1 | +---------+----------------------------------------------------------------------------------+ Affect(row-cnt:1) cost in 15 ms. ga?>
幫助信息組成
幫助文檔分紅**Useage**、**Options**、**Example**三個區域,分別是**用途說明**、**參數列表**、**實際例子**
ga?>help session +---------+----------------------------------------------------------------------------------+ | USAGE | -[c:] | | | Display current session information | +---------+----------------------------------------------------------------------------------+ | OPTIONS | [c:] | Modify the character set of session | +---------+----------------------------------------------------------------------------------+ | EXAMPLE | session | | | session -c GBK | | | session -c UTF-8 | +---------+----------------------------------------------------------------------------------+ Affect(row-cnt:1) cost in 2 ms. ga?>
參數選項說明
[]
中的參數爲選填項,好比[d]
,意思是該命令可接受一個名稱爲d的選填參數,且不用參數值。[:]
中的參數則爲選填,但有值的參數,好比[c:]
class-pattern
/method-pattern
,這兩個參數爲隱性參數,即在輸入的時候不須要特地聲明參數。class-pattern
爲類路徑.類名稱
的表達式匹配,method-pattern
則爲方法名的表達式匹配。「Search-Class」的簡寫,這個命令能搜索出全部已經加載到JVM中的Class信息。
參數說明
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
[d] | 輸出當前類的詳細信息,包括這個類所加載的原始文件來源、類的聲明、加載的e2ClassLoader等詳細信息。 若是一個類被多個ClassLoader所加載,則會出現屢次 |
[f] | 輸出當前類的成員變量信息 |
[E] | 支持正則表達式匹配 |
使用參考
ga?>sc -df *alibaba.Address +------------------+-----------------------------------------------+ | class-info | com.alibaba.Address | +------------------+-----------------------------------------------+ | code-source | /Users/vlinux/temp/agent-test/target/ | +------------------+-----------------------------------------------+ | name | com.alibaba.Address | +------------------+-----------------------------------------------+ | isInterface | false | +------------------+-----------------------------------------------+ | isAnnotation | false | +------------------+-----------------------------------------------+ | isEnum | false | +------------------+-----------------------------------------------+ | isAnonymousClass | false | +------------------+-----------------------------------------------+ | isArray | false | +------------------+-----------------------------------------------+ | isLocalClass | false | +------------------+-----------------------------------------------+ | isMemberClass | false | +------------------+-----------------------------------------------+ | isPrimitive | false | +------------------+-----------------------------------------------+ | isSynthetic | false | +------------------+-----------------------------------------------+ | simple-name | Address | +------------------+-----------------------------------------------+ | modifier | public | +------------------+-----------------------------------------------+ | annotation | | +------------------+-----------------------------------------------+ | interfaces | | +------------------+-----------------------------------------------+ | super-class | com.alibaba.CountObject | | | `-java.lang.Object | +------------------+-----------------------------------------------+ | class-loader | sun.misc.Launcher$AppClassLoader@2a139a55 | | | `-sun.misc.Launcher$ExtClassLoader@5fb20bfd | +------------------+-----------------------------------------------+ | fields | modifier : private | | | type : java.lang.String | | | name : username | | | | | | modifier : private | | | type : int | | | name : addressId | | | | | | modifier : private | | | type : java.lang.String | | | name : addressName | | | | +------------------+-----------------------------------------------+ Affect(row-cnt:1) cost in 9 ms. ga?>
「Search-Method」的簡寫,這個命令能搜索出全部已經加載了Class信息的方法信息。
參數說明
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
[d] | 展現每一個方法的詳細信息 |
[E] | 支持正則表達式匹配 |
使用參考
ga?>sm -d *alibaba.Address * +-----------------------------+-------------------------------------------------+ | DECLARED-CLASS | VISIBLE-METHOD | +-----------------------------+-------------------------------------------------+ | com.alibaba.Address | declaring-class : class com.alibaba.Address | | | method-name : getAddressId | | | modifier : public | | | annotation : | | | parameters : | | | return : int | | | exceptions : | +-----------------------------+-------------------------------------------------+ | com.alibaba.Address | declaring-class : class com.alibaba.Address | | | method-name : getAddressName | | | modifier : public | | | annotation : | | | parameters : | | | return : java.lang.String | | | exceptions : | +-----------------------------+-------------------------------------------------+ | com.alibaba.Address | declaring-class : class com.alibaba.CountObject | | `-com.alibaba.CountObject | method-name : finalize | | | modifier : protected | | | annotation : | | | parameters : | | | return : void | | | exceptions : java.lang.Throwable | +-----------------------------+-------------------------------------------------+ | com.alibaba.Address | declaring-class : class com.alibaba.CountObject | | `-com.alibaba.CountObject | method-name : count | | | modifier : public | | | annotation : | | | parameters : | | | return : int | | | exceptions : | +-----------------------------+-------------------------------------------------+ Affect(row-cnt:4) cost in 9 ms. ga?>
對匹配class-pattern
/method-pattern
的類.方法的調用進行監控。
monitor
命令是介紹到的第一個非實時返回命令,實時返回命令是輸入以後當即返回,而非實時返回的命令,則是不斷的等待目標Java進程返回信息,直到用戶輸入Ctrl+D
爲止。服務端是以任務的形式在後臺跑任務,植入的代碼隨着任務的停止而被不會被執行,因此任務關閉後,不會對原有性能產生太大影響,並且原則上,任何Greys的命令也不會引發任何原有業務邏輯的改變。
監控的維度說明
監控項 | 說明 |
---|---|
timestamp | 時間戳 |
class | Java類 |
method | 方法(構造方法、普通方法) |
total | 調用次數 |
success | 成功次數 |
fail | 失敗次數 |
rt | 平均RT |
fail-rate | 失敗率 |
參數說明
方法擁有一個命名參數[c:]
,意思是統計週期(cycle of output),擁有一個整形的參數值
參數名稱 | 參數說明 |
---|---|
[c:] | 統計週期,默認值爲120秒 |
使用參考
ga?>monitor -c 5 *alibaba*Test printAddress Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 22 ms. +-----------+-------+--------+-------+---------+------+-----------+------------+------------+------------+ | TIMESTAMP | CLASS | METHOD | TOTAL | SUCCESS | FAIL | FAIL-RATE | AVG-RT(ms) | MIN-RT(ms) | MAX-RT(ms) | +-----------+-------+--------+-------+---------+------+-----------+------------+------------+------------+ +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ | TIMESTAMP | CLASS | METHOD | TOTAL | SUCCESS | FAIL | FAIL-RATE | AVG-RT(ms) | MIN-RT(ms) | MAX-RT(ms) | +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ | 2015-12-06 16:34:56 | com.alibaba.AgentTest | printAddress | 5 | 3 | 2 | 40.00% | 0.20 | 0 | 1 | +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ | TIMESTAMP | CLASS | METHOD | TOTAL | SUCCESS | FAIL | FAIL-RATE | AVG-RT(ms) | MIN-RT(ms) | MAX-RT(ms) | +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ | 2015-12-06 16:35:01 | com.alibaba.AgentTest | printAddress | 5 | 3 | 2 | 40.00% | 0.00 | 0 | 0 | +---------------------+-----------------------+--------------+-------+---------+------+-----------+------------+------------+------------+ ga?>
命令能主動搜索class-pattern
/method-pattern
所渲染的方法調用路徑,渲染和統計整個調用鏈路上的全部性能開銷和追蹤調用鏈路。
參數說明
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
condition-express | 條件表達式 |
[n:] | 命令執行次數 |
[E] | 支持正則表達式匹配 |
注意事項
trace
能方便的幫助你定位和發現因RT高而致使的性能問題缺陷,但其每次只能跟蹤一級方法的調用鏈路,目前暫時沒有精力去解決往下幾個層級的調用。若是真有需求能夠Issues我。
使用參考
ga?>trace com.alibaba.manager.DefaultAddressManager toStringPass2
Press Ctrl+D to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 19 ms. `---+Tracing for : thread_name="agent-test-address-printer" thread_id=0xb;is_daemon=false;priority=5; `---+[0,0ms]com.alibaba.manager.DefaultAddressManager:toStringPass2() +---[0,0ms]com.alibaba.Address:getAddressId() +---[0,0ms]com.alibaba.Address:getAddressId() +---[0,0ms]java.lang.Integer:valueOf() +---[0,0ms]com.alibaba.Address:getAddressName() +---[0,0ms]com.alibaba.Address:count() +---[0,0ms]java.lang.Integer:valueOf() `---[0,0ms]java.lang.String:format()
是否是很眼熟,沒錯,在JProfiler等收費軟件中你曾經見識相似的功能,這裏你將能夠經過命令就能打印出指定調用路徑。
[10,1ms]
的含義,10
所表明的含義是:當前節點的總體耗時;1
的含義是:當前節點在當前步驟的耗時;二者之間用逗號分割,單位爲毫秒。
命令解釋
命令爲trace
命令的強化版,經過指定渲染路徑來完成對方法執行路徑的渲染過程
命令能主動搜索tracing-path-pattern
所渲染的路徑,渲染和統計整個調用鏈路上的全部性能開銷和追蹤調用鏈路。
參數說明
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
condition-express | 條件表達式 |
[t] | 記錄下渲染路徑上全部方法的入參與返回值,記錄下的返回值能夠與tt 命令聯動 |
[n:] | 命令執行次數 |
[E] | 支持正則表達式匹配 |
[path:] | 渲染路徑表達式匹配,該參數可屢次使用 |
[Epath:] | 正則表達式渲染路徑表達式匹配,該參數可屢次使用 |
使用例子
ga?>ptrace -t *alibaba*Test printAddress --path=*alibaba* Press Ctrl+D to abort. Affect(class-cnt:10 , method-cnt:36) cost in 148 ms. `---+pTracing for : thread_name="agent-test-address-printer" thread_id=0xb;is_daemon=false;priority=5;process=1004; `---+[2,2ms]com.alibaba.AgentTest:printAddress(); index=1021; +---+[1,1ms]com.alibaba.manager.DefaultAddressManager:newAddress(); index=1014; | +---[1,1ms]com.alibaba.CountObject:<init>(); index=1012; | `---[1,0ms]com.alibaba.Address:<init>(); index=1013; +---+[2,1ms]com.alibaba.manager.DefaultAddressManager:toString(); index=1020; | +---+[2,1ms]com.alibaba.manager.DefaultAddressManager:toStringPass1(); index=1019; | | +---+[2,1ms]com.alibaba.manager.DefaultAddressManager:toStringPass2(); index=1017; | | | +---[1,0ms]com.alibaba.Address:getAddressId(); index=1015; | | | +---+[1,0ms]com.alibaba.manager.DefaultAddressManager:throwRuntimeException(); index=1016; | | | | `---[1,0ms]throw:java.lang.RuntimeException | | | `---[1,0ms]throw:java.lang.RuntimeException | | +---[2,0ms]com.alibaba.AddressException:<init>(); index=1018; | | `---[2,0ms]throw:com.alibaba.AddressException | `---[2,0ms]throw:com.alibaba.AddressException `---[2,0ms]throw:com.alibaba.AddressException +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | INDEX | PROCESS-ID | TIMESTAMP | COST(ms) | IS-RET | IS-EXP | OBJECT | CLASS | METHOD | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1012 | 1004 | 2015-12-06 16:46:49 | 0 | true | false | 0x943cff | CountObject | <init> | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1013 | 1004 | 2015-12-06 16:46:49 | 0 | true | false | 0x943cff | Address | <init> | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1014 | 1004 | 2015-12-06 16:46:49 | 1 | true | false | 0x6833b8a5 | DefaultAddressManager | newAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1015 | 1004 | 2015-12-06 16:46:49 | 0 | true | false | 0x943cff | Address | getAddressId | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1016 | 1004 | 2015-12-06 16:46:49 | 0 | false | true | 0x6833b8a5 | DefaultAddressManager | throwRuntimeException | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1017 | 1004 | 2015-12-06 16:46:49 | 0 | false | true | 0x6833b8a5 | DefaultAddressManager | toStringPass2 | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1018 | 1004 | 2015-12-06 16:46:49 | 0 | true | false | 0x67e7a923 | AddressException | <init> | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1019 | 1004 | 2015-12-06 16:46:49 | 1 | false | true | 0x6833b8a5 | DefaultAddressManager | toStringPass1 | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1020 | 1004 | 2015-12-06 16:46:49 | 1 | false | true | 0x6833b8a5 | DefaultAddressManager | toString | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1021 | 1004 | 2015-12-06 16:46:49 | 2 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+
能方便的讓你觀察到指定方法的調用狀況。能觀察到的範圍爲:返回值
、拋出異常
、入參
,經過編寫OGNL表達式進行對應變量的查看。
參數說明
watch的參數比較多,主要是由於它能在4個不一樣的場景觀察對象
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
condition-express | 條件表達式 |
express | 觀察表達式 |
[b] | 在**方法調用以前**觀察 |
[e] | 在**方法異常以後**觀察 |
[s] | 在**方法返回以後**觀察 |
[f] | 在**方法結束以後**(正常返回和異常返回)觀察 |
這裏重點要說明的是觀察表達式,觀察表達式的構成主要由OGNL表達式組成,因此你能夠這樣寫params[0]+"$"+target
,只要是一個合法的OGNL表達式,都能被正常支持。
觀察的維度也比較多,主要體如今參數advice
的數據結構上。Advice
參數最主要是封裝了通知節點的全部信息。參考表達式核心變量中關於該節點的描述。
使用參考
ga?>watch -b *Test printAddress '"params[0]="+params[0]' Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 32 ms. params[0]=3163 params[0]=3164 params[0]=3165 params[0]=3166
這裏須要說明的一個參數x
,這個參數決定了輸出的結果的層級遍歷輸出對象,當加上這個參數以後,greys會將輸出的對象按照指定層級進行剝開。-x 1
代表展開第1層級。
ga?>watch -s com.alibaba.manager.DefaultAddressManager newAddress returnObj -x 1 Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 34 ms. @Address[ username=@String[dukun], addressId=@Integer[3244], addressName=@String[ADDRESS], ]
時間隧道命令是我在使用watch
命令進行問題排查的時候衍生出來的想法。watch
雖然很方便和靈活,但須要提早想清楚觀察表達式的拼寫,這對排查問題而言要求過高,由於不少時候咱們並不清楚問題出自於何方,只能靠蛛絲馬跡進行猜想。
這個時候若是能記錄下當時方法調用的全部入參和返回值、拋出的異常會對整個問題的思考與判斷很是有幫助。
因而乎,TimeTunnel命令就誕生了。
記錄方法的調用
基本用法
對於一個最基本的使用來講,就是記錄下當前方法的每次調用環境現場。
ga?>tt -t -n 3 *Test printAddress Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 33 ms. +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | INDEX | PROCESS-ID | TIMESTAMP | COST(ms) | IS-RET | IS-EXP | OBJECT | CLASS | METHOD | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1036 | 1009 | 2015-12-06 16:57:06 | 1 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1037 | 1010 | 2015-12-06 16:57:07 | 0 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1038 | 1011 | 2015-12-06 16:57:08 | 0 | true | false | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ ga?>
命令參數解析
-t
tt命令有不少個主參數,-t
就是其中之一。這個參數的代表但願記錄下類*Test
的print
方法的每次執行狀況。
-n 3
當你執行一個調用量不高的方法時可能你還能有足夠的時間用CTRL+D
中斷tt命令記錄的過程,但若是遇到調用量很是大的方法,瞬間就能將你的JVM內存撐爆。
此時你能夠經過-n
參數指定你須要記錄的次數,當達到記錄次數時greys會主動中斷tt命令的記錄過程,避免人工操做沒法中止的狀況。
表格字段說明
表格字段 | 字段解釋 |
---|---|
INDEX | 時間片斷記錄編號,每個編號表明着一次調用,後續tt還有不少命令都是基於此編號指定記錄操做,很是重要。 |
PROCESS-ID | 過程編號,咱們認爲同一個線程的一次同步調用爲一個過程 |
TIMESTAMP | 方法執行的本機時間,記錄了這個時間片斷所發生的本機時間 |
COST(ms) | 方法執行的耗時 |
IS-RET | 方法是否以正常返回的形式結束 |
IS-EXP | 方法是否以拋異常的形式結束 |
OBJECT | 執行對象的hashCode() ,注意,曾經有人誤認爲是對象在JVM中的無力內存地址,但很遺憾他不是。但他能幫助你簡單的標記當前執行方法的類實體 |
CLASS | 執行的類名 |
METHOD | 執行的方法名 |
條件表達式
不知道你們是否有在使用過程當中遇到如下困惑
- 彷佛很難區分出重載的方法
- 我只須要觀察特定參數,可是tt卻所有都給我記錄了下來
從1.6.0.6
版本以後,應廣大婦女羣衆的要求,增長了條件表達式,這樣你能夠經過簡單的條件表達式解決上邊的困惑。
條件表達式也是用OGNL來編寫,核心的判斷對象依然是Advice
對象。
除了
tt
命令以外,watch
、trace
、stack
命令也都支持條件表達式
解決方法重載
tt -t *Test print params[0].length==1
經過制定參數個數的形式解決不一樣的方法簽名,若是參數個數同樣,你還能夠這樣寫
tt -t *Test print 'params[1].class == Integer.class'
解決指定參數
tt -t *Test print params[0].mobile=="13989838402"
檢索調用記錄
當你用tt
記錄了一大片的時間片斷以後,你但願能從中篩選出本身須要的時間片斷,這個時候你就須要對現有記錄進行檢索。
假設咱們有這些記錄
ga?>tt -l +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | INDEX | PROCESS-ID | TIMESTAMP | COST(ms) | IS-RET | IS-EXP | OBJECT | CLASS | METHOD | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1047 | 1020 | 2015-12-06 17:03:00 | 1 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1048 | 1021 | 2015-12-06 17:03:01 | 0 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1049 | 1022 | 2015-12-06 17:03:01 | 1 | true | false | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1050 | 1023 | 2015-12-06 17:03:01 | 0 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1051 | 1024 | 2015-12-06 17:03:02 | 1 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1052 | 1025 | 2015-12-06 17:03:02 | 1 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1053 | 1026 | 2015-12-06 17:03:02 | 0 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1054 | 1027 | 2015-12-06 17:03:03 | 0 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1055 | 1028 | 2015-12-06 17:03:03 | 0 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1056 | 1029 | 2015-12-06 17:03:03 | 0 | true | false | 0x2062a3d | AgentTest | printUser | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ Affect(row-cnt:10) cost in 3 ms. ga?>
我須要篩選出printAddress
方法的調用信息
ga?>tt -s method.name=="printAddress" +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | INDEX | PROCESS-ID | TIMESTAMP | COST(ms) | IS-RET | IS-EXP | OBJECT | CLASS | METHOD | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1049 | 1022 | 2015-12-06 17:03:01 | 1 | true | false | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1052 | 1025 | 2015-12-06 17:03:02 | 1 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ | 1055 | 1028 | 2015-12-06 17:03:03 | 0 | false | true | 0x2062a3d | AgentTest | printAddress | +----------+------------+----------------------+------------+----------+----------+-----------------+--------------------------------+--------------------------------+ Affect(row-cnt:3) cost in 8 ms. ga?>
你須要一個-s
參數。一樣的,搜索表達式的核心對象依舊是Advice
對象。
查看調用信息
對於具體一個時間片的信息而言,你能夠經過-i
參數後邊跟着對應的INDEX
編號查看到他的詳細信息。
ga?>tt -i 1055 +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | INDEX | 1055 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | PROCESS-ID | 1028 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | GMT-CREATE | 2015-12-06 17:03:03 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | COST(ms) | 0 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | OBJECT | 0x2062a3d | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | CLASS | com.alibaba.AgentTest | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | METHOD | printAddress | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | IS-RETURN | false | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | IS-EXCEPTION | true | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | PARAMETERS[0] | 3789 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | THROW-EXCEPTION | com.alibaba.AddressException: java.lang.RuntimeException: test | | | at com.alibaba.manager.DefaultAddressManager.toStringPass1(DefaultAddressManager.java:22) | | | at com.alibaba.manager.DefaultAddressManager.toString(DefaultAddressManager.java:15) | | | at com.alibaba.AgentTest.printAddress(AgentTest.java:80) | | | at com.alibaba.AgentTest.access$300(AgentTest.java:7) | | | at com.alibaba.AgentTest$3.null(Unknown Source) | | | Caused by: java.lang.RuntimeException: test | | | at com.alibaba.manager.DefaultAddressManager.throwRuntimeException(DefaultAddressManager.java:39) | | | at com.alibaba.manager.DefaultAddressManager.toStringPass2(DefaultAddressManager.java:29) | | | at com.alibaba.manager.DefaultAddressManager.toStringPass1(DefaultAddressManager.java:20) | | | ... 4 more | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | STACK | thread_name="agent-test-address-printer" thread_id=0xb;is_daemon=false;priority=5; | | | @com.alibaba.AgentTest.access$300() | | | at com.alibaba.AgentTest$3.null(null:-1) | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ Affect(row-cnt:1) cost in 7 ms. ga?>
重作一次調用
當你少少作了一些調整以後,你可能須要前端系統從新觸發一次你的調用,此時得求爺爺告奶奶的須要前端配合聯調的同窗再次發起一次調用。而有些場景下,這個調用不是這麼好觸發的。
tt
命令因爲保存了當時調用的全部現場信息,因此咱們能夠本身主動對一個INDEX
編號的時間片自主發起一次調用,從而解放你的溝通成本。此時你須要-p
參數。
ga?>tt -i 1055 -p +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | INDEX | 1055 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | PROCESS-ID | 1028 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | GMT-CREATE | 2015-12-06 17:03:03 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | COST(ms) | 1 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | OBJECT | 0x2062a3d | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | CLASS | com.alibaba.AgentTest | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | METHOD | printAddress | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | IS-RETURN | true | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | IS-EXCEPTION | false | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | PARAMETERS[0] | 3789 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | RETURN-OBJ | 1 | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | STACK | thread_name="ga-command-execute-daemon" thread_id=0x22;is_daemon=true;priority=9; | | | @com.github.ompc.greys.core.command.TimeTunnelCommand$6.action() | | | at com.github.ompc.greys.core.server.DefaultCommandHandler.execute(DefaultCommandHandler.java:210) | | | at com.github.ompc.greys.core.server.DefaultCommandHandler.executeCommand(DefaultCommandHandler.java:87) | | | at com.github.ompc.greys.core.server.GaServer$4.run(GaServer.java:332) | | | at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) | | | at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) | | | at java.lang.Thread.run(Thread.java:745) | +-----------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ Time fragment[1055] successfully replayed. Affect(row-cnt:1) cost in 2 ms. ga?>
你會發現結果雖然同樣,但調用的路徑發生了變化,有原來的程序發起變成了greys本身的內部線程發起的調用了。
得益於Greys的ClassLoader隔離策略,Greys在內部本身發起線程請求調用的時候,依然採用的是目標類所歸屬的ClassLoader,因此在OSGI、Tomcat容器等場景下,Greys依然能正確的重作這次調用。
須要強調的點
ThreadLocal信息丟失
不少框架偷偷的將一些環境變量信息塞到了發起調用線程的ThreadLocal中,因爲調用線程發生了變化,這些ThreadLocal線程信息沒法經過greys保存,因此這些信息將會丟失。
一些常見的CASE好比:阿里鷹眼系統的TraceId、阿里全鏈路平臺的壓測流量標記位。
引用的對象
須要強調的是,tt
命令是將當前環境的對象引用保存起來,但僅僅也只能保存一個引用而已。若是方法內部對入參進行了變動,或者返回的對象通過了後續的處理,那麼在tt
查看的時候將沒法看到當時最準確的值。這也是爲何watch
命令存在的意義。
不少時候咱們都知道一個方法被執行,但這個方法被執行的路徑很是多。或者你根本就不知道這個方法是從那裏被執行了,正在鬱悶,正在彷徨。此時你須要的是stack命令。
參數說明
參數名稱 | 參數說明 |
---|---|
class-pattern | 類名錶達式匹配 |
method-pattern | 方法名錶達式匹配 |
condition-express | 條件表達式 |
[n:] | 命令執行次數 |
[E] | 支持正則表達式匹配 |
使用例子
ga?>stack com.alibaba.manager.DefaultAddressManager newAddress Press Ctrl+D to abort. Affect(class-cnt:1 , method-cnt:1) cost in 36 ms. thread_name="agent-test-address-printer" thread_id=0xb;is_daemon=false;priority=5; @java.lang.reflect.Method.invoke() at com.alibaba.manager.DefaultAddressManager.newAddress(DefaultAddressManager.java:-1) at com.alibaba.AgentTest.printAddress(AgentTest.java:73) at com.alibaba.AgentTest.access$300(AgentTest.java:7) at com.alibaba.AgentTest$3.null(null:-1)
這估計是最好理解的一個命令了,輸出當前Greys的版本號,這裏輸出的版本號不是Client的版本,而是當前加載到目標Java進程中的Greys版本。
這裏說明下與shutdown
命令的區別,quit
命令僅僅是將客戶端關閉,而不會將目標Java進程中的與Greys的Server關閉。因此若是僅僅是但願簡單的推出Greys控制檯,則使用quit
命令足矣。
一旦Greys控制檯退出,控制檯所綁定的Session將會被關閉,Session上全部存活的事務也都會被停止並釋放。
命令執行後將會完成兩件事:
關閉Greys在目標Java所加載的Socket服務,所佔用的端口將會被釋放,同時,本地的Greys控制檯也由於遠程Socket關閉而主動退出。
重置全部被Greys所加強的類。同reset
命令。
重置指定被Greys所加強的類。
會話命令是在1.6版本以後新增,整個命令的定位是維護好會話級的參數。目前可修改的就一個字符集。
參數說明
參數名稱 | 參數說明 |
---|---|
[c:] | 指定會話字符集 |
使用例子
直接查看會話信息
ga?>session +------------+------------------+ | JAVA_PID | 8609 | +------------+------------------+ | SESSION_ID | 2 | +------------+------------------+ | DURATION | 300000 | +------------+------------------+ | CHARSET | UTF-8 | +------------+------------------+ | PROMPT | ga?> | +------------+------------------+ | FROM | /127.0.0.1:58186 | +------------+------------------+ | TO | /127.0.0.1:3658 | +------------+------------------+ Affect(row-cnt:1) cost in 0 ms. ga?>
修改字符集
ga?>session -c GBK change charset before[UTF-8] -> new[GBK] Affect(row-cnt:1) cost in 26 ms.
查看當前JVM的信息,無參數。
ga?>jvm
+--------------------+-----------------------------------------------------------------------------------------------------+
| CATEGORY | INFO | +--------------------+-----------------------------------------------------------------------------------------------------+ | RUNTIME | MACHINE-NAME : 25428@vlinux-air.local | | | JVM-START-TIME : 2015-06-16 22:12:20 | | | MANAGEMENT-SPEC-VERSION : 1.2 | | | SPEC-NAME : Java Virtual Machine Specification | | | SPEC-VENDOR : Oracle Corporation | | | SPEC-VERSION : 1.8 | | | VM-NAME : Java HotSpot(TM) 64-Bit Server VM | | | VM-VENDOR : Oracle Corporation | | | VM-VERSION : 25.25-b02 | | | INPUT-ARGUMENTS : [] | | | CLASS-PATH : . | | | BOOT-CLASS-PATH : /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/li | | | b/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Conte | | | nts/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_25.j | | | dk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMachin | | | es/jdk1.8.0_25.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVir | | | tualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/jce.jar:/Library/Jav | | | a/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/charsets.ja | | | r:/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/l | | | ib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/H | | | ome/jre/classes | | | LIBRARY-PATH : /Users/vlinux/Library/Java/Extensions:/Library/Java/Extensions:/Networ | | | k/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java | | | :. | +--------------------+-----------------------------------------------------------------------------------------------------+ | CLASS-LOADING | LOADED-CLASS-COUNT : 3045 | | | TOTAL-LOADED-CLASS-COUNT : 3045 | | | UNLOADED-CLASS-COUNT : 0 | | | IS-VERBOSE : false | +--------------------+-----------------------------------------------------------------------------------------------------+ | COMPILATION | NAME : HotSpot 64-Bit Tiered Compilers | | | TOTAL-COMPILE-TIME : 8903(ms) | +--------------------+-----------------------------------------------------------------------------------------------------+ | GARBAGE-COLLECTORS | PS Scavenge : 4/40(ms) | | | [count/time] | | | PS MarkSweep : 0/0(ms) | | | [count/time] | +--------------------+-----------------------------------------------------------------------------------------------------+ | MEMORY-MANAGERS | CodeCacheManager : Code Cache | | | Metaspace Manager : Metaspace | | | Compressed Class Space | | | PS Scavenge : PS Eden Space | | | PS Survivor Space | | | PS MarkSweep : PS Eden Space | | | PS Survivor Space | | | PS Old Gen | +--------------------+-----------------------------------------------------------------------------------------------------+ | MEMORY | HEAP-MEMORY-USAGE : 163053568/134217728/1908932608/54796080 | | | [committed/init/max/used] | | | NO-HEAP-MEMORY-USAGE : 32571392/2555904/-1/31757608 | | | [committed/init/max/used] | | | PENDING-FINALIZE-COUNT : 0 | +--------------------+-----------------------------------------------------------------------------------------------------+ | OPERATING-SYSTEM | OS : Mac OS X | | | ARCH : x86_64 | | | PROCESSORS-COUNT : 4 | | | LOAD-AVERAGE : 2.50634765625 | | | VERSION : 10.10.3 | +--------------------+-----------------------------------------------------------------------------------------------------+ | THREAD | COUNT : 8 | | | DAEMON-COUNT : 7 | | | LIVE-COUNT : 9 | | | STARTED-COUNT : 19 | +--------------------+-----------------------------------------------------------------------------------------------------+ Affect cost in 22 ms.
安裝失敗
$:>curl -sLk http://ompc.oss.aliyuncs.com/greys/install.sh|ksh downloading... greys.zip.43678 download file failed!
問:
爲何我在安裝Greys的時候失敗了?
答:
Greys的安裝腳本首先先從阿里雲上下載greys.zip
,而後進行解壓、安裝。因此必需要求執行安裝腳本的用戶必須有對當前目錄的寫權限,通常出現這個問題,能夠檢查網絡
、磁盤空間
以及當前目錄是否有寫權限
。
問:
若是我在/tmp/
目錄下執行安裝腳本,請問Greys會怎麼安裝?
答:
安裝在/tmp/greys/
目錄下,該目錄下一共有三個重要文件
/tmp/greys/greys.sh
其中greys-core.jar
爲greys的程序主體,啓動類、加載類都在這個jar包當中;greys-agent.jar
則爲目標JVM的加載引導程序;greys.sh
爲一個可執行腳本,爲Greys的啓動腳本。
問:
安裝的Greys如何進行刪除?
答:
Greys是一個綠色環保軟件,不會修改你的註冊表,但會在$HOME
目錄下建立隱藏目錄$HOME/.greys
,用於保存不一樣的版本和Jline的歷史命令,你能夠直接刪除。
Operation not permitted
錯誤問:
我在啓動Greys的時候報這樣的錯誤
$:> ./greys.sh 11064 start greys failed, because : Operation not permitted
答:
Greys要求執行執行啓動命令的用戶必須擁有和目標進程ID一樣的權限(在這個Case中,目標進程ID爲11064
),不然JVM將沒法掛載Greys對應的jar包
No such process
錯誤問:
我在啓動Greys的時候報這樣的錯誤
$:> ./greys.sh 11063 start greys failed, because : No such process
答:
目標進程ID不存在。報這樣的錯誤,請覈對你的目標進程是否存在。
Unable to open socket file: target process not responding or HotSpot VM not loaded
- **問:**
我在啓動Greys的時候報這樣的錯誤
```shell $:> sudo -u admin ./greys.sh 12592 密碼: start greys failed, because : Unable to open socket file: target process not responding or HotSpot VM not loaded ```
答:
目標進程ID不是JVM進程,或目標JVM進程不支持加載操做,好比低於JDK6的版本等。
通常遇到這種問題必定要很是當心謹慎的執行,若是對方進程編程不嚴謹,極可能會讓對方進程CORE掉。上次我就弄死了個nginx的worker -_-!!
問:
啓動以後就什麼反應也沒有,也沒有出現預期的ga?>
提示符
答:
頗有可能你的3658端口已經被別的進程佔據,請覈對你當前機器所開的端口
netstat -anp|grep LIST
解決方案也很簡單,換個端口
./greys.sh 4567@127.0.0.1:6666
sudo -u
方式啓動報權限不足
請給你的sudo命令加上-H
參數
sudo -u admin -H ./greys.sh 4567
哪些命令會致使性能問題
Greys的大部分命令性能開銷都很是低廉,固然前提是一次性操做的類不要太多。
是否能加強由BootstrapClassLoader所加載的類
固然是能夠的,但默認我*封印*了這個能力。主要是Greys本身也使用了大量BootstrapClassLoader所加載的類,若是處理很差極其容易形成故障。
你能夠經過隱藏命令options
激活這個功能
ga?>options unsafe true +--------+--------------+-------------+ | NAME | BEFORE-VALUE | AFTER-VALUE | +--------+--------------+-------------+ | unsafe | false | true | +--------+--------------+-------------+ Affect(row-cnt:1) cost in 2 ms.
接下來你能夠嘗試加強系統類了
ga?>monitor -c 5 java.lang.String substring
Press Ctrl+D or Ctrl+X to abort. Affect(class-cnt:1 , method-cnt:2) cost in 35 ms. +---------------------+------------------+-----------+-------+---------+------+------+-----------+ | timestamp | class | method | total | success | fail | rt | fail-rate | +---------------------+------------------+-----------+-------+---------+------+------+-----------+ | 2015-06-16 23:44:54 | java.lang.String | substring | 30 | 30 | 0 | 0.23 | 0.00% | +---------------------+------------------+-----------+-------+---------+------+------+-----------+
但我話就放在這裏,隨意加強系統類。後果自負!
Greys支持將信息輸出到文件中麼?
支持,不過要作一些小命令。
./greys.sh 4567|tee -a ./greys.log
Greys能使用在Sun JDK5的版本麼?
很遺憾抱歉的說,不行。
Greys的原理和Btrace同樣,依賴了JDK6+提供的Instumentation
特性,因此必需要求目標的JDK環境是**JDK6及其以上的版本**。
理論上Greys應該能在各類實現了SUN標準的各類JVM實現中運行,好比JRockit
、Zing
等,但我本身沒有機會嘗試,如有朋友能提供環境能進行測試並反饋測,我將不勝感激。
JRE中因爲缺乏tools.jar,因此沒法直接運行Greys,須要稍做一些修改。一樣的也沒有需求要在JRE中運行Greys,我這裏也偷個懶,留給其餘有須要的人來實現吧。
程序中是否有彩蛋?
有,我當初作這個軟件的惟一目的就是但願能快速定位問題,而後好陪她逛街。爲了避免忘記這個初心,我將這位她的英文名做爲命令整合到了Greys中,能夠嘗試找找!
1.7.3.0
多版本管理
從1.7.x.x
版本開始,greys.sh
腳本支持自動更新,在網絡容許的狀況下會自動監測遠程服務器上是否存在可升級的最新版本。
若網絡不可達(網絡隔離的環境)則須要進行本地安裝。本地安裝也同樣會歸入到多版本管理識別範圍。
大版本兼容性問題
大版本之間不作任何兼容性保障,好比1.7.x.x
版本的客戶端不保證能訪問1.6.x.x
啓動的服務端。
主版本
.大版本
.小版本
.漏洞修復
主版本
這個版本更新說明程序架構體系進行了重大升級,好比以前的0.1版升級到1.0版本,整個軟件的架構從單機版升級到了SOCKET多機版。並將Greys的性質進行的肯定:Java版的HouseMD,但要比前輩們更強。
大版本
程序的架構設計進行重大改造,但不影響用戶對這款軟件的定位。
小版本
增長新的命令和功能
漏洞修復
對現有版本進行漏洞修復和加強
主版本
、大版本
之間不作任何向下兼容的承諾。即0.1
版本的Client不保證必定能正常訪問1.0
版本的Server。小版本
不兼容的功能會在版本升級中指出漏洞修復
保證全部功能向下兼容我編寫和維護這款軟件已經5年了,5年中Greys也從0.1
版本一直重構到如今的1.7
。在這個過程當中我獲得了許多人的幫助與建議,並在年末我計劃發佈2.0
版本,將開放Greys的底層通信協議,支持websocket訪問。
多年的問題排查經驗我沒有過多的分享,一個Java程序員箇中的苦悶也無從分享,一切我都融入到了這款軟件的命令中,但願這些沉澱能幫助到可能須要到的你少走一些彎路,同時我也很是期待大家對她的反饋,這樣我將感到很是開心和有成就