2021年6月28日星期一

性能工具之Java调试工具BTrace入门

引言

在我们对Java应用做问题分析的时候,往往采用log进行问题定位和分析,但是如果我们的log缺乏相关的信息呢?远程调试会影响应用的正常工作,修改代码重新部署应用,实时性和灵活性难以保证,有没有不影响正常应用运行,又灵活并无侵入性的方法呢?

答案是有,它就是Java中的神器-BTrace

BTrace是什么?

BTrace使用Java的Attach技术,可以让我们无缝的将我们BTrace脚本挂到JVM上,通过脚本你可以获取到任何你想拿到的数据,在侵入性和安全性都非常可靠,特别是定位线上问题的神器。

BTrace原理

BTrace是基于动态字节码修改技术(Hotswap)向目标程序的字节码注入追踪代码。

安装配置

关于BTrace的安装配置使用,此处就不再重复造轮子,网上有太多的教程。

官网地址:https://github.com/btraceio/btrace

注意事项

生产环境可以使用,但修改的字节码不会被还原,使用Btrace时,需要确保追踪的动作是只读的(即:追踪行为不能修改目标程序的状态)和有限的行为(即:追踪行为需要在有限的时间内终止),一个追踪行为需要满足以下的限制:

  • 不能创建新的对象

  • 不能创建新的数组

  • 不能抛出异常

  • 不能捕获异常

  • 不能对实例或静态方法调用-只有从BTraceUtils中的public static方法中或在当前脚本中声明的方法,可以被BTrace调用

  • 不能有外部,内部,嵌套或本地类

  • 不能有同步块或同步方法

  • 不能有循环(for,while,do..while)

  • 不能继承抽象类(父类必须是java.lang.Object)

  • 不能实现接口

  • 不能有断言语句

  • 不能有class保留字

以上的限制可以通过通过unsafe模式绕过。追踪脚本和引擎都必须设置为unsafe模式。脚本需要使用注解为 @BTrace(unsafe=true),需要修改BTrace安装目录下bin中btrace脚本将 -Dcom.sun.btrace.unsafe=false改为 -Dcom.sun.btrace.unsafe=true

注:关于unsafe的使用,如果你的程序一旦被btrace追踪过,那么unsafe的设置会一直伴随该进程的整个生命周期。如果你修改了unsafe的设置,只有通过重启目标进程,才能获得想要的结果。所以该用法不是很好使用,如果你的应用不能随便重启,那么你在第一次使用btrace最终目标进程之前,先想好到底使用那种模式来启动引擎。

使用示例

拦截一个普通方法

control方法

  1. @GetMapping(value = "/arg1")

  2.    public String arg1(@RequestParam("name") String name) throws InterruptedException {

  3.        Thread.sleep(2000);

  4.        return "7DGroup," + name;

  5.    }

BTrace脚本

  1. /**

  2. * 拦截示例

  3. */

  4. @BTrace

  5. public class PrintArgSimple {

  6.  

  7.    @OnMethod(

  8.            //类名

  9.            clazz = "com.techstar.monitordemo.controller.UserController",

  10.            //方法名

  11.            method = "arg1",

  12.            //拦截时刻:入口

  13.            location = @Location(Kind.ENTRY))

  14.  

  15.    /**

  16.     * 拦截类名和方法名

  17.     */ public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {

  18.        BTraceUtils.printArray(args);

  19.        BTraceUtils.println(pcn + "," + pmn);

  20.        BTraceUtils.println();

  21.    }

  22. }

拦截结果:

  1. 192:Btrace apple$ jps -l

  2. 369

  3. 5889 /Users/apple/Downloads/performance/apache-jmeter-4.0/bin/ApacheJMeter.jar

  4. 25922 sun.tools.jps.Jps

  5. 23011 org.jetbrains.idea.maven.server.RemoteMavenServer

  6. 25914 org.jetbrains.jps.cmdline.Launcher

  7. 25915 com.techstar.monitordemo.MonitordemoApplication

  8. 192:Btrace apple$ btrace 25915 PrintArgSimple.java

  9. [zuozewei, ]

  10. com.techstar.monitordemo.controller.UserController,arg1

  11.  

  12. [zee, ]

  13. com.techstar.monitordemo.controller.UserController,arg1

