Java Agent Internals: Building Non-Invasive Profiling with Instrumentation & ASM

This article demonstrates how to build a Java Agent using the Instrumentation API and ASM for bytecode manipulation, covering premain/agentmain entry points, dynamic class redefinition via Attach API, and explores Arthas trace command's implementation using the bytekit framework for non-invasive method profiling.

SpringMeng
SpringMeng
SpringMeng
Java Agent Internals: Building Non-Invasive Profiling with Instrumentation & ASM

The article begins with a practical problem: developers manually inserting StopWatch calls to measure method latency, which is invasive and clutters code. It introduces Java Agent technology as a non-invasive solution via the java.lang.instrument package (since JDK 1.5).

Instrumentation API

The Instrumentation interface provides methods to register ClassFileTransformer instances, retransform classes, and query loaded classes. A transformer's transform method receives the raw class bytes and returns modified bytes, enabling bytecode enhancement at load time.

Two Agent Deployment Modes

Startup Agent : Specify -javaagent:agent.jar at JVM launch. The agent's premain(String agentArgs, Instrumentation inst) runs before main().

Attach API : Dynamically load an agent into a running JVM via VirtualMachine.attach(pid).loadAgent(agentPath). The agent's agentmain(String agentArgs, Instrumentation inst) executes after attachment, allowing runtime class redefinition with retransformClasses().

Startup Agent Example: Method Timing

A demo agent instruments com.example.aop.agent.MyTest using ASM (version 7). The transformer ( MyClassFileTransformer) checks the class name, then uses a ClassVisitor chain: TimeStatisticsVisitor → TimeStatisticsAdapter (extends AdviceAdapter). On method enter, it calls TimeStatistics.start(); on exit, TimeStatistics.end() prints elapsed time via a ThreadLocal timestamp.

public class TimeStatisticsAdapter extends AdviceAdapter {
    protected TimeStatisticsAdapter(int api, MethodVisitor mv, int access, String name, String descriptor) {
        super(api, mv, access, name, descriptor);
    }
    @Override
    protected void onMethodEnter() {
        super.visitMethodInsn(INVOKESTATIC, "com/example/aop/agent/TimeStatistics", "start", "()V", false);
        super.onMethodEnter();
    }
    @Override
    protected void onMethodExit(int opcode) {
        super.onMethodExit(opcode);
        super.visitMethodInsn(INVOKESTATIC, "com/example/aop/agent/TimeStatistics", "end", "()V", false);
    }
}

The agent JAR is built with maven-assembly-plugin (jar-with-dependencies) and manifest entries: Premain-Class, Can-Redefine-Classes=true, Can-Retransform-Classes=true. Execution:

java -javaagent:.../aop-0.0.1-SNAPSHOT-jar-with-dependencies.jar com.example.aop.agent.MyTest

.

Attach API Example: Dynamic Constant Change

A running PrintNumTest loops printing getNum() (returns 100). An attach agent ( PrintNumAgent) uses agentmain to register a transformer ( PrintNumTransformer) that rewrites getNum to return 50 via ASM: TransformPrintNumAdapter.onMethodEnter emits BIPUSH 50 and IRETURN. The agent iterates inst.getAllLoadedClasses(), finds PrintNumTest, and calls retransformClasses(). The attach client ( MyAttachMain) loads the agent JAR into the target PID (found via jps). Command:

java -cp $JAVA_HOME/lib/tools.jar:agent.jar com.example.aop.agent.MyAttachMain <pid>

. Output switches from 100 to 50.

Arthas Trace Command & bytekit

The article then examines Alibaba Arthas's trace command, which uses a similar agent mechanism. It first introduces bytekit , a higher-level API over ASM that simplifies bytecode enhancement via annotations: @AtEnter, @AtExit, @AtExceptionExit with @Binding parameters (this, class, args, method name, return value, etc.). A SampleInterceptor demonstrates inline vs. call insertion, exception suppression, and field binding.

Bytekit processes interceptors via DefaultInterceptorClassParser → InterceptorProcessor list, then applies them to target methods using MethodProcessor. The decompiled result shows the original hello(String, boolean) method wrapped in try/catch blocks with inserted calls to SampleInterceptor.atEnter, atExit, and atExceptionExit.

Arthas Trace Implementation Walkthrough

Arthas commands follow a template pattern: AnnotatedCommand.process() → EnhancerCommand.enhance() → Enhancer.enhance(Instrumentation). The core transformer ( Enhancer.transform) performs:

ClassLoader check: ensure SpyAPI is loadable (skip if not).

Re-filter matching classes (new classes may appear during transform).

Parse class bytes into ASM ClassNode (ASM9), remove JSR instructions (issue #1304).

Match target methods via methodNameMatcher, skip native methods, handle CGLIB-generated constructors (issue #1690).

For each matched method, if already enhanced (contains SpyAPI.atBeforeInvoke), register trace listeners for invoked methods; otherwise, run each InterceptorProcessor to modify bytecode, then register enter/exit listeners via AdviceListenerManager.

Adjust class version to at least 49 (Java 5) if older (issue #1223).

Write enhanced bytes, cache, dump if needed, return byte array.

An example interceptor SpyInterceptor1 shows @AtEnter(inline=true) delegating to SpyAPI.atEnter. The article concludes that bytekit's annotation-driven model mirrors the manual ASM approach but with less boilerplate, and notes that deeper bytekit internals require further study.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Performance ProfilingJava AgentArthasASMBytecode ManipulationAttach APIbytekitInstrumentation API
SpringMeng
Written by

SpringMeng

Focused on software development, sharing source code and tutorials for various systems.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.