Btrace入门级别的简单使用

       Btrace是什么?简单来说就是我们线上进行代码调试的一款利器,它可以动态的修改线上代码,从而达到既保留现场又能定位问题的效果,一句话总结就是:Btrace可以动态的向目标应用程序的字节码注入追踪代码

下面来讲解以下Btrace如何使用

Btrace有两种运行脚本的方式:

     1)在JVisualVM中添加BTrace插件,添加classpath

     2)使用命令行btrace <pid> <trace_script>

然后后面的例子当中两种方式我都会进行演示如何使用这个工具

后面的例子大概会以下面这三个方面进行Btrace的讲解:

1)拦截方法

     1.1)普通方法 @OnMethod(clazz="",method="")

     1.2)构造函数 @OnMethod(clazz="",method="")

     1.3)拦截同名函数,用参数区分

2)拦截时机

     2.1)Kind.ENTRY:入口,默认值

     2.2)Kind.RETURN:返回

     2.3)Kind.THROW:异常

     2.4)Kind.Line:行

3)拦截this,参数,返回值

    3.1)this:@Self

    3.2)入参:可以使用AnyType,也可以用真实类型,同名的用真实的

    3.3)返回:@Return

首先我们在Idea当中创建一个SpringBoot的项目,然后做一个简单的demo进行访问,细节不再多说,因为比较简单,下面是demo代码:

@RestController
@RequestMapping("/btrace")
public class Ch4Controller {
	
	@RequestMapping("/method1")
	public String arg1(@RequestParam("name")String name) {
		return "hello,"+name;
	}
}

 然后就是我们的主角上场,首先得从gitHub上进行下载,这是下载地址:

https://github.com/btraceio/btrace

下载完之后需要简单的两步:

1)进行解压,解压到自己保存工程的路劲下面

2)配置环境变量,和配置jdk环境变量方式一摸一样,我们给变量名叫:BTRACE_HOME

然后加入刚才解压好的路径,最后修改一下path环境变量加入 :%BTRACE_HOME%\bin;即可,最后cmd下输入btrace,显示如下图即表示成功

1.1)拦截普通方法

import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
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;

@BTrace
public class PrintArgSimple {
	
	@OnMethod(
	        clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller",
	        method="method1",
	        location=@Location(Kind.ENTRY)
	)
	public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
		BTraceUtils.printArray(args);
		BTraceUtils.println(pcn+","+pmn);
		BTraceUtils.println();
    }
}

第一种运行脚本的方式:【在JVisualVM中添加BTrace插件】

找到我们的jdk安装路径,找到bin,双击【jvisualvm.exe】即可运行,进去之后我们需要安装一下插件,具体安装细节这里不再多说,因为这个不是本章的重点。

然后直接在我们运行的java进程上右击【Trace application】便可进入编写我们的运行脚本了,直接粘贴上面代码即可。最后点击start即可运行

第二种运行脚本的方式:cmd下直接切换到我们存放脚本的地方,因为脚本类似java,但是他又是独立存在的,所以需要将其详细的的路径粘贴过来,然后输入脚本运行命令:

btrace  pid  PrintArgSimple.java(脚本名称)

基本流程都是如此,下面测试过程一样,直接贴测试代码

先是运行的脚本代码:

1.2)拦截构造器

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;

@BTrace
public class PrintConstructor {
	
	@OnMethod(
	        clazz="com.imooc.monitor_tuning.chapter2.User",
	        method="<init>"
	)
	public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
		BTraceUtils.println(pcn+","+pmn);
		BTraceUtils.printArray(args);
		BTraceUtils.println();
    }
}

1.3)拦截同名函数,用参数区分

 

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;

@BTrace
public class PrintSame {
	
	@OnMethod(
	        clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller",
	        method="same"
	)
	public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, String name) {
		BTraceUtils.println(pcn+","+pmn + "," + name);
		BTraceUtils.println();
    }
}

2.1)Kind.ENTRY:入口,默认值

 

看第一个脚本即可

2.2)Kind.RETURN:返回

import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
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.Return;

@BTrace
public class PrintReturn {
	
	@OnMethod(
	        clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller",
	        method="method1",
	        location=@Location(Kind.RETURN)
	)
	public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, @Return AnyType result) {
		BTraceUtils.println(pcn+","+pmn + "," + result);
		BTraceUtils.println();
    }
}

2.3)Kind.THROW:异常

import com.sun.btrace.BTraceUtils;
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.Self;
import com.sun.btrace.annotations.TLS;

