前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【文章】Java应用程序运行时监控方法之JVMTI的应用

【文章】Java应用程序运行时监控方法之JVMTI的应用

作者头像
Criss@陈磊
发布2019-08-02 10:48:49
2.7K0
发布2019-08-02 10:48:49
举报
文章被收录于专栏:测试技术圈

1、概述

The JVM Tool Interface (JVMTI) 是一个由JVM提供的用于开发针对Java程序开发与监控工具的编程接口,通过JVMTI接口(Native API)可以创建代理程序(Agent)以监视和控制 Java 应用程序,包括剖析、调试、监控、分析线程等。著名的JProfiler利用该项技术实现其对Java程序的运行态监控与性能分析。

值得注意的是JVMTI 并不一定在所有的 Java 虚拟机上都得到实现,目前Oracle(SUN)、IBM、OpenJDK以及一些开源的如 Apache HarmonyDRLVM均对其进行了标准实现。

由于JVMTI 是一套Native接口,因此使用 JVMTI 需要我们使用C/C++ 操纵JNI。

JVMTI程序通常通过Agent方式在JVM OnLoadphase(启动时)Start-Up,这个加载处于虚拟机初始化的早期,此时所有的 Java 类都未被初始化、所有的对象实例也都未被创建(也支持Live phase(运行时)的Start-Up)。在启动Java应用程序时,需加入以下JVM参数:

代码语言:javascript
复制
-agentlib:agent-lib-name=options
代码语言:javascript
复制
-agentpath:path-to-agent=options

JVMTI是基于事件驱动的,JVM每执行到一定的逻辑就会主动调用一些事件的回调接口,这些接口可以供开发者扩展自己的逻辑,实际上,对于JVMTI程序的Load过程可以遵循一种模板式的流程框架来完成:

(1)获取JVMTI环境(JVMTIEnvironment)

(2)注册所需的功能(Capabilities)

(3)注册事件通知(Event Notification)

(4)指定事件回调函数(Callback Method)

可以通过http://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html 和https://www.ibm.com/developerworks/cn/java/j-lo-jpda2/ 进一步了解相关知识。

接下来,我们通过举例的方式,看看JVMTI能够为Java应用监测带来些什么?

2、概述测试程序

我们首先编写一个简单的测试程序,用于展示我们举例中JVMTI Agent程序的功能,程序清单参考如下:

(1)Foo类

代码语言:javascript
复制
package org.xreztento.tester;
代码语言:javascript
复制
publicclassFoo {
代码语言:javascript
复制
    publicvoidbar() throws InterruptedException {
代码语言:javascript
复制
            Thread.sleep(500);
代码语言:javascript
复制
          System.out.println("Executing Foo.bar()");
代码语言:javascript
复制
       }
代码语言:javascript
复制
       publicvoidbaz() {
代码语言:javascript
复制
          System.out.println("Executing Foo.baz()");
代码语言:javascript
复制
       }
代码语言:javascript
复制
}

(2)Main类

代码语言:javascript
复制
package org.xreztento.tester;
代码语言:javascript
复制
publicclassMain {
代码语言:javascript
复制
    publicstaticvoidmain(String[] args) throws InterruptedException{
代码语言:javascript
复制
        Thread[] threads = new Thread[5];
代码语言:javascript
复制
        Foo foo = new Foo();
代码语言:javascript
复制
        foo.bar();
代码语言:javascript
复制
        foo.baz();
代码语言:javascript
复制
        for(int i = 0; i < threads.length; i++){
代码语言:javascript
复制
            threads[i] = new Thread(new Runnable(){
代码语言:javascript
复制
                @Override
代码语言:javascript
复制
                publicvoidrun() {
代码语言:javascript
复制
                    System.out.println(Thread.currentThread().getName());
代码语言:javascript
复制
                }
代码语言:javascript
复制
            });
代码语言:javascript
复制
        }
代码语言:javascript
复制
        for(Thread thread : threads){
代码语言:javascript
复制
            thread.start();
代码语言:javascript
复制
            thread.join();
代码语言:javascript
复制
        }
代码语言:javascript
复制
    }
代码语言:javascript
复制
}

我们将项目打包为tester.jar包,运行后输出结果如下:

3、Bytecode Instrumentation

使用Instrumentation开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。

利用Instrumentation实现字节码增强是许多监控工具针对Java应用程序实现非“侵入式”监控技术的基础,JVMTI为其提供了Native接口,Java SE 5将其从本地代码中解放出来通过JavaAgent利用该本地接口实现了Java语言层级的接口。

