Java动态,安全追踪工具

在我们日常的开发中,总是难以避免的要解决线上的问题.如果线上的问题我们在本地调试的时候无论调试多少次发现明明本地调用了这个方法呀,怎么线上就是没调呢?还有就是出了问题的时候由于没有打日志,所以不得不去价格logger,然后换个包,然后再重启,然后再调用,如果在用户很多的时候这么搞,无疑面临着巨大的风险,还不得不去处理用户的大量的投诉,在领导面前也只能默默的低着头承受着批评

那么有没有一种方法可以不用重启应用,又可以在线上追踪代码呢? 答案当然是有的,就是使用Btrace这个工具了

BTrace是sun公司推出的一款Java 动态、安全追踪(监控)工具,可以在不用重启的情况下监控系统运行情况,方便的获取程序运行时的数据信息,如方法参数、返回值、全局变量和堆栈信息等,并且做到最少的侵入,占用最少的系统资源。

应用场景:

  1. 服务慢,但是不知道慢在哪一步,哪个函数慢
  2. 谁调用了System.gc(),调用栈如何?
  3. 谁构造了一个超大的ArrayList?
  4. 执行某个方法抛出异常时,分析运行时参数

......

快速开始

btrace的官方的网址是http://github.com/btraceio/btrace , 可以从里面下载最新的版本, 目前版本是1.3.11.2

下载好之后doc里面有很多例子, 我们这里来跑一个UserGuide的例子

import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnMethod; import static com.sun.deploy.trace.Trace.println; /**
* @author luozhiyun on 2019-01-06.
*/
@BTrace
public class HelloWorld { @OnMethod(clazz="java.lang.Thread", method="start")
public static void onThreadStart() {
println("thread start!"); } }

然后在要监控的机器上打jps, 找到应用的pid, 然后 btrace $pid HelloWorld.java就可以跑起来了

如果还想要监控其他内容,直接修改HelloWorld.java的内容,然后再执行一次btrace就可以了,完全不需要重启应用!!!

拦截

1.直接指出类和方法 ,如HelloWorld的例子

2.正则表达式拦截

如下, 拦截所有的有关InputStream类的readXXX方法,正则表达式要写在两个/中间

package com.sun.btrace.samples;

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*; /**
* This BTrace class demonstrates that we can
* probe into multiple classes and methods by a
* single probe specification using regular
* expressions for class and/or method names as
* given below. In the example, we put probe into
* all readXXX methods of all InputStream classes.
*/
@BTrace public class MultiClass {
@OnMethod(
clazz="/java\\.io\\..*Input.*/",
method="/read.*/"
)
public static void onread(@ProbeClassName String pcn) {
println("read on " + pcn);
}
}

3.拦截所有有这个注解的类或方法

如: @OnMethod( clazz="@javax.jws.WebService", method="@javax.jws.WebMethod" )

4.拦截接口的实现类

如:@OnMethod( clazz="+java.lang.Runnable", method="run" )

5.拦截构造器

@OnMethod(clazz="java.net.ServerSocket", method="<init>")

6.拦截静态内部类

@OnMethod(clazz="com.vip.MyServer$MyInnerClass", method="hello") 只要在类和内部类前面加上$

拦截时机

Kind.ENTRY

在@onMethod这个注解里面可以加上一个location参数,表示拦截的这个方法的拦截时机,如果不加,默认就是Kind.ENTRY

Kind.RETURN

如果要获得返回结果或执行时间, 那么就要加上Kind.RETURN.

OnMethod(clazz = "java.net.ServerSocket", method = "getLocalPort", location = @Location(Kind.RETURN))
public static void onGetPort(@Return int port, @Duration long duration) duration的单位是纳秒,要除以 1,000,000 才是毫秒。

Kind.Error, Kind.Throw和 Kind.Catch

这几个主要用于异常情况的跟踪

在拦截函数的参数定义里注入一个Throwable的参数,代表异常。

@OnMethod(clazz = "java.net.ServerSocket", method = "bind", location = @Location(Kind.ERROR))
public static void onBind(Throwable exception, @Duration long duration)

Kind.Line

下例监控代码是否到达了Socket类的第363行

