package com.github.ompc.greys.core.command;
import com.github.ompc.greys.core.advisor.Enhancer;
import com.github.ompc.greys.core.command.annotation.Cmd;
import com.github.ompc.greys.core.command.annotation.IndexArg;
import com.github.ompc.greys.core.command.annotation.NamedArg;
import com.github.ompc.greys.core.manager.ReflectManager;
import com.github.ompc.greys.core.server.Session;
import com.github.ompc.greys.core.util.affect.AsmAffect;
import com.github.ompc.greys.core.util.affect.RowAffect;
import com.github.ompc.greys.core.util.matcher.ClassMatcher;
import com.github.ompc.greys.core.util.matcher.Matcher;
import com.github.ompc.greys.core.util.matcher.PatternMatcher;
import com.github.ompc.greys.core.util.matcher.TrueMatcher;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.*;
import org.objectweb.asm.util.*;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.instrument.Instrumentation;
import java.util.Collection;
/**
* clone from {@link TraceClassVisitor}
*/
class GaTraceClassVisitor extends ClassVisitor {
/**
* The print writer to be used to print the class. May be null.
*/
private final PrintWriter pw;
/**
* The object that actually converts visit events into text.
*/
public final Printer p;
/**
* Constructs a new {@link GaTraceClassVisitor}.
*
* @param pw the print writer to be used to print the class.
*/
public GaTraceClassVisitor(final PrintWriter pw) {
super(Opcodes.ASM5, null);
this.pw = pw;
this.p = new Textifier();
}
@Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName,
final String[] interfaces) {
p.visit(version, access, name, signature, superName, interfaces);
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public void visitSource(final String file, final String debug) {
p.visitSource(file, debug);
super.visitSource(file, debug);
}
@Override
public void visitOuterClass(final String owner, final String name,
final String desc) {
p.visitOuterClass(owner, name, desc);
super.visitOuterClass(owner, name, desc);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
Printer p = this.p.visitClassAnnotation(desc, visible);
AnnotationVisitor av = cv == null ? null : cv.visitAnnotation(desc,
visible);
return new TraceAnnotationVisitor(av, p);
}
@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
Printer p = this.p.visitClassTypeAnnotation(typeRef, typePath, desc,
visible);
AnnotationVisitor av = cv == null ? null : cv.visitTypeAnnotation(
typeRef, typePath, desc, visible);
return new TraceAnnotationVisitor(av, p);
}
@Override
public void visitAttribute(final Attribute attr) {
p.visitClassAttribute(attr);
super.visitAttribute(attr);
}
@Override
public void visitInnerClass(final String name, final String outerName,
final String innerName, final int access) {
p.visitInnerClass(name, outerName, innerName, access);
super.visitInnerClass(name, outerName, innerName, access);
}
@Override
public FieldVisitor visitField(final int access, final String name,
final String desc, final String signature, final Object value) {
Printer p = this.p.visitField(access, name, desc, signature, value);
FieldVisitor fv = cv == null ? null : cv.visitField(access, name, desc,
signature, value);
return new TraceFieldVisitor(fv, p);
}
@Override
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
Printer p = this.p.visitMethod(access, name, desc, signature,
exceptions);
MethodVisitor mv = cv == null ? null : cv.visitMethod(access, name,
desc, signature, exceptions);
return new TraceMethodVisitor(mv, p);
}
@Override
public void visitEnd() {
p.visitClassEnd();
if (pw != null) {
p.print(pw);
pw.flush();
}
super.visitEnd();
}
}
/**
* 查看指定类的字节码
* Created by oldmanpushcart@gmail.com on 15/12/10.
*/
@Cmd(name = "asm", sort = 11, summary = "Display class bytecode by asm format",
eg = {
"asm -f java.lang.String",
"asm -f *StringUtils",
"asm *StringUtils isTop"
})
public class AsmCommand implements Command {
@IndexArg(index = 0, name = "class-pattern", summary = "Path and classname of Pattern Matching")
private String classPattern;
@IndexArg(index = 1, isRequired = false, name = "method-pattern", summary = "Method of Pattern Matching")
private String methodPattern;
@NamedArg(name = "E", summary = "Enable regular expression to match (wildcard matching by default)")
private boolean isRegEx = false;
@NamedArg(name = "f", summary = "Display all the member variables")
private boolean isField = false;
private final ReflectManager reflectManager = ReflectManager.Factory.getInstance();
@Override
public Action getAction() {
return new RowAction() {
@Override
public RowAffect action(Session session, Instrumentation inst, Printer printer) throws Throwable {
final RowAffect affect = new RowAffect();
final StringBuilder outputSB = new StringBuilder();
// 找到所有匹配的类
final Matcher<String> classNameMatcher = new PatternMatcher(isRegEx, classPattern);
final Matcher<String> methodNameMatcher;
if (StringUtils.isBlank(methodPattern)) {
methodNameMatcher = new TrueMatcher<String>();
} else {
methodNameMatcher = new PatternMatcher(isRegEx, methodPattern);
}
final Collection<Class<?>> matchedClassSet = reflectManager.searchClass(new ClassMatcher(classNameMatcher));
final AsmAffect asmAffect = Enhancer.getClassByteArray(matchedClassSet, inst);
for( AsmAffect.ClassInfo classInfo : asmAffect.getClassInfos() ) {
final Class<?> clazz = classInfo.getClazz();
final ClassLoader loader = classInfo.getLoader();
// 不是所有类都能看字节码的
if (clazz.isArray()) {
continue;
}
final String title = String.format("// ASM BYTECODE FOR \"%s\" @ClassLoader:%s",
clazz.getName(),
loader
);
final InputStream is = new ByteArrayInputStream(classInfo.getByteArray());
final StringWriter writer = new StringWriter();
try {
final ClassReader cr = new ClassReader(is);
final GaTraceClassVisitor trace = new GaTraceClassVisitor(new PrintWriter(writer, true)) {
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if (isField) {
return super.visitField(access, name, desc, signature, value);
} else {
return null;
}
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (methodNameMatcher.matching(name)) {
return super.visitMethod(access, name, desc, signature, exceptions);
} else {
return null;
}
}
};
cr.accept(trace, ClassReader.SKIP_DEBUG);
outputSB.append(title).append("\n").append(writer.toString()).append("\n");
affect.rCnt(1);
} finally {
IOUtils.closeQuietly(writer);
IOUtils.closeQuietly(is);
}
}
printer.print(outputSB.toString()).finish();
return affect;
}
};
}
}