我们这里先不讨论JavaAgent的上层实现方式,你可以直接利用JVMTI的Native接口完成class字节码加载时的字节码修改增强。在JVM加载class字节码时会产生一个JVMTI_EVENT_CLASS_FILE_LOAD_HOOK事件,你可以通过ClassFileLoadHook回调函数完成新字节码的定义工作。

需要特别注意的地方是,对字节码的修改需要开辟出一块新的内存空间,因此就像向操作系统申请内存空间使用如malloc一样,你需要使用(*jvmti)->Allocate在JVM内部申请出一块内存空间,参考如下代码:

代码语言:javascript
复制
#include <stdio.h>
代码语言:javascript
复制
#include <memory.h>
代码语言:javascript
复制
#include <string.h>
代码语言:javascript
复制
#include <jvmti.h>
代码语言:javascript
复制
void  JNICALL callbackClassFileLoadHook(jvmtiEnv *jvmti,
代码语言:javascript
复制
                                  JNIEnv *jni,
代码语言:javascript
复制
                                  jclass class_being_redefined,
代码语言:javascript
复制
                                  jobject loader,
代码语言:javascript
复制
                                  constchar *name,
代码语言:javascript
复制
                                  jobject protection_domain,
代码语言:javascript
复制
                                  jint class_data_len,
代码语言:javascript
复制
                                  constunsignedchar *class_data,
代码语言:javascript
复制
                                  jint *new_class_data_len,
代码语言:javascript
复制
                                  unsignedchar **new_class_data) {
代码语言:javascript
复制
    jvmtiError error;
代码语言:javascript
复制
    if(strcmp(name, "org/xreztento/tester/Foo") == 0){
代码语言:javascript
复制
      printf("loaded class name=%s\n ", name); 
代码语言:javascript
复制
      jint size = class_data_len;
代码语言:javascript
复制
      *new_class_data_len = size;
代码语言:javascript
复制
      //为新的class字节码数据区分配JVM内存
代码语言:javascript
复制
      error = (*jvmti)->Allocate(jvmti, size, new_class_data);
代码语言:javascript
复制
      memset(*new_class_data, 0, size);
代码语言:javascript
复制
      if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
        fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI");
代码语言:javascript
复制
      } 
代码语言:javascript
复制
      int i;
代码语言:javascript
复制
      //遍历旧的字节码字符,将E字符修改为P
代码语言:javascript
复制
      for(i = 0; i < size; i++){
代码语言:javascript
复制
        if(class_data[i] == 'E'){
代码语言:javascript
复制
          (*new_class_data)[i] = 'P';
代码语言:javascript
复制
        } else {
代码语言:javascript
复制
          (*new_class_data)[i] = class_data[i];
代码语言:javascript
复制
        }
代码语言:javascript
复制
      }
代码语言:javascript
复制
    }
代码语言:javascript
复制
} 
代码语言:javascript
复制
JNIEXPORT jint JNICALL
代码语言:javascript
复制
Agent_OnLoad(JavaVM* vm, char *options, void *reserved){ 
代码语言:javascript
复制
  jvmtiEnv *jvmti = NULL;
代码语言:javascript
复制
  jvmtiError error; 
代码语言:javascript
复制
  //获取JVMTI environment
代码语言:javascript
复制
  error = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_1);
代码语言:javascript
复制
  if (error != JNI_OK) {
代码语言:javascript
复制
      fprintf(stderr, "ERROR: Couldn't get JVMTI environment");
代码语言:javascript
复制
      return JNI_ERR;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  //注册功能
代码语言:javascript
复制
  jvmtiCapabilities capabilities;
代码语言:javascript
复制
  (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities)); 
代码语言:javascript
复制
  capabilities.can_generate_all_class_hook_events  =  1 ;
代码语言:javascript
复制
  capabilities.can_retransform_classes             =  1 ;
代码语言:javascript
复制
  capabilities.can_retransform_any_class           =  1 ;
代码语言:javascript
复制
  error = (*jvmti)->AddCapabilities(jvmti, &capabilities); 
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  }  
代码语言:javascript
复制
  //设置JVM事件回调
代码语言:javascript
复制
  jvmtiEventCallbacks callbacks;
代码语言:javascript
复制
  callbacks.ClassFileLoadHook = &callbackClassFileLoadHook;
代码语言:javascript
复制
  error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!");
代码语言:javascript
复制
    return error;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  //设置事件通知