拦截构造函数

构造函数

  1. @Data

  2. public class User {

  3.  

  4.    private int id;

  5.    private String name;

  6.  

  7. }

control方法

  1. @GetMapping(value = "/arg2")

  2.    public User arg2(User user) {

  3.        return user;

  4.    }

BTrace脚本

  1. /**

  2. * 拦截构造函数

  3. */

  4. @BTrace

  5. public class PrintConstructor {

  6.  

  7.    @OnMethod(clazz = "com.techstar.monitordemo.domain.User", method = "<init>")

  8.    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {

  9.        BTraceUtils.println(pcn + "," + pmn);

  10.        BTraceUtils.printArray(args);

  11.        BTraceUtils.println();

  12.    }

  13. }

拦截结果

  1. 192:Btrace apple$ btrace 34119 PrintConstructor.java

  2. com.techstar.monitordemo.domain.User,<init>

  3. [1, zuozewei, ]

拦截同名函数,以参数区分

control方法

  1. @GetMapping(value = "/same1")

  2.    public String same(@RequestParam("name") String name) {

  3.        return "7DGroup," + name;

  4.    }

  5.  

  6.    @GetMapping(value = "/same2")

  7.    public String same(@RequestParam("id") int id, @RequestParam("name") String name) {

  8.        return "7DGroup," + name + "," + id;

  9.    }

BTrace脚本

  1. /**

  2. * 拦截同名函数,通过输入的参数区分

  3. */

  4.  

  5. @BTrace

  6. public class PrintSame {

  7.  

  8.    @OnMethod(clazz = "com.techstar.monitordemo.controller.UserController", method = "same")

  9.    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, String name) {

  10.        BTraceUtils.println(pcn + "," + pmn + "," + name);

  11.        BTraceUtils.println();

  12.    }

  13. }

拦截结果

  1. 192:Btrace apple$ jps -l

  2. 369

  3. 5889 /Users/apple/Downloads/performance/apache-jmeter-4.0/bin/ApacheJMeter.jar

  4. 34281 sun.tools.jps.Jps

  5. 34220 org.jetbrains.jps.cmdline.Launcher

  6. 34221 com.techstar.monitordemo.MonitordemoApplication

  7. 192:Btrace apple$ btrace 34221 PrintSame.java

  8. com.techstar.monitordemo.controller.UserController,same,zuozewei

  9.  

  10. com.techstar.monitordemo.controller.UserController,same,zuozewei

  11.  

  12. com.techstar.monitordemo.controller.UserController,same,zuozewei

拦截方法返回值

BTrace脚本

  1. /**

  2. * 拦截返回值

  3. */

  4. @BTrace

  5. public class PrintReturn {

  6.  

  7.    @OnMethod(clazz = "com.techstar.monitordemo.controller.UserController", method = "arg1",

  8.            //拦截时刻:返回值

  9.            location = @Location(Kind.RETURN))

  10.    public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, @Return AnyType result) {

  11.        BTraceUtils.println(pcn + "," + pmn + "," + result);

  12.        BTraceUtils.println();

  13.    }

  14. }

拦截结果

  1. 192:Btrace apple$ jps -l

  2. 34528 org.jetbrains.jps.cmdline.Launcher

  3. 34529 com.techstar.monitordemo.MonitordemoApplication

  4. 369

  5. 5889 /Users/apple/Downloads/performance/apache-jmeter-4.0/bin/ApacheJMeter.jar

  6. 34533 sun.tools.jps.Jps

  7. 192:Btrace apple$ btrace 34529 PrintReturn.java

  8. com.techstar.monitordemo.controller.UserController,arg1,7DGroup,zuozewei

异常分析

有时候开发人员对异常处理不合理,导致某些重要异常人为被吃掉,并且没有日志或者日志不详细,导致性能分析定位问题困难,我们可以使用BTrace来处理

