`
nijiaben
  • 浏览: 130494 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

javaagent加载机制分析

    博客分类:
  • JVM
 
阅读更多

      在启动和运行期都可以加载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 <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 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 <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;
}

 

3
1
分享到:
评论
1 楼 zxjlwt 2015-11-18  
学习了。
http://surenpi.com

相关推荐

    深入探索Java Agent:机制解析与使用指南

    Java Agent是Java平台的一个强大特性,它允许开发者在运行时对JVM中的类进行操作,包括类的加载、修改和卸载。这种能力使得Java Agent成为实现各种高级功能的理想选择,如监控、性能分析、安全增强等。本文将详细...

    JavaAgent例子-agentmain方式

    JavaAgent是一种强大的技术,它允许我们在Java应用程序启动后或运行时动态地注入代码,从而实现对程序行为的监控、性能分析、安全控制等目的。在Java中,有两种启动JavaAgent的方式:premain和agentmain。本示例主要...

    JavaAgent例子-agentmain方式 demo

    JavaAgent技术是Java平台提供的一种增强程序功能的机制,它可以在程序启动或者运行时注入代码,实现对目标程序的行为监控、性能分析、日志记录等多种功能。本示例"JavaAgent-agentmain方式 demo"着重展示了如何使用...

    java项目实现热更源码-javaagent

    Java项目实现热更源码基于Java Agent...同时,了解JVM内存模型和类加载机制也是必不可少的。通过学习这些知识,你将能够创建一个高效且可靠的热更新解决方案,使Java应用程序能够在不停止服务的情况下进行升级和修复。

    基于Bytebuddy的Java Agent AOP框架.zip

    Java Agent是一种在JVM级别对程序进行...通过学习和实践,我们可以掌握如何使用ByteBuddy来创建高效、灵活的代理类,以及如何利用Java Agent实现字节码级别的方法拦截,从而提升我们的编程技巧和对Java底层机制的理解。

    Java Agent源码(带Jar包)

    在源码中,我们可能会看到如何注册和使用Java Agent的示例,这通常涉及到`-javaagent`命令行参数的使用,以及`Premain-Class`和`Agent-Class` manifest条目的配置。同时,源码可能包含如何利用Instrumentation API来...

    idea maven 搭建java agent项目,手把手教你实现方法耗时统计的java agent.zip

    使用`-javaagent`参数指定你的Agent jar路径,以及`premain`方法的参数,如果有的话。 7. **收集和展示数据**:在Agent中,你需要实现数据的收集和展示。这可能包括将统计信息记录到日志文件,或者通过Socket发送到...

    第二课:调用链核心技术-Agent采集埋点实现1

    4. 使用 `-javaagent` JVM 参数将 Agent 附加到目标应用程序。 例如,创建一个简单的 Agent 类 `TulingAgent`,并在 Maven 的 `maven-jar-plugin` 配置中添加 `Premain-Class` 参数。 **Javaassist 实现机制与使用...

    基于 javaagent 对 java 原生类的 方法进行字节码动态修改, 以此引发的一些关于 绕过 Java 软件.zip

    Javaagent 的核心是 `premain` 方法,它在 JVM 启动时调用,允许我们注册一个代理,这个代理可以在类加载到 JVM 之前对其进行修改。`Instrumentation` 接口是与 javaagent 交互的关键,它提供了添加、移除类文件转换...

    agent+javassist例子

    `Java Agent`是Java平台提供的一种机制,允许开发者在程序运行前或运行时对类进行修改。通常,Java Agent用于AOP(面向切面编程)或者性能监控,它可以通过`Premain`方法在JVM启动时注入代码,也可以通过`Agentmain`...

    java8集合源码分析-java-agent:基于java5Instrumentapi实现的mock框架

    java8 集合源码分析 1 介绍 1.1 用途 单元测试mock 联调、集成测试mock 支持mock静态方法,final方法,私有...理解java类加载机制、tomcat类加载机制 学习groovy语言 学习使用javassist增强字节码 了解dubbo消费端执

    Java字节代码学习(agent)1

    接着,Java Agent是Java提供的一个用于运行时代码增强的机制,它允许我们自定义类加载过程,特别是在`ClassLoader`的`defineClass`或`Instrumentation`的`redefineClass`方法执行时。`java.lang.instrument`包提供了...

    Java Instrumentation笔记

    - **启动**:当 JVM 启动时,会读取 `-javaagent` 参数并加载指定的 Agent JAR 文件。Agent 的 `premain` 方法会在应用程序的 `main` 方法之前被调用。 - **运行时附加**:除了启动时加载外,还可以通过 `attach` ...

    基于java_字节码的灰盒动态漏洞检测.pdf

    在启动时,通过`-javaagent`参数加载agent代理,而在运行期则可以通过Attach API动态加载。 在Java Agent中,`Instrumentation`接口提供了动态修改字节码的能力。Javassist是一个强大的开源库,它允许开发者无需...

    agent-3.0.38.jar.zip

    2. 在Java应用程序启动时,通过`-javaagent`参数指定代理库的位置,例如:`java -javaagent:path/to/agent-3.0.38.jar -jar your-application.jar`。 3. 根据库的文档配置相应的代理逻辑,这可能涉及到定义预加载类...

    测量Java对象大小的demo

    首先,`javaagent`是一个特殊类型的Java应用程序,它可以在目标应用程序启动时被加载,用于提供诸如预处理类、拦截方法调用等功能。在本案例中,`javaagent`被用来获取对象大小信息。 `Instrumentation`接口是Java...

    apache-skywalking-java-agent-8.13.0.tgz

    1. **类加载时拦截**:Java代理通过Java的Instrumentation接口,在类加载时进行字节码操作,添加监控逻辑。 2. **服务调用跟踪**:当应用执行方法时,代理会捕获并记录调用信息,形成追踪日志。 3. **数据上报**:...

    02-5的深入理解Java虚拟机书的学习-上篇

    2. **02_ClassLodingLinkingInitializing.md**:这部分内容可能涉及到Java的类加载机制,包括加载、链接(验证、准备、解析)和初始化三个阶段。理解这些过程对于优化类加载和解决类冲突问题至关重要。 3. **03_01_...

    JConsole(观察分析Java程序的运行)

    这对于了解类加载机制和潜在的内存泄漏问题非常有帮助。 综上所述,JConsole是一个功能强大的工具,能够帮助开发人员和运维人员更好地理解和优化Java应用程序的性能。通过对上述各个标签页的深入研究,可以提高对...

Global site tag (gtag.js) - Google Analytics