代码语言:javascript
复制
  error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL);
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  return JNI_OK;
代码语言:javascript
复制
} 
代码语言:javascript
复制
JNIEXPORT jint JNICALL
代码语言:javascript
复制
Agent_OnAttach(JavaVM* vm, char *options, void *reserved){
代码语言:javascript
复制
  //do nothing
代码语言:javascript
复制
}  
代码语言:javascript
复制
JNIEXPORT void JNICALL
代码语言:javascript
复制
Agent_OnUnload(JavaVM *vm){
代码语言:javascript
复制
  //do nothing
代码语言:javascript
复制
}

字节码增强的意义是你可以在原有执行方法内部添加自己的代码逻辑如一些方法执行期的性能监控逻辑,并且无需修改原程序Class文件,以完全无侵入式的代价完成对Java程序的监测。

我们的例子非常简单,将org/xreztento/tester/Foo类字节码中的E字符全部替换成P字符。

首先,编译一个JVMTI程序的静态库,参考以下脚本:

代码语言:javascript
复制
gcc agent.c -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.65-3.b17.el7.x86_64/include -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.65-3.b17.el7.x86_64/include/linux -shared-fPIC-o./libtestagent.so

之后带agent运行我们的测试程序,如:

代码语言:javascript
复制
java -jar -agentpath:/root/jvmti/libtestagent.so tester.jar

运行后输出结果如下:

4、Method执行性能

JVMTI提供了对每个Java方法执行的监控事件,当进入方法时触发JVMTI_EVENT_METHOD_ENTRY事件,方法执行完成触发JVMTI_EVENT_METHOD_EXIT,我们可以为两个事件编写回调函数完成对指定方法的执行性能数据的记录。

