☆JVMTI Agent 工作原理及核心源码分析

0 前言

前一节讲述了基于JVMTI如何实现Agent,还有一种是基于Java Instrument API实现Agent,可以在Java代码层面编写Agent代码,而非基于C++/C的代码,具体使用可参考《Java Instrument 功能使用及原理》

-javaagent:为开头的默认为instrument的agent;

那么以上这两种Agent实现方式,又是在JVMTI源码中如何运行工作呢?

1 初始化 Agent

在JVM启动时,会读取JVM命令行参数 -agentlib -agentpath -javaagent并构建了Agent Library链表初始化 Agent 代码如下

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);  
    }  
    if ((strcmp(name, "hprof") == 0) || (strcmp(name, "jdwp") == 0)) {  
      warning("profiling and debugging agents are not supported with Kernel VM");    
    } else  if
      // JVMTI_KERNEL 构建Agent Library链表 
      add_init_agent(name, options, is_absolute_path);  
    } 
} else if (match_option(option, "-javaagent:", &tail)) {
  // -javaagent   
  if(tail != NULL) {  
    char *options = strcpy(NEW_C_HEAP_ARRAY(char, strlen(tail) + 1), tail);  
    // 构建Agent Library链表
    add_init_agent("instrument", options, false);  
  }  
  // -Xnoclassgc  
}

2 加载Agent链接库

在启动JVM create_vm时,对agent链表中的每个agent库,加载所指定的动态库, 并调用里面的Agent_OnLoad方法,比如:对于Java Instrument Agent加载就是对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) {  
      // 调用 Agent_OnLoad 函数  
      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();  
}

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;
    // 创建一个新的JPLISAgent对象  
    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 把jar文件追加到agent的classpath中。
         */  
        appendClassPath(agent, jarfile);  
        ……  
}  

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

4 JVMTI 回调接口注册与执行

以下是JVMTI的一些回调接口,通过这些回调接口设置回调函数指针:

typedef struct {
    /* 50 : VM Initialization Event */  
    jvmtiEventVMInit VMInit;   
    /* 51 : VM Death Event */  
    jvmtiEventVMDeath VMDeath; 
    /* 52 : Thread Start */  
    jvmtiEventThreadStart ThreadStart;
    /* 53 : Thread End */  
    jvmtiEventThreadEnd ThreadEnd;  
    /* 54 : Class File Load Hook */  
    jvmtiEventClassFileLoadHook ClassFileLoadHook;
    /* 55 : Class Load */  
    jvmtiEventClassLoad ClassLoad; 
    /* 56 : Class Prepare */  
    jvmtiEventClassPrepare ClassPrepare;
    /* 57 : VM Start Event */  
    jvmtiEventVMStart VMStart;
    /* 58 : Exception */  
    jvmtiEventException Exception;
    /* 59 : Exception Catch */  
    jvmtiEventExceptionCatch ExceptionCatch; 
    /* 60 : Single Step */  
    jvmtiEventSingleStep SingleStep;
    /* 61 : Frame Pop */  
    jvmtiEventFramePop FramePop;
    /* 62 : Breakpoint */  
    jvmtiEventBreakpoint Breakpoint; 
    /* 63 : Field Access */  
    jvmtiEventFieldAccess FieldAccess;
    /* 64 : Field Modification */  
    jvmtiEventFieldModification FieldModification; 
    /* 65 : Method Entry */  
    jvmtiEventMethodEntry MethodEntry;
    /* 66 : Method Exit */  
    jvmtiEventMethodExit MethodExit;
    /* 67 : Native Method Bind */  
    jvmtiEventNativeMethodBind NativeMethodBind;
    /* 68 : Compiled Method Load */  
    jvmtiEventCompiledMethodLoad CompiledMethodLoad;
    /* 69 : Compiled Method Unload */  
    jvmtiEventCompiledMethodUnload CompiledMethodUnload; 
    /* 70 : Dynamic Code Generated */  
    jvmtiEventDynamicCodeGenerated DynamicCodeGenerated; 
    /* 71 : Data Dump Request */  
    jvmtiEventDataDumpRequest DataDumpRequest;
    /* 72 */  
    jvmtiEventReserved reserved72;
    /* 73 : Monitor Wait */  
    jvmtiEventMonitorWait MonitorWait;
    /* 74 : Monitor Waited */  
    jvmtiEventMonitorWaited MonitorWaited;
    /* 75 : Monitor Contended Enter */  
    jvmtiEventMonitorContendedEnter MonitorContendedEnter;
    /* 76 : Monitor Contended Entered */  
    jvmtiEventMonitorContendedEntered MonitorContendedEntered;
    /* 77 */  
    jvmtiEventReserved reserved77;
    /* 78 */  
    jvmtiEventReserved reserved78; 
    /* 79 */  
    jvmtiEventReserved reserved79; 
    /* 80 : Resource Exhausted */  
    jvmtiEventResourceExhausted ResourceExhausted;
    /* 81 : Garbage Collection Start */  
    jvmtiEventGarbageCollectionStart GarbageCollectionStart;
    /* 82 : Garbage Collection Finish */  
    jvmtiEventGarbageCollectionFinish GarbageCollectionFinish;
    /* 83 : Object Free */  
    jvmtiEventObjectFree ObjectFree;
    /* 84 : VM Object Allocation */  
    jvmtiEventVMObjectAlloc VMObjectAlloc;  
} jvmtiEventCallbacks;  

