Whats is Java Agent?   .. java.lang.instrument.Instrumentation

之前有写 基于AOP的日志调试 讨论一种跟踪Java程序的方法, 但不是很完美.后来发现了 Btrace , 由于它借助动态字节码注入技术 , 实现优雅且功能强大.
只不过, 用起来总是磕磕绊绊的, 时常为了跟踪某个问题, 却花了大把的时间调试Btrace的脚本. 为此, 我尝试将几种跟踪模式固化成脚本模板, 待用的时候去调整一下正则表达式之类的.
跟踪过程往往是假设与验证的螺旋迭代过程, 反复的用BTrace跟踪目标进程, 总有那么几次莫名其妙的不可用, 最后不得不重启目标进程. 若真是线上不能停的服务, 我想这种方式还是不靠谱啊.
为此, 据决定自己的搞个用起来简单, 又能良好支持反复跟踪而不用重启目标进程的工具.

AOP

AOP是Btrace, jip1等众多监测工具的核心思想, 用一段代码最容易说明:

1
2
3
4
5
public void say(String words){
  Trace.enter();
  System.out.println(words);
  Trace.exit();
}

如上, Trace.enter() 和 Trace.exit() 将say(words)内的代码环抱起来, 对方法进出的进行切面的处理, 便可获取运行时的上下文, 如:

  • 调用栈
  • 当前线程
  • 时间消耗
  • 参数与返回值
  • 当前实例状态

实现的选择

实现切面的方式, 我知道的有以下几种:

代理(装饰器)模式

设计模式中装饰器模式和代理模式, 尽管解决的问题域不同, 代码实现是非常相似, 均可以实现切面处理, 这里视为等价. 依旧用代码说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface Person {
  void say(String words);
}
 
class Officer implements Person {
  public void say(String words) { lie(words); }
  private void lie(String words) {...}
}
 
class Proxy implements Person {
  private final Officer officer;
  public Proxy(Officer officer) { this.officer = officer; }
  public void say(String words) {
    enter();
    officer.say(words);
    exit();
  }
  private void enter() { ... }
  private void exit() { ... }
}
 
Person p = new Proxy(new Officer());

很明显, 上述enter() 和exit()是实现切面的地方, 通过获取Officer的Proxy实例, 便可对Officer实例的行为进行跟踪. 这种方式实现起来最简单, 也最直接.

Java Proxy

Java Proxy是JDK内置的代理API, 借助反射机制实现. 用它来是完成切面则会是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ProxyInvocationHandler implements InvocationHandler {
  private final Object target;
  public ProxyInvocationHandler(Object target) { this.target = target;}
  public Object handle(Object proxy, Method method, Object[] args) {
    enter();
    method.invoke(target, args);
    exit();
  }
  private void enter() { ... }
  private void exit() { ... }
}
ClassLoader loader = ...
Class<?>[]  interfaces = {Person.class};
Person p = (Person)Proxy.newInstance(loader, interfaces, new ProxyInvocationHandler(new Officer()));

相比较上一中方法, 这种不太易读, 但更为通用, 对具体实现依赖很少.

AspectJ

AspectJ是基于字节码操作(运行时利用ASM库)的AOP实现, 相比较Java proxy, 它会显得对调用更”透明”, 编写更简明(类似DSL), 性能更好. 如下代码:

1
2
3
pointcut say(): execute(* say(..))
before(): say() { ... }
after() : say() { ... }

Aspectj实现切面的时机有两种: 静态编译和类加载期编织(load-time weaving). 并且它对IDE的支持很丰富.

CGlib

与AspectJ一样CGlib也是操作字节码来实现AOP的, 使用上与Java Proxy非常相似, 只是不像Java Proxy对接口有依赖, 我们熟知的Spring, Guice之类的IoC容器实现AOP都是使用它来完成的.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Callback implements MethodInterceptor {
  public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
    enter();
    proxy.invokeSuper(obj, args);
    exit();
  }
  private void enter() { ... }
  private void exit() { ... }
}
Enhancer e = new Enhancer();
e.setSuperclass(Officer.class);
e.setCallback(new Callback());
Person p = e.create();

字节码操纵