@OnMethod(clazz = "java.net.ServerSocket", location = @Location(value = Kind.LINE, line = 363))

public static void onBind4() {

   println("socket bind reach line:363");

}

Kind.Call

下例就是打印出run()方法里面调用的所有其他方法

import com.sun.btrace.annotations.*;

import static com.sun.btrace.BTraceUtils.println;

/**
* @author luozhiyun on 2019-01-06.
*/
@BTrace
public class HelloWorld { @OnMethod(clazz="BtraceCase", method="run",
location =@Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/", where = Where.AFTER))
public static void onThreadStart(@Self Object self, @TargetInstance Object instance,
@TargetMethodOrField String method, @Duration long duration) {
println("self: " + self);
println("instance: " + instance);
println("method: " + method);
println("duration: " + duration); }
}

BtraceCase

import java.util.Random;

/**
* @author luozhiyun on 2019-01-06.
*/
public class BtraceCase {
public static Random random = new Random();
public int size; public static void main(String[] args) throws Exception {
Thread.sleep(1000*15);
new BtraceCase().run();
} public void run() throws Exception {
while (true) {
add(random.nextInt(10), random.nextInt(10));
}
} public int add(int a, int b) throws Exception {
Thread.sleep(random.nextInt(10) * 100);
return a + b;
}
}

打印结果:

self: BtraceCase@63947c6b
instance: BtraceCase@63947c6b
method: add
duration: 402211413
self: BtraceCase@63947c6b
instance: java.util.Random@2b193f2d
method: nextInt
duration: 2602

所调用的类及方法名所注入到@TargetInstance与 @TargetMethodOrField中.如果想获得执行时间,必须把Where定义成AFTER

打印this,参数与返回值

import com.sun.btrace.AnyType;

@OnMethod(clazz = "java.io.File", method = "createTempFile", location = @Location(value = Kind.RETURN))

public static void o(@Self Object self, String prefix, String suffix, @Return AnyType result)

Self:

如果是静态函数,self为空 , 用@Self可以表示当前方法,也就是this

参数:

参数列表要么不要定义, 要定义就要定义完整,否则BTrace无法处理不同参数的同名函数

结果:

结果的类型用AnyType来定义,特别是用正则表达式匹配多个函数的时候,连void都可以表示

一点经验

  1. 用于匹配方法入参或返回类型时,因嫌麻烦不想引入外部依赖(一般也没有必要),外部类型请用AnyType代替,而不是Object!因为你可能用Object来准确匹配方法返回参数或返回类型。

  2. 启动跟踪脚本时,请使用和启动Java进程相同的Linux账号,不然会因为权限问题而attach失败。

  3. 另一个和BTrace类似的Java诊断工具greys-anatomy,由阿里释出,感兴趣的也可以学习一下。

  4. 若报错"Port 2020 unavailable.",则使用btrace -p 2021 ...来指定其它端口。

  5. Linux下已经有个命令也叫btrace,注意别用混了。

  6. 由于Btrace会把脚本逻辑直接侵入到运行的代码中,所以在使用上做很多限制:

    1、不能创建对象

    2、不能使用数组

    3、不能抛出或捕获异常

    4、不能使用循环

    5、不能使用synchronized关键字

    6、属性和方法必须使用static修饰

    根据官方声明,不恰当的使用BTrace可能导致JVM崩溃,如在BTrace脚本使用错误的class文件,所以在上生产环境之前,务必在本地充分的验证脚本的正确性。

Java动态,安全追踪工具的更多相关文章

  1. java动态编译笔记

    1 前言 Java的动态编译知识,真真在实际开发中并不是经常遇到.但是学习java动态编译有助于我们从更深一层次去了解java.对掌握jdk的动态代理模式,这样我们在学习其他一些开源框架的时候就能够知 ...

  2. Java动态代理全面分析

    代理模式 解说:给某一个对象提供一个代理,并由代理对象控制对原对象的引用: 代理模式需要以下几个角色: 1  主题:规定代理类和真实对象共同对外暴露的接口: 2  代理类:专门代理真实对象的类: 3 ...

  3. 推荐6款常用的Java开源报表制作工具

