在启动和运行期都可以加载agent代理,在启动的时候可通过-javaagent参数来执行agent代理,而在运行期就是通过attach这种机制动态load了。
如果在vm启动过程中加载agent,那么会在vm初始化过程中先执行libinstrument.dylib里InvocationAdapter.c的Agent_OnLoad方法,这里主要是实例化agent,解析agent的MF文件,将相关属性取出来,并注册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 api来load agent,那么会在收到load指令之后,会调用InvocationAdapter.c的Agent_OnAttach方法,其实现基本和Agent_OnLoad一致,只是还会调用Agent-Class的agentmain方法,还有点不同就是对vmint事件没有再关注(都运行期了,关注也没用),而是直接对ClassFileLoad关注,也不会再调用Pre-Main指定的类的premain方法(顾名思义,是在执行main方法之前执行的,所以运行期搞执行Pre-Main的class也不妥)。
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; }