我们使用一个HashMap数据结构来对方法的执行过程进行保存,key为执行方法的线程标识+方法名,value记录Entry方法时的系统nanos。(本例中hashmap采用https://github.com/japeq/hashmap)

实现一个记录bar方法执行时的运行时间的逻辑,参考如下代码实现:

代码语言:javascript
复制
#include <stdio.h>
代码语言:javascript
复制
#include <stdint.h>
代码语言:javascript
复制
#include <sys/time.h>
代码语言:javascript
复制
#include <memory.h>
代码语言:javascript
复制
#include <string.h>
代码语言:javascript
复制
#include <stdlib.h>
代码语言:javascript
复制
#include <jvmti.h>
代码语言:javascript
复制
#include "hashmap.h" 
代码语言:javascript
复制
#define KEY_MAX_LENGTH 256  
代码语言:javascript
复制
#ifndef offsetof
代码语言:javascript
复制
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
代码语言:javascript
复制
#endif 
代码语言:javascript
复制
#define container_of(ptr, type, member) \
代码语言:javascript
复制
    ((type *) ((char *) (ptr) - offsetof(type, member)))  
代码语言:javascript
复制
struct method_t {
代码语言:javascript
复制
    struct hash_node node;
代码语言:javascript
复制
  int hash;
代码语言:javascript
复制
    jlong start_time;
代码语言:javascript
复制
}; 
代码语言:javascript
复制
/**
代码语言:javascript
复制
hash算法
代码语言:javascript
复制
**/
代码语言:javascript
复制
staticunsignedint
代码语言:javascript
复制
djb_hash(char* str, unsignedint len)
代码语言:javascript
复制
{
代码语言:javascript
复制
   unsignedint hash = 5381;
代码语言:javascript
复制
   unsignedint i    = 0; 
代码语言:javascript
复制
   for(i = 0; i < len; str++, i++)
代码语言:javascript
复制
   {
代码语言:javascript
复制
      hash = ((hash << 5) + hash) + (*str);
代码语言:javascript
复制
   } 
代码语言:javascript
复制
   return hash;
代码语言:javascript
复制
}  
代码语言:javascript
复制
static size_t
代码语言:javascript
复制
hash_test(void *key)
代码语言:javascript
复制
{
代码语言:javascript
复制
    unsignedint i = djb_hash(key, strlen(key));
代码语言:javascript
复制
    return i;
代码语言:javascript
复制
} 
代码语言:javascript
复制
staticint
代码语言:javascript
复制
cmp_test(struct hash_node *node, void *key)
代码语言:javascript
复制
{
代码语言:javascript
复制
  struct method_t *t = container_of(node, struct method_t, node);
代码语言:javascript
复制
    int i = hash_test(key);
代码语言:javascript
复制
    return t->hash == i;
代码语言:javascript
复制
}  
代码语言:javascript
复制
staticstruct hashmap map;  
代码语言:javascript
复制
void JNICALL
代码语言:javascript
复制
callbackMethodEntry(jvmtiEnv *jvmti, JNIEnv* env,
代码语言:javascript
复制
jthread thr, jmethodID method) {
代码语言:javascript
复制
    char *name;
代码语言:javascript
复制
    char *signature;
代码语言:javascript
复制
    char *generic;  
代码语言:javascript
复制
    (*jvmti)->GetMethodName(jvmti, method, &name, &signature, &generic); 
代码语言:javascript
复制
    if (strcmp(name, "bar") == 0){
代码语言:javascript
复制
        jvmtiThreadInfo info;
代码语言:javascript
复制
        jlong nanos;
代码语言:javascript
复制
        struct method_t *t; 
代码语言:javascript
复制
        char key[KEY_MAX_LENGTH] = "thread-"; 
代码语言:javascript
复制
        (*jvmti)->GetThreadInfo(jvmti, thr, &info); 
代码语言:javascript
复制
        strcat(key, info.name);
代码语言:javascript
复制
        strcat(key, ":");
代码语言:javascript
复制
        strcat(key, name);
代码语言:javascript
复制
        strcat(key, "\0"); 
代码语言:javascript
复制
        (*jvmti)->GetTime(jvmti, &nanos); 
代码语言:javascript
复制
        t = calloc(1, sizeof(*t));
代码语言:javascript
复制
        t->hash = hash_test(key);
代码语言:javascript
复制
        t->start_time = nanos; 
代码语言:javascript
复制
        hashmap_insert(&map, &t->node, key);
代码语言:javascript
复制
        (*jvmti)->Deallocate(jvmti, (void *)info.name); 
代码语言:javascript
复制
    } 
代码语言:javascript
复制
} 
代码语言:javascript
复制
void JNICALL
代码语言:javascript
复制
callbackMethodExit(jvmtiEnv *jvmti, JNIEnv* env,
代码语言:javascript
复制
jthread thr, jmethodID method){
代码语言:javascript
复制
  char *name;
代码语言:javascript
复制
  char *signature;
代码语言:javascript
复制
  char *generic; 
代码语言:javascript
复制
  (*jvmti)->GetMethodName(jvmti, method, &name, &signature, &generic);
代码语言:javascript
复制
  if (strcmp(name, "bar")== 0){ 
代码语言:javascript
复制
      jvmtiThreadInfo info;
代码语言:javascript
复制
      jlong nanos;
代码语言:javascript
复制
      struct method_t *t; 
代码语言:javascript
复制
      char key[KEY_MAX_LENGTH] = "thread-"; 
代码语言:javascript
复制
      (*jvmti)->GetThreadInfo(jvmti,
代码语言:javascript
复制
                  thr,
代码语言:javascript
复制
                  &info);
代码语言:javascript
复制
      strcat(key, info.name);
代码语言:javascript
复制
      strcat(key, ":");
代码语言:javascript
复制
      strcat(key, name);
代码语言:javascript
复制
      struct hash_node *node = hashmap_get(&map, key); 
代码语言:javascript
复制
        if (node == NULL) {
代码语言:javascript
复制
            printf("%s not found\n", key); 
代码语言:javascript
复制
        } else {
代码语言:javascript
复制
        (*jvmti)->GetTime(jvmti, &nanos);
代码语言:javascript
复制
        t = container_of(node, struct method_t, node);
代码语言:javascript
复制
        printf("method<%s> running: %ld ms\n", key, (nanos - t->start_time) / (1000 * 1000));
代码语言:javascript
复制
      } 
代码语言:javascript
复制
      (*jvmti)->Deallocate(jvmti, (void *)info.name);
代码语言:javascript
复制
  } 
代码语言:javascript
复制
} 
代码语言:javascript
复制
JNIEXPORT jint JNICALL
代码语言:javascript
复制
Agent_OnLoad(JavaVM* vm, char *options, void *reserved){ 
代码语言:javascript
复制
  jvmtiEnv *jvmti = NULL;
代码语言:javascript
复制
  jvmtiError error;
代码语言:javascript
复制
  hashmap_init(&map, hash_test, cmp_test); 
代码语言:javascript
复制
  //获取JVMTI environment
代码语言:javascript
复制
  error = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_1);
代码语言:javascript
复制
  if (error != JNI_OK) {
代码语言:javascript
复制
      fprintf(stderr, "ERROR: Couldn't get JVMTI environment");
代码语言:javascript
复制
      return JNI_ERR;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  //注册功能
代码语言:javascript
复制
  jvmtiCapabilities capabilities;
代码语言:javascript
复制
  (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities));
代码语言:javascript
复制
  capabilities.can_generate_method_entry_events = 1;
代码语言:javascript
复制
  capabilities.can_generate_method_exit_events  = 1;
代码语言:javascript
复制
  capabilities.can_access_local_variables       = 1; 
代码语言:javascript
复制
  error = (*jvmti)->AddCapabilities(jvmti, &capabilities); 
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  }  
代码语言:javascript
复制
  //设置JVM事件回调