    JasperReports是一个基于Java的开源报表工具,它可以在Java环境下像其它IDE报表工具一样来制作报表.JasperReports 支持PDF.HTML.XLS.CSV和XML文件输出格 ...

  4. 常用 Java 静态代码分析工具的分析与比较

    常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...

  5. [转]java动态代理(JDK和cglib)

    转自:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html java动态代理(JDK和cglib) JAVA的动态代理 代理模式 代理 ...

  6. 彻底理解JAVA动态代理

    代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 代理模式的结构如下图所示. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 代理模式示例代码 public ...

  7. 四种java代码静态检查工具

    [转载]常用 Java 静态代码分析工具的分析与比较 转载自 开源中国社区 http://www.oschina.net/question/129540_23043       1月16日厦门 OSC ...

  8. Java 动态眨眼 EyesJPanel (整理)

    /** * Java 动态眨眼 EyesJPanel (整理) * * 2016-1-2 深圳 南山平山村 曾剑锋 * 注意事项: * 1.本程序为java程序,同时感谢您花费宝贵的时间来阅读本文档: ...

  9. Java动态代理简单应用

    概念 代理模式是基本的设计模式之一,它是开发者为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象.这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色. Java动态代理比 ...

随机推荐

  1. memecached存放session数据

    memcached存放session 1.session数据需要频繁调用. 2.session数据不需要永久性的保存在服务端. 3.在集群中,可以将session存放在memcached中或者是在数据 ...

  2. centos6.5虚拟机配置Nat模式连接外网

    想来在虚拟机上搭点软件,于是乎就想让虚拟机连上外网,就用到了Nat模式,自己对网络了解不是太深,以至于配置联网花了一下午.总结下联网步骤. (1)点击虚拟网络编辑器 (2)注意以下几点标红处 (3)点 ...

  3. HDU 2121:Ice_cream’s world II(不定根的最小树形图)

    题目链接 题意 求有向图的最小生成树,且根不定. 思路 最小树形图即求有向图的最小生成树,用的是朱刘算法. 这里不定根,那么可以建立一个虚根,让虚根和所有点相连,权值为一个很大的数(这里直接设为所有边 ...

  4. java LineNumberReader的使用

    前段时间需要读报表打印,需求是可以从第N行读到第N行,发现百度出来的LineNumberReader方法都不怎么理想 然后就找到一篇和众百度里脱颖而出的好文章,所以分享之:   从类 的命名来看,貌似 ...

  5. JVM中有哪些内存区域,分别是用来干什么的

    前言 之前我们探讨过一个.class文件是如何被加载到jvm中的.但是jvm内又是如何划分内存的呢?这个内被加载到了那一块内存中?jvm内存划分也是面试当中必被问到的一个面试题. 什么是jvm内存区域 ...

  6. 探寻 webpack_bundle_analyzer 原理

    webpack_bundle_analyzer 是什么? 这是webpack官方出品的,对项目中模块依赖关系及体积的分析插件,其界面如下: 问题来了,这是如何来进行统计的? 这里提供一个插件的代码,可 ...

  7. 【tf.keras】在 cifar 上训练 AlexNet,数据集过大导致 OOM

    cifar-10 每张图片的大小为 32×32,而 AlexNet 要求图片的输入是 224×224(也有说 227×227 的,这是 224×224 的图片进行大小为 2 的 zero paddin ...

  8. 彻底透析SpringBoot jar可执行原理

    ​ 文章篇幅较长,但是包含了SpringBoot 可执行jar包从头到尾的原理,请读者耐心观看.同时文章是基于SpringBoot-2.1.3进行分析.涉及的知识点主要包括Maven的生命周期以及自定 ...

  9. U盘被写保护大全解

    相信大家的U盘在使用的过程中多或少都有出现过一些问题,写保护,程序写蹦而造成的逻辑错误,或者在使用过程中因电脑而中毒,内部零件损伤等等各种各样倒霉的错误. 简单了解一下是个什么东西吧.U盘写保护其实就 ...

  10. Docker学习第二天

    CentOS 系列安装 Docker Docker 支持 CentOS6 及以后的版本. CentOS6 对于 CentOS6,可以使用 EPEL 库安装 Docker,命令如下 [root@MSJT ...