control方法

  1. @GetMapping(value = "/exception")

  2.    public String exception() {

  3.        try {

  4.            System.out.println("start...");

  5.            System.out.println(1 / 0); //模拟异常

  6.            System.out.println("end...");

  7.        } catch (Exception e) {}

  8.        return "successful...";

  9.    }

BTrace脚本

  1. /**

  2. * 有时候,有些异常被人为吃掉,日志又没有打印,这个时候可以用该类定位问题

  3. * This example demonstrates printing stack trace

  4. * of an exception and thread local variables. This

  5. * trace script prints exception stack trace whenever

  6. * java.lang.Throwable's constructor returns. This way

  7. * you can trace all exceptions that may be caught and

  8. * "eaten" silently by the traced program. Note that the

  9. * assumption is that the exceptions are thrown soon after

  10. * creation [like in "throw new FooException();"] rather

  11. * that be stored and thrown later.

  12. */

  13. @BTrace

  14. public class PrintOnThrow {

  15.    // store current exception in a thread local

  16.    // variable (@TLS annotation). Note that we can't

  17.    // store it in a global variable!

  18.    @TLS

  19.    static Throwable currentException;

  20.  

  21.    // introduce probe into every constructor of java.lang.Throwable

  22.    // class and store "this" in the thread local variable.

  23.    @OnMethod(clazz = "java.lang.Throwable", method = "<init>")

  24.    public static void onthrow(@Self Throwable self) {

  25.        currentException = self;

  26.    }

  27.  

  28.    @OnMethod(clazz = "java.lang.Throwable", method = "<init>")

  29.    public static void onthrow1(@Self Throwable self, String s) {

  30.        currentException = self;

  31.    }

  32.  

  33.    @OnMethod(clazz = "java.lang.Throwable", method = "<init>")

  34.    public static void onthrow1(@Self Throwable self, String s, Throwable cause) {

  35.        currentException = self;

  36.    }

  37.  

  38.    @OnMethod(clazz = "java.lang.Throwable", method = "<init>")

  39.    public static void onthrow2(@Self Throwable self, Throwable cause) {

  40.        currentException = self;

  41.    }

  42.  

  43.    // when any constructor of java.lang.Throwable returns

  44.    // print the currentException's stack trace.

  45.    @OnMethod(clazz = "java.lang.Throwable", method = "<init>", location = @Location(Kind.RETURN))

  46.    public static void onthrowreturn() {

  47.        if (currentException != null) {

  48.            Threads.jstack(currentException);

  49.            BTraceUtils.println("=====================");

  50.            currentException = null;

  51.        }

  52.    }

  53. }

拦截结果

  1. 192:Btrace apple$ jps -l

  2. 369

  3. 5889 /Users/apple/Downloads/performance/apache-jmeter-4.0/bin/ApacheJMeter.jar

  4. 34727 sun.tools.jps.Jps

  5. 34666 org.jetbrains.jps.cmdline.Launcher

  6. 34667 com.techstar.monitordemo.MonitordemoApplication

  7. 192:Btrace apple$ btrace 34667 PrintOnThrow.java

  8. java.lang.ClassNotFoundException: org.apache.catalina.webresources.WarResourceSet

  9.    java.net.URLClassLoader.findClass(URLClassLoader.java:381)

  10.    java.lang.ClassLoader.loadClass(ClassLoader.java:424)

  11.    java.lang.ClassLoader.loadClass(ClassLoader.java:411)

  12.    sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)

  13.    java.lang.ClassLoader.loadClass(ClassLoader.java:357)

  14.    org.apache.catalina.webresources.StandardRoot.isPackedWarFile(StandardRoot.java:656)

  15.    org.apache.catalina.webresources.CachedResource.validateResource(CachedResource.java:109)

  16.    org.apache.catalina.webresources.Cache.getResource(Cache.java:69)

  17.    org.apache.catalina.webresources.StandardRoot.getResource(StandardRoot.java:216)

  18.    org.apache.catalina.webresources.StandardRoot.getResource(StandardRoot.java:206)

  19.    org.apache.catalina.mapper.Mapper.internalMapWrapper(Mapper.java:1027)

  20.    org.apache.catalina.mapper.Mapper.internalMap(Mapper.java:842)

  21.    org.apache.catalina.mapper.Mapper.map(Mapper.java:698)

  22.    org.apache.catalina.connector.CoyoteAdapter.postParseRequest(CoyoteAdapter.java:679)

  23.    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:336)

  24.    org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)

  25.    org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)

  26.    org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800)

  27.    org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)

  28.    org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)

  29.    java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

  30.    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

  31.    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

  32.    java.lang.Thread.run(Thread.java:748)

  33. =====================

  34. ...

