码迷,mamicode.com
首页 > 编程语言 > 详细

Java千百问_08JDK详解(014)_如何编写JVMTI agent程序

时间:2016-07-02 10:34:01      阅读:247      评论:0      收藏:0      [点我收藏+]

标签:

点击进入_更多_Java千百问

1、如何编写JVMTI agent程序

了解JPDA看这里:JPDA是什么
了解JVMTI看这里:JVMTI是什么
我们需要使用C++编写agent程序,JVM在不同时机回调下面的接口函数:

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)

JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved);

JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *jvm)

其中第一个是jvm启动时调用,第二个是jvm运行时发出attach时调用,第三个是jvm卸载时调用

其中*jvm参数传入JavaVM指针,可以用来操作JVM;*options参数是命令行传入的参数;*reserved是一个预留参数。

给出运行加载模式实现的一个简单功能:打印jvm内所有已经装载成功的class。具体如下:

/*
*  JVMTI agent
*
*  Created on: 2016-07-02
*      Author: sunjie
*/
#include <jvmti.h>
#include <string>
#include <cstring>
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <stdlib.h>
#include <jni_md.h>

JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options,
                void *reserved) {
        jvmtiEnv *jvmti;
        jint result = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_1);
        if (result != JNI_OK) {
                printf("ERROR: Unable to access JVMTI!\n");
        }
        jvmtiError err = (jvmtiError) 0;
        jclass *classes;
        jint count;

        err = jvmti->GetLoadedClasses(&count, &classes);//获取class
        if (err) {
                printf("ERROR: JVMTI GetLoadedClasses failed!\n");
        }
        for (int i = 0; i < count; i++) {
                char *sig;
                jvmti->GetClassSignature(classes[i], &sig, NULL);//获取并打印class签名
                printf("cls sig=%s\n", sig);
        }
        return err;
}

JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) {
        // nothing to do
}

我们将它命名为agent.cpp,下一步就是将它编译为动态链接库,不同操作系统的命令并不相同,这里以mac系统为例,具体如下:

g++ -I${JAVA_HOME}/include/ -I${JAVA_HOME}/include/darwin Agent.cpp -fPIC -shared -o libagent.so

由于是mac系统,所以引入了${JAVA_HOME}/include/darwin,linux和windows对应的应该是/include/linux、\include\win32。
执行后,我们会看到生成了了libagent.so文件

2、如何测试agent程序

我们已经有了agent,下面我们测试一下。测试需要有一个目标JVM,所以我们简单模拟一个,具体如下:

public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("JVMTI agent Test start");
        int i = 0;
        while (true) {
            Thread.sleep(1000);
            i++;
        }
    }
}

死循环的目的在于不能让它结束,方便我们使用JVMTI agent进行操作。运行后结果如下:

JVMTI agent Test start

这时我们需要通过AttachAPI为运行中的JVM加载我们的agent,我们首先获取到目标JVM的pid(通过进程监控获取),然后指定agent动态资源库的路径,具体如下:

了解更多 AttachAPI看这里:[AttachAPI是什么][4]

public class TestAgent {
    public static void main(String[] args) throws AttachNotSupportedException, IOException, AgentLoadException,
            AgentInitializationException {
        String pid = "831"; // java进程pid
        String agentPath = "/Users/sunjie/Desktop/libagent.so"; // agent.so的路径
        String options = null;// 传入agent的参数
        VirtualMachine virtualMachine = com.sun.tools.attach.VirtualMachine.attach(pid);
        virtualMachine.loadAgentPath(agentPath, options);
        virtualMachine.detach();
    }
}

这里我们需要引入${JAVA_HOME}/lib/tools.jar包,引入com.sun.tools.attach包。运行后,在目标JVM的Console中出现如下结果:

JVMTI agent Test start
cls sig=Ljava/lang/ClassLoader$NativeLibrary;
cls sig=Ljava/util/concurrent/ConcurrentMap;
cls sig=Ljava/lang/Error;
cls sig=[Ljava/lang/Error;
cls sig=Ljava/util/Set;
cls sig=Ljava/util/WeakHashMap;
cls sig=Ljava/lang/ref/Reference;
cls sig=[Ljava/lang/ref/Reference;
cls sig=Ljava/lang/StackOverflowError;
cls sig=Ljava/io/Serializable;
….

成功打印出了所有已经装载成功的class。

顺便提一句,如果采用启动加载模式,则需要在运行TestMain时加上如下参数:

java -agentlib:Agent=opt1 TestMain

注意,这里传入了*options参数:opt1,不过,我们的agent并没有使用他。

了解更多JVMTI的功能看这里:[JVMTI提供哪些功能][5]

Java千百问_08JDK详解(014)_如何编写JVMTI agent程序

标签:

原文地址:http://blog.csdn.net/ooppookid/article/details/51809925

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!