4.1 执行jvmtiEventVMInit的回调函数

虚拟机在创建create_vm的时候,初始化了JVMTI环境后会执行JvmtiExport::post_vm_initialized(); 方法,代码如下:

void JvmtiExport::post_vm_initialized() {  
  EVT_TRIG_TRACE(JVMTI_EVENT_VM_INIT, ("JVMTI Trg VM init event triggered" ));  
  // can now enable events  
  JvmtiEventController::vm_init();  
  JvmtiEnvIterator it;  
  for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {  
    if (env->is_enabled(JVMTI_EVENT_VM_INIT)) {  
      EVT_TRACE(JVMTI_EVENT_VM_INIT, ("JVMTI Evt VM init event sent" ));  
      JavaThread *thread  = JavaThread::current();  
      JvmtiThreadEventMark jem(thread);  
      JvmtiJavaThreadEventTransition jet(thread);  
      jvmtiEventVMInit callback = env->callbacks()->VMInit;  
      if (callback != NULL) {
         // 调用了VMInit的回调函数  
         (*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread());  
      }  
    }  
  }  
}  

4.2 执行jvmtiEventClassFileLoadHook的回调函数

钩子方法是jvmtiEventClassFileLoadHook的回调方法,代码在classFileParser的文件中

instanceKlassHandle ClassFileParser::parseClassFile(symbolHandle name, Handle class_loader,Handle protection_domain, KlassHandle host_klass, GrowableArray<Handle>* cp_patches, symbolHandle& parsed_name,bool verify, TRAPS) {  
  ……  
  if (JvmtiExport::should_post_class_file_load_hook()) {  
     unsigned char* ptr = cfs->buffer();  
     unsigned char* end_ptr = cfs->buffer() + cfs->length();  
     JvmtiExport::post_class_file_load_hook(name, class_loader, protection_domain,  
                                           &ptr, &end_ptr,  
                                           &cached_class_file_bytes,  
                                           &cached_class_file_length);  
     if (ptr != cfs->buffer()) {  
        // JVMTI agent has modified class file data.  
        // Set new class file stream using JVMTI agent modified  
        // class file data.  
        cfs = new ClassFileStream(ptr, end_ptr - ptr, cfs->source());  
        set_stream(cfs);  
     }  
  }  
  …  
}

jvmtiexport::post_class_file_load_hook函数最后 调用了post_to_env()函数

void post_to_env(JvmtiEnv* env, bool caching_needed) {  
   unsigned char *new_data = NULL;  
   jint new_len = 0;  
   JvmtiClassFileLoadEventMark jem(_thread, _h_name, _class_loader,  
                                    _h_protection_domain,  
                                    _h_class_being_redefined);  
  
   JvmtiJavaThreadEventTransition jet(_thread);  
  
   JNIEnv* jni_env =  (JvmtiEnv::get_phase() == JVMTI_PHASE_PRIMORDIAL)? NULL : jem.jni_env();  
  
   jvmtiEventClassFileLoadHook callback = env->callbacks()->ClassFileLoadHook;  
  
   if (callback != NULL) {  
      (*callback)(env->jvmti_external(), jni_env,  
            jem.class_being_redefined(),  
            jem.jloader(), jem.class_name(),  
            jem.protection_domain(),  
            _curr_len, _curr_data,  
            &new_len, &new_data);
   }
   ......  
}  

函数中调用了jvmtiEventClassFileLoadHook的回调函数,也就是刚才在结构体中定义的jvmtiEventCallbacks。钩子函数eventHandlerClassFileLoadHook

void JNICALL eventHandlerClassFileLoadHook(  jvmtiEnv *              jvmtienv,  
                                JNIEnv *                jnienv,  
                                jclass                  class_being_redefined,  
                                jobject                 loader,  
                                const char*             name,  
                                jobject                 protectionDomain,  
                                jint                    class_data_len,  
                                const unsigned char*    class_data,  
                                jint*                   new_class_data_len,  
                                unsigned char**         new_class_data) {  
    JPLISEnvironment * environment  = NULL;  
  
    environment = getJPLISEnvironment(jvmtienv);  
  
    /* if something is internally inconsistent (no agent), just silently return without touching the buffer */  
    if ( environment != NULL ) {  
        jthrowable outstandingException = preserveThrowable(jnienv);  
        transformClassFile(environment->mAgent,  
                            jnienv,  
                            loader,  
                            name,  
                            class_being_redefined,  
                            protectionDomain,  
                            class_data_len,  
                            class_data,  
                            new_class_data_len,  
                            new_class_data,  
                            environment->mIsRetransformer);  
        restoreThrowable(jnienv, outstandingException);  
    }  
}  

重要的是transformClassFile函数,看看它究竟做了啥事情:

transformedBufferObject = (*jnienv)->CallObjectMethod(  
                                                jnienv,  
                                                agent->mInstrumentationImpl,  
                                                agent->mTransform,  
                                                loaderObject,  
                                                classNameStringObject,  
                                                classBeingRedefined,  
                                                protectionDomain,  
                                                classFileBufferObject,  
                                                is_retransformer);  

也就是调用了InstrumentationImpl里的transform方法,在InstrumentationImpl类里通过TransformerManager的transform的方法最终调用我们自定义的MyTransformer的类的transform方法

    private byte[] transform(ClassLoader var1, String var2, Class var3, ProtectionDomain var4, byte[] var5, boolean var6) {
        TransformerManager var7 = var6 ? this.mRetransfomableTransformerManager : this.mTransformerManager;
        return var7 == null ? null : var7.transform(var1, var2, var3, var4, var5);
    }

4.3 注册钩子函数jvmtiEventClassFileLoadHook

如上,那么钩子函数jvmtiEventClassFileLoadHook是何时注册的,回到刚才创建新的JPLISAgent代码中

JPLISInitializationError  createNewJPLISAgent(JavaVM * vm, JPLISAgent **agent_ptr) {  
    JPLISInitializationError initerror       = JPLIS_INIT_ERROR_NONE;  
    jvmtiEnv *               jvmtienv        = NULL;  
    jint                     jnierror        = JNI_OK;  
    *agent_ptr = NULL;  
    jnierror = (*vm)->GetEnv(vm,(void **) &jvmtienv,JVMTI_VERSION);  
    if (jnierror != JNI_OK) {  
        initerror = JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT;  
    } else {  
        JPLISAgent * agent = allocateJPLISAgent(jvmtienv);  
        if (agent == NULL) {  
            initerror = JPLIS_INIT_ERROR_ALLOCATION_FAILURE;  
        } else {  
            initerror = initializeJPLISAgent(agent, vm, jvmtienv);  
            if (initerror == JPLIS_INIT_ERROR_NONE) {  
                *agent_ptr = agent;  
            } else {  
                deallocateJPLISAgent(jvmtienv, agent);  
            }  
        }  
        /* don't leak envs */  
        if ( initerror != JPLIS_INIT_ERROR_NONE ) {  
            jvmtiError jvmtierror = (*jvmtienv)->DisposeEnvironment(jvmtienv);  
            jplis_assert(jvmtierror == JVMTI_ERROR_NONE);  
        }  
    }  
    return initerror;  
}  

函数initializeJPLISAgent初始化了JPLISAgent:

JPLISInitializationError initializeJPLISAgent(   JPLISAgent *    agent,JavaVM *        vm,jvmtiEnv *      jvmtienv) {  
   ……  
    checkCapabilities(agent);  
    jvmtierror == (*jvmtienv)->GetPhase(jvmtienv, &phase);  
    jplis_assert(jvmtierror == JVMTI_ERROR_NONE);  
    if (phase == JVMTI_PHASE_LIVE) {  
        return JPLIS_INIT_ERROR_NONE;  
    }  
    /* now turn on the VMInit event */  
    if ( jvmtierror == JVMTI_ERROR_NONE ) {  
        jvmtiEventCallbacks callbacks;  
        memset(&callbacks, 0, sizeof(callbacks));  
        callbacks.VMInit = &eventHandlerVMInit;  
        jvmtierror = (*jvmtienv)->SetEventCallbacks( jvmtienv, &callbacks,  sizeof(callbacks));  
  
        jplis_assert(jvmtierror == JVMTI_ERROR_NONE);  
    }  
  ……  
}  

JPLISAgent里首先 注册了一个VMInit的初始化函数eventHandlerVMInit,跟踪eventHandlerVMInit函数

void JNICALL eventHandlerVMInit( jvmtiEnv * jvmtienv,  
                    JNIEnv * jnienv,  
                    jthread thread) {  
    JPLISEnvironment * environment  = NULL;  
    jboolean success = JNI_FALSE;  
  
    environment = getJPLISEnvironment(jvmtienv);  
  
    /* process the premain calls on the all the JPL agents */  
    if ( environment != NULL ) {  
        jthrowable outstandingException = preserveThrowable(jnienv);  
        success = processJavaStart( environment->mAgent,  
                                    jnienv);  
        restoreThrowable(jnienv, outstandingException);  
    }  
  
    /* if we fail to start cleanly, bring down the JVM */  
    if ( !success ) {  
        abortJVM(jnienv, JPLIS_ERRORMESSAGE_CANNOTSTART);  
    }  
} 

在processJavaStart里

jboolean processJavaStart(JPLISAgent * agent, JNIEnv * jnienv) {  
    jboolean    result;  
    result = initializeFallbackError(jnienv);  
    jplis_assert(result);  
    if ( result ) {  
        result = createInstrumentationImpl(jnienv, agent);  
        jplis_assert(result);  
    }  
    if ( result ) {  
        result = setLivePhaseEventHandlers(agent);  
        jplis_assert(result);  
    }  
    if ( result ) {  
        result = startJavaAgent(agent, jnienv, agent->mAgentClassName, agent->mOptionsString,agent->mPremainCaller);  
    }  
    if ( result ) {  
        deallocateCommandLineData(agent);  
    }  
    return result;  
} 

在setLivePhaseEventHandler函数中注册了,代码如下

callbacks.ClassFileLoadHook = &eventHandlerClassFileLoadHook; 

5 JPLISAgent结构体

struct _JPLISAgent {  
    JavaVM *                mJVM;                   /* handle to the JVM */  
    JPLISEnvironment        mNormalEnvironment;     /* for every thing but retransform stuff */  
    JPLISEnvironment        mRetransformEnvironment;/* for retransform stuff only */  
    jobject                 mInstrumentationImpl;   /* handle to the Instrumentation instance */  
    jmethodID               mPremainCaller;         /* method on the InstrumentationImpl that does the premain stuff (cached to save lots of lookups) */  
    jmethodID               mAgentmainCaller;       /* method on the InstrumentationImpl for agents loaded via attach mechanism */  
    jmethodID               mTransform;             /* method on the InstrumentationImpl that does the class file transform */  
    jboolean                mRedefineAvailable;     /* cached answer to "does this agent support redefine" */  
    jboolean                mRedefineAdded;         /* indicates if can_redefine_classes capability has been added */  
    jboolean                mNativeMethodPrefixAvailable; /* cached answer to "does this agent support prefixing" */  
    jboolean                mNativeMethodPrefixAdded;     /* indicates if can_set_native_method_prefix capability has been added */  
    char const *            mAgentClassName;        /* agent class name */  
    char const *            mOptionsString;         /* -javaagent options string */  
};  
struct _JPLISEnvironment {  
    jvmtiEnv *              mJVMTIEnv;              /* the JVM TI environment */  
    JPLISAgent *            mAgent;                 /* corresponding agent */  
    jboolean                mIsRetransformer;       /* indicates if special environment */  
};  
  1. mNormalEnvironment:agent环境;
  2. mRetransformEnvironment:retransform环境;
  3. mInstrumentationImpl:sun自己提供的instrument对象;
  4. mPremainCallersun.instrument.InstrumentationImpl.loadClassAndCallPremain方法,agent启动时加载会被调用该方法;
  5. mAgentmainCallersun.instrument.InstrumentationImpl.loadClassAndCallAgentmain方法,agent attach动态加载agent的时会被调用该方法;
  6. mTransformsun.instrument.InstrumentationImpl.transform方法;
  7. mAgentClassName:javaagent的MANIFEST.MF里指定的Agent-Class
  8. mOptionsString:agent初始参数;
  9. mRedefineAvailable:MANIFEST.MF里的参数Can-Redefine-Classes:true
  10. mNativeMethodPrefixAvailable:MANIFEST.MF里的参数Can-Set-Native-Method-Prefix:true
  11. mIsRetransformer:MANIFEST.MF里的参数Can-Retransform-Classes:true

在startJavaAgent的方法中调用了启动JPLISAgent的方式,我们来看invokeJavaAgentMainMethod

jboolean invokeJavaAgentMainMethod(JNIEnv * jnienv,  
                           jobject instrumentationImpl,  
                           jmethodID mainCallingMethod,  
                           jstring className,  
                           jstring optionsString) {  
    jboolean errorOutstanding = JNI_FALSE;  
    jplis_assert(mainCallingMethod != NULL);  
    if (mainCallingMethod != NULL ) {  
        (*jnienv)->CallVoidMethod(jnienv,  
                         instrumentationImpl,  
                         mainCallingMethod,  
                         className,  
                         optionsString);  
        errorOutstanding = checkForThrowable(jnienv);  
        if ( errorOutstanding ) {  
            logThrowable(jnienv);  
        }  
        checkForAndClearThrowable(jnienv);  
    }  
    return !errorOutstanding;  
}  

在函数里,实际上是调用java类sun.instrument.InstrumentationImpl 类里的方法loadClassAndCallPremain。

    private void loadClassAndCallPremain(String var1, String var2) throws Throwable {
        this.loadClassAndStartAgent(var1, "premain", var2);
    }

    private void loadClassAndCallAgentmain(String var1, String var2) throws Throwable {
        this.loadClassAndStartAgent(var1, "agentmain", var2);
    }

继续查看Java的sun.instrument.InstrumentationImpl类的方法loadClassAndStartAgent:

