這期內容當中小編將會給大家帶來有關Java運行時如何使用追蹤工具BTrace,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
Btrace是由sundararajan在2009年6月開發的一個開源項目,是一種動態跟蹤分析一個運行中的Java應用程序的工具。 BTrace是一個為Java平臺開發的安全、動態的追蹤工具。BTrace動態地向目標應用程序的字節碼注入追蹤代碼(字節碼追蹤),這些追蹤字節碼追蹤代碼使用Java語言表達,也就是BTrace的腳本。
為了保證追蹤動作是“只讀”的(也就是這些動作不可以修改被追蹤程序的狀態)和有限度的(比如在固定時間里結束)。一個BTrace程序只允許完成一些指定的動作。下面是BTrace一些不可以完成的事情:
不能創建新的對象
不能創建新的數組
不能拋出異常
不能捕獲異常
不能進行任何的實例函數或者靜態函數 – 只有com.sun.btrace.BTraceUtils類中的靜態函數或者BTrace程序自己聲明的函數才可以被BTrace調用
不可以在目標程序的類,或者對象的靜態或者實例級別的field進行賦值。但是,BTrace自身的類是可以給它的靜態field進行賦值的
不能有outer,inner,嵌套的或者本地類。
不能有同步代碼塊或者同步的函數
不能有循環語句(for,while, do..while)
不能繼承其它類(父類只能是java.lang.Object)
不能實現接口
不能包含斷言(assert)語句
不能使用類字面值
這上面的種種限制可以通過一個配置改變:unsafe=true,在使用BTrace注解時修改該屬性的默認值(false)為true,即@BTrace(unsafe=true);也可以啟動選項中顯式聲明-Dcom.sun.btrace.unsafe=true(響應也有-u參數);現在你可以為所欲為了。BUT,這樣做之前最好考慮好風險并再三檢查腳本,請斟酌使用!
btrace git下載地址 ,下載release版本下來直接解壓就可以使用。
btrace <pid> <btrace-script>腳本
btrace命令行工具運行命令如下:
btrace <options> <pid> <btrace source or .class file> <btrace arguments> 常用選項: [-I <include-path>] [-p <port>] [-cp <classpath>]
參數說明:
where possible options include: --version Show the version -v Run in verbose mode -o <file> The path to store the probe output (will disable showing the output in console) -u Run in trusted mode -d <path> Dump the instrumented classes to the specified path -pd <path> The search path for the probe XML descriptors -classpath <path> Specify where to find user class files and annotation processors -cp <path> Specify where to find user class files and annotation processors -I <path> Specify where to find include files -p <port> Specify port to which the btrace agent listens for clients -statsd <host[:port]> Specify the statsd server, if any
include-path : 是一些用來查找頭文件的目錄。BTrace包含一個簡單的預處理,支持# define,# + include和條件編譯。它不像一個完整的C / c++預處理器–而是一個有用的子集。詳見demo代碼“ThreadBean.java”,如果沒有顯式的聲明選項-I,Btrace跳過預處理程序調用步驟。
port: BTrace代理程序所偵聽的端口,這是可選的選項。默認是2020
classpath: 是一些用來查找jar文件的目錄。默認是當前目錄”.”
pid:是要追蹤目標程序id
btrace-script: 就是追蹤程序本身。如果這是個java文件,那么提交前會進行編譯。否則,它被認為已預編譯(即它必須是一個類)并提交
arguments: 這是傳遞給BTrace程序的參數。BTrace程序可以通過內置的符號來引用這些參數,length是這些參數的個數。
在samples目錄下有很多示例,并且有的跟蹤很有用可直接使用.
@com.sun.btrace.annotations.OnMethod 該注解可用來指定目標類,目標方法,以及目標方法里的“位置”。加了該注解后的操作方法會在對應的方法運行到指定的“位置”時被執行。該注解中,目標類用“clazz”屬性來指定,而目標方法用“method”屬性來指定?!眂lazz”可以是類的全路徑(比如java.awt.Component或者用兩個反斜杠中間的正則表達式,參考例子NewComponent.java和Classload.java來看它們的用法,正則表達式可以匹配0個或多個目標類,這個時候多個類都會被進行動態指令更換。如/java.awt.+/匹配java.awt包下的所有類)。方法名也可以用這樣的正則表達式 來匹配零個或者多個多個方法。參考例子MultiClass.java來查看用法。還有一種方法來指定追蹤類和函數。被追蹤的類和函數可以用注解來指定。比如,如果”clazz”屬性是@javax.jws.Webservice.那么BTrace會會把所有注解是這個的函數都進行動態指令更換。類似地,方法級別的注解也可以用來執行方法。參看例子WebServiceTracker.java來了解如何使用??梢园颜齽t表達式和注解放在一起用,比如@/com.acme..+/可以匹配任何類,只要這個類的注解能跟那段正則表達式匹配即可??梢酝ㄟ^指定父類來匹配多個類名,比如+java.lang.Runnable就可以匹配所有實現了java.lang.Runnable這個接口的類。參考例子SubtypeTracer.java來看它的用法。
@com.sun.btrace.annotations.OnTimer 該注解可以用來執行那些需要周期性(間隔是毫秒)的追蹤操作。參考Histogram.java來看它的用法。
@com.sun.btrace.annotations.OnError 該注解可以用來指定當任何異常拋出時需要執行的操作。被該注解修飾后的BTrace函數會在同一個BTrace類的其他操作方法拋出異常時執行。
@com.sun.btrace.annotations.OnExit 該注解用來執行黨BTrace代碼調用了exit(int)結束追蹤會話后需要執行的操作。參考例子ProbeExit.java來了解如何使用。
@com.sun.btrace.annotations.OnEvent 該注解用來追蹤函數與”外部”的事件關聯起來。當BTrace客戶端發送了一個“事件”后,該注解里的操作就會被執行??蛻舳税l送的事件可能是由用戶觸發的(比如按下Ctrl-C)。事件的名字是個字符串,這樣追蹤操作就只會在對應的事件觸發后被執行。到目標為止,BTrace命令行客戶端會在用戶按下Ctrl-C后發送事件,參考例子HistoOnEvent.java來了解用法。
@com.sun.btrace.annotations.OnLowMemory 該注解可以用來追蹤特定內存閾值被用光的事件。參看例子MemAlerter.java了解用法。
@com.sun.btrace.annotations.OnProbe 該注解可以用來避免使用BTrace腳本的內部類。@OnProbe探測點被映射到一個或多個@OnMethod上。目前這個映射是通過一個XML探測描述文件類指定的(這個文件會被BTrace代理所使用)。參考例子SocketTracker1.java和對應的描述文件java.net.socket.xml.當運行這個例子時,xml文件需要放在目標JVM所有運行的目錄下(或者修改btracer.bat中的probeDescPath選項來指向任意的xml文件)。
@com.sun.btrace.annotations.Location:該注解在一個traced/probed方法中指定一個特定的“位置”
@com.sun.btrace.annotations.Simpled:標記@OnMethod注解處理器采樣。采樣處理程序時并不是所有的事件將被追蹤,只有一個統計樣品與給定的意思。在默認情況下使用一種自適應采樣。BTrace將增加或減少樣品之間的調用數量保持平均時間窗口,因此減少整體的開銷。
@com.sun.btrace.annotations.Self:該注解把一個參數標識為保留了目標函數所指向的this的值。參考例子AWTEventTracer.java和AllCalls1.java.
@com.sun.btrace.annotations.Return:該注解說明這個參數保存目標函數的返回值。參考例子Classload.java
@com.sun.btrace.annotations.ProbeClassName:所修飾的參數保留了探測類的類名 。參看AllMethods.java(有多個探測類)
@com.sun.btrace.annotations.ProbeMethodName:所修飾的參數保留了探測函數的函數名。參考WebServiceTracker.java(多個探測函數)
@com.sun.btrace.annotations.TargetInstance:修飾的參數保留了被調用的實例。參考例子AllCall2.java.
@com.sun.btrace.annotations.TargetMethodOrField:該注解修飾的參數保存了調用的函數名。參考AllCalls1.java 和AllCall2.java
@com.sun.btrace.annotations.Duration:探測方法參數標記為持續時間值的接收者,即目標方法執行的時間,單位納秒。只是用帶Location屬性的@OnMethod,并且需要配合Kind.ERROR或者Kind.RETURN使用
沒有注解的BTrace探測函數參數是用來作簽名匹配的,因為他們必須必須在固定的位置上出現。然而,它們可以和其他的注解的參數進行交換。如果一個參數的類型是_AnyType[]_,它就會“吃”掉所所有剩下的參數。沒有注解的參數的具體含義與他們所在的位置有關:
名稱 | 作用 |
---|---|
Kind.ARRAY_GET | 數組元素加載 |
Kind.ARRAY_SET | 數組元素存儲 |
Kind.CALL | 方法調用 |
Kind.CATCH | 異常捕獲 |
Kind.CHECKCAST | checkcast |
Kind.ENTRY | 方法進入。意指進入匹配probe點,跟你@Location設置的clazz和method沒有任何關系 |
Kind.ERROR | 錯誤,異常沒有捕獲,返回 |
Kind.FIELD_GET | field獲取 |
Kind.FIELD_SET | field設置 |
Kind.INSTANCEOF | 實例檢測 |
Kind.LINE | 源代碼行號 |
Kind.NEW | 創建新實例 |
Kind.NEWARRAY | 新的數組對象被創建 |
Kind.RETURN | 意指從某個匹配probe的方法中調用了匹配A class method的點,一定要和claz |
Kind.SYNC_ENTRY | 進入一個同步方法鎖 |
Kind.SYNC_EXIT | 離開一個同步方法鎖 |
Kind.THROW | 拋出異常 |
@com.sun.btrace.annotations.Export BTrace字段使用該注解來說明它已經被映射到一個jvmstat計數器上。使用該注解,BTrace程序可以把追蹤計數器暴露給外部的jvmstat客戶端(比如jstat)。參考例子ThreadCounter.java
@com.sun.btrace.annotations.Property 該注解可以把一個字段標識為一個MBean屬性。如果一個BTrace類至少有一個靜態的字段使用了該注解。那么一個MBean就會被創建并且注冊到平臺MBean服務器上。JMX客戶端比如VisualVM,jconsole可以訪問這個字段來查看BTrace的MBean。在把BTrace附加到目標程序上后,你可以把VisualVM或者jconsole也附加到同一個目標程序上來查看剛創建好的MBean屬性。通過VisualVM或者jconsole,你可以通過MBeans tab頁來查看BTrace相關的域,然后查看它們的值。參考例子ThreadCounterBean.java 和HistogramBean.java來了解用法
@com.sun.btrace.annotations.TLS BTrace字段使用該注解來說明它自己是一個線程本地字段(thread local field).注意你只能在@OnMethod注解后的函數里訪問這樣的字段。每個Java線程都有一個這個字段的拷貝。為了讓這樣的方式能夠工作,這個字段的類型只能是immutable(比如原始類型) 或者是cloneable (實現了Cloneable接口并且覆蓋了clone()函數)的。這些線程本地字段可以被BTrace程序用來識別它是否在同一個線程里執行了多個探測操作。參考例子OnThrow.java和WebServiceTracker.java
@com.sun.btrace.annotations.DTrace該注解用來把一小段D腳本(嵌在BTrace 的java類中)和BTrace程序關聯起來。參考例子DTraceInline.java
@com.sun.btrace.annotations.DTraceRef 和上個注解一樣,不同的是D腳本是在獨立的文件中,不是嵌在java類中。
@com.sun.btrace.annotations.BTrace必須使用該注解來指定一個Java類是BTrace程序。BTrace編譯器會強制查找該注解,BTrace代理也會檢查這個是否有該注解。如果沒有,則提示錯誤,并且不會執行。
package com.gitee.funcy.jtools.btrace; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; /** * 該程序周期性地進行網絡讀取操作 * * @author chengyan * @date 2019-11-03 10:41 下午 */ public class HoldNetTask implements Runnable{ public void visitWeb(String strUrl) { URL url = null; URLConnection urlconn = null; InputStream is = null; try { url = new URL(strUrl); urlconn = url.openConnection(); is = urlconn.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is)); StringBuffer bs = new StringBuffer(); String l = null; while((l = bufferedReader.readLine()) != null) { bs.append(l).append("\r\n"); } } catch (Exception e) { e.printStackTrace(); } finally { if(is != null) { try { is.close(); } catch (Exception e) { } } } } @Override public void run() { while(true) { visitWeb("http://www.sina.com.cn"); } } public static void main(String[] args) { new Thread(new HoldNetTask()).start(); } }
BTrace對應的java包在Btrace release文件的build目錄下,一共有三個jar包:
btrace-agent.jar btrace-boot.jar btrace-client.jar
也可使用maven引入依賴包,在pom文件中添加以下內容:
<properties> ...省略其他 <btrace.version>1.3.11.3</btrace.version> </properties> <dependencies> ...省略其他 <dependency> <groupId>com.sun.tools.btrace</groupId> <artifactId>btrace-agent</artifactId> <version>${btrace.version}</version> </dependency> <dependency> <groupId>com.sun.tools.btrace</groupId> <artifactId>btrace-boot</artifactId> <version>${btrace.version}</version> </dependency> <dependency> <groupId>com.sun.tools.btrace</groupId> <artifactId>btrace-client</artifactId> <version>${btrace.version}</version> </dependency> </dependencies> <repositories> <repository> <id>btrace-repo</id> <name>btrace-repo</name> <url>https://dl.bintray.com/btraceio/maven/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
需要注意的是,maven中央倉庫中可能沒有btrace的最新版本,為此需要配置額外的倉庫。
使用BTrace腳本可以通過正則表達式指定監控特定類的特定方法的耗時,以下代碼將監控所有類中名為visitWeb()
的函數的執行時間。
package com.gitee.funcy.jtools.btrace; import com.sun.btrace.annotations.BTrace; import com.sun.btrace.annotations.Kind; import com.sun.btrace.annotations.Location; import com.sun.btrace.annotations.OnMethod; import com.sun.btrace.annotations.ProbeClassName; import com.sun.btrace.annotations.ProbeMethodName; import com.sun.btrace.annotations.TLS; import static com.sun.btrace.BTraceUtils.*; /** * {這里添加描述} * * @author chengyan * @date 2019-11-03 12:13 上午 */ @BTrace public class PrintTimes { @TLS private static long startTime = 0; /** * 方法調用開始 * clazz = "/.+/" : 監控做任意類 * method="/visitWeb/":監控visitWeb方法 */ @OnMethod(clazz = "/.+/", method="/visitWeb/") public static void startMethod() { startTime = timeMillis(); } @OnMethod(clazz = "/.+/", method="/visitWeb/", location=@Location(Kind.RETURN)) public static void endMethod(@ProbeClassName String pcm, @ProbeMethodName String pmn) { println(pcm + "." + pmn + " [Time taken: " + str(timeMillis() - startTime) + "ms]"); } }
運行結果:
$ btrace 42188 PrintTimes.java com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 55ms] com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 50ms] com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 52ms] com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 49ms] com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 54ms] com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 54ms]
除了執行時間,BTrace也可輸出函數的參數:
package com.gitee.funcy.jtools.btrace; import com.sun.btrace.AnyType; import com.sun.btrace.BTraceUtils; import com.sun.btrace.annotations.BTrace; import com.sun.btrace.annotations.OnMethod; import com.sun.btrace.annotations.ProbeClassName; import com.sun.btrace.annotations.ProbeMethodName; /** * {這里添加描述} * * @author chengyan * @date 2019-11-03 10:43 下午 */ @BTrace public class PrintArgs { @OnMethod(clazz = "/.*HoldNetTask/",method = "/visitWeb/") public static void anyWriteFile(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) { BTraceUtils.print(pcn + "-" + pmn); BTraceUtils.printArray(args); } }
運行結果:
$ btrace 46877 PrintArgs.java com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ] com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ] com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ] com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ] com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ] com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
通過BTrace腳本@Location
注解,可以指定程序運行到某一行代碼時,觸發某一行為。下例顯示了通過BTrace腳本獲取HoldNetTask
類第27行代碼的信息(當目標程序運行到第27行時,觸發BTrace腳本)。
package com.gitee.funcy.jtools.btrace; import static com.sun.btrace.BTraceUtils.*; import com.sun.btrace.annotations.BTrace; import com.sun.btrace.annotations.Location; import com.sun.btrace.annotations.OnMethod; import com.sun.btrace.annotations.Kind; import com.sun.btrace.annotations.ProbeClassName; import com.sun.btrace.annotations.ProbeMethodName; /** * {這里添加描述} * * @author chengyan * @date 2019-11-04 10:02 下午 */ @BTrace public class AllLines { @OnMethod(clazz = "/.*HoldNetTask/", location = @Location(value = Kind.LINE, line = 27)) public static void online(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) { println(pcn + "." + pmn + ":" + line); } }
運行結果:
$ btrace 46877 AllLines.java com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27 com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27 com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27 com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27 com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27 com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
由腳本輸出可以看到,BTrace正確識別出HoldNetTask類的第27行代碼,正處于visitWeb()
函數的運行區間中。若將 @Location
中line的值設置為-1,則BTrace腳本將在每一行觸發。
BTrace腳本支持定時觸發??梢灾芷谛缘貓绦心骋恍袨?,獲取系統信息。下例使用@OnTimer
標記制定兩個周期性任務,分別為每秒運行一次和每3秒運行一次。
package com.gitee.funcy.jtools.btrace; import com.sun.btrace.BTraceUtils; import com.sun.btrace.annotations.BTrace; import com.sun.btrace.annotations.OnTimer; import static com.sun.btrace.BTraceUtils.jstackAll; import static com.sun.btrace.BTraceUtils.println; /** * {這里添加描述} * * @author chengyan * @date 2019-11-04 10:11 下午 */ @BTrace public class Timers { @OnTimer(1000) public static void getUpTime() { println(BTraceUtils.Strings.strcat("1000 msec:", BTraceUtils.Strings.str(BTraceUtils.Sys.VM.vmUptime()))); } @OnTimer(3000) public static void getStack() { jstackAll(); } }
運行結果:
$ btrace 46969 Timers.java 1000 msec:27654 1000 msec:28647 Thread[Attach Listener,9,system] Thread[Signal Dispatcher,9,system] Thread[Thread-1,9,system] java.net.PlainSocketImpl.socketAccept(Native Method) java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409) java.net.ServerSocket.implAccept(ServerSocket.java:545) java.net.ServerSocket.accept(ServerSocket.java:513) com.sun.btrace.agent.Main.startServer(Main.java:668) com.sun.btrace.agent.Main.access$000(Main.java:63) com.sun.btrace.agent.Main$2.run(Main.java:128) java.lang.Thread.run(Thread.java:748)
在getUpTime()
方法中,指定了一個每秒運行一次的任務,并打印虛擬機的啟動時間。在getStack()
方法中,指定了一個每3秒運行一次的任務,每次都將導出系統的線程快照。
在程序運行過程中,BTrace可以在指定的位置獲得對象實例的字段信息。比如,在本實例中,可以在調用URL.openConnection()
時查看實際打開的URL地地址。
package com.gitee.funcy.jtools.btrace; import com.sun.btrace.annotations.BTrace; import com.sun.btrace.annotations.Location; import com.sun.btrace.annotations.OnMethod; import com.sun.btrace.annotations.Kind; import com.sun.btrace.annotations.ProbeClassName; import com.sun.btrace.annotations.ProbeMethodName; import com.sun.btrace.annotations.Self; import static com.sun.btrace.BTraceUtils.*; /** * {這里添加描述} * * @author chengyan * @date 2019-11-04 10:20 下午 */ @BTrace public class PrintField { @OnMethod(clazz = "/.*URL/", method = "/.*openConnection/", location = @Location(value = Kind.ENTRY)) public static void visitWebEntry(@Self Object self, @ProbeClassName String pcn, @ProbeMethodName String pmn) { println(pcn + "." + pmn); println(self); // 只能取值static變量 println(get(field(classOf(self), "protocolPathProp"))); // 獲得實例變量 println(get(field(classOf(self), "host"), self)); println("==============="); } }
運行結果:
$ btrace 46969 PrintField.java java.net.URL.openConnection http://www.sina.com.cn java.protocol.handler.pkgs www.sina.com.cn ===============
這里監控URL對象,在調用openConnection()
方法時,獲得當前對象實例self,然后獲取對應實例的host等屬性。
腳本中方法參數需要跟原方法參數類型保持一致
腳本中不允許使用除btrace之外的類,拼接字符串使用BTraceUtils.strcat()
,打印使用BTraceUtils.println()
,獲取線程使用BTraceUtils.Threads
BTrace植入過的代碼,會一直在,直到應用重啟為止。所以即使Btrace退出了,業務函數每次執行時都會執行Btrace植入的代碼
上述就是小編為大家分享的Java運行時如何使用追蹤工具BTrace了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。