动态跟踪 java 运行程序,通过 hotswap 技术,动态将跟踪的字节码输入到运行类,对运行代码侵入小。
instrument + asm
1.对一个周期性读取网络操作的程序,增加监控程序耗时程序
被监控的程序
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
/**
* @version 1.0
* @author: mousycoder
* @date: 2019-07-22 15:49
*/
public class HoldNetTask implements Runnable {
public static void main(String[] args) {
new Thread(new HoldNetTask()).start();
}
public void visitWeb(String strUrl) {
URL url = null;
URLConnection urlConnection = null;
InputStream is = null;
try {
url = new URL(strUrl);
urlConnection = url.openConnection();
is = urlConnection.getInputStream();
BufferedReader buffer = new BufferedReader(new InputStreamReader(is));
StringBuffer bs = new StringBuffer();
String l = null;
while ((l = buffer.readLine()) != null) {
bs.append(l).append("/r/n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void run() {
while (true) {
visitWeb("http://www.sina.com.cn");
}
}
}
监控程序
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
/**
* @version 1.0
* @author: mousycoder
* @date: 2019-07-22 15:58
*/
@BTrace
public class PrintTimes {
@TLS
private static long startTime = 0;
@OnMethod(clazz = "/.+/",method = "/visitWeb/") // visitWeb方法开始的执行执行
public static void startMethod(){
startTime = timeMillis();
}
@OnMethod(clazz = "/.+/",method = "/visitWeb/",location = @Location(Kind.RETURN)) //visitWeb方法结束的时候执行
public static void endMethod(){
print(strcat(strcat(name(probeClass()),"."),probeMethod()));
print("[");
print(strcat("Time taken: " , str(timeMillis() - startTime)));
println("]");
}
}
编写监控程序的时候,需要下载 btrace 客户端,maven 中配置该路径(中央仓库没有对应版本)
<!-- https://mvnrepository.com/artifact/io.btrace/btrace-maven -->
<dependency>
<groupId>com.sun.tools.btrace</groupId>
<artifactId>btrace-agent</artifactId>
<version>1.3.11.3</version>
<scope>system</scope>
<systemPath>/Users/mousycoder/My/software/btrace-bin-1.3.11.3/build/btrace-agent.jar</systemPath>
</dependency>
<dependency>
<groupId>com.sun.tools.btrace</groupId>
<artifactId>btrace-boot</artifactId>
<scope>system</scope>
<systemPath>/Users/mousycoder/My/software/btrace-bin-1.3.11.3/build/btrace-boot.jar</systemPath>
</dependency>
<dependency>
<groupId>com.sun.tools.btrace</groupId>
<artifactId>btrace-client</artifactId>
<scope>system</scope>
<systemPath>/Users/mousycoder/My/software/btrace-bin-1.3.11.3/build/btrace-client.jar</systemPath>
</dependency>
先启动被监控程序 找到 pid 然后
btrace -v 16352 PrintTimes.java
即可开启监控程序
输出
DEBUG: received com.sun.btrace.comm.MessageCommand@2b515b5c HoldNetTask.visitWebDEBUG: received com.sun.btrace.comm.MessageCommand@31ba0af0 [DEBUG: received com.sun.btrace.comm.MessageCommand@752ccee0 Time taken: 20DEBUG: received com.sun.btrace.comm.MessageCommand@28ec9c23
打印出了visitWeb执行方法的耗时
2.输出被监控程序的参数
import com.sun.btrace.AnyType;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
import static com.sun.btrace.BTraceUtils.*;
/**
* @version 1.0
* @author: mousycoder
* @date: 2019-07-22 17:05
*/
@BTrace
public class PrintTimes2 {
@OnMethod(clazz = "/.*HoldNetTask/",method = "/visitWeb/")
public static void anyWriteFile(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args){
print(pcn);
print(".");
print(pmn);
printArray(args);
}
}
输出
DEBUG: received com.sun.btrace.comm.MessageCommand@6f96c77 HoldNetTaskDEBUG: received com.sun.btrace.comm.MessageCommand@be64738 .DEBUG: received com.sun.btrace.comm.MessageCommand@3ba9ad43 visitWebDEBUG: received com.sun.btrace.comm.MessageCommand@49d904ec [http://www.sina.com.cn, ]
可见已经输出了参数 http://www.sina.com.cn
3.输出被监控程序 27 行信息
import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.print;
import static com.sun.btrace.BTraceUtils.printArray;
/**
* @version 1.0
* @author: mousycoder
* @date: 2019-07-22 17:05
*/
@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){
print(BTraceUtils.Strings.strcat(pcn,"."));
print(BTraceUtils.Strings.strcat(pmn,":"));
print(line);
}
}
输出
27DEBUG: received com.sun.btrace.comm.MessageCommand@39529185 HoldNetTask.DEBUG: received com.sun.btrace.comm.MessageCommand@72f926e6 visitWeb:DEBUG: received com.sun.btrace.comm.MessageCommand@3daa422a 27DEBUG: received com.sun.btrace.comm.MessageCommand@31c88ec8 HoldNetTask.DEBUG: received com.sun.btrace.comm.MessageCommand@1cbbffcd
4.新增定时任务(每秒输出虚拟机启动时间+每 3 秒导出线程堆栈)
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;
/**
* @version 1.0
* @author: mousycoder
* @date: 2019-07-22 17:05
*/
@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) //每 3 秒运行
public static void getStack(){
jstackAll(); //导出线程堆栈
}
}
输出
com.sun.btrace.BTraceRuntime$19.run(BTraceRuntime.java:2387)
java.util.TimerThread.mainLoop(Timer.java:555)
java.util.TimerThread.run(Timer.java:505)
Thread[RMI Scheduler(0),9,system]
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:744)
DEBUG: received com.sun.btrace.comm.MessageCommand@3aeaafa6
1000 msec:1543007
5.打印实例属性
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
/**
* @version 1.0
* @author: mousycoder
* @date: 2019-07-22 17:05
*/
@BTrace
public class PrintField {
@OnMethod(clazz = "/.*URL/",method = "/.*openConnection/",location = @Location(value = Kind.ENTRY))
public static void visitWebEntry(@Self Object self){
println(strcat(strcat(name(probeClass()),"."),probeMethod()));
println(self);
println(get(field(classOf(self),"protocolPathProp"))); //只能取 static 变量
println(get(field(classOf(self),"host"),self)); //获取实例变量
println("===========");
}
}
监控 URL 对象,在调用 openConnectio()方法的时候,获得当前实例 self ,然后获取属性
输出
DEBUG: received com.sun.btrace.comm.MessageCommand@3ba987b8 java.net.URL.openConnection DEBUG: received com.sun.btrace.comm.MessageCommand@3f191845 http://www.sina.com.cn
6.谁调用了 GC
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
/**
* @version 1.0
* @author: mousycoder
* @date: 2019-07-22 17:05
*/
@BTrace
public class OnSystemGC {
@OnMethod(clazz = "java.lang.System",method = "gc")
public static void onSystemGC(){
println("enterd System.gc()");
jstack();
}
}
7.更多例子可以查看 btrace 客户端目录下的 samples 文件夹