上面四种方法各有适用的场景, 但唯独对运行着的Java进程进行动态的跟踪支持不了, 当然也许是我了解的不够深入, 若有基于上述方案的办法还请不吝赐教.

还是回到Btrace的思路上来, 在理解了它借助java.lang.Instrumentation进行字节码注入的实现原理后, 实现动态变化跟踪方式或目标应该没有问题.

借下来的问题, 如何操作(注入)字节码实现切面的处理. 可喜的是, “构建自己的监测工具”一文给我提供了一个很好的切入点. 在此基础上, 经过一些对ASM的深入研究, 可以实现:

  • 方法调用进入时, 获取当前实例(this) 和 参数值列表;
  • 方法调用出去时, 获取返回值;
  • 方法异常抛出时, 触发回调并获取异常实例.

其切面实现的核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
private static class ProbeMethodAdapter extends AdviceAdapter {
 
    protected ProbeMethodAdapter(MethodVisitor mv, int access, String name, String desc, String className) {
      super(mv, access, name, desc);
      start = new Label();
      end = new Label();
      methodName = name;
      this.className = className;
    }
 
    @Override
    public void visitMaxs(int maxStack, int maxLocals) {
      mark(end);
      catchException(start, end, Type.getType(Throwable.class));
      dup();
      push(className);
      push(methodName);
      push(methodDesc);
      loadThis();
      invokeStatic(Probe.TYPE, Probe.EXIT);
      visitInsn(ATHROW);
      super.visitMaxs(maxStack, maxLocals);
    }
 
    @Override
    protected void onMethodEnter() {
      push(className);
      push(methodName);
      push(methodDesc);
      loadThis();
      loadArgArray();
      invokeStatic(Probe.TYPE, Probe.ENTRY);
      mark(start);
    }
 
    @Override
    protected void onMethodExit(int opcode) {
      if (opcode == ATHROW) return; // do nothing, @see visitMax
      prepareResultBy(opcode);
      push(className);
      push(methodName);
      push(methodDesc);
      loadThis();
      invokeStatic(Probe.TYPE, Probe.EXIT);
    }
 
    private void prepareResultBy(int opcode) {
      if (opcode == RETURN) { // void
        push((Type) null);
      } else if (opcode == ARETURN) { // object
        dup();
      } else {
        if (opcode == LRETURN || opcode == DRETURN) { // long or double
          dup2();
        } else {
          dup();
        }
        box(Type.getReturnType(methodDesc));
      }
    }
 
    private final String className;
    private final String methodName;
    private final Label start;
    private final Label end;
}

更多参考请见这里的 Demo , 它是javaagent, 在伴随宿主进程启动后, 提供MBean可用jconsole进行动态跟踪的管理.

后续的方向

  1. 提供基于Web的远程交互界面;
  2. 提供基于Shell的本地命令行接口;
  3. 提供Profile统计和趋势输出;
  4. 提供跟踪日志定位与分析.

参考

  1. The Java Interactive Profiler
  2. Proxy Javadoc
  3. Aspectj
  4. CGlib
  5. BTrace User’s Guide
  6. java动态跟踪分析工具BTrace实现原理
  7. 构建自己的监测工具
  8. ASM Guide
  9. 常用 Java Profiling 工具的分析与比较
  10. AOP@Work: Performance monitoring with AspectJ
  11. The JavaTM Virtual Machine Specification
  12. 来自rednaxelafx的JVM分享, 他的 Blog
  13. BCEL

