com.taobao.arthas.grpcweb.grpc.service.advisor.Enhancer Maven / Gradle / Ivy
The newest version!
package com.taobao.arthas.grpcweb.grpc.service.advisor;
import com.alibaba.arthas.deps.org.slf4j.Logger;
import com.alibaba.arthas.deps.org.slf4j.LoggerFactory;
import com.alibaba.bytekit.asm.MethodProcessor;
import com.alibaba.bytekit.asm.interceptor.InterceptorProcessor;
import com.alibaba.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser;
import com.alibaba.bytekit.asm.location.Location;
import com.alibaba.bytekit.asm.location.LocationType;
import com.alibaba.bytekit.asm.location.MethodInsnNodeWare;
import com.alibaba.bytekit.asm.location.filter.GroupLocationFilter;
import com.alibaba.bytekit.asm.location.filter.InvokeCheckLocationFilter;
import com.alibaba.bytekit.asm.location.filter.InvokeContainLocationFilter;
import com.alibaba.bytekit.asm.location.filter.LocationFilter;
import com.alibaba.bytekit.utils.AsmOpUtils;
import com.alibaba.bytekit.utils.AsmUtils;
import com.alibaba.deps.org.objectweb.asm.ClassReader;
import com.alibaba.deps.org.objectweb.asm.Opcodes;
import com.alibaba.deps.org.objectweb.asm.Type;
import com.alibaba.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.deps.org.objectweb.asm.tree.MethodNode;
import com.taobao.arthas.common.Pair;
import com.taobao.arthas.core.GlobalOptions;
import com.taobao.arthas.core.advisor.AdviceListener;
import com.taobao.arthas.core.advisor.SpyInterceptors.*;
import com.taobao.arthas.core.util.ArthasCheckUtils;
import com.taobao.arthas.core.util.ClassUtils;
import com.taobao.arthas.core.util.FileUtils;
import com.taobao.arthas.core.util.SearchUtils;
import com.taobao.arthas.core.util.affect.EnhancerAffect;
import com.taobao.arthas.core.util.matcher.Matcher;
import com.taobao.arthas.grpcweb.grpc.DemoBootstrap;
import java.arthas.SpyAPI;
import java.io.File;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.*;
import static com.taobao.arthas.core.util.ArthasCheckUtils.isEquals;
import static java.lang.System.arraycopy;
/**
* 对类进行通知增强 Created by vlinux on 15/5/17.
* @author hengyunabc
*/
public class Enhancer implements ClassFileTransformer {
private static final Logger logger = LoggerFactory.getLogger(Enhancer.class);
private final AdviceListener listener;
private final boolean isTracing;
private final boolean skipJDKTrace;
private final Matcher classNameMatcher;
private final Matcher classNameExcludeMatcher;
private final Matcher methodNameMatcher;
private final EnhancerAffect affect;
private Set> matchingClasses = null;
private static final ClassLoader selfClassLoader = Enhancer.class.getClassLoader();
// 被增强的类的缓存
private final static Map/* Class */, Object> classBytesCache = new WeakHashMap, Object>();
private static SpyImpl spyImpl = new SpyImpl();
static {
SpyAPI.setSpy(spyImpl);
}
/**
* @param adviceId 通知编号
* @param isTracing 可跟踪方法调用
* @param skipJDKTrace 是否忽略对JDK内部方法的跟踪
* @param matchingClasses 匹配中的类
* @param methodNameMatcher 方法名匹配
* @param affect 影响统计
*/
public Enhancer(AdviceListener listener, boolean isTracing, boolean skipJDKTrace, Matcher classNameMatcher,
Matcher classNameExcludeMatcher,
Matcher methodNameMatcher) {
this.listener = listener;
this.isTracing = isTracing;
this.skipJDKTrace = skipJDKTrace;
this.classNameMatcher = classNameMatcher;
this.classNameExcludeMatcher = classNameExcludeMatcher;
this.methodNameMatcher = methodNameMatcher;
this.affect = new EnhancerAffect();
affect.setListenerId(listener.id());
}
@Override
public byte[] transform(final ClassLoader inClassLoader, String className, Class> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
try {
// 检查classloader能否加载到 SpyAPI,如果不能,则放弃增强
try {
if (inClassLoader != null) {
inClassLoader.loadClass(SpyAPI.class.getName());
}
} catch (Throwable e) {
logger.error("the classloader can not load SpyAPI, ignore it. classloader: {}, className: {}",
inClassLoader.getClass().getName(), className, e);
return null;
}
// 这里要再次过滤一次,为啥?因为在transform的过程中,有可能还会再诞生新的类
// 所以需要将之前需要转换的类集合传递下来,再次进行判断
if (matchingClasses != null && !matchingClasses.contains(classBeingRedefined)) {
return null;
}
//keep origin class reader for bytecode optimizations, avoiding JVM metaspace OOM.
ClassNode classNode = new ClassNode(Opcodes.ASM9);
ClassReader classReader = AsmUtils.toClassNode(classfileBuffer, classNode);
// remove JSR https://github.com/alibaba/arthas/issues/1304
classNode = AsmUtils.removeJSRInstructions(classNode);
// 生成增强字节码
DefaultInterceptorClassParser defaultInterceptorClassParser = new DefaultInterceptorClassParser();
final List interceptorProcessors = new ArrayList();
interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyInterceptor1.class));
interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyInterceptor2.class));
interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyInterceptor3.class));
if (this.isTracing) {
if (!this.skipJDKTrace) {
interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyTraceInterceptor1.class));
interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyTraceInterceptor2.class));
interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyTraceInterceptor3.class));
} else {
interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyTraceExcludeJDKInterceptor1.class));
interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyTraceExcludeJDKInterceptor2.class));
interceptorProcessors.addAll(defaultInterceptorClassParser.parse(SpyTraceExcludeJDKInterceptor3.class));
}
}
List matchedMethods = new ArrayList();
for (MethodNode methodNode : classNode.methods) {
if (!isIgnore(methodNode, methodNameMatcher)) {
matchedMethods.add(methodNode);
}
}
// https://github.com/alibaba/arthas/issues/1690
if (AsmUtils.isEnhancerByCGLIB(className)) {
for (MethodNode methodNode : matchedMethods) {
if (AsmUtils.isConstructor(methodNode)) {
AsmUtils.fixConstructorExceptionTable(methodNode);
}
}
}
// 用于检查是否已插入了 spy函数,如果已有则不重复处理
GroupLocationFilter groupLocationFilter = new GroupLocationFilter();
LocationFilter enterFilter = new InvokeContainLocationFilter(Type.getInternalName(SpyAPI.class), "atEnter",
LocationType.ENTER);
LocationFilter existFilter = new InvokeContainLocationFilter(Type.getInternalName(SpyAPI.class), "atExit",
LocationType.EXIT);
LocationFilter exceptionFilter = new InvokeContainLocationFilter(Type.getInternalName(SpyAPI.class),
"atExceptionExit", LocationType.EXCEPTION_EXIT);
groupLocationFilter.addFilter(enterFilter);
groupLocationFilter.addFilter(existFilter);
groupLocationFilter.addFilter(exceptionFilter);
LocationFilter invokeBeforeFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class),
"atBeforeInvoke", LocationType.INVOKE);
LocationFilter invokeAfterFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class),
"atInvokeException", LocationType.INVOKE_COMPLETED);
LocationFilter invokeExceptionFilter = new InvokeCheckLocationFilter(Type.getInternalName(SpyAPI.class),
"atInvokeException", LocationType.INVOKE_EXCEPTION_EXIT);
groupLocationFilter.addFilter(invokeBeforeFilter);
groupLocationFilter.addFilter(invokeAfterFilter);
groupLocationFilter.addFilter(invokeExceptionFilter);
for (MethodNode methodNode : matchedMethods) {
if (AsmUtils.isNative(methodNode)) {
logger.info("ignore native method: {}",
AsmUtils.methodDeclaration(Type.getObjectType(classNode.name), methodNode));
continue;
}
// 先查找是否有 atBeforeInvoke 函数,如果有,则说明已经有trace了,则直接不再尝试增强,直接插入 listener
if(AsmUtils.containsMethodInsnNode(methodNode, Type.getInternalName(SpyAPI.class), "atBeforeInvoke")) {
for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode
.getNext()) {
if (insnNode instanceof MethodInsnNode) {
final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if(this.skipJDKTrace) {
if(methodInsnNode.owner.startsWith("java/")) {
continue;
}
}
// 原始类型的box类型相关的都跳过
if(AsmOpUtils.isBoxType(Type.getObjectType(methodInsnNode.owner))) {
continue;
}
AdviceListenerManager.registerTraceAdviceListener(inClassLoader, className,
methodInsnNode.owner, methodInsnNode.name, methodInsnNode.desc, listener);
}
}
}else {
MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode, groupLocationFilter);
for (InterceptorProcessor interceptor : interceptorProcessors) {
try {
List locations = interceptor.process(methodProcessor);
for (Location location : locations) {
if (location instanceof MethodInsnNodeWare) {
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
AdviceListenerManager.registerTraceAdviceListener(inClassLoader, className,
methodInsnNode.owner, methodInsnNode.name, methodInsnNode.desc, listener);
}
}
} catch (Throwable e) {
logger.error("enhancer error, class: {}, method: {}, interceptor: {}", classNode.name, methodNode.name, interceptor.getClass().getName(), e);
}
}
}
// enter/exist 总是要插入 listener
AdviceListenerManager.registerAdviceListener(inClassLoader, className, methodNode.name, methodNode.desc,
listener);
affect.addMethodAndCount(inClassLoader, className, methodNode.name, methodNode.desc);
}
// https://github.com/alibaba/arthas/issues/1223 , V1_5 的major version是49
if (AsmUtils.getMajorVersion(classNode.version) < 49) {
classNode.version = AsmUtils.setMajorVersion(classNode.version, 49);
}
byte[] enhanceClassByteArray = AsmUtils.toBytes(classNode, inClassLoader, classReader);
// 增强成功,记录类
classBytesCache.put(classBeingRedefined, new Object());
// dump the class
dumpClassIfNecessary(className, enhanceClassByteArray, affect);
// 成功计数
affect.cCnt(1);
return enhanceClassByteArray;
} catch (Throwable t) {
logger.warn("transform loader[{}]:class[{}] failed.", inClassLoader, className, t);
affect.setThrowable(t);
}
return null;
}
/**
* 是否抽象属性
*/
private boolean isAbstract(int access) {
return (Opcodes.ACC_ABSTRACT & access) == Opcodes.ACC_ABSTRACT;
}
/**
* 是否需要忽略
*/
private boolean isIgnore(MethodNode methodNode, Matcher methodNameMatcher) {
return null == methodNode || isAbstract(methodNode.access) || !methodNameMatcher.matching(methodNode.name)
|| ArthasCheckUtils.isEquals(methodNode.name, "");
}
/**
* dump class to file
*/
private static void dumpClassIfNecessary(String className, byte[] data, EnhancerAffect affect) {
if (!GlobalOptions.isDump) {
return;
}
final File dumpClassFile = new File("./arthas-class-dump/" + className + ".class");
final File classPath = new File(dumpClassFile.getParent());
// 创建类所在的包路径
if (!classPath.mkdirs() && !classPath.exists()) {
logger.warn("create dump classpath:{} failed.", classPath);
return;
}
// 将类字节码写入文件
try {
FileUtils.writeByteArrayToFile(dumpClassFile, data);
affect.addClassDumpFile(dumpClassFile);
if (GlobalOptions.verbose) {
logger.info("dump enhanced class: {}, path: {}", className, dumpClassFile);
}
} catch (IOException e) {
logger.warn("dump class:{} to file {} failed.", className, dumpClassFile, e);
}
}
/**
* 是否需要过滤的类
*
* @param classes 类集合
*/
private List, String>> filter(Set> classes) {
List, String>> filteredClasses = new ArrayList, String>>();
final Iterator> it = classes.iterator();
while (it.hasNext()) {
final Class> clazz = it.next();
boolean removeFlag = false;
if (null == clazz) {
removeFlag = true;
}
// else if (isSelf(clazz)) {
// filteredClasses.add(new Pair, String>(clazz, "class loaded by arthas itself"));
// removeFlag = true;
// }
else if (isUnsafeClass(clazz)) {
filteredClasses.add(new Pair, String>(clazz, "class loaded by Bootstrap Classloader, try to execute `options unsafe true`"));
removeFlag = true;
} else if (isExclude(clazz)) {
filteredClasses.add(new Pair, String>(clazz, "class is excluded"));
removeFlag = true;
} else {
Pair unsupportedResult = isUnsupportedClass(clazz);
if (unsupportedResult.getFirst()) {
filteredClasses.add(new Pair, String>(clazz, unsupportedResult.getSecond()));
removeFlag = true;
}
}
if (removeFlag) {
it.remove();
}
}
return filteredClasses;
}
private boolean isExclude(Class> clazz) {
if (this.classNameExcludeMatcher != null) {
return classNameExcludeMatcher.matching(clazz.getName());
}
return false;
}
/**
* 是否过滤Arthas加载的类
*/
private static boolean isSelf(Class> clazz) {
return null != clazz && isEquals(clazz.getClassLoader(), selfClassLoader);
}
/**
* 是否过滤unsafe类
*/
private static boolean isUnsafeClass(Class> clazz) {
return !GlobalOptions.isUnsafe && clazz.getClassLoader() == null;
}
/**
* 是否过滤目前暂不支持的类
*/
private static Pair isUnsupportedClass(Class> clazz) {
if (ClassUtils.isLambdaClass(clazz)) {
return new Pair(Boolean.TRUE, "class is lambda");
}
if (clazz.isInterface() && !GlobalOptions.isSupportDefaultMethod) {
return new Pair(Boolean.TRUE, "class is interface");
}
if (clazz.equals(Integer.class)) {
return new Pair(Boolean.TRUE, "class is java.lang.Integer");
}
if (clazz.equals(Class.class)) {
return new Pair(Boolean.TRUE, "class is java.lang.Class");
}
if (clazz.equals(Method.class)) {
return new Pair(Boolean.TRUE, "class is java.lang.Method");
}
if (clazz.isArray()) {
return new Pair(Boolean.TRUE, "class is array");
}
return new Pair(Boolean.FALSE, "");
}
/**
* 对象增强
*
* @param inst inst
* @param maxNumOfMatchedClass 匹配的class最大数量
* @return 增强影响范围
* @throws UnmodifiableClassException 增强失败
*/
public synchronized EnhancerAffect enhance(final Instrumentation inst, int maxNumOfMatchedClass) throws UnmodifiableClassException {
// 获取需要增强的类集合
this.matchingClasses = GlobalOptions.isDisableSubClass
? SearchUtils.searchClass(inst, classNameMatcher)
: SearchUtils.searchSubClass(inst, SearchUtils.searchClass(inst, classNameMatcher));
if (matchingClasses.size() > maxNumOfMatchedClass) {
affect.setOverLimitMsg("The number of matched classes is " +matchingClasses.size()+ ", greater than the limit value " + maxNumOfMatchedClass + ". Try to change the limit with option '-m '.");
return affect;
}
// 过滤掉无法被增强的类
List, String>> filtedList = filter(matchingClasses);
if (!filtedList.isEmpty()) {
for (Pair, String> filted : filtedList) {
logger.info("ignore class: {}, reason: {}", filted.getFirst().getName(), filted.getSecond());
}
}
logger.info("enhance matched classes: {}", matchingClasses);
affect.setTransformer(this);
try {
DemoBootstrap.getRunningInstance().getTransformerManager().addTransformer(this, isTracing);
// 批量增强
if (GlobalOptions.isBatchReTransform) {
final int size = matchingClasses.size();
final Class>[] classArray = new Class>[size];
arraycopy(matchingClasses.toArray(), 0, classArray, 0, size);
if (classArray.length > 0) {
inst.retransformClasses(classArray);
logger.info("Success to batch transform classes: " + Arrays.toString(classArray));
}
} else {
// for each 增强
for (Class> clazz : matchingClasses) {
try {
inst.retransformClasses(clazz);
logger.info("Success to transform class: " + clazz);
} catch (Throwable t) {
logger.warn("retransform {} failed.", clazz, t);
if (t instanceof UnmodifiableClassException) {
throw (UnmodifiableClassException) t;
} else if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new RuntimeException(t);
}
}
}
}
} catch (Throwable e) {
logger.error("Enhancer error, matchingClasses: {}", matchingClasses, e);
affect.setThrowable(e);
}
return affect;
}
/**
* 重置指定的Class
*
* @param inst inst
* @param classNameMatcher 类名匹配
* @return 增强影响范围
* @throws UnmodifiableClassException
*/
public static synchronized EnhancerAffect reset(final Instrumentation inst, final Matcher classNameMatcher)
throws UnmodifiableClassException {
final EnhancerAffect affect = new EnhancerAffect();
final Set> enhanceClassSet = new HashSet>();
for (Class> classInCache : classBytesCache.keySet()) {
if (classNameMatcher.matching(classInCache.getName())) {
enhanceClassSet.add(classInCache);
}
}
try {
enhance(inst, enhanceClassSet);
logger.info("Success to reset classes: " + enhanceClassSet);
} finally {
for (Class> resetClass : enhanceClassSet) {
classBytesCache.remove(resetClass);
affect.cCnt(1);
}
}
return affect;
}
// 批量增强
private static void enhance(Instrumentation inst, Set> classes)
throws UnmodifiableClassException {
int size = classes.size();
Class>[] classArray = new Class>[size];
arraycopy(classes.toArray(), 0, classArray, 0, size);
if (classArray.length > 0) {
inst.retransformClasses(classArray);
}
}
}