`
923080512
  • 浏览: 187136 次
  • 性别: Icon_minigender_1
  • 来自: 商丘
社区版块
存档分类
最新评论

Java Attach API

    博客分类:
  • java
 
阅读更多

 

Java Attach API

 

catalog

1. instrument与Attach API
2. BTrace: VM Attach的两种方式
3. Sun JVM Attach API

 

1. instrument与Attach API

JDK5中增加了一个包java.lang.instrument,能够对JVM底层组件进行访问。在JDK 5中,Instrument 要求在运行前利用命令行参数或者系统参数来设置代理类,在实际的运行之中,虚拟机在初始化之时(在绝大多数的 Java 类库被载入之前),instrumentation的设置已经启动,并在虚拟机中设置了回调函数,检测特定类的加载情况,并完成实际工作
​在Java5中,开发基于Instrument的应用,需要以下几个步骤

1. 编写premain函数
​2. jar文件打包
​3. 运行agent 

但是在实际的很多的情况下,我们没有办法在虚拟机启动之时就为其设定代理,这样实际上限制了instrument的应用。而Java SE 6的新特性改变了这种情况,通过Java Tool API中的attach方式,我们可以很方便地在运行过程中动态地设置加载代理类,以达到instrumentation的目的
​在JDK6中,针对这点做了改进,开发者可以在main开始执行以后,再开启自己的Instrument程序
Attach API不是Java的标准API,而是Sun公司提供的一套扩展API,用来向目标JVM"附着"(Attach)代理工具程序的。有了它,开发者可以方 便的监控一个JVM,运行一个外加的代理程序,Sun JVM Attach API功能上非常简单,仅提供了如下几个功能

1. 列出当前所有的JVM实例描述
2. Attach到其中一个JVM上,建立通信管道
3. 让目标JVM加载Agent

Relevant Link:

http://iamzhongyong.iteye.com/blog/1843558

 

2. BTrace: VM Attach的两种方式

BTrace的特点之一就是可以动态Attach到一个运行的JVM进程上,然后根据BTrace脚本来对目标JVM进行相应的操作
JVM的 Attach有两种方式

1. 指定javaagent参数
2. 运行时动态attach

0x1: 指定javaagent参数

这种方式的特点就是在目标JVM启动时,就确定好了要加载什么样的代理对象,例如

java -javaagent:xxxx.jar TestMain

TestMain.java

复制代码
package test;

public class TestMain 
{ 
    public static void main(String[] args) throws InterruptedException
    {
        System.out.println("Hello");
    }

}
复制代码

TestAgent.java

复制代码
package test;

import java.lang.instrument.Instrumentation;
import java.io.*;

public class TestMain 
{ 
    public static void agentmain(String args, Instrumentation inst) throws Exception 
    {
        System.out.println("Args:" + args);
    }
    
    public static void premain(String args, Instrumentation inst) throws Exception 
    {
        System.out.println("Pre Args:" + args);
        Class[] classes = inst.getAllLoadedClasses();
        for (Class clazz : classes) 
        {
           System.out.println(clazz.getName());
        }
    } 
}
复制代码

TestAgent类比较简单,最终它会在目标类的Main方法执行之前,执行premain方法,其主要动作是将以及加载的类打印出来。 我们需要将这个类打包成jar文件,以便在目标JVM启动时候,以参数形式指定给它。打成jar的同时,设定MANIFEST.MF文件的内容。告知目标 JVM该如何处理

Agent-Class: TestAgent
Premain-Class: TestAgent
Can-Redine-Classes: true
Can-Retransform-Classes: true

用jar命令将TestAgent打包

1. 编译TestAgent
javac TestAgent.java

2. jar打包
jar cvmf MANIFEST.MF xxx.jar TestAgent.class

启动TestMain,并设置javaagent参数

1. 编译TestMain
javac TestMain.java 

2. 启动TestMain
java -javaagent:xxx.jar TestMain

0x2: 动态Attach,load指定Agent

这种方式与之前指定参数的不同在于,其可以在JVM已经运行的情况下,动态的附着上去,并可以动态加载agent
TestMain.java

复制代码
public class TestMain 
{
    public static void main(String[] args) throws InterruptedException 
    {  
        while(true)
        {  
            Thread.sleep(10000);  
            new Thread(new WaitThread()).start();  
        }  
    }  
      
   static class WaitThread implements Runnable 
   {  
        @Override  
        public void run() 
        {  
            System.out.println("Hello"); 
        }       
   }  
}
复制代码

TestAgent.java

复制代码
import java.lang.instrument.Instrumentation;
import java.io.*;

public class TestAgent
{ 
    public static void agentmain(String args, Instrumentation inst) throws Exception 
    {
        System.out.println("Args:" + args);
    }
    
    public static void premain(String args, Instrumentation inst) throws Exception 
    {
        System.out.println("Pre Args:" + args);
        Class[] classes = inst.getAllLoadedClasses();
        for (Class clazz : classes) 
        {
           System.out.println(clazz.getName());
        }
    } 
}
复制代码

动态加载agent的情况下,被调用的是agentmain方法, 其会在JVMload的时候,被调用
MANIFEST.MF

Agent-Class: TestAgent
Premain-Class: TestAgent
Can-Redine-Classes: true
Can-Retransform-Classes: true

将类打包为jar包

1. 编译TestAgent
javac TestAgent.java

2. jar打包
jar cvmf MANIFEST.MF xxx.jar TestAgent.class

动态附着到对应的JVM需要使用到JDK的Attach API
Main.java

复制代码
import com.sun.tools.attach.VirtualMachine;

public class Main 
{  
  public static void main(String[] args) throws Exception
  {  
    VirtualMachine vm = null;  
    String agentjarpath = "C:/Users/zhenghan.zh/Desktop/新建文件夹/xxx.jar"; //agentjar路径  
    vm = VirtualMachine.attach("9730");//目标JVM的进程ID(PID)  
    vm.loadAgent(agentjarpath, "This is Args to the Agent.");  
    vm.detach();  
  }  
}
复制代码

一旦运行这个Main方法, 其就会动态的附着到我们对应的JVM进程中,并为目标JVM加载我们指定的Agent,以达到我们想做的事情, 比如BTrace就为在附着到目标JVM后,开启一个ServerSocket,以便达到与目标进程通讯的目的

Relevant Link:

http://ivanzhangwb.github.io/btrace-vm-attach-api/ 

 

3. Sun JVM Attach API

Sun JVM Attach API是Sun JVM中的一套非标准的可以连接到JVM上的API,从JDK6开始引入,除了Solaris平台的Sun JVM支持远程的Attach,在其他平台都只允许Attach到本地的JVM上

0x1: 列出当前所有的JVM实例描述

复制代码
package test;
import java.util.List;

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

public class Test 
{

    public static void main(String[] args) 
    {
        List<VirtualMachineDescriptor> list = VirtualMachine.list();  
        for (VirtualMachineDescriptor vmd : list)  
        {  
            System.out.println("pid:" + vmd.id() + ":" + vmd.displayName());  
        }  
    }

}
//tools.jar needs to be added to the IDE's library path and the program's classpath. The tools.jar file is found in the JDK's lib directory.
复制代码

0x2: Attach到特定进程的JVM上,并加载Agent

复制代码
//Attach到JVM上
VirtualMachine virtualmachine = VirtualMachine.attach(pid);  
//加载Agent
String javaHome = virtualmachine.getSystemProperties().getProperty("java.home");  
String agentPath = javaHome + File.separator + "jre" + File.separator + "lib" + File.separator + "management-agent.jar");  
File file = new File(agentPath);  
if(!file.exists())  
{  
     agentPath = javaHome + File.separator + "lib" + File.separator + "management-agent.jar";  
      file = new File(agentPath);  
      if(!file.exists())  
          throw new IOException("Management agent not found");  
      }  
}  
  
agentPath = file.getCanonicalPath();  
try  
{  
     virtualmachine.loadAgent(agentPath, "com.sun.management.jmxremote");  
}  
catch(AgentLoadException e)  
{  
     throw new IOException(e);  
}  
catch(AgentInitializationException agentinitializationexception)  
{  
     throw new IOException(e);  
}  
Properties properties = virtualmachine.getAgentProperties();  
address = (String)properties.get("com.sun.management.jmxremote.localConnectorAddress");  
virtualmachine.detach(); 
复制代码

0x3: Attach API底层实现(windows)

\openjdk\jdk\src\windows\classes\sun\tools\attach\WindowsAttachProvider.java

复制代码
public VirtualMachine attachVirtualMachine(String vmid) throws AttachNotSupportedException, IOException
{
    checkAttachPermission();

    // AttachNotSupportedException will be thrown if the target VM can be determined
    // to be not attachable.
    testAttachable(vmid);

    return new WindowsVirtualMachine(this, vmid);
}
复制代码

\openjdk\jdk\src\windows\classes\sun\tools\attach\WindowsVirtualMachine.java

复制代码
WindowsVirtualMachine(AttachProvider provider, String id) throws AttachNotSupportedException, IOException
{
    //继承HotSpotVirtualMachine
    super(provider, id);

    int pid;
    try 
    {
        pid = Integer.parseInt(id);
    } 
    catch (NumberFormatException x) 
    {
        throw new AttachNotSupportedException("Invalid process identifier");
    }
    //先连接上目标JVM
    hProcess = openProcess(pid);

    // The target VM might be a pre-6.0 VM so we enqueue a "null" command
    // which minimally tests that the enqueue function exists in the target
    // VM.
    try 
    {
        enqueue(hProcess, stub, null, null);
    } 
    catch (IOException x) 
    {
        throw new AttachNotSupportedException(x.getMessage());
    }
}
复制代码

WindowsVirtualMachine继承HotSpotVirtualMachine,先看看HotSpotVirtualMachine的loadAgent方法
\openjdk\jdk\src\share\classes\sun\tools\attach\HotSpotVirtualMachine.java

复制代码
/*
* Load JPLIS agent which will load the agent JAR file and invoke
* the agentmain method.
*/
public void loadAgent(String agent, String options) throws AgentLoadException, AgentInitializationException, IOException
{
    String args = agent;
    if (options != null) 
    {
        args = args + "=" + options;
    }
    try 
    {
        loadAgentLibrary("instrument", args);
    } 
    catch (AgentLoadException x) 
    {
        throw new InternalError("instrument library is missing in target VM");
    } 
    catch (AgentInitializationException x) 
    {
        /*
         * Translate interesting errors into the right exception and
         * message (FIXME: create a better interface to the instrument
         * implementation so this isn't necessary)
         */
        int rc = x.returnValue();
        switch (rc) 
        {
        case JNI_ENOMEM:
            throw new AgentLoadException("Insuffient memory");
        case ATTACH_ERROR_BADJAR:
            throw new AgentLoadException("Agent JAR not found or no Agent-Class attribute");
        case ATTACH_ERROR_NOTONCP:
            throw new AgentLoadException("Unable to add JAR file to system class path");
        case ATTACH_ERROR_STARTFAIL:
            throw new AgentInitializationException("Agent JAR loaded but agent failed to initialize");
        default :
            throw new AgentLoadException("Failed to load agent - unknown reason: " + rc);
        }
    }
}
复制代码

loadAgentLibrary("instrument", args);

复制代码
/*
* Load agent library
* If isAbsolute is true then the agent library is the absolute path
* to the library and thus will not be expanded in the target VM.
* if isAbsolute is false then the agent library is just a library
* name and it will be expended in the target VM.
*/
private void loadAgentLibrary(String agentLibrary, boolean isAbsolute, String options) throws AgentLoadException, AgentInitializationException, IOException
{
    InputStream in = execute("load",
                 agentLibrary,
                 isAbsolute ? "true" : "false",
                 options);
    try 
    {
        int result = readInt(in);
        if (result != 0) 
        {
        throw new AgentInitializationException("Agent_OnAttach failed", result);
        }
    } 
    finally 
    {
        in.close();

    }
}
复制代码

可以看到,Java在Attach到目标进行后,调用execute让目标进行加载Agent类,我们继续分析execute的实现方式,可以看到,JVM进程间通信是JVM Attach API的核心,JVM自身就预留了执行来自Attach进程的指令接口
\openjdk\jdk\src\windows\classes\sun\tools\attach\WindowsVirtualMachine.java

复制代码
InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException
{
    assert args.length <= 3;        // includes null

    // create a pipe using a random name
    int r = (new Random()).nextInt();
    String pipename = "\\\\.\\pipe\\javatool" + r;
    long hPipe = createPipe(pipename);

    // check if we are detached - in theory it's possible that detach is invoked
    // after this check but before we enqueue the command.
    if (hProcess == -1) 
    {
        closePipe(hPipe);
        throw new IOException("Detached from target VM");
    }

    try 
    {
        // enqueue the command to the process
        enqueue(hProcess, stub, cmd, pipename, args);

        // wait for command to complete - process will connect with the
        // completion status
        connectPipe(hPipe);

        // create an input stream for the pipe
        PipedInputStream is = new PipedInputStream(hPipe);

        // read completion status
        int status = readInt(is);
        if (status != 0) 
        {
        // special case the load command so that the right exception is thrown
        if (cmd.equals("load")) 
        {
            throw new AgentLoadException("Failed to load agent library");
        } 
        else 
        {
            throw new IOException("Command failed in target VM");
        }
        }

        // return the input stream
        return is;

    } 
    catch (IOException ioe) 
    {
        closePipe(hPipe);
        throw ioe;
    }
}
复制代码

JVM的execute方法中调用了大量native方法,并且从代码中可以看出,JVM Attach的进程间通信使用了管道进行通信

Relevant Link:

http://ayufox.iteye.com/blog/655761 
http://docs.oracle.com/javase/7/docs/jdk/api/attach/spec/com/sun/tools/attach/VirtualMachine.html
http://docs.oracle.com/javase/7/docs/jdk/api/attach/spec/index.html 

 

Copyright (c) 2015 LittleHann All rights reserved

分享到:
评论

相关推荐

    JavaProbe:Java运行时信息收集工具,该工具使用Java Attach API进行信息获取

    关于JavaProbe: 一个Java运行时信息收集工具,该工具使用Java Attach API进行信息获取(相关的技术文章链接附在本文的结尾)。指示: 环境: 1. jdk version &gt;= 1.6, linux, runuser (used to solve privilege ...

    tt2016_attach_api_agent_loader

    先决条件系统变量JAVA_HOME设置为JDK目录执行发现正在运行的虚拟机的进程ID: jps输出示例: $ jps101607832 jar3356 Jps加载Java代理: java -classpath $JAVA_HOME/lib/tools.jar:attach-api-agent-loader.jar -D...

    Java邮件开发Fundamentals of the JavaMail API

    Fundamentals of the JavaMail API Presented by developerWorks, your source for great tutorials ibm.com/developerWorks Table of Contents If you're viewing this document online, you can click ...

    jdk-9.0.1_doc-all 最新版

    Defines the Java API for XML Processing (JAXP), the Streaming API for XML (StAX), the Simple API for XML (SAX), and the W3C Document Object Model (DOM) API. java.xml.bind Defines the Java ...

    JDK1.8 中文API手册

    JDK1.8 中文API手册,预览图:https://attach.52pojie.cn/forum/201904/26/091904f1n3x29n6k5511wg.png

    Shizuku:通过以app_process开头的Java进程直接从普通应用程序使用具有adbroot特权的系统API

    首先,我们需要讨论应用程序如何使用系统API。 例如,如果应用程序想要安装应用程序,我们都知道我们应该使用PackageManager#getInstalledPackages() 。 这实际上是应用程序进程和系统服务器进程的进程间通信(IPC...

    JavaSE-6.0-英文手册(2008/11/30_FullUpdate)

    Attach API javac Tool Javadoc Tool Java Platform Debugger Architecture (JPDA) Java Debug Interface (JDI) Java Debug Wire Protocol (JDWP) JVMTM Tool Interface (JVM TI) (replaces JVMPI ...

    tt2016_byte_buddy_agent_demo:字节好友Java代理演示

    字节好友Java代理演示 这是为在克拉科夫举行的Motorola Tech Talks 2016准备的Byte Buddy Java代理演示。 它检测,将默认日志附加器更改为FileAppender并...使用Attach API执行代理 选中项目以使用Attach API加载代理。

    使用数字证书进行PKCS#7数字签名

    越来越多的应用需要我们使用USB接口数字证书进行PKCS#7数字签名。本文分别介绍了使用微软CryptoAPI方式和OpenSSL Engine方式进行数字签名。特别地,提出了OpenSSL Engine简化方式,这种方式更为灵活方便易行。

    【Alluxio分布式存储系统 v2.4.1】以前称为Tachyon+通过公共接口连接众多存储系统.zip

    1、灵活的文件API:Alluxio的本地API类似于java.io.File类,提供了 InputStream和OutputStream的接口和对内存映射I/O的高效支持。我们推荐使用这套API以获得Alluxio的完整功能以及最佳性能。 2、兼容Hadoop HDFS的...

    jLibLXC:liblxc的JavaJVM绑定

    jLibLXC liblxc的Scala / Java绑定关于该项目旨在为liblxc提供良好的,已记录的绑定和文档。用法// Start all containersLxc .getContainers.map(container =&gt; new LxcContainer (container)).foreach()// Start a ...

    Knife是一个基于JVMTI开发的JVM诊断工具,可以attach运行中的jvm,查看和修改堆内存中任意对象的内容。.zip

    API管理工具则方便开发者创建、测试、发布和维护API接口。 持续集成与持续部署(CI/CD): Jenkins、Travis CI、GitHub Actions等工具负责自动化构建、测试和部署流程,提高交付效率和可靠性。 数据库管理与...

    maven的优缺点 项目

    &lt;attach&gt;true&lt;/attach&gt; &lt;phase&gt;compile &lt;goal&gt;jar &lt;!-- 文件的编码 --&gt; &lt;groupId&gt;org.apache.maven.plugins &lt;artifactId&gt;maven...

    单点登录源码

    | └── zheng-api-server -- api系统服务端[端口:6666] ├── zheng-oss -- 对象存储系统 | ├── zheng-oss-sdk -- 开发工具包 | ├── zheng-oss-web -- 前台接口[端口:7771] | └── zheng-oss-admin -- ...

    apktool documentation

    apks are nothing more than a zip file containing resources and compiled java. If you were to simply unzip an apk like so, you would be left with files such as classes.dex and resources.arsc. $ unzip...

    SqliteDev 384

     ATTACH DATABASE  BEGIN TRANSACTION  comment  COMMIT TRANSACTION  COPY  CREATE INDEX  CREATE TABLE  CREATE TRIGGER  CREATE VIEW  DELETE  DETACH DATABASE  DROP INDEX  DROP TABLE  DROP ...

    Windouws 64 位Tomcat7.0.40 + 64位jdk1.7.0u21 绿色版.part1

    │ │ │ │ attach.dll │ │ │ │ awt.dll │ │ │ │ dcpr.dll │ │ │ │ decora-sse.dll │ │ │ │ deploy.dll │ │ │ │ dt_shmem.dll │ │ │ │ dt_socket.dll │ │ │ │ eula.dll │ │ │ │ ...

    SQLite(SqliteDev)

     ATTACH DATABASE  BEGIN TRANSACTION  comment  COMMIT TRANSACTION  COPY  CREATE INDEX  CREATE TABLE  CREATE TRIGGER  CREATE VIEW  DELETE  DETACH DATABASE  DROP INDEX  DROP TABLE  DROP ...

    一本android的好书beginning android 2 和 源码

    And How Do We Attach These to the Java? The Rest of the Story ■Chapter 5: Employing Basic Widgets Assigning Labels Button, Button, Who’s Got the Button? Fleeting Images Fields of Green Or Other ...

    Android插件框架Android-Plugin-Framework.zip

    由于此框架没有实际的项目应用,目前对activity的提供标准API的测试还不够完全,可能在其他开发场景中,activity的部分标准API可能会出现问题。毕竟这里使用了很多反射,会涉及到多机型多系统版本的兼容问题。后续还...

Global site tag (gtag.js) - Google Analytics