Java Instrument(一) Java Agent

1  Agent

Agentjava中本质是一个动态库,利用JVMTI暴露出来的一些接口实现逻辑的入侵,需要实现如下的一个或者多个函数:

JNIEXPORT jint JNICALL

Agent_OnLoad(JavaVM *vm, char *options, void *reserved);

JNIEXPORT jint JNICALL

Agent_OnAttach(JavaVM* vm, char* options, void* reserved);

JNIEXPORT void JNICALL

Agent_OnUnload(JavaVM *vm);

Agent_OnLoad    函数  加载agent后调用的函数

Agent_OnAttach     函数     agent在启动时加载,所调用的Agent函数

Agent_OnUnload   函数     当agent卸载时调用

1.1 初始化Agent

目前常见的几个Agent

1.      以-agentlib: 或者-agentpath:为开头的格式,例如:常见的eclipse中的debug代码 -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:xxxx,这种情况下的agent 是jdwp

2.      以-javaagent:为开头的默认为instrument的agent

JVM在启动的时候,读取参数 -agentlib  -agentpath  -javaagent 构建了AgentLibrary的链表

代码如下:

    if (match_option(option, "-agentlib:", &tail) ||
          (is_absolute_path = match_option(option, "-agentpath:", &tail))) {
      if(tail != NULL) {
        const char* pos = strchr(tail, '=');
        size_t len = (pos == NULL) ? strlen(tail) : pos - tail;
        char* name = strncpy(NEW_C_HEAP_ARRAY(char, len + 1), tail, len);
        name[len] = '\0';
        char *options = NULL;
        if(pos != NULL) {
          options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(pos + 1) + 1), pos + 1);
        }
#ifdef JVMTI_KERNEL
        if ((strcmp(name, "hprof") == 0) || (strcmp(name, "jdwp") == 0)) {
          warning("profiling and debugging agents are not supported with Kernel VM");

        } else

#endif // JVMTI_KERNEL
        add_init_agent(name, options, is_absolute_path);
      }
    // -javaagent

    } else if (match_option(option, "-javaagent:", &tail)) {

      if(tail != NULL) {
        char *options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(tail) + 1), tail);
        add_init_agent("instrument", options, false);
      }
    // -Xnoclassgc
    }</span>
1.2 加载Agent链接库

在启动JVM create_vm的时候会初始化agent的链表中的每个agent库,加载所指定的动态库, 并调用里面的Agent_OnLoad方法,对instrument的就是加载libinstrument的动态库instrument.so

// Create agents for -agentlib:  -agentpath:  and converted -Xrun
void Threads::create_vm_init_agents() {
  extern struct JavaVM_ main_vm;
  AgentLibrary* agent;
  JvmtiExport::enter_onload_phase();
  for (agent = Arguments::agents(); agent != NULL; agent = agent->next()) {
    OnLoadEntry_t  on_load_entry = lookup_agent_on_load(agent);  
    if (on_load_entry != NULL) {
      // Invoke the Agent_OnLoad function
      jint err = (*on_load_entry)(&main_vm, agent->options(), NULL);
      if (err != JNI_OK) {
        vm_exit_during_initialization("agent library failed to init", agent->name());
      }
    } else {
      vm_exit_during_initialization("Could not find Agent_OnLoad function in the agent library", agent->name());
    }
  }
  JvmtiExport::enter_primordial_phase();
}</span>

在函数里,通过OnloadEntry方法来调用instruments动态库里的Onload方法

// Create agents for -agentlib:  -agentpath:  and converted -Xrun
void Threads::create_vm_init_agents() {
  extern struct JavaVM_ main_vm;
  AgentLibrary* agent;
  JvmtiExport::enter_onload_phase();
  for (agent = Arguments::agents(); agent != NULL; agent = agent->next()) {
    OnLoadEntry_t  on_load_entry = lookup_agent_on_load(agent);
    if (on_load_entry != NULL) {
      // Invoke the Agent_OnLoad function
      jint err = (*on_load_entry)(&main_vm, agent->options(), NULL);
      if (err != JNI_OK) {
        vm_exit_during_initialization("agent library failed to init", agent->name());
      }
    } else {
      vm_exit_during_initialization("Could not find Agent_OnLoad function in the agent library", agent->name());
    }
  }
  JvmtiExport::enter_primordial_phase();
}

1.3 Instrument  JPLISAgent

在方法Agent_OnLoad中创建一个新的JPLISAgent(Java Programming Language Instrumentation Services Agent), 初始化了类和包里的配置文件,并且同时从Vm环境中获取了jvmtiEnv 的环境。

JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *tail, void * reserved) {
    JPLISInitializationError initerror  = JPLIS_INIT_ERROR_NONE;
    jint                     result     = JNI_OK;
    JPLISAgent *             agent      = NULL;
    initerror = createNewJPLISAgent(vm, &agent);
    if ( initerror == JPLIS_INIT_ERROR_NONE ) {
        if (parseArgumentTail(tail, &jarfile, &options) != 0) {
            fprintf(stderr, "-javaagent: memory allocation failure.\n");
            return JNI_ERR;
        }
        attributes = readAttributes(jarfile);
        if (attributes == NULL) {
            fprintf(stderr, "Error opening zip file or JAR manifest missing : %s\n", jarfile);
            free(jarfile);
            if (options != NULL) free(options);
            return JNI_ERR;
        }
        premainClass = getAttribute(attributes, "Premain-Class");
        if (premainClass == NULL) {

            fprintf(stderr, "Failed to find Premain-Class manifest attribute in %s\n",

                jarfile);

            free(jarfile);

            if (options != NULL) free(options);

            freeAttributes(attributes);

            return JNI_ERR;

        }
        /*

         * Add to the jarfile

         */
        appendClassPath(agent, jarfile);

        ……

}

…..

}

在代码中,可以看到在读取jar的配置文件MANIFEST 里Premain-Class,并且把jar文件追加到agent的class path中

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值