Commit 4c4af792 authored by Skylot's avatar Skylot

core: fix dot graph dump

parent a0d8d9fc
...@@ -8,7 +8,6 @@ import jadx.core.dex.nodes.RootNode; ...@@ -8,7 +8,6 @@ import jadx.core.dex.nodes.RootNode;
import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.IDexTreeVisitor;
import jadx.core.dex.visitors.SaveCode; import jadx.core.dex.visitors.SaveCode;
import jadx.core.utils.ErrorsCounter; import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.InputFile; import jadx.core.utils.files.InputFile;
...@@ -116,21 +115,12 @@ public final class Decompiler { ...@@ -116,21 +115,12 @@ public final class Decompiler {
LOG.info("processing ..."); LOG.info("processing ...");
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount); ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadsCount);
for (final ClassNode cls : root.getClasses(false)) { for (final ClassNode cls : root.getClasses(false)) {
if (cls.getCode() == null) { executor.execute(new Runnable() {
Runnable job = new Runnable() { @Override
@Override public void run() {
public void run() { ProcessClass.process(cls, passList);
ProcessClass.process(cls, passList);
}
};
executor.execute(job);
} else {
try {
savePass.visit(cls);
} catch (CodegenException e) {
LOG.error("Can't save class {}", cls, e);
} }
} });
} }
executor.shutdown(); executor.shutdown();
return executor; return executor;
......
...@@ -23,15 +23,11 @@ import java.util.Set; ...@@ -23,15 +23,11 @@ import java.util.Set;
public class DotGraphVisitor extends AbstractVisitor { public class DotGraphVisitor extends AbstractVisitor {
private static final String NL = "\\l"; private static final String NL = "\\l";
private static final boolean PRINT_REGISTERS_STATES = false;
private final File dir; private final File dir;
private final boolean useRegions; private final boolean useRegions;
private final boolean rawInsn; private final boolean rawInsn;
private CodeWriter dot;
private CodeWriter conn;
public DotGraphVisitor(File outDir, boolean useRegions, boolean rawInsn) { public DotGraphVisitor(File outDir, boolean useRegions, boolean rawInsn) {
this.dir = outDir; this.dir = outDir;
this.useRegions = useRegions; this.useRegions = useRegions;
...@@ -47,186 +43,179 @@ public class DotGraphVisitor extends AbstractVisitor { ...@@ -47,186 +43,179 @@ public class DotGraphVisitor extends AbstractVisitor {
if (mth.isNoCode()) { if (mth.isNoCode()) {
return; return;
} }
dot = new CodeWriter(); new DumpDotGraph().process(mth);
conn = new CodeWriter(); }
dot.startLine("digraph \"CFG for"); private class DumpDotGraph {
dot.add(escape(mth.getParentClass().getFullName() + "." + mth.getMethodInfo().getShortId())); private CodeWriter dot = new CodeWriter();
dot.add("\" {"); private CodeWriter conn = new CodeWriter();
if (useRegions) { public void process(MethodNode mth) {
if (mth.getRegion() == null) { dot.startLine("digraph \"CFG for");
return; dot.add(escape(mth.getParentClass().getFullName() + "." + mth.getMethodInfo().getShortId()));
} dot.add("\" {");
processMethodRegion(mth);
} else { if (useRegions) {
for (BlockNode block : mth.getBasicBlocks()) { if (mth.getRegion() == null) {
processBlock(mth, block); return;
}
processMethodRegion(mth);
} else {
for (BlockNode block : mth.getBasicBlocks()) {
processBlock(mth, block, false);
}
} }
}
dot.startLine("MethodNode[shape=record,label=\"{"); dot.startLine("MethodNode[shape=record,label=\"{");
dot.add(escape(mth.getAccessFlags().makeString())); dot.add(escape(mth.getAccessFlags().makeString()));
dot.add(escape(mth.getReturnType() + " " dot.add(escape(mth.getReturnType() + " "
+ mth.getParentClass().getFullName() + "." + mth.getName() + mth.getParentClass().getFullName() + "." + mth.getName()
+ "(" + Utils.listToString(mth.getArguments(true)) + ") ")); + "(" + Utils.listToString(mth.getArguments(true)) + ") "));
String attrs = attributesString(mth); String attrs = attributesString(mth);
if (attrs.length() != 0) { if (attrs.length() != 0) {
dot.add(" | ").add(attrs); dot.add(" | ").add(attrs);
} }
dot.add("}\"];"); dot.add("}\"];");
dot.startLine("MethodNode -> ").add(makeName(mth.getEnterBlock())).add(';'); dot.startLine("MethodNode -> ").add(makeName(mth.getEnterBlock())).add(';');
dot.add(conn.toString()); dot.add(conn.toString());
dot.startLine('}'); dot.startLine('}');
dot.startLine(); dot.startLine();
String fileName = Utils.escape(mth.getMethodInfo().getShortId()) String fileName = Utils.escape(mth.getMethodInfo().getShortId())
+ (useRegions ? ".regions" : "") + (useRegions ? ".regions" : "")
+ (rawInsn ? ".raw" : "") + (rawInsn ? ".raw" : "")
+ ".dot"; + ".dot";
dot.save(dir, mth.getParentClass().getClassInfo().getFullPath() + "_graphs", fileName); dot.save(dir, mth.getParentClass().getClassInfo().getFullPath() + "_graphs", fileName);
} }
private void processMethodRegion(MethodNode mth) { private void processMethodRegion(MethodNode mth) {
processRegion(mth, mth.getRegion()); processRegion(mth, mth.getRegion());
for (ExceptionHandler h : mth.getExceptionHandlers()) { for (ExceptionHandler h : mth.getExceptionHandlers()) {
if (h.getHandlerRegion() != null) { if (h.getHandlerRegion() != null) {
processRegion(mth, h.getHandlerRegion()); processRegion(mth, h.getHandlerRegion());
}
} }
} Set<BlockNode> regionsBlocks = new HashSet<BlockNode>(mth.getBasicBlocks().size());
Set<BlockNode> regionsBlocks = new HashSet<BlockNode>(mth.getBasicBlocks().size()); RegionUtils.getAllRegionBlocks(mth.getRegion(), regionsBlocks);
RegionUtils.getAllRegionBlocks(mth.getRegion(), regionsBlocks); for (ExceptionHandler handler : mth.getExceptionHandlers()) {
for (ExceptionHandler handler : mth.getExceptionHandlers()) { IContainer handlerRegion = handler.getHandlerRegion();
IContainer handlerRegion = handler.getHandlerRegion(); if (handlerRegion != null) {
if (handlerRegion != null) { RegionUtils.getAllRegionBlocks(handlerRegion, regionsBlocks);
RegionUtils.getAllRegionBlocks(handlerRegion, regionsBlocks); }
} }
} for (BlockNode block : mth.getBasicBlocks()) {
for (BlockNode block : mth.getBasicBlocks()) { if (!regionsBlocks.contains(block)) {
if (!regionsBlocks.contains(block)) { processBlock(mth, block, true);
processBlock(mth, block, true); }
} }
} }
}
private void processRegion(MethodNode mth, IContainer region) { private void processRegion(MethodNode mth, IContainer region) {
if (region instanceof IRegion) { if (region instanceof IRegion) {
IRegion r = (IRegion) region; IRegion r = (IRegion) region;
dot.startLine("subgraph " + makeName(region) + " {"); dot.startLine("subgraph " + makeName(region) + " {");
dot.startLine("label = \"").add(r); dot.startLine("label = \"").add(r);
String attrs = attributesString(r); String attrs = attributesString(r);
if (attrs.length() != 0) { if (attrs.length() != 0) {
dot.add(" | ").add(attrs); dot.add(" | ").add(attrs);
} }
dot.add("\";"); dot.add("\";");
dot.startLine("node [shape=record,color=blue];"); dot.startLine("node [shape=record,color=blue];");
for (IContainer c : r.getSubBlocks()) { for (IContainer c : r.getSubBlocks()) {
processRegion(mth, c); processRegion(mth, c);
} }
dot.startLine('}'); dot.startLine('}');
} else if (region instanceof BlockNode) { } else if (region instanceof BlockNode) {
processBlock(mth, (BlockNode) region); processBlock(mth, (BlockNode) region, false);
}
} }
}
private void processBlock(MethodNode mth, BlockNode block) { private void processBlock(MethodNode mth, BlockNode block, boolean error) {
processBlock(mth, block, false); String attrs = attributesString(block);
} dot.startLine(makeName(block));
dot.add(" [shape=record,");
if (error) {
dot.add("color=red,");
}
dot.add("label=\"{");
dot.add(block.getId()).add("\\:\\ ");
dot.add(InsnUtils.formatOffset(block.getStartOffset()));
if (attrs.length() != 0) {
dot.add('|').add(attrs);
}
String insns = insertInsns(mth, block);
if (insns.length() != 0) {
dot.add('|').add(insns);
}
dot.add("}\"];");
private void processBlock(MethodNode mth, BlockNode block, boolean error) { BlockNode falsePath = null;
String attrs = attributesString(block); List<InsnNode> list = block.getInstructions();
if (PRINT_REGISTERS_STATES) { if (!list.isEmpty() && list.get(0).getType() == InsnType.IF) {
if (block.getStartState() != null) { falsePath = ((IfNode) list.get(0)).getElseBlock();
if (attrs.length() != 0) { }
attrs += "|"; for (BlockNode next : block.getSuccessors()) {
conn.startLine(makeName(block)).add(" -> ").add(makeName(next));
if (next == falsePath) {
conn.add("[style=dotted]");
} }
attrs += escape("RS: " + block.getStartState()) + NL; conn.add(';');
attrs += escape("RE: " + block.getEndState()) + NL;
} }
} }
dot.startLine(makeName(block));
dot.add(" [shape=record,");
if (error) {
dot.add("color=red,");
}
dot.add("label=\"{");
dot.add(block.getId()).add("\\:\\ ");
dot.add(InsnUtils.formatOffset(block.getStartOffset()));
if (attrs.length() != 0) {
dot.add('|').add(attrs);
}
String insns = insertInsns(mth, block);
if (insns.length() != 0) {
dot.add('|').add(insns);
}
dot.add("}\"];");
BlockNode falsePath = null; private String attributesString(IAttributeNode block) {
List<InsnNode> list = block.getInstructions(); StringBuilder attrs = new StringBuilder();
if (!list.isEmpty() && list.get(0).getType() == InsnType.IF) { for (String attr : block.getAttributes().getAttributeStrings()) {
falsePath = ((IfNode) list.get(0)).getElseBlock(); attrs.append(escape(attr)).append(NL);
}
for (BlockNode next : block.getSuccessors()) {
conn.startLine(makeName(block)).add(" -> ").add(makeName(next));
if (next == falsePath) {
conn.add("[style=dotted]");
} }
conn.add(';'); return attrs.toString();
} }
}
private String attributesString(IAttributeNode block) { private String makeName(IContainer c) {
StringBuilder attrs = new StringBuilder(); String name;
for (String attr : block.getAttributes().getAttributeStrings()) { if (c instanceof BlockNode) {
attrs.append(escape(attr)).append(NL); name = "Node_" + ((BlockNode) c).getId();
} else {
name = "cluster_" + c.getClass().getSimpleName() + "_" + c.hashCode();
}
return name;
} }
return attrs.toString();
}
private static String makeName(IContainer c) { private String insertInsns(MethodNode mth, BlockNode block) {
String name; if (rawInsn) {
if (c instanceof BlockNode) { StringBuilder str = new StringBuilder();
name = "Node_" + ((BlockNode) c).getId(); for (InsnNode insn : block.getInstructions()) {
} else { str.append(escape(insn + " " + insn.getAttributes()));
name = "cluster_" + c.getClass().getSimpleName() + "_" + c.hashCode(); str.append(NL);
} }
return name; return str.toString();
} } else {
CodeWriter code = new CodeWriter(0);
private String insertInsns(MethodNode mth, BlockNode block) { MethodGen.addFallbackInsns(code, mth, block.getInstructions(), false);
if (rawInsn) { String str = escape(code.newLine().toString());
StringBuilder str = new StringBuilder(); if (str.startsWith(NL)) {
for (InsnNode insn : block.getInstructions()) { str = str.substring(NL.length());
str.append(escape(insn + " " + insn.getAttributes())); }
str.append(NL); return str;
}
return str.toString();
} else {
CodeWriter code = new CodeWriter(0);
MethodGen.addFallbackInsns(code, mth, block.getInstructions(), false);
String str = escape(code.newLine().toString());
if (str.startsWith(NL)) {
str = str.substring(NL.length());
} }
return str;
} }
}
private static String escape(String string) { private String escape(String string) {
return string return string
.replace("\\", "") // TODO replace \" .replace("\\", "") // TODO replace \"
.replace("/", "\\/") .replace("/", "\\/")
.replace(">", "\\>").replace("<", "\\<") .replace(">", "\\>").replace("<", "\\<")
.replace("{", "\\{").replace("}", "\\}") .replace("{", "\\{").replace("}", "\\}")
.replace("\"", "\\\"") .replace("\"", "\\\"")
.replace("-", "\\-") .replace("-", "\\-")
.replace("|", "\\|") .replace("|", "\\|")
.replace("\n", NL); .replace("\n", NL);
}
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment