Agent机制-整理

作者: andersonoy | 来源:发表于2017-06-25 21:16 被阅读219次

agent有两种: native(jvmti接口) 和 java层面的(instrumentation)

  • c/c++ 层面的 jvmti 接口

    • jvmti官方文档
    • JVM TI是JDK提供的一套用于开发JVM监控, 问题定位与性能调优工具的通用编程接口(API)。 通过JVMTI,我们可以开发各式各样的JVMTI Agent。这个Agent的表现形式是一个以c/c++语言编写的动态共享库
    • JVMTI Agent原理
      • Java启动或运行时,动态加载一个外部基于JVM TI编写的dynamic module到Java进程内,然后触发JVM源生线程Attach Listener来执行这个dynamic module的回调函数。在函数体内,你可以获取各种各样的VM级信息,注册感兴趣的VM事件,甚至控制VM的行为。
    • jvmti api
      • JVMTI是基于事件驱动的,JVM每执行到一定的逻辑就会调用一些事件的回调接口(如果有的话),这些接口可以供开发者去扩展自己的逻辑。
      • 可以获取各种各样的信息
    • 开发jvm ti agent,简单的来讲,就是开发一个c/c++的共享库。在windows下后缀是dll,linux/unix下是so,mac下就是dylib。所以我们创建工程和编译环境的时候,记得以共享库(share library)的形式来构建
    • 两种方式载入
      • 随java进程启动时,自动载入共享库
        • 共享库路径是环境变量路径: java -agentlib:foo=opt1,opt2,java启动时会从linux的LD_LIBRARY_PATH或windows的PATH环境变量定义的路径处装载foo.so或foo.dll,找不到则抛异常
        • 以绝对路径的方式装载共享库: java -agentpath:/home/admin/agentlib/foo.so=opt1,opt2 Sample
      • java运行时,通过attach api动态载入
        public static void main(String[] args) throws Exception { // args[0]为java进程id VirtualMachine virtualMachine = com.sun.tools.attach.VirtualMachine.attach(args[0]); // args[1]为共享库路径,args[2]为传递给agent的参数 virtualMachine.loadAgentPath(args[1], args[2]); virtualMachine.detach(); }
    • 开发jvmti agent
      • jvmti.h头文件里包含了所有jvm ti要用到的数据结构和回调函数定义
      • 确定JVMTI的启动方式
        • JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM vm, char options, void *reserved)//启动载入方式
        • JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM jvm, char options, void *reserved)//动态载入方式
        • JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)//卸载都是一样
      • 具体例子可以google或jvmti官网上找
  • java层面的instrumentation

    • Instrumentation是Java5提供的新特性,使用Instrumentation,开发者可以构建一个代理,用来监测运行在JVM上的程序

    • java.lang.instrument.ClassFileTransformer

      • 每个代理类必须实现 ClassFileTransformer接口,这个接口提供了一个transform方法:
      • byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException
      • 通过这个方法,代理可以得到虚拟机载入的类的字节码,并可对其进行修改,完成字节码级的修改。
      • classfileBuffer这个便是被代理类字节码流,正是通过操作这个buffer完成对字节码的修改
      • 对于函数的返回值,如果返回null,则表示不对类的字节码做任何的修改,否则应该返回修改过的byte[]对象
    • 提供一个公共的静态方法 public static void premain(String agentArgs, Instrumentation inst)

      • 一般会在这个方法中创建一个代理对象,通过Instrumentation对象的addTransformer()方法,将创建的代理对象再传递给虚拟机
        public class HelloWorld implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<>; classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println("java.lang.instrument, hello world!"); return null; } public static void premain(String args,Instrumentation inst){ inst.addTransformer(new HelloWorld()); } }

      public class Example { public static void main(String[] args){ System.out.println("main class of proxy!"); } }

      • 将agent类HelloWorld编译成可运行的jar(helloworld.jar),这里注意在manifest文件中添加premain入口,也即其配置为:
        • Premain-Class: cn.dstrace.instrument.HelloWorld
      • 运行
        • java -javaagent:helloworld.jar cn.dstrace.instrument.Example
    • 小节

      • java的各种性能监控工具中都有instrument的身影,如jconsole等
      • 这样的特性实际上提供了一种虚拟机级别支持的 AOP 实现方式
    • 动态的javaagent

      • 在 Java SE6 里面,最大的改变使运行时的 Instrumentation 成为可能;java attach api
      • 但是在实际的很多的情况下,我们没有办法在虚拟机启动之时就为其设定代理
      • jdk5局限
        • 在 Java SE 5 当中,开发者可以让 Instrumentation 代理在 main 函数运行前执行。
        • 在 Java SE 5 当中,开发者只能在 premain 当中施展想象力,所作的 Instrumentation 也仅限与 main 函数执行前,这样的方式存在一定的局限性。
        • 在 Java SE 5 的基础上,Java SE 6 针对这种状况做出了改进,开发者可以在main 函数开始执行以后,再启动自己的 Instrumentation 程序。
        • 在 Java SE 6 的 Instrumentation 当中,有一个跟 premain“并驾齐驱”的“agentmain”方法,可以在 main 函数开始运行之后再运行。跟 premain 函数一样, 开发者可以编写一个含有“agentmain”函数的 Java 类:
        • 在 Java SE 6 的新特性里面,有一个不太起眼的地方,揭示了 agentmain 的用法。这就是Java SE 6 当中提供的 Attach API
  • javaagent原理完全解读

    • javaagent的主要的功能如下
      • 可以在加载class文件之前做拦截把字节码做修改
      • 可以在运行期将已经加载的类的字节码做变更,但是这种情况下会有很多的限制
      • 还有其他的一些小众的功能
        • 获取所有已经被加载过的类
        • 获取所有已经被初始化过了的类(执行过了clinit方法,是上面的一个子集)
        • 获取某个对象的大小
        • 将某个jar加入到bootstrapclasspath里作为高优先级被bootstrapClassloader加载
        • 将某个jar加入到classpath里供AppClassloard去加载
        • 设置某些native方法的前缀,主要在查找native方法的时候做规则匹配
    • 其实我们每天都在和JVMTIAgent打交道,只是你可能没有意识到而已,比如我们经常使用eclipse等工具对java代码做调试,其实就利用了jre自带的jdwp agent来实现的,只是由于eclipse等工具在没让你察觉的情况下将相关参数(类似-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:61349)给自动加到程序启动参数列表里了,其中agentlib参数就是用来跟要加载的agent的名字,比如这里的jdwp(不过这不是动态库的名字,而JVM是会做一些名称上的扩展,比如在linux下会去找libjdwp.so的动态库进行加载,也就是在名字的基础上加前缀lib,再加后缀.so),接下来会跟一堆相关的参数,会将这些参数传给Agent_OnLoad或者Agent_OnAttach函数里对应的options参数。
    • javaagent
      • 说到javaagent必须要讲的是一个叫做instrument的JVMTIAgent(linux下对应的动态库是libinstrument.so),因为就是它来实现javaagent的功能的,另外instrument agent还有个别名叫JPLISAgent(Java Programming Language Instrumentation Services Agent),从这名字里也完全体现了其最本质的功能:就是专门为java语言编写的插桩服务提供支持的。
    • instrument agent (libinstrument.so实现)
      • instrument agent实现了Agent_OnLoad和Agent_OnAttach两方法
      • instrument agent的核心数据结构如下
        • tobecontinued...
  • References

