Realization of a simple JavaAgent

First, what is javaagent

javaagent is a JVM "plug-in", a specially crafted .jar file, it can take advantage Instrumentation API JVM provides.

1.1, Overview

Java Agent consists of three parts: the proxy class, the meta information and proxy class loading mechanisms and .jar JVM agent, the entire contents as shown below:
Here Insert Picture Description

1.2, the cornerstone javaagent

java.lang.instrumentBy modifying the method of javaagent manner bytecode program running on the JVM services. javaagent deployed as a JAR package, specify the attributes to be loaded proxy class JAR manifest file to start the agent.

javaagent start the following ways:

  • Start by specifying parameters on the command line.

  • Start JVM started. For example, to provide a tool that can be attached to the application already running, and allows loading agents in the application has been running.

  • Packaged as an executable file with the application.

1.3, start javaagent

1.3.1, the command line to start

Command line to start the following parameters:

-javaagent:<jarpath>[=<options>]

<jarpath>: Javaagent path, for example /opt/var/Agent-1.0.0.jar.
<options>: Javaagent parameters, analytical parameters responsible javaagent.
javaagent JAR manifest file must contain Premain-Classattributes, the full path name of the agent class attribute value (package name + class name). Agent class must implement premainmethods, premainmethods, and mainmethods are the same as the entry point and the application proxy. First, call the agent premain function after JVM initialization is complete, and then call the main function of the application, after premain method must return the process to start.

premain Method signature as follows:

public static void premain(String agentArgs, Instrumentation inst)
public static void premain(String agentArgs)

JVM first attempts to call the method signature in the proxy, if the agent class does not implement the method signature, signature JVM attempts to invoke a method 2:

Acting classes can have a agentmainfunction, the function call will start after the completion of the JVM. If you use the command line to start the agent,agentmain the way will not be called.

All parameters are treated as agents by a string agentArgsvariable transmission, the agent responsible for parsing the parameter string.

If the agent because the agent class can not be loaded, the agent class does not implement premainthe method throws an exception or uncaught, JVM will exit.

javaagent start implementation is not required to provide a certain command line, if achieved support from the command line to start, implementation must support the command line by specifying the -javaagentstartup parameters. -javaagentCan be used multiple times on the command line, start multiple agents. premainCalling sequence and functions in the order specified in the command line is consistent, the same plurality of agents may be used <jarpath>.

Not a strict model to define premainthe scope of work function, any mainfunction can do the job, such as creating a thread, the premainfunction is legal.

1.3.2, the JVM startup boot

Implementation may provide a mechanism to re-start the agent after JVM startup. How to start the agent implementation-specific details, usually the application has been launched, and its mainmethods have been called. If the implementation supports boot agent JVM started, the agent must meet the following criteria:

  • The manifest file contains Agent-Classattributes, attribute is the full name of the proxy class.

  • Agent class must implement public static agentmainmethod.

agentmain signature method has the following two functions:

public static void agentmain(String agentArgs, Instrumentation inst)
public static void agentmain(String agentArgs)

JVM first attempts to call the method signature has 1, if the agent class does not implement this method, JVM attempts to invoke the method signature 2.

Acting classes can be achieved simultaneously premainand agentmaintwo methods, when the agent starts the command line, JVM calls the premainfunction, when the agent started after the start JVM, JVM calls the agentmainfunction, and JVM does not call the premainfunction.

agentmainAlso through the transfer function parameters agentArgs, all parameters are combined into one string, the agent responsible for the resolution parameters.

agentmainFunction must complete all the necessary Boot Agent initialization operation, when the startup is completed, agentmainthe function must return. If the agent can not be started or throw an uncaught exception, JVM will exit.

1.3.3, packaged as an executable file

If the agent is packaged into an executable JAR file, a list of executable JAR file must contain the Launcher-Agent-Classattribute that specifies a proxy in the application before the start of the main function calls the class. JVM attempts to invoke the method on the proxy:

public static void agentmain(String agentArgs, Instrumentation inst)

If the agent class does not implement the above method, JVM following method is called.

public static void agentmain(String agentArgs)

agentArgs Parameter must be the empty string.

agentmainThe initialization function must complete all actions the agent must start and return after startup. If the agent fails to start or throws an uncaught exception, JVM will exit.

1.3.4, loading module agent class and available proxy class / class

System class loader is responsible for loading all the classes agent JAR file, and became a member of the unnamed module system class loader. The system also typically defined class loader application contains mainclass methods. All classes of proxy classes are visible to the system class loader visible, you must meet the following minimum requirements:

  • Start layer module derived classes in the package. Starting layer contains all the initial start-up mode depending on the platform module or application module.

  • Class may be defined as the system class loader.

  • All proxy classes start class loader module defined its members unnamed.

