Validating Bytecode Enhancement and SpyAPI Integration in Arthas
This test suite validates the bytecode enhancement functionality in Arthas, focusing on the Enhancer class that handles method instrumentation and SpyAPI integration. It verifies the correct insertion of monitoring points and ensures consistent bytecode transformation across multiple passes.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
alibaba/arthas
core/src/test/java/com/taobao/arthas/core/advisor/EnhancerTest.java
package com.taobao.arthas.core.advisor;
import java.arthas.SpyAPI;
import java.lang.instrument.Instrumentation;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.mockito.Mockito;
import com.alibaba.bytekit.utils.AsmUtils;
import com.alibaba.bytekit.utils.Decompiler;
import com.alibaba.deps.org.objectweb.asm.Type;
import com.alibaba.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.deps.org.objectweb.asm.tree.MethodNode;
import com.taobao.arthas.core.bytecode.TestHelper;
import com.taobao.arthas.core.server.ArthasBootstrap;
import com.taobao.arthas.core.util.matcher.EqualsMatcher;
import demo.MathGame;
import net.bytebuddy.agent.ByteBuddyAgent;
/**
*
* @author hengyunabc 2020-05-19
*
*/
public class EnhancerTest {
@Test
public void test() throws Throwable {
Instrumentation instrumentation = ByteBuddyAgent.install();
TestHelper.appendSpyJar(instrumentation);
ArthasBootstrap.getInstance(instrumentation, "ip=127.0.0.1");
AdviceListener listener = Mockito.mock(AdviceListener.class);
EqualsMatcher<String> methodNameMatcher = new EqualsMatcher<String>("print");
EqualsMatcher<String> classNameMatcher = new EqualsMatcher<String>(MathGame.class.getName());
Enhancer enhancer = new Enhancer(listener, true, false, classNameMatcher, null, methodNameMatcher);
ClassLoader inClassLoader = MathGame.class.getClassLoader();
String className = MathGame.class.getName();
Class<?> classBeingRedefined = MathGame.class;
ClassNode classNode = AsmUtils.loadClass(MathGame.class);
byte[] classfileBuffer = AsmUtils.toBytes(classNode);
byte[] result = enhancer.transform(inClassLoader, className, classBeingRedefined, null, classfileBuffer);
ClassNode resultClassNode1 = AsmUtils.toClassNode(result);
// FileUtils.writeByteArrayToFile(new File("/tmp/MathGame1.class"), result);
result = enhancer.transform(inClassLoader, className, classBeingRedefined, null, result);
ClassNode resultClassNode2 = AsmUtils.toClassNode(result);
// FileUtils.writeByteArrayToFile(new File("/tmp/MathGame2.class"), result);
MethodNode resultMethodNode1 = AsmUtils.findMethods(resultClassNode1.methods, "print").get(0);
MethodNode resultMethodNode2 = AsmUtils.findMethods(resultClassNode2.methods, "print").get(0);
Assertions
.assertThat(AsmUtils
.findMethodInsnNode(resultMethodNode1, Type.getInternalName(SpyAPI.class), "atEnter").size())
.isEqualTo(AsmUtils.findMethodInsnNode(resultMethodNode2, Type.getInternalName(SpyAPI.class), "atEnter")
.size());
Assertions.assertThat(AsmUtils
.findMethodInsnNode(resultMethodNode1, Type.getInternalName(SpyAPI.class), "atExceptionExit").size())
.isEqualTo(AsmUtils
.findMethodInsnNode(resultMethodNode2, Type.getInternalName(SpyAPI.class), "atExceptionExit")
.size());
Assertions.assertThat(AsmUtils
.findMethodInsnNode(resultMethodNode1, Type.getInternalName(SpyAPI.class), "atBeforeInvoke").size())
.isEqualTo(AsmUtils
.findMethodInsnNode(resultMethodNode2, Type.getInternalName(SpyAPI.class), "atBeforeInvoke")
.size());
Assertions.assertThat(AsmUtils
.findMethodInsnNode(resultMethodNode1, Type.getInternalName(SpyAPI.class), "atInvokeException").size())
.isEqualTo(AsmUtils
.findMethodInsnNode(resultMethodNode2, Type.getInternalName(SpyAPI.class), "atInvokeException")
.size());
String string = Decompiler.decompile(result);
System.err.println(string);
}
}