代码语言:javascript
复制
  jvmtiEventCallbacks callbacks; 
代码语言:javascript
复制
  callbacks.MethodEntry = &callbackMethodEntry;
代码语言:javascript
复制
  callbacks.MethodExit = &callbackMethodExit; 
代码语言:javascript
复制
  error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!");
代码语言:javascript
复制
    return error;
代码语言:javascript
复制
  }
代码语言:javascript
复制
  //设置事件通知
代码语言:javascript
复制
  error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, (jthread)NULL);
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, (jthread)NULL);
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
   fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!");
代码语言:javascript
复制
   return  error;
代码语言:javascript
复制
  }  
代码语言:javascript
复制
  return JNI_OK;
代码语言:javascript
复制
} 
代码语言:javascript
复制
JNIEXPORT jint JNICALL
代码语言:javascript
复制
Agent_OnAttach(JavaVM* vm, char *options, void *reserved){
代码语言:javascript
复制
  //do nothing
代码语言:javascript
复制
}  
代码语言:javascript
复制
JNIEXPORT void JNICALL
代码语言:javascript
复制
Agent_OnUnload(JavaVM *vm){
代码语言:javascript
复制
  hashmap_free(&map);
代码语言:javascript
复制
}

编译时,增加hashmap.c,运行后输出结果如下:

5、Thread监视

JVMTI提供了对JVM内部所有线程生命周期的监控事件,并可以控制这些线程的行为,比如当一个Java线程开始执行时触发JVMTI_EVENT_THREAD_START事件,结束时触发JVMTI_EVENT_THREAD_END,通过实现回调函数,可以获得触发该事件下的线程,并获取线程信息或操作该线程。

我们可以记录该线程的执行和CPU-Time,参考代码如下:

代码语言:javascript
复制
#include <stdio.h>
代码语言:javascript
复制
#include <memory.h>
代码语言:javascript
复制
#include <string.h>
代码语言:javascript
复制
#include <jvmti.h> 
代码语言:javascript
复制
void JNICALL
代码语言:javascript
复制
callbackThreadStart(jvmtiEnv *jvmti, JNIEnv* env, jthread thr){
代码语言:javascript
复制
    jvmtiThreadInfo info;
代码语言:javascript
复制
    jlong cpu_time;
代码语言:javascript
复制
    jvmtiFrameInfo frames[5];
代码语言:javascript
复制
    jint count;
代码语言:javascript
复制
    jvmtiError err;
代码语言:javascript
复制
    //获取启动线程信息
代码语言:javascript
复制
    (*jvmti)->GetThreadInfo(jvmti, thr, &info);
代码语言:javascript
复制
    //获取启动线程CPU-Time
代码语言:javascript
复制
    (*jvmti)->GetThreadCpuTime(jvmti, thr, &cpu_time); 
代码语言:javascript
复制
    printf("thread-%s start...\n", info.name);
代码语言:javascript
复制
    printf("thread-%s cpu-time : %ld nanos\n", info.name, cpu_time);   
代码语言:javascript
复制
} 
代码语言:javascript
复制
void JNICALL callbackThreadEnd(jvmtiEnv *jvmti,
代码语言:javascript
复制
            JNIEnv* env,
代码语言:javascript
复制
            jthread thr){
代码语言:javascript
复制
    jvmtiThreadInfo info;
代码语言:javascript
复制
    (*jvmti)->GetThreadInfo(jvmti,
代码语言:javascript
复制
                          thr,
代码语言:javascript
复制
                          &info);
代码语言:javascript
复制
                          printf("thread-%s end...\n", info.name);
代码语言:javascript
复制
} 
代码语言:javascript
复制
JNIEXPORT jint JNICALL
代码语言:javascript
复制
Agent_OnLoad(JavaVM* vm, char *options, void *reserved){ 
代码语言:javascript
复制
  jvmtiEnv *jvmti = NULL;
代码语言:javascript
复制
  jvmtiError error;
代码语言:javascript
复制
//获取JVMTI environment
代码语言:javascript
复制
  error = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_1);
代码语言:javascript
复制
  if (error != JNI_OK) {
代码语言:javascript
复制
      fprintf(stderr, "ERROR: Couldn't get JVMTI environment");
代码语言:javascript
复制
      return JNI_ERR;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  //注册功能
代码语言:javascript
复制
  jvmtiCapabilities capabilities;
代码语言:javascript
复制
  (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities));  
代码语言:javascript
复制
  capabilities.can_get_current_thread_cpu_time     =  1 ;
代码语言:javascript
复制
  capabilities.can_get_thread_cpu_time             =  1 ; 
代码语言:javascript
复制
  error = (*jvmti)->AddCapabilities(jvmti, &capabilities); 
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  }  
代码语言:javascript
复制
  //jvmtiEventThreadStart ThreadStart;
代码语言:javascript
复制
   //jvmtiEventThreadEnd ThreadEnd;
代码语言:javascript
复制
  //设置JVM事件回调
代码语言:javascript
复制
  jvmtiEventCallbacks callbacks;
代码语言:javascript
复制
  callbacks.ThreadStart = &callbackThreadStart;
代码语言:javascript
复制
  callbacks.ThreadEnd = &callbackThreadEnd;
代码语言:javascript
复制
  error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!");
代码语言:javascript
复制
    return error;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  //设置事件通知
代码语言:javascript
复制
  error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, (jthread)NULL);
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_END, (jthread)NULL);
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  return JNI_OK;
代码语言:javascript
复制
} 
代码语言:javascript
复制
JNIEXPORT jint JNICALL
代码语言:javascript
复制
Agent_OnAttach(JavaVM* vm, char *options, void *reserved){
代码语言:javascript
复制
  //do nothing
代码语言:javascript
复制
}  
代码语言:javascript
复制
JNIEXPORT void JNICALL
代码语言:javascript
复制
Agent_OnUnload(JavaVM *vm){
代码语言:javascript
复制
  //do nothing
代码语言:javascript
复制
}

运行后输出结果如下:

JVMTI提供了对Moniter的支持,可以监视Lock,并且可以获取一个执行方法的本地对象,我们可以结合Method完成一个父子线程关系的监视,参考代码如下:

代码语言:javascript
复制
#include <stdio.h>
代码语言:javascript
复制
#include <memory.h>
代码语言:javascript
复制
#include <jvmti.h> 
代码语言:javascript
复制
void JNICALL
代码语言:javascript
复制
callbackMethodEntry(jvmtiEnv *jvmti, JNIEnv *env, jthread thr, jmethodID method) {
代码语言:javascript
复制
    jrawMonitorID monitor;
代码语言:javascript
复制
    char *name; 
代码语言:javascript
复制
    (*jvmti)->RawMonitorEnter(jvmti, monitor); 
代码语言:javascript
复制
    (*jvmti)->GetMethodName(jvmti, method, &name, NULL, NULL);
代码语言:javascript
复制
    if (strcmp(name, "start") == 0 || strcmp(name, "interrupt") == 0 ||
代码语言:javascript
复制
        strcmp(name, "join") == 0 || strcmp(name, "stop") == 0 ||
代码语言:javascript
复制
        strcmp(name, "suspend") == 0 || strcmp(name, "resume") == 0){
代码语言:javascript
复制
        jobject *thd_ptr;
代码语言:javascript
复制
        jint hash_code;
代码语言:javascript
复制
        jvmtiThreadInfo info;
代码语言:javascript
复制
        //获取子线程对象
代码语言:javascript
复制
        (*jvmti)->GetLocalObject(jvmti, thr, 0, 0, thd_ptr);
代码语言:javascript
复制
        //获取父线程信息
代码语言:javascript
复制
        (*jvmti)->GetThreadInfo(jvmti,
代码语言:javascript
复制
                    thr,
代码语言:javascript
复制
                    &info);
代码语言:javascript
复制
        //获取子线程对象hashcode
代码语言:javascript
复制
        (*jvmti)->GetObjectHashCode(jvmti, *thd_ptr, &hash_code); 
代码语言:javascript
复制
        printf("<thread-%s %s thread@%d>\n", info.name, name, hash_code); 
代码语言:javascript
复制
        (*jvmti)->Deallocate(jvmti, (void *)info.name);
代码语言:javascript
复制
    } 
代码语言:javascript
复制
    (*jvmti)->RawMonitorExit(jvmti, monitor);
代码语言:javascript
复制
} 
代码语言:javascript
复制
void JNICALL
代码语言:javascript
复制
callbackMethodExit(jvmtiEnv *jvmti, JNIEnv* env, jthread thr, jmethodID method){ 
代码语言:javascript
复制
}
代码语言:javascript
复制
JNIEXPORT jint JNICALL
代码语言:javascript
复制
Agent_OnLoad(JavaVM* vm, char *options, void *reserved){ 
代码语言:javascript
复制
  jvmtiEnv *jvmti = NULL;
代码语言:javascript
复制
  jvmtiError error; 
代码语言:javascript
复制
  //获取JVMTI environment
代码语言:javascript
复制
  error = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_1);