@BTrace 
public class PrintOnThrow {    
    // store current exception in a thread local
    // variable (@TLS annotation). Note that we can't
    // store it in a global variable!
    @TLS 
    static Throwable currentException;

    // introduce probe into every constructor of java.lang.Throwable
    // class and store "this" in the thread local variable.
    @OnMethod(
        clazz="java.lang.Throwable",
        method="<init>"
    )
    public static void onthrow(@Self Throwable self) {//new Throwable()
        currentException = self;
    }

    @OnMethod(
        clazz="java.lang.Throwable",
        method="<init>"
    )
    public static void onthrow1(@Self Throwable self, String s) {//new Throwable(String msg)
        currentException = self;
    }

    @OnMethod(
        clazz="java.lang.Throwable",
        method="<init>"
    )
    public static void onthrow1(@Self Throwable self, String s, Throwable cause) {//new Throwable(String msg, Throwable cause)
        currentException = self;
    }

    @OnMethod(
        clazz="java.lang.Throwable",
        method="<init>"
    )
    public static void onthrow2(@Self Throwable self, Throwable cause) {//new Throwable(Throwable cause)
        currentException = self;
    }

    // when any constructor of java.lang.Throwable returns
    // print the currentException's stack trace.
    @OnMethod(
        clazz="java.lang.Throwable",
        method="<init>",
        location=@Location(Kind.RETURN)
    )
    public static void onthrowreturn() {
        if (currentException != null) {
        	BTraceUtils.Threads.jstack(currentException);
        	BTraceUtils.println("=====================");
            currentException = null;
        }
    }
}

2.4)Kind.Line:行

import com.sun.btrace.BTraceUtils;
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;

@BTrace
public class PrintLine {
	
	@OnMethod(
	        clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller",
	        method="exception",
	        location=@Location(value=Kind.LINE, line=35)
	)
	public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) {
		BTraceUtils.println(pcn+","+pmn + "," +line);
		BTraceUtils.println();
    }
}

3)拦截this,参数,返回值

 

上面测试脚本基本都已包含,现在若是需要测试一个自己定义的对象,不如一个user对象,如何获取他的属性等,也就是

获取复杂类型,btrace是通过反射,类名+属性名的方式获得的,下面我们看脚本的编写:

import java.lang.reflect.Field;
import com.imooc.monitor_tuning.chapter2.User;
import com.sun.btrace.BTraceUtils;
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;

@BTrace
public class PrintArgComplex {
	
	
	@OnMethod(
	        clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller",
	        method="arg2",
	        location=@Location(Kind.ENTRY)
	)
	public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, User user) {
		//print all fields
		BTraceUtils.printFields(user);
		//print one field
		Field filed2 = BTraceUtils.field("com.imooc.monitor_tuning.chapter2.User", "name");
		BTraceUtils.println(BTraceUtils.get(filed2, user));
		BTraceUtils.println(pcn+","+pmn);
		BTraceUtils.println();
    }
}

 

这里需要注意运行脚本的时候可能会识别不了user,那么我们的脚本命令就需要稍微改一下:

还是先切换到相应脚本的路径下:然后执行

btrace  12112(进程id)  -cp  <classpath>(我们编译之后的class路径) PrintArgComplex.java

最后是我们测试每一个脚本的代码:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.imooc.monitor_tuning.chapter2.User;
@RestController
@RequestMapping("/btrace")
public class Ch4Controller {
	
	@RequestMapping("/method1")
	public String arg1(@RequestParam("name")String name) {
		return "hello,"+name;
	}
	
	@RequestMapping("/method2")
	public User arg2(User user) {
		return user;
	}
	
	@RequestMapping("/constructor")
	public User constructor(User user) {
		return user;
	}
	
	@RequestMapping("/same1")
	public String same(@RequestParam("name")String name) {
		return "hello,"+name;
	}
	@RequestMapping("/same2")
	public String same(@RequestParam("name")String name,@RequestParam("id")int id) {
		return "hello,"+name+","+id;
	}
	
	@RequestMapping("/exception")
	public String exception() {
		try {
			System.out.println("start...");
			System.out.println(1/0);
			System.out.println("end...");
		}catch(Exception e) {
			//
		}
		return "success";
	}
}

执行脚本之后的效果图省略...

感兴趣的小伙伴可以直接本地测试一下,熟悉之后就可以运用到我们的实际生产环境当中了,还有需要注意的有两点:

1)上面演示的代码默认只能本地运行,要想测试远程的感兴趣的小伙伴得单独改写代码

2)生产环境下可以使用,但是被修改的字节码不会被还原

 

好了,Btarce的使用简单说到这里,相深入了解的可以去github上进行更详细的额查看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值