private void loadClassAndStartAgent(String classname,  
                            String methodname,  
                            String optionsString) throws Throwable {  
        ...  
        try {  
            m = javaAgentClass.getDeclaredMethod( methodname,  
                                 new Class<?>[] {  
                                     String.class,  
                                     java.lang.instrument.Instrumentation.class  
                                 }  
                               );  
            twoArgAgent = true;  
        } catch (NoSuchMethodException x) {  
            // remember the NoSuchMethodException  
            firstExc = x;  
        }  
  
        if (m == null) {  
            // now try the declared 1-arg method  
            try {  
                m = javaAgentClass.getDeclaredMethod(methodname, new Class<?>[] { String.class });  
            } catch (NoSuchMethodException x) {  
                // ignore this exception because we'll try  
                // two arg inheritance next  
            }  
        }  
  
        if (m == null) {  
            // now try the inherited 2-arg method  
            try {  
                m = javaAgentClass.getMethod( methodname,  
                                 new Class<?>[] {  
                                     String.class,  
                                     java.lang.instrument.Instrumentation.class  
                                 }  
                               );  
                twoArgAgent = true;  
            } catch (NoSuchMethodException x) {  
                // ignore this exception because we'll try  
                // one arg inheritance next  
            }  
        }  
  
        if (m == null) {  
            // finally try the inherited 1-arg method  
            try {  
                m = javaAgentClass.getMethod(methodname, new Class<?>[] { String.class });  
            } catch (NoSuchMethodException x) {  
                // none of the methods exists so we throw the  
                // first NoSuchMethodException as per 5.0  
                throw firstExc;  
            }  
        }  
  
        // the premain method should not be required to be public,  
        // make it accessible so we can call it  
        // Note: The spec says the following:  
        //     The agent class must implement a public static premain method...  
        setAccessible(m, true);  
  
        // invoke the 1 or 2-arg method  
        if (twoArgAgent) {  
            m.invoke(null, new Object[] { optionsString, this });  
        } else {  
            m.invoke(null, new Object[] { optionsString });  
        }  
  
        // don't let others access a non-public premain method  
        setAccessible(m, false);  
    } 

在InstrumentationImpl的类中初始化了我们自定义的Transformer的premain方法:

public class MyInjectTransformer  implements ClassFileTransformer{  
    public static void premain(String options, Instrumentation ins) {  
        ins.addTransformer(new SQLInjectTransformer());  
    }  
      
    @Override  
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,  
            ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {  
        return null;  
    }  
      
}  

6 工作原理及运行时序图

6.1 启动并创建JVM,注册vmInit回调函数

启动并创建JVM

6.2 执行vmInit回调函数,注册jvmtiEventClassFileLoadHook回调函数,加载并初始化 Instrument Agent

执行vmInit回调函数,注册jvmtiEventClassFileLoadHook回调函数,加载并初始化 Instrument Agent

6.3 加载解析Class文件,执行jvmtiEventClassFileLoadHook回调函数

加载解析Class文件,执行jvmtiEventClassFileLoadHook回调函数

6.4 以-javaagent为例,工作原理

  1. 在JVM启动时,通过JVM参数-javaagent,传入agent jar,Instrument Agent被加载;
  2. 在Instrument Agent 初始化时,注册了JVMTI初始化函数eventHandlerVMinit
  3. 在JVM启动时,会调用初始化函数eventHandlerVMinit,启动了Instrument Agent,用sun.instrument.instrumentationImpl类里的方法loadClassAndCallPremain方法去初始化Premain-Class指定类的premain方法
  4. 初始化函数eventHandlerVMinit,注册了class解析的ClassFileLoadHook函数
  5. 在解析Class之前,JVM调用JVMTI的ClassFileLoadHook函数,钩子函数调用sun.instrument.instrumentationImpl类里的transform方法,通过TransformerManager的transformer方法最终调用我们自定义的Transformer类的transform方法
  6. 因为字节码在解析Class之前改的,直接使用修改后的字节码的数据流替代,最后进入Class解析,对整个Class解析无影响;
  7. 重新加载Class依然重新走5-6步骤;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,924评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,781评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,813评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,264评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,273评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,383评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,800评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,482评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,673评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,497评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,545评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,240评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,802评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,866评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,101评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,673评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,245评论 2 341

推荐阅读更多精彩内容