1. instrucment与Attach API
JDK5中增加了一个包java.lang.instrucment,能够对JVM底层组件进行访问。在JDK 5中,Instrument 要求在运行前利用命令行参数或者系统参数来设置代理类,在实际的运行之中,虚拟机在初始化之时(在绝大多数的 Java 类库被载入之前),instrumentation的设置已经启动,并在虚拟机中设置了回调函数,检测特定类的加载情况,并完成实际工作
在Java5中,开发基于Instrucment的应用,需要以下几个步骤
- 编写premain函数
- jar文件打包
- 运行agent
但是在实际的很多的情况下,我们没有办法在虚拟机启动之时就为其设定代理,这样实际上限制了instrument的应用。而Java SE 6的新特性改变了这种情况,通过Java Tool API中的attach方式,我们可以很方便地在运行过程中动态地设置加载代理类,以达到instrumentation的目的
在JDK6中,针对这点做了改进,开发者可以在main开始执行以后,再开启自己的Instrucment程序
Attach API不是Java的标准API,而是Sun公司提供的一套扩展API,用来向目标JVM"附着"(Attach)代理工具程序的。有了它,开发者可以方便的监控一个JVM,运行一个外加的代理程序,Sun JVM Attach API功能上非常简单,仅提供了如下几个功能
- 列出当前所有的JVM实例描述
- Attach到其中一个JVM上,建立通信管道
- 让目标JVM加载Agent
Relevant Link:
http://iamzhongyong.iteye.com/blog/1843558
2. BTrace: VM Attach的两种方式
BTrace的特点之一就是可以动态Attach到一个运行的JVM进程上,然后根据BTrace脚本来对目标JVM进行相应的操作
JVM的 Attach有两种方式
. 指定javaagent参数
. 运行时动态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打包
. 编译TestAgent
javac TestAgent.java . jar打包
jar cvmf MANIFEST.MF xxx.jar TestAgent.class
启动TestMain,并设置javaagent参数
. 编译TestMain
javac TestMain.java . 启动TestMain
java -javaagent:xxx.jar TestMain
aaarticlea/png;base64,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" alt="" />
0x2: 动态Attach,load指定Agent
这种方式与之前指定参数的不同在于,其可以在JVM已经运行的情况下,动态的附着上去,并可以动态加载agent
TestMain.java
public class TestMain
{
public static void main(String[] args) throws InterruptedException
{
while(true)
{
Thread.sleep();
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包
. 编译TestAgent
javac TestAgent.java . 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("");//目标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.
aaarticlea/png;base64,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" alt="" />
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 != )
{
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 <= ; // 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 == -)
{
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 != )
{
// 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