代码语言:javascript
复制
  if (error != JNI_OK) {
代码语言:javascript
复制
      fprintf(stderr, "ERROR: Couldn't get JVMTI environment");
代码语言:javascript
复制
      return JNI_ERR;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  //注册功能
代码语言:javascript
复制
  jvmtiCapabilities capabilities;
代码语言:javascript
复制
  (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities));
代码语言:javascript
复制
  capabilities.can_generate_method_entry_events = 1;
代码语言:javascript
复制
  capabilities.can_generate_method_exit_events  = 1;
代码语言:javascript
复制
  capabilities.can_access_local_variables       = 1;
代码语言:javascript
复制
  capabilities.can_get_monitor_info             = 1; 
代码语言:javascript
复制
  error = (*jvmti)->AddCapabilities(jvmti, &capabilities); 
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  }  
代码语言:javascript
复制
  //设置JVM事件回调
代码语言:javascript
复制
  jvmtiEventCallbacks callbacks; 
代码语言:javascript
复制
  callbacks.MethodEntry = &callbackMethodEntry;
代码语言:javascript
复制
  callbacks.MethodExit = &callbackMethodExit; 
代码语言:javascript
复制
  error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!");
代码语言:javascript
复制
    return error;
代码语言:javascript
复制
  }
代码语言:javascript
复制
  //设置事件通知
代码语言:javascript
复制
  error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, (jthread)NULL);
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, (jthread)NULL);
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
   fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!");
代码语言:javascript
复制
   return  error;
代码语言:javascript
复制
 }  
代码语言:javascript
复制
  return JNI_OK;
代码语言:javascript
复制
} 
代码语言:javascript
复制
JNIEXPORT jint JNICALL
代码语言:javascript
复制
Agent_OnAttach(JavaVM* vm, char *options, void *reserved){
代码语言:javascript
复制
  //do nothing
代码语言:javascript
复制
}  
代码语言:javascript
复制
JNIEXPORT void JNICALL
代码语言:javascript
复制
Agent_OnUnload(JavaVM *vm){
代码语言:javascript
复制
  //do nothing
代码语言:javascript
复制
}

运行后输出结果如下:

获取Stack Trace

JVMTI可以获取当前JVM下所有线程以及线程内执行方法的Stack Trace。

我们需要在JVMTI_EVENT_VM_INIT事件被触发时,在回调函数中利用RunAgentThread方法创建一个Agent级别的thread(创建过程非常类似pthread),之后按固定时间间隔,获取相关线程的Stack Trace信息。

参考如下代码:

代码语言:javascript
复制
#include <stdio.h>
代码语言:javascript
复制
#include <memory.h>
代码语言:javascript
复制
#include <string.h>
代码语言:javascript
复制
#include <signal.h>
代码语言:javascript
复制
#include <unistd.h>
代码语言:javascript
复制
#include <jvmti.h>  
代码语言:javascript
复制
static jthread mt; 
代码语言:javascript
复制
static JNICALL
代码语言:javascript
复制
monitor_runnable(jvmtiEnv* jvmti, JNIEnv* env, jthread thr, int *args){ 
代码语言:javascript
复制
  jthread* threads;
代码语言:javascript
复制
  jint thread_count;
代码语言:javascript
复制
  while (1) {
代码语言:javascript
复制
    printf("----------------------Stack Trace-------------------------\n");
代码语言:javascript
复制
    int i;
代码语言:javascript
复制
    //获取当前JVM所有线程
代码语言:javascript
复制
    (*jvmti)->GetAllThreads(jvmti, &thread_count, &threads); 
代码语言:javascript
复制
    for(i = 0; i < thread_count; i++){
代码语言:javascript
复制
      jvmtiFrameInfo frames[5];
代码语言:javascript
复制
      jint count;
代码语言:javascript
复制
      jvmtiError err; 
代码语言:javascript
复制
      err = (*jvmti)->GetStackTrace(jvmti, threads[i], 0, 5, frames, &count);
代码语言:javascript
复制
      if (err == JVMTI_ERROR_NONE && count >= 1) {
代码语言:javascript
复制
        char *methodName;
代码语言:javascript
复制
        err = (*jvmti)->GetMethodName(jvmti, frames[0].method,
代码语言:javascript
复制
                       &methodName, NULL, NULL);
代码语言:javascript
复制
        if (err == JVMTI_ERROR_NONE) {
代码语言:javascript
复制
          printf("Thread Stack Trace Executing method: %s\n", methodName);
代码语言:javascript
复制
        }
代码语言:javascript
复制
      }
代码语言:javascript
复制
    }
代码语言:javascript
复制
    sleep(2);
代码语言:javascript
复制
  } 
代码语言:javascript
复制
}  
代码语言:javascript
复制
void JNICALL
代码语言:javascript
复制
callbackVMInit(jvmtiEnv *jvmti,
代码语言:javascript
复制
            JNIEnv* env,
代码语言:javascript
复制
            jthread thr){ 
代码语言:javascript
复制
            (*jvmti)->RunAgentThread(jvmti,
代码语言:javascript
复制
                        thr,
代码语言:javascript
复制
                        (void *)monitor_runnable,
代码语言:javascript
复制
                        (void *)NULL,
代码语言:javascript
复制
                        1);
代码语言:javascript
复制
} 
代码语言:javascript
复制
void JNICALL
代码语言:javascript
复制
callbackVMDeath(jvmtiEnv *jvmti,
代码语言:javascript
复制
            JNIEnv* env){
代码语言:javascript
复制
              (*jvmti)->StopThread(jvmti,
代码语言:javascript
复制
                          mt,
代码语言:javascript
复制
                          NULL);
代码语言:javascript
复制
            } 
