javaagent加载机制分析

      在启动和运行期都可以加载agent代理,在启动的时候可通过-javaagent参数来执行agent代理,而在运行期就是通过attach这种机制动态load了。

 

      如果在vm启动过程中加载agent,那么会在vm初始化过程中先执行libinstrument.dylibInvocationAdapter.cAgent_OnLoad方法,这里主要是实例化agent,解析agentMF文件,将相关属性取出来,并注册jvmti的一些回调函数,在vm初始化完成之后,会通过回调函数去实例化Instrumentation实现对象,设置ClassFileLoadHook函数,并调用Pre-Main指定类的premain方法。

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 ) {
        int             oldLen, newLen;
        char *          jarfile;
        char *          options;
        jarAttribute*   attributes;
        char *          premainClass;
        char *          agentClass;
        char *          bootClassPath;

        /*
         * Parse [=options] into jarfile and options
         */
        if (parseArgumentTail(tail, &jarfile, &options) != 0) {
            fprintf(stderr, "-javaagent: memory allocation failure.\n");
            return JNI_ERR;
        }

        /*
         * Agent_OnLoad is specified to provide the agent options
         * argument tail in modified UTF8. However for 1.5.0 this is
         * actually in the platform encoding - see 5049313.
         *
         * Open zip/jar file and parse archive. If can't be opened or
         * not a zip file return error. Also if Premain-Class attribute
         * isn't present we return an error.
         */
        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);

        /*
         * The value of the Premain-Class attribute becomes the agent
         * class name. The manifest is in UTF8 so need to convert to
         * modified UTF8 (see JNI spec).
         */
        oldLen = (int)strlen(premainClass);
        newLen = modifiedUtf8LengthOfUtf8(premainClass, oldLen);
        if (newLen == oldLen) {
            premainClass = strdup(premainClass);
        } else {
            char* str = (char*)malloc( newLen+1 );
            if (str != NULL) {
                convertUtf8ToModifiedUtf8(premainClass, oldLen, str, newLen);
            }
            premainClass = str;
        }
        if (premainClass == NULL) {
            fprintf(stderr, "-javaagent: memory allocation failed\n");
            free(jarfile);
            if (options != NULL) free(options);
            freeAttributes(attributes);
            return JNI_ERR;
        }

        /*
         * If the Boot-Class-Path attribute is specified then we process
         * each relative URL and add it to the bootclasspath.
         */
        bootClassPath = getAttribute(attributes, "Boot-Class-Path");
        if (bootClassPath != NULL) {
            appendBootClassPath(agent, jarfile, bootClassPath);
        }

        /*
         * Convert JAR attributes into agent capabilities
         */
        convertCapabilityAtrributes(attributes, agent);

        /*
         * Track (record) the agent class name and options data
         */
        initerror = recordCommandLineData(agent, premainClass, options);

        /*
         * Clean-up
         */
        free(jarfile);
        if (options != NULL) free(options);
        freeAttributes(attributes);
        free(premainClass);
    }

    switch (initerror) {
    case JPLIS_INIT_ERROR_NONE:
      result = JNI_OK;
      break;
    case JPLIS_INIT_ERROR_CANNOT_CREATE_NATIVE_AGENT:
      result = JNI_ERR;
      fprintf(stderr, "java.lang.instrument/-javaagent: cannot create native agent.\n");
      break;
    case JPLIS_INIT_ERROR_FAILURE:
      result = JNI_ERR;
      fprintf(stderr, "java.lang.instrument/-javaagent: initialization of native agent failed.\n");
      break;
    case JPLIS_INIT_ERROR_ALLOCATION_FAILURE:
      result = JNI_ERR;
      fprintf(stderr, "java.lang.instrument/-javaagent: allocation failure.\n");
      break;
    case JPLIS_INIT_ERROR_AGENT_CLASS_NOT_SPECIFIED:
      result = JNI_ERR;
      fprintf(stderr, "-javaagent: agent class not specified.\n");
      break;
    default:
      result = JNI_ERR;
      fprintf(stderr, "java.lang.instrument/-javaagent: unknown error\n");
      break;
    }
    return result;
}

 

      如果在运行期通过attach apiload agent,那么会在收到load指令之后,会调用InvocationAdapter.cAgent_OnAttach方法,其实现基本和Agent_OnLoad一致,只是还会调用Agent-Classagentmain方法,还有点不同就是对vmint事件没有再关注(都运行期了,关注也没用),而是直接对ClassFileLoad关注,也不会再调用Pre-Main指定的类的premain方法(顾名思义,是在执行main方法之前执行的,所以运行期搞执行Pre-Mainclass也不妥)。

JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM* vm, char *args, void * reserved) {
    JPLISInitializationError initerror  = JPLIS_INIT_ERROR_NONE;
    jint                     result     = JNI_OK;
    JPLISAgent *             agent      = NULL;
    JNIEnv *                 jni_env    = NULL;

    /*
     * Need JNIEnv - guaranteed to be called from thread that is already
     * attached to VM
     */
    result = (*vm)->GetEnv(vm, (void**)&jni_env, JNI_VERSION_1_2);
    jplis_assert(result==JNI_OK);

    initerror = createNewJPLISAgent(vm, &agent);
    if ( initerror == JPLIS_INIT_ERROR_NONE ) {
        int             oldLen, newLen;
        char *          jarfile;
        char *          options;
        jarAttribute*   attributes;
        char *          agentClass;
        char *          bootClassPath;
        jboolean        success;

        /*
         * Parse [=options] into jarfile and options
         */
        if (parseArgumentTail(args, &jarfile, &options) != 0) {
            return JNI_ENOMEM;
        }

        /*
         * Open the JAR file and parse the manifest
         */
        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 AGENT_ERROR_BADJAR;
        }

        agentClass = getAttribute(attributes, "Agent-Class");
        if (agentClass == NULL) {
            fprintf(stderr, "Failed to find Agent-Class manifest attribute from %s\n",
                jarfile);
            free(jarfile);
            if (options != NULL) free(options);
            freeAttributes(attributes);
            return AGENT_ERROR_BADJAR;
        }

        /*
         * Add the jarfile to the system class path
         */
        if (appendClassPath(agent, jarfile)) {
            fprintf(stderr, "Unable to add %s to system class path "
                "- not supported by system class loader or configuration error!\n",
                jarfile);
            free(jarfile);
            if (options != NULL) free(options);
            freeAttributes(attributes);
            return AGENT_ERROR_NOTONCP;
        }

        /*
         * The value of the Agent-Class attribute becomes the agent
         * class name. The manifest is in UTF8 so need to convert to
         * modified UTF8 (see JNI spec).
         */
        oldLen = strlen(agentClass);
        newLen = modifiedUtf8LengthOfUtf8(agentClass, oldLen);
        if (newLen == oldLen) {
            agentClass = strdup(agentClass);
        } else {
            char* str = (char*)malloc( newLen+1 );
            if (str != NULL) {
                convertUtf8ToModifiedUtf8(agentClass, oldLen, str, newLen);
            }
            agentClass = str;
        }
        if (agentClass == NULL) {
            free(jarfile);
            if (options != NULL) free(options);
            freeAttributes(attributes);
            return JNI_ENOMEM;
        }

        /*
         * If the Boot-Class-Path attribute is specified then we process
         * each URL - in the live phase only JAR files will be added.
         */
        bootClassPath = getAttribute(attributes, "Boot-Class-Path");
        if (bootClassPath != NULL) {
            appendBootClassPath(agent, jarfile, bootClassPath);
        }

        /*
         * Convert JAR attributes into agent capabilities
         */
        convertCapabilityAtrributes(attributes, agent);

        /*
         * Create the java.lang.instrument.Instrumentation instance
         */
        success = createInstrumentationImpl(jni_env, agent);
        jplis_assert(success);

        /*
         *  Turn on the ClassFileLoadHook.
         */
        if (success) {
            success = setLivePhaseEventHandlers(agent);
            jplis_assert(success);
        }

        /*
         * Start the agent
         */
        if (success) {
            success = startJavaAgent(agent,
                                     jni_env,
                                     agentClass,
                                     options,
                                     agent->mAgentmainCaller);
        }

        if (!success) {
            fprintf(stderr, "Agent failed to start!\n");
            result = AGENT_ERROR_STARTFAIL;
        }

        /*
         * Clean-up
         */
        free(jarfile);
        if (options != NULL) free(options);
        free(agentClass);
        freeAttributes(attributes);
    }

    return result;
}

 

你可能感兴趣的:(JVM)