package com.navercorp.pinpoint.profiler.instrument;
import com.navercorp.pinpoint.bootstrap.config.ProfilerConfig;
import com.navercorp.pinpoint.common.util.StringUtils;
import com.navercorp.pinpoint.profiler.util.JavaAssistUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Woonduk Kang(emeroad)
*/
public class ASMBytecodeDumpService implements BytecodeDumpService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public static final String ENABLE_BYTECODE_DUMP = "bytecode.dump.enable";
public static final boolean ENABLE_BYTECODE_DUMP_DEFAULT_VALUE = false;
public static final String BYTECODE_DUMP_BYTECODE = "bytecode.dump.bytecode";
public static final boolean BYTECODE_DUMP_BYTECODE_DEFAULT_VALUE = true;
public static final String BYTECODE_DUMP_VERIFY = "bytecode.dump.verify";
public static final boolean BYTECODE_DUMP_VERIFY_DEFAULT_VALUE = false;
public static final String BYTECODE_DUMP_ASM = "bytecode.dump.asm";
public static final boolean BYTECODE_DUMP_ASM_DEFAULT_VALUE = true;
public static final String DUMP_CLASS_LIST = "bytecode.dump.classlist";
private final boolean dumpBytecode;
private final boolean dumpVerify;
private final boolean dumpASM;
private final Set<String> dumpClassInternalNameSet;
private ASMBytecodeDisassembler disassembler = new ASMBytecodeDisassembler();
public ASMBytecodeDumpService(ProfilerConfig profilerConfig) {
if (profilerConfig == null) {
throw new NullPointerException("profilerConfig must not be null");
}
this.dumpBytecode = profilerConfig.readBoolean(BYTECODE_DUMP_BYTECODE, BYTECODE_DUMP_BYTECODE_DEFAULT_VALUE);
this.dumpVerify = profilerConfig.readBoolean(BYTECODE_DUMP_VERIFY, BYTECODE_DUMP_VERIFY_DEFAULT_VALUE);
this.dumpASM = profilerConfig.readBoolean(BYTECODE_DUMP_ASM, BYTECODE_DUMP_ASM_DEFAULT_VALUE);
this.dumpClassInternalNameSet = getClassName(profilerConfig);
}
private Set<String> getClassName(ProfilerConfig profilerConfig) {
final String classNameList = profilerConfig.readString(DUMP_CLASS_LIST, "");
if (classNameList.isEmpty()) {
return Collections.emptySet();
} else {
final List<String> classList = StringUtils.tokenizeToStringList(classNameList, ",");
final List<String> classInternalNameList = toInternalNames(classList);
return new HashSet<String>(classInternalNameList);
}
}
public ASMBytecodeDumpService(boolean dumpBytecode, boolean dumpVerify, boolean dumpASM, List<String> classNameList) {
if (classNameList == null) {
throw new NullPointerException("classNameList must not be null");
}
this.dumpBytecode = dumpBytecode;
this.dumpVerify = dumpVerify;
this.dumpASM = dumpASM;
List<String> classInternalNameList = toInternalNames(classNameList);
this.dumpClassInternalNameSet = new HashSet<String>(classInternalNameList);
}
private List<String> toInternalNames(List<String> classNameList) {
List<String> classInternalNameList = new ArrayList<String>(classNameList.size());
for (String className : classNameList) {
classInternalNameList.add(JavaAssistUtils.javaNameToJvmName(className));
}
return classInternalNameList;
}
@Override
public void dumpBytecode(String dumpMessage, final String classInternalName, final byte[] bytes, ClassLoader classLoader) {
if (classInternalName == null) {
throw new NullPointerException("classInternalName must not be null");
}
if (!filterClassName(classInternalName)) {
return;
}
if (dumpBytecode) {
final String dumpBytecode = this.disassembler.dumpBytecode(bytes);
logger.info("{} class:{} bytecode:{}", dumpMessage, classInternalName, dumpBytecode);
}
if (dumpVerify) {
if (classLoader == null) {
logger.debug("classLoader is null, classInternalName:{}", classInternalName);
}
classLoader = getClassLoader(classLoader);
final String dumpVerify = this.disassembler.dumpVerify(bytes, classLoader);
logger.info("{} class:{} verify:{}", dumpMessage, classInternalName, dumpVerify);
}
if (dumpASM) {
final String dumpASM = this.disassembler.dumpASM(bytes);
logger.info("{} class:{} asm:{}", dumpMessage, classInternalName, dumpASM);
}
}
private static ClassLoader getClassLoader(ClassLoader classLoader) {
if (classLoader == null) {
return ClassLoader.getSystemClassLoader();
}
return classLoader;
}
private boolean filterClassName(String classInternalName) {
return this.dumpClassInternalNameSet.contains(classInternalName);
}
}