代码语言:javascript
复制
JNIEXPORT jint JNICALL
代码语言:javascript
复制
Agent_OnLoad(JavaVM* vm, char *options, void *reserved){ 
代码语言:javascript
复制
  jvmtiEnv *jvmti = NULL;
代码语言:javascript
复制
  jvmtiError error; 
代码语言:javascript
复制
  //获取JVMTI environment
代码语言:javascript
复制
  error = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_1);
代码语言:javascript
复制
  if (error != JNI_OK) {
代码语言:javascript
复制
      fprintf(stderr, "ERROR: Couldn't get JVMTI environment");
代码语言:javascript
复制
      return JNI_ERR;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  //注册功能
代码语言:javascript
复制
  jvmtiCapabilities capabilities;
代码语言:javascript
复制
  (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities));  
代码语言:javascript
复制
  capabilities.can_get_current_thread_cpu_time     =  1 ;
代码语言:javascript
复制
  capabilities.can_get_thread_cpu_time             =  1 ;
代码语言:javascript
复制
  capabilities.can_signal_thread = 1;
代码语言:javascript
复制
  capabilities.can_pop_frame = 1; 
代码语言:javascript
复制
  error = (*jvmti)->AddCapabilities(jvmti, &capabilities); 
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  }  
代码语言:javascript
复制
  //jvmtiEventThreadStart ThreadStart;
代码语言:javascript
复制
   //jvmtiEventThreadEnd ThreadEnd;
代码语言:javascript
复制
  //设置JVM事件回调
代码语言:javascript
复制
  jvmtiEventCallbacks callbacks;
代码语言:javascript
复制
  callbacks.VMInit = &callbackVMInit;
代码语言:javascript
复制
  callbacks.VMDeath = &callbackVMDeath; 
代码语言:javascript
复制
  error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!");
代码语言:javascript
复制
    return error;
代码语言:javascript
复制
  }
代码语言:javascript
复制
  //设置事件通知
代码语言:javascript
复制
  error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,  JVMTI_EVENT_VM_INIT, (jthread)NULL);
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,  JVMTI_EVENT_VM_DEATH, (jthread)NULL);
代码语言:javascript
复制
  if(error != JVMTI_ERROR_NONE) {
代码语言:javascript
复制
    fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!");
代码语言:javascript
复制
    return  error;
代码语言:javascript
复制
  } 
代码语言:javascript
复制
  return JNI_OK;
代码语言:javascript
复制
} 
代码语言:javascript
复制
JNIEXPORT jint JNICALL
代码语言:javascript
复制
Agent_OnAttach(JavaVM* vm, char *options, void *reserved){
代码语言:javascript
复制
  //do nothing
代码语言:javascript
复制
}  
代码语言:javascript
复制
JNIEXPORT void JNICALL
代码语言:javascript
复制
Agent_OnUnload(JavaVM *vm){
代码语言:javascript
复制
  //do nothing
代码语言:javascript
复制
}

运行后输出结果如下:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-06-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 质问 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 3、Bytecode Instrumentation
  • 4、Method执行性能
  • 5、Thread监视
  • 获取Stack Trace
相关产品与服务
应用性能监控
应用性能监控(Application Performance Management,APM)是一款应用性能管理平台,基于实时多语言应用探针全量采集技术,为您提供分布式性能分析和故障自检能力。APM 协助您在复杂的业务系统里快速定位性能问题,降低 MTTR(平均故障恢复时间),实时了解并追踪应用性能,提升用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档