Принцип работы агента JVMTI и анализ кода исходного кода

Java задняя часть JVM C++

0 Предисловие

В предыдущем разделе описано, как реализовать агент на основе JVMTI. Другой раздел — это реализация агента на основе Java Instrument API. Код агента может быть написан на уровне кода Java вместо кода на основе C++/C. Подробнее см.«Использование и принцип функции инструмента Java»:

от-javaagent: значение по умолчанию для начала инструмента;

Итак, как две приведенные выше реализации агента работают в исходном коде JVMTI?

1 Инициализировать агент

При запуске JVM считываются параметры командной строки JVM.-agentlib -agentpath -javaagent,И создайте связанный список библиотеки агентов.Код для инициализации агента выглядит следующим образом.:

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 Загрузите библиотеку ссылок агента

При запуске JVM create_vm для каждой библиотеки агентов в связанном списке агентовЗагрузите указанную динамическую библиотеку и вызовите метод Agent_OnLoad внутри,Например:Для загрузки агента инструментов Java необходимо загрузить динамическую библиотеку Instrument.so из libinstrument.:

// 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 Создать инструмент jplisagent

Создайте новый в методе Agent_OnLoadJPLISAgent (агент служб инструментария языка программирования Java) инициализирует файлы конфигурации в классе и пакете и одновременно получает среду jvmtiEnv из среды Vm..

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);  
        ……  
}  

В коде вы можете видеть, что вПрочитайте Premain-Class в файле конфигурации jar MANIFEST и добавьте файл jar в путь к классу агента..

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,В классе InstrumentationImpl метод преобразования нашего пользовательского класса MyTransformer, наконец, вызывается через метод преобразования TransformerManager..

    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 Структура агента JPLISA

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: среда агента;
  2. mRetransformEnvironment:преобразовать окружающую среду;
  3. mInstrumentationImpl: Объект-инструмент, предоставленный самим солнцем;
  4. mPremainCaller:sun.instrument.InstrumentationImpl.loadClassAndCallPremainметод, который будет вызван при загрузке агента;
  5. mAgentmainCaller:sun.instrument.InstrumentationImpl.loadClassAndCallAgentmainметод, который будет вызываться при подключении агента, динамически загружает агент;
  6. mTransform:sun.instrument.InstrumentationImpl.transformметод;
  7. mAgentClassName: указано в MANIFEST.MF javaagentAgent-Class;
  8. mOptionsString: агент начальные параметры;
  9. mRedefineAvailable: Параметры в MANIFEST.MFCan-Redefine-Classes:true;
  10. mNativeMethodPrefixAvailable: параметры в MANIFEST.MFCan-Set-Native-Method-Prefix:true;
  11. mIsRetransformer: параметры в MANIFEST.MFCan-Retransform-Classes:true;

Метод запуска JPLISAgent вызывается в методе startJavaAgentМы смотрим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;  
}  

В функции фактически вызывается метод loadClassAndCallPremain в java-классе sun.instrument.InstrumentationImpl.

    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);
    }

Продолжайте просматривать метод loadClassAndStartAgent класса Java sun.instrument.InstrumentationImpl:

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);  
    } 

Предварительный метод нашего пользовательского Transformer инициализируется в классе InstrumentationImpl:

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, загружает и инициализирует агент прибора

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

6.3 Загрузите и проанализируйте файл класса и выполните функцию обратного вызова jvmtiEventClassFileLoadHook.

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

6.4 Взяв в качестве примера -javaagent, принцип работы

  1. Когда JVM запускается,Передайте jar агента через параметр JVM -javaagent, агент инструмента загружается;
  2. Когда инструментальный агент инициализируется,JVMTI Зарегистрированная функция инициализации EventHandLervminit;
  3. Когда JVM запускается,Будет вызвана функция инициализации eventHandlerVMinit, запускает агент инструмента,Используйте метод loadClassAndCallPremain в классе sun.instrument.instrumentationImpl для инициализации метода premain указанного класса Premain-Class.;
  4. Функция инициализации EventHandLervminit,Функция ClassFileLoadHook зарегистрирована для разрешения класса;
  5. Перед разбором классаJVM вызывает функцию ClassFileLoadHook JVMTI, функция ловушки вызывает метод преобразования в классе sun.instrument.instrumentationImpl,Наконец, вызовите метод преобразования нашего пользовательского класса Transformer через метод преобразования TransformerManager.;
  6. Поскольку Bytecode изменяется перед анализом класса, поток данных модифицированного Bytecode напрямую используется вместо этого, и, наконец, входит в анализ класса, который не влияет на все расставание класса;
  7. Перезагрузить класс все равно заново сделать 5-6 шагов;