From e6a8e4624ae60eb830f99941e5a4f894cd5650a7 Mon Sep 17 00:00:00 2001 From: hengyunabc Date: Fri, 16 Nov 2018 00:28:42 +0800 Subject: [PATCH] jad command support classes created by lambda method. fix #305 --- .../core/command/klass100/JadCommand.java | 30 +-- .../taobao/arthas/core/util/Decompiler.java | 194 ++++++++++++++++++ 2 files changed, 196 insertions(+), 28 deletions(-) create mode 100644 core/src/main/java/com/taobao/arthas/core/util/Decompiler.java diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java index 6ab325aee..3429fa7ad 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/JadCommand.java @@ -5,6 +5,7 @@ import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.Decompiler; import com.taobao.arthas.core.util.FileUtils; import com.taobao.arthas.core.util.LogUtil; import com.taobao.arthas.core.util.SearchUtils; @@ -119,7 +120,7 @@ public class JadCommand extends AnnotatedCommand { File classFile = classFiles.get(c); String source; - source = decompileWithCFR(classFile.getAbsolutePath(), c, methodName); + source = Decompiler.decompile(classFile.getAbsolutePath(), methodName); if (source != null) { source = pattern.matcher(source).replaceAll(""); } else { @@ -163,33 +164,6 @@ public class JadCommand extends AnnotatedCommand { process.write("No class found for: " + classPattern + "\n"); } - private String decompileWithCFR(String classPath, Class clazz, String methodName) { - List options = new ArrayList(); - options.add(classPath); -// options.add(clazz.getName()); - if (methodName != null) { - options.add(methodName); - } - options.add(OUTPUTOPTION); - options.add(DecompilePath); - options.add(COMMENTS); - options.add("false"); - String args[] = new String[options.size()]; - options.toArray(args); - Main.main(args); - String outputFilePath = DecompilePath + File.separator + Type.getInternalName(clazz) + ".java"; - File outputFile = new File(outputFilePath); - if (outputFile.exists()) { - try { - return FileUtils.readFileToString(outputFile, Charset.defaultCharset()); - } catch (IOException e) { - logger.error(null, "error read decompile result in: " + outputFilePath, e); - } - } - - return null; - } - public static void main(String[] args) { String[] names = { "com.taobao.container.web.arthas.mvc.AppInfoController", diff --git a/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java b/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java new file mode 100644 index 000000000..e6837121f --- /dev/null +++ b/core/src/main/java/com/taobao/arthas/core/util/Decompiler.java @@ -0,0 +1,194 @@ +package com.taobao.arthas.core.util; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair; +import org.benf.cfr.reader.entities.ClassFile; +import org.benf.cfr.reader.entities.Method; +import org.benf.cfr.reader.relationship.MemberNameResolver; +import org.benf.cfr.reader.state.ClassFileSourceImpl; +import org.benf.cfr.reader.state.DCCommonState; +import org.benf.cfr.reader.state.TypeUsageCollector; +import org.benf.cfr.reader.state.TypeUsageInformation; +import org.benf.cfr.reader.util.AnalysisType; +import org.benf.cfr.reader.util.CannotLoadClassException; +import org.benf.cfr.reader.util.ConfusedCFRException; +import org.benf.cfr.reader.util.ListFactory; +import org.benf.cfr.reader.util.getopt.GetOptParser; +import org.benf.cfr.reader.util.getopt.Options; +import org.benf.cfr.reader.util.getopt.OptionsImpl; +import org.benf.cfr.reader.util.output.Dumper; +import org.benf.cfr.reader.util.output.DumperFactory; +import org.benf.cfr.reader.util.output.DumperFactoryImpl; +import org.benf.cfr.reader.util.output.IllegalIdentifierDump; +import org.benf.cfr.reader.util.output.StreamDumper; +import org.benf.cfr.reader.util.output.ToStringDumper; + +/** + * + * @author hengyunabc 2018-11-16 + * + */ +public class Decompiler { + + /** + * @see org.benf.cfr.reader.Main#main(String[]) + * @param classFilePath + * @param methodName + * @return + */ + public static String decompile(String classFilePath, String methodName) { + StringBuilder result = new StringBuilder(8192); + + List argList = new ArrayList(); + argList.add(classFilePath); + if (methodName != null) { + argList.add("--methodname"); + argList.add(methodName); + } + String args[] = argList.toArray(new String[0]); + + GetOptParser getOptParser = new GetOptParser(); + + Options options = null; + List files = null; + try { + Pair processedArgs = getOptParser.parse(args, OptionsImpl.getFactory()); + files = (List) processedArgs.getFirst(); + options = (Options) processedArgs.getSecond(); + } catch (Exception e) { + getOptParser.showHelp(OptionsImpl.getFactory(), e); + System.exit(1); + } + + if ((options.optionIsSet(OptionsImpl.HELP)) || (files.isEmpty())) { + getOptParser.showOptionHelp(OptionsImpl.getFactory(), options, OptionsImpl.HELP); + return ""; + } + + ClassFileSourceImpl classFileSource = new ClassFileSourceImpl(options); + + boolean skipInnerClass = (files.size() > 1) + && (((Boolean) options.getOption(OptionsImpl.SKIP_BATCH_INNER_CLASSES)).booleanValue()); + + Collections.sort(files); + + for (String path : files) { + classFileSource.clearConfiguration(); + DCCommonState dcCommonState = new DCCommonState(options, classFileSource); + DumperFactory dumperFactory = new DumperFactoryImpl(options); + + path = classFileSource.adjustInputPath(path); + + AnalysisType type = (AnalysisType) options.getOption(OptionsImpl.ANALYSE_AS); + if (type == null) + type = dcCommonState.detectClsJar(path); + + if (type == AnalysisType.JAR) { + // doJar(dcCommonState, path, dumperFactory); + } + if (type == AnalysisType.CLASS) + result.append(doClass(dcCommonState, path, skipInnerClass, dumperFactory)); + } + return result.toString(); + } + + public static String doClass(DCCommonState dcCommonState, String path, boolean skipInnerClass, + DumperFactory dumperFactory) { + StringBuilder result = new StringBuilder(8192); + Options options = dcCommonState.getOptions(); + IllegalIdentifierDump illegalIdentifierDump = IllegalIdentifierDump.Factory.get(options); + Dumper d = new ToStringDumper(); + try { + ClassFile c = dcCommonState.getClassFileMaybePath(path); + if ((skipInnerClass) && (c.isInnerClass())) + return ""; + dcCommonState.configureWith(c); + dumperFactory.getProgressDumper().analysingType(c.getClassType()); + try { + c = dcCommonState.getClassFile(c.getClassType()); + } catch (CannotLoadClassException e) { + } + if (((Boolean) options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)).booleanValue()) { + c.loadInnerClasses(dcCommonState); + } + if (((Boolean) options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)).booleanValue()) { + MemberNameResolver.resolveNames(dcCommonState, + ListFactory.newList(dcCommonState.getClassCache().getLoadedTypes())); + } + + c.analyseTop(dcCommonState); + + TypeUsageCollector collectingDumper = new TypeUsageCollector(c); + c.collectTypeUsages(collectingDumper); + + d = new StringDumper(collectingDumper.getTypeUsageInformation(), options, illegalIdentifierDump); + + // d = dumperFactory.getNewTopLevelDumper(c.getClassType(), summaryDumper, + // collectingDumper.getTypeUsageInformation(), illegalIdentifierDump); + + String methname = (String) options.getOption(OptionsImpl.METHODNAME); + if (methname == null) + c.dump(d); + else { + try { + for (Method method : c.getMethodByName(methname)) + method.dump(d, true); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("No such method '" + methname + "'."); + } + } + d.print(""); + result.append(d.toString()); + } catch (ConfusedCFRException e) { + result.append(e.toString()).append("\n"); + for (Object x : e.getStackTrace()) + result.append(x).append("\n"); + } catch (CannotLoadClassException e) { + result.append("Can't load the class specified:").append("\n"); + result.append(e.toString()).append("\n"); + } catch (RuntimeException e) { + result.append(e.toString()).append("\n"); + for (Object x : e.getStackTrace()) + result.append(x).append("\n"); + } finally { + if (d != null) + d.close(); + } + return result.toString(); + } + + public static class StringDumper extends StreamDumper { + private StringWriter sw = new StringWriter(); + + public StringDumper(TypeUsageInformation typeUsageInformation, Options options, + IllegalIdentifierDump illegalIdentifierDump) { + super(typeUsageInformation, options, illegalIdentifierDump); + } + + public void addSummaryError(Method paramMethod, String paramString) { + + } + + public void close() { + try { + sw.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + protected void write(String source) { + sw.write(source); + } + + public String toString() { + return sw.toString(); + } + } +}