Copy from : http://nijiaben.iteye.com/blog/1847212
在啟動和運行期都可以加載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 <jarfile>[=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 <jarfile>[=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;
- }