Java Agent-based Full Link Monitoring II "Increasing Monitoring Execution Time by Bytecode"

Keywords: Java Maven jvm git

Chapter List | Focus on Wechat Public Number, bugstack wormhole stack, reply to <Java Agent-based Full Link Monitoring> Get Source Code

  • Java Agent-based full-link monitoring Java Agent
  • Java Agent-based Full Link Monitoring II "Increasing Monitoring Execution Time by Bytecode"
  • Java Agent-based Full Link Monitoring Three ByteBuddy Operational Monitoring Method Bytecode
  • Java Agent-based Full Link Monitoring IV "JVM Memory and GC Information"
  • Java Agent-based Full Link Monitoring Five "ThreadLocal Link Tracking"
  • Java Agent-based Full Link Monitoring VI "Development and Application Level Monitoring"

Brief description of cases
Through the introduction of the previous chapter, "Hi! java Agent, we already know that by configuring - javaagent: file. jar, the premain method is executed when the java program starts. Next, we use javassist bytecode enhancement to monitor the time-consuming execution of method programs.

Javassist is an open source class library for analyzing, editing, and creating Java bytecodes. It was founded by Shigeru Chiba, Department of Mathematics and Computer Science, Tokyo University of Technology. It has joined the open source JBoss application server project to implement a dynamic "AOP" framework for JBoss by using Javassist to manipulate bytecodes.

There are many tools for the processing of java bytecode, such as bcel and asm. However, all of these require dealing directly with virtual machine instructions. If you don't want to understand the virtual machine instructions, you can use javassist. Javassist is a sub-project of jboss. Its main advantage is that it is simple and fast. It can dynamically change the structure of classes or generate classes directly in the form of java encoding without understanding the virtual machine instructions.

Environmental preparation
1,IntelliJ IDEA Community Edition
2. jdk1.8.0_4564 bits

Configuration information (path-related modifications for your own)
1. Configuration location: Run/Debug Configurations - > VM options
2. Configuration: - javaagent: E: itstack GIT itstack. org itstack - Demo - agent itstack - Demo - agent - 02 target itstack - Demo - agent - 02 - 1.0.0 - SNAPSHOT. jar = args

Code examples

itstack-demo-agent-02
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── org.itstack.demo.agent
    │   │       ├── MyAgent.java
    │   │	    └── MyMonitorTransformer.java
    │	└── resources
    │       └── META-INF
    │           └── MANIFEST.MF 	
    └── test
         └── java
             └── org.itstack.demo.test
                 └── ApiTest.java

pom.xml (introducing javassist and entering into Agent package)

 <properties>
        <!-- Build args -->
	<argline>-Xms512m -Xmx512m</argline>
	<skip_maven_deploy>false</skip_maven_deploy>
	<updateReleaseInfo>true</updateReleaseInfo>
	<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
	<maven.test.skip>true</maven.test.skip>
	<!-- custom MANIFEST.MF -->
	<maven.configuration.manifestFile>src/main/resources/META-INF/MANIFEST.MF</maven.configuration.manifestFile>
</properties>

<dependencies>
	<dependency>
		<groupId>javassist</groupId>
		<artifactId>javassist</artifactId>
		<version>3.12.1.GA</version>
		<type>jar</type>
	</dependency>
</dependencies>

<!-- take javassist Packing to Agent in -->
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>shade</goal>
			</goals>
		</execution>
	</executions>
	<configuration>
		<artifactSet>
			<includes>
				<include>javassist:javassist:jar:</include>
			</includes>
		</artifactSet>
	</configuration>
</plugin>            

MyAgent.java

/**
 * Blog: http://itstack.org
 * Forum: http://bugstack.cn
 * Public number: bugstack wormhole stack
 * create by fuzhengwei on 2019
 */
public class MyAgent {

    //The JVM first attempts to invoke the following method on the proxy class
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("this is my agent: " + agentArgs);
        MyMonitorTransformer monitor = new MyMonitorTransformer();
        inst.addTransformer(monitor);
    }

    //If the proxy class does not implement the above method, the JVM will attempt to invoke the method
    public static void premain(String agentArgs) {
    }

}

MyMonitorTransformer.java

/**
 * Blog: http://itstack.org
 * Forum: http://bugstack.cn
 * Public number: bugstack wormhole stack
 * create by fuzhengwei on 2019
 */
public class MyMonitorTransformer implements ClassFileTransformer {

    private static final Set<String> classNameSet = new HashSet<>();

    static {
        classNameSet.add("org.itstack.demo.test.ApiTest");
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        try {
            String currentClassName = className.replaceAll("/", ".");
            if (!classNameSet.contains(currentClassName)) { // Promote the classes contained in the classNameSet
                return null;
            }
            System.out.println("transform: [" + currentClassName + "]");

            CtClass ctClass = ClassPool.getDefault().get(currentClassName);
            CtBehavior[] methods = ctClass.getDeclaredBehaviors();
            for (CtBehavior method : methods) {
                enhanceMethod(method);
            }
            return ctClass.toBytecode();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return null;

    }


    private void enhanceMethod(CtBehavior method) throws Exception {
        if (method.isEmpty()) {
            return;
        }
        String methodName = method.getName();
        if ("main".equalsIgnoreCase(methodName)) {
            return;
        }

        final StringBuilder source = new StringBuilder();
        // Pre-Enhancement: Input Timestamp
        // Keep the original code processing logic
        source.append("{")
                .append("long start = System.nanoTime();\n") //Pre-Enhancement: Input Timestamp
                .append("$_ = $proceed($$);\n")              //Call the original code, similar to method(); ($$) represents all the parameters
                .append("System.out.print(\"method:[")
                .append(methodName).append("]\");").append("\n")
                .append("System.out.println(\" cost:[\" +(System.nanoTime() - start)+ \"ns]\");") // Post-enhancement, computational output method execution time-consuming
                .append("}");

        ExprEditor editor = new ExprEditor() {
            @Override
            public void edit(MethodCall methodCall) throws CannotCompileException {
                methodCall.replace(source.toString());
            }
        };
        method.instrument(editor);
    }

}

MANIFEST.MF

Manifest-Version: 1.0
Premain-Class: org.itstack.demo.agent.MyAgent
Can-Redefine-Classes: true

ApiTest.java

/**
 * Blog: http://itstack.org
 * Forum: http://bugstack.cn
 * Public number: bugstack wormhole stack
 * create by fuzhengwei on 2019
 *
 * VM options: 
 * -javaagent:E:\itstack\GIT\itstack.org\itstack-demo-agent\itstack-demo-agent-02\target\itstack-demo-agent-02-1.0.0-SNAPSHOT.jar=testargs
 *
 */
public class ApiTest {

    public static void main(String[] args) {
        ApiTest apiTest = new ApiTest();
        apiTest.echoHi();
    }

    private void echoHi(){
        System.out.println("hi agent");
    }

}

test result

this is my agent: testargs
transform: [org.itstack.demo.test.ApiTest]
hi agent
method:[echoHi] cost:[294845ns]

Focus on bugstack wormhole stack public number for source code

Posted by ali_p on Fri, 23 Aug 2019 04:48:11 -0700