If you need to link to a proxy class layer platform (or other) module class does not start, you need to ensure that these modules are located in layers starting way to start the application. For example, the JDK implementation, --add-modulescommand-line option can be used to add modules to be resolved at the start concentrated root module.

Start class loader can load proxy support classes (by appendToBootstrapClassLoaderSearchor designated Boot-Class-Path attribute) must only link to the custom boot class loader class. We can not guarantee the boot class loader can work on all platforms.

If the custom system class loader (configured by getSystemClassLoaderthe specified method system properties java.system.class.loader), must be defined appendToSystemClassLoaderSearchin the specified appendToClassPathForInstrumentationmethod. In other words, the custom class loader system must support the addition of the agent JAR files to the system class loader mechanism within the search range.

1.4, javaagent List Properties

Attributes Explanation Are Required Defaults
Premain-Class Premain class method comprising Start-dependent manner no
Agent-Class The method comprising a class agentmain Start-dependent manner no
Boot-Class-Path Start class loader search path no no
Can-Redefine-Classis Can I re-define the required proxy class no false
Can-Retransform-Classis You are able to re-convert the classes needed by this agent no false
Can-Set-Native-Method-Prefix This agent can be provided if desired native method prefix no false

Second, write a Java Agent

Based on the above description, we achieve a javaagent JVM all non-system classes download.

Throughout the development process includes the following three steps:

  • 1) the definition of the proxy class, the implementation class download;

  • 2) configuration package;

  • 3) command line to start the test.

2.1, the proxy class implements

Implement premainfunction

package io.ct.java.agent;

import java.lang.instrument.Instrumentation;

public class AgentApplication {
    public static void premain(String arg, Instrumentation instrumentation) {
        System.err.println("agent startup , args is " + arg);
        // 注册我们的文件下载函数
        instrumentation.addTransformer(new DumpClassesService());
    }
}

Download class implements ClassFileTransformerthe interface, when the class is loaded downloaded bytecode classes:

package io.ct.java.agent;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.List;

/**
 * Copyright (C), 2018-2018, open source
 * FileName: DumpClassesService
 *
 * @author : 大哥
 * Date:     2018/12/8 21:01
 */
public class DumpClassesService implements ClassFileTransformer {
    private static final List<String> SYSTEM_CLASS_PREFIX = Arrays.asList("java", "sum", "jdk");

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        if (!isSystemClass(className)) {
            System.out.println("load class " + className);
            FileOutputStream fos = null;
            try {
                // 将类名统一命名为classNamedump.class格式
                fos = new FileOutputStream(className + "dump.class");
                fos.write(classfileBuffer);
                fos.flush();
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } finally {
                // 关闭文件输出流
                if (null != fos) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return classfileBuffer;
    }

    /**
     * 判断一个类是否为系统类
     *
     * @param className 类名
     * @return System Class then return true,else return false
     */
    private boolean isSystemClass(String className) {
        // 假设系统类的类名不为NULL而且不为空
        if (null == className || className.isEmpty()) {
            return false;
        }

        for (String prefix : SYSTEM_CLASS_PREFIX) {
            if (className.startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }
}

2.2, configuration MANIFEST.MF

MANIFEST.MFFile generated in two ways: manual and automatic configuration generation, manual configuration only need to resourcescreate the next file META-INF/MENIFEST.MFfile. Manually removing the outer configuration can be automatically generated using maven plug packing stage, maven plugin configuration as it follows:

             <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>io.ct.java.agent.AgentApplication</Premain-Class>
                            <Agent-Class>io.ct.java.agent.AgentApplication</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>

Generating a jar following format:
Here Insert Picture Description
wherein MANIFEST.MF file follows (generating a different configuration content file is not exactly):

Manifest-Version: 1.0
Implementation-Title: agent
Premain-Class: io.ct.java.agent.AgentApplication
Implementation-Version: 0.0.1-SNAPSHOT
Built-By: chentong
Agent-Class: io.ct.java.agent.AgentApplication
Can-Redefine-Classes: true
Implementation-Vendor-Id: io.ct.java
Can-Retransform-Classes: true
Created-By: Apache Maven 3.5.4
Build-Jdk: 1.8.0_171
Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo
 ot-starter-parent/agent

2.3, the command line to start Java Agent

Execute the following command to run the already compiled class Hello, can generate a file named Hellodump.class in the same directory.

java -javaagent:agent-0.0.1-SNAPSHOT.jar Hello
Published 100 original articles · won praise 45 · views 640 000 +

Guess you like

Origin blog.csdn.net/wangzhongshun/article/details/100287986