首页 > 代码库 > javaagent加载机制分析
javaagent加载机制分析
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方法。
C代码
- 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也不妥)。
C代码
- 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;
- }
javaagent加载机制分析
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。