相关文章

  • Agent机制-整理

    agent有两种: native(jvmti接口) 和 java层面的(instrumentation) c/c+...

  • RASP研发踩坑之 热加载与热卸载

    在RASP研发踩坑之agent 加载机制中,当想要卸载 Java agent 时,原生Java Agent 不能够...

  • Java探针(javaagent)

    JDK1.5开始引入了Agent机制(即启动java程序时添加“-javaagent”参数,Java Agent机...

  • 10-Scrapy反爬策略&模拟登录

    一.反反爬虫相关机制 通常反反爬主要有以下几个策略: 动态设置User-Agent(随机切换User-Agent,...

  • 使用javaagent技术实现无侵入监听

    什么是 Java Agent Java Agent是JVM启动时给应用程序一种机会去修改class文件的机制,在启...

  • 爬虫

    1、反反爬虫相关机制 1、动态设置User-Agent(随机切换User-Agent,模拟不同用户的浏览器信息)2...

  • 20171204 Puppet(二)

    Puppet模块Puppet部署master/agent模式Puppet多环境配置Puppet kick机制 一、...

  • 深入理解 Skywalking Agent

    概述 Agent 功能介绍 + 整体结构 + 设计 插件机制详解 Trace Segment Span 详解 异步...

  • open-falcon架构图例

    整理原理以及工作流程: 1、app执行agent 2、agent获取各种系统的监控项数值传给Transfer模块 ...

  • zabbix介绍

    特性 数据采样snmp,agent,ipmi,jmx 报警升级:步进式升级机制 数据存储mysql/pgsql 展...

网友评论

本文标题:Agent机制-整理

本文链接:https://www.haomeiwen.com/subject/umircxtx.html