【转】动态字节码技术跟踪Java程序的更多相关文章

  1. 使用java动态字节码技术简单实现arthas的trace功能。

    参考资料 ASM 系列详细教程 编译时,找不到asm依赖 用过[Arthas]的都知道,Arthas是alibaba开源的一个非常强大的Java诊断工具. 不管是线上还是线下,我们都可以用Arthas ...

  2. Java 动态字节码技术

    对 Debug 的好奇 初学 Java 时,我对 IDEA 的 Debug 非常好奇,不止是它能查看断点的上下文环境,更神奇的是我可以在断点处使用它的 Evaluate 功能直接执行某些命令,进行一些 ...

  3. 动态字节码技术Javassist

    字节码技术可以动态改变某个类的结构(添加/删除/修改  新的属性/方法) 关于字节码的框架有javassist,asm,bcel等 引入依赖 <dependency> <groupI ...

  4. 字节码技术---------动态代理,lombok插件底层原理。类加载器

    字节码技术应用场景 AOP技术.Lombok去除重复代码插件.动态修改class文件等 字节技术优势  Java字节码增强指的是在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用 ...

  5. JVM性能优化--字节码技术

    一.字节码技术应用场景 AOP技术.Lombok去除重复代码插件.动态修改class文件等 二.字节技术优势 Java字节码增强指的是在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于 ...

  6. JVM探针与字节码技术

    JVM探针是自jdk1.5以来,由虚拟机提供的一套监控类加载器和符合虚拟机规范的代理接口,结合字节码指令能够让开发者实现无侵入的监控功能.如:监控生产环境中的函数调用情况或动态增加日志输出等等.虽然在 ...

  7. JVM:类加载与字节码技术-2

    JVM:类加载与字节码技术-2 说明:这是看了 bilibili 上 黑马程序员 的课程 JVM完整教程 后做的笔记 内容 这部分内容在上一篇笔记中: 类文件结构 字节码指令 编译期处理 类加载阶段 ...

  8. 图解jvm--(三)类加载与字节码技术

    类加载与字节码技术 1.类文件结构 根据 JVM 规范,类文件结构如下 ClassFile { u4 magic; //魔数 u2 minor_version; //小版本号 u2 major_ver ...

  9. jvm系列四类加载与字节码技术

    四.类加载与字节码技术 1.类文件结构 首先获得.class字节码文件 方法: 在文本文档里写入java代码(文件名与类名一致),将文件类型改为.java java终端中,执行javac X:...\ ...

随机推荐

  1. Tweak 中系统方法写入文件到根目录下面失败

    Tweak 中系统方法写入文件到根目录下面失败 失败原因: Error Domain=NSCocoaErrorDomain Code=513 "The operation couldn’t ...

  2. C#深复制与浅复制

    C#深复制与浅复制 C#中对于数据的复制机制虽然简单但是容易让人误解.C#数据类型大体分为值类型(value type)与引用类型(reference type).对于值类型数据,复制的时候直接将数据 ...

  3. hdu 5833 Zhu and 772002 异或方程组高斯消元

    ccpc网赛卡住的一道题 蓝书上的原题 但是当时没看过蓝书 今天又找出来看看 其实也不是特别懂 但比以前是了解了一点了 主要还是要想到构造异或方程组 异或方程组的消元只需要xor就好搞了 数学真的是硬 ...

  4. iOS 发布项目到CocoaPods其实没那么复杂😆

    首先大家必须要了解一下CocoaPods (如果你连CocoaPods是啥都不知道可以不用往下看了

  5. iOS APP中Versions和build版本区别

    version是app发布时用户看到的版本号. build的为了方便开发者多次提交binary, 比如被苹果reject后. 例如,第一次提交version和build都是1.0. 假如审核没过,那么 ...

  6. "malloc: * error for object 0x17415d0c0: Invalid pointer dequeued from free list * set a breakpoint in malloc_error_break to debug";

    I've fixed this error with Xcode 8 on iOS 8.3. I've just changed Deployment Target from 8.3 to 8.0. ...

  7. for循环执行步骤

    for循环的具体步骤: for(var i=0;i<5;i++){ alert(1); } 第一步--->初始化i(初始化只在for循环中执行一次); 第二步--->执行条件i< ...

  8. 【angular+bootstrap】angular初级的时间选择器

    近期的一个项目,是用angular来写的,本来框架就是第一次接触,使用相关插件的时候就感觉更加没有头绪了,其中一个插件就是时间选择器.比较好用时间选择器就是bootstrap里面的datetimepi ...

  9. (ASP.NET )去除字符串中的HTML标签

    string strDoContent = "执行增加<a href="/AdminCX/Admin_CompanyDetail.aspx?CompanyGuid=cd8e1 ...

  10. ECOS-Ecstore证书生产失效问题排查

    无法生成证书问题排查 无法生成证书问题排查 author :James,jimingsong@vip.qq.com since :2015-03-02 名称解释(官方) 常见错误 1. 名称解释(官方 ...