定位某个超过阈值的函数

BTrace脚本

  1. **

  2. * 探测某个包路径下的方法执行时间是否超过某个阈值的程序,如果超过了该阀值,则打印当前线程的栈信息。

  3. */

  4.  

  5. import com.sun.btrace.BTraceUtils;

  6. import com.sun.btrace.annotations.*;

  7.  

  8. import static com.sun.btrace.BTraceUtils.*;

  9.  

  10. @BTrace

  11. public class PrintDurationTracer {

  12.    @OnMethod(clazz = "/com\\.techstar\\.monitordemo\\..*/", method = "/.*/", location = @Location(Kind.RETURN))

  13.    public static void trace(@ProbeClassName String pcn, @ProbeMethodName String pmn, @Duration long duration) {

  14.        //duration的单位是纳秒

  15.        if (duration > 1000 * 1000 * 2) {

  16.            BTraceUtils.println(Strings.strcat(Strings.strcat(pcn, "."), pmn));

  17.            BTraceUtils.print(" 耗时:");

  18.            BTraceUtils.print(duration);

  19.            BTraceUtils.println("纳秒,堆栈信息如下");

  20.     &n......

    原文转载:http://www.shaoqun.com/a/834010.html

    跨境电商:https://www.ikjzd.com/

    杨帆:https://www.ikjzd.com/w/1648

    首信易支付:https://www.ikjzd.com/w/1841

    声网:https://www.ikjzd.com/w/2176


    引言在我们对Java应用做问题分析的时候,往往采用log进行问题定位和分析,但是如果我们的log缺乏相关的信息呢?远程调试会影响应用的正常工作,修改代码重新部署应用,实时性和灵活性难以保证,有没有不影响正常应用运行,又灵活并无侵入性的方法呢?答案是有,它就是Java中的神器-BTraceBTrace是什么?BTrace使用Java的Attach技术,可以让我们无缝的将我们BTrace脚本挂到JVM
    好东东网:https://www.ikjzd.com/w/1238
    三维度科技:https://www.ikjzd.com/w/1312
    收藏!24个亚马逊大麦必备运营技能总结!:https://www.ikjzd.com/articles/20988
    干货:亚马逊后台数据解读:https://www.ikjzd.com/articles/20990
    欧美市场新"卖点"强势来袭:服饰类卖家别错过:https://www.ikjzd.com/articles/20991
    跨境电商库存问题堪忧:环球易购存货跌价准备增加近4亿:https://www.ikjzd.com/articles/20996
    撞击旗袍丝袜老师 小坏蛋你太厉害了我受不了了:http://lady.shaoqun.com/a/247953.html
    少妇口述:乱性丈夫将我拖进换妻游戏:http://lady.shaoqun.com/m/a/73636.html
    小心!2021年7月9日之前未向 Wish 提供欧盟责任人,将根据《商户政策13》被处以赔款!!!:https://www.ikjzd.com/articles/146137
    广告结构简化完全教程,不惧公共主页广告数量上限:https://www.ikjzd.com/articles/146151
    女性应该知道的35个妇科常识,作为母亲应该多加注意:http://lady.shaoqun.com/a/390546.html
    男人如何提高自己的能力?这五项技能旨在增强"战斗力":http://lady.shaoqun.com/a/390547.html

没有评论:

发表评论