/* Soot - a J*va Optimization Framework
* Copyright (C) 2002 Sable Research Group
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the Sable Research Group and others 2002-2003.
* See the 'credits' file distributed with Soot for the complete list of
* contributors. (Soot is distributed at http://www.sable.mcgill.ca/soot)
*/
package soot.tools;
import java.util.*;
import soot.*;
import soot.jimple.JimpleBody;
import soot.toolkits.graph.*;
import soot.options.Options;
import soot.util.dot.DotGraph;
import soot.util.cfgcmd.AltClassLoader;
import soot.util.cfgcmd.CFGGraphType;
import soot.util.cfgcmd.CFGIntermediateRep;
import soot.util.cfgcmd.CFGToDotGraph;
/**
* A utility class for generating dot graph file for a control flow graph
*
* @author Feng Qian
*/
public class CFGViewer extends BodyTransformer {
private static final String packToJoin = "jtp";
private static final String phaseSubname = "printcfg";
private static final String phaseFullname = packToJoin + '.' + phaseSubname;
private static final String altClassPathOptionName = "alt-class-path";
private static final String graphTypeOptionName = "graph-type";
private static final String defaultGraph = "BriefUnitGraph";
private static final String irOptionName = "ir";
private static final String defaultIR = "jimple";
private static final String multipageOptionName = "multipages";
private static final String briefLabelOptionName = "brief";
private CFGGraphType graphtype;
private CFGIntermediateRep ir;
private CFGToDotGraph drawer;
private Map methodsToPrint; // If the user specifies particular
// methods to print, this is a map
// from method name to the class
// name declaring the method.
protected void internalTransform(Body b, String phaseName, Map options) {
initialize(options);
SootMethod meth = b.getMethod();
if ((methodsToPrint == null) ||
(meth.getDeclaringClass().getName() ==
methodsToPrint.get(meth.getName()))) {
Body body = ir.getBody((JimpleBody) b);
print_cfg(body);
}
}
public static void main(String[] args) {
CFGViewer viewer = new CFGViewer();
Transform printTransform = new Transform(phaseFullname, viewer);
printTransform.setDeclaredOptions("enabled " +
altClassPathOptionName + ' ' +
graphTypeOptionName + ' ' +
irOptionName + ' ' +
multipageOptionName + ' ' +
briefLabelOptionName + ' ');
printTransform.setDefaultOptions("enabled " +
altClassPathOptionName + ": " +
graphTypeOptionName + ':' + defaultGraph +
' ' + irOptionName + ':' + defaultIR +
' ' + multipageOptionName + ":false " +
' ' + briefLabelOptionName + ":false ");
PackManager.v().getPack("jtp").add(printTransform);
args = viewer.parse_options(args);
if (args.length == 0) {
usage();
} else {
soot.Main.main(args);
}
}
private static void usage(){
G.v().out.println(
"Usage:\n" +
" java soot.util.CFGViewer [soot options] [CFGViewer options] [class[:method]]...\n\n" +
" CFGViewer options:\n" +
" (When specifying the value for an '=' option, you only\n" +
" need to type enough characters to specify the choice\n" +
" unambiguously, and case is ignored.)\n" +
"\n" +
" --alt-classpath PATH :\n" +
" specifies the classpath from which to load classes\n" +
" that implement graph types whose names begin with 'Alt'.\n" +
" --graph={" +
CFGGraphType.help(0, 70,
" ".length()) + "} :\n" +
" show the specified type of graph.\n" +
" Defaults to " + defaultGraph + ".\n" +
" --ir={" +
CFGIntermediateRep.help(0, 70,
" ".length()) + "} :\n" +
" create the CFG from the specified intermediate\n" +
" representation. Defaults to " + defaultIR + ".\n" +
" --brief :\n" +
" label nodes with the unit or block index,\n" +
" instead of the text of their statements.\n" +
" --multipages :\n" +
" produce dot file output for multiple 8.5x11\" pages.\n" +
" By default, a single page is produced.\n" +
" --help :\n" +
" print this message.\n" +
"\n" +
" Particularly relevant soot options (see \"soot --help\" for details):\n" +
" --soot-class-path PATH\n" +
" --show-exception-dests\n" +
" --throw-analysis {pedantic|unit}\n" +
" --omit-excepting-unit-edges\n" +
" --trim-cfgs\n"
);
}
/**
* Parse the command line arguments specific to CFGViewer,
* and convert them into phase options for jtp.printcfg.
*
* @return an array of arguments to pass on to Soot.Main.main().
*/
private String[] parse_options(String[] args){
List sootArgs = new ArrayList(args.length);
for (int i=0, n=args.length; i<n; i++) {
if (args[i].equals("--alt-classpath") ||
args[i].equals("--alt-class-path")) {
sootArgs.add("-p");
sootArgs.add(phaseFullname);
sootArgs.add(altClassPathOptionName + ':' + args[++i]);
} else if (args[i].startsWith("--graph=")) {
sootArgs.add("-p");
sootArgs.add(phaseFullname);
sootArgs.add(graphTypeOptionName + ':' + args[i].substring("--graph=".length()));
} else if (args[i].startsWith("--ir=")) {
sootArgs.add("-p");
sootArgs.add(phaseFullname);
sootArgs.add(irOptionName + ':' + args[i].substring("--ir=".length()));
} else if (args[i].equals("--brief")) {
sootArgs.add("-p");
sootArgs.add(phaseFullname);
sootArgs.add(briefLabelOptionName + ":true");
} else if (args[i].equals("--multipages")) {
sootArgs.add("-p");
sootArgs.add(phaseFullname);
sootArgs.add(multipageOptionName + ":true");
} else if (args[i].equals("--help")) {
return new String[0]; // This is a cheesy method to inveigle
// our caller into printing the help
// and exiting.
} else if (args[i].equals("--soot-class-path") ||
args[i].equals("-soot-class-path") ||
args[i].equals("--soot-classpath") ||
args[i].equals("-soot-classpath")) {
// Pass classpaths without treating ":" as a method specifier.
sootArgs.add(args[i]);
sootArgs.add(args[++i]);
} else if (args[i].equals("-p") ||
args[i].equals("--phase-option") ||
args[i].equals("-phase-option")) {
// Pass phase options without treating ":" as a method specifier.
sootArgs.add(args[i]);
sootArgs.add(args[++i]);
sootArgs.add(args[++i]);
} else {
int smpos = args[i].indexOf(':');
if (smpos == -1) {
sootArgs.add(args[i]);
} else {
String clsname = args[i].substring(0, smpos);
sootArgs.add(clsname);
String methname = args[i].substring(smpos+1);
if (methodsToPrint == null) {
methodsToPrint = new HashMap();
}
methodsToPrint.put(methname, clsname);
}
}
}
String[] sootArgsArray = new String[sootArgs.size()];
return (String[]) sootArgs.toArray(sootArgsArray);
}
private void initialize(Map options) {
if (drawer == null) {
drawer = new CFGToDotGraph();
drawer.setBriefLabels(PhaseOptions.getBoolean(options, briefLabelOptionName));
drawer.setOnePage(! PhaseOptions.getBoolean(options, multipageOptionName));
drawer.setUnexceptionalControlFlowAttr("color", "black");
drawer.setExceptionalControlFlowAttr("color", "red");
drawer.setExceptionEdgeAttr("color", "lightgray");
drawer.setShowExceptions(Options.v().show_exception_dests());
ir = CFGIntermediateRep.getIR(PhaseOptions.getString(options, irOptionName));
graphtype = CFGGraphType.getGraphType(PhaseOptions.getString(options,
graphTypeOptionName));
AltClassLoader.v().setAltClassPath(PhaseOptions.getString(options,
altClassPathOptionName));
AltClassLoader.v().setAltClasses(new String[] {
"soot.toolkits.graph.ArrayRefBlockGraph",
"soot.toolkits.graph.Block",
"soot.toolkits.graph.Block$AllMapTo",
"soot.toolkits.graph.BlockGraph",
"soot.toolkits.graph.BriefBlockGraph",
"soot.toolkits.graph.BriefUnitGraph",
"soot.toolkits.graph.CompleteBlockGraph",
"soot.toolkits.graph.CompleteUnitGraph",
"soot.toolkits.graph.TrapUnitGraph",
"soot.toolkits.graph.UnitGraph",
"soot.toolkits.graph.ZonedBlockGraph",
});
}
}
protected void print_cfg(Body body) {
DirectedGraph graph = graphtype.buildGraph(body);
DotGraph canvas = graphtype.drawGraph(drawer, graph, body);
String methodname = body.getMethod().getSubSignature();
String filename = soot.SourceLocator.v().getOutputDir();
if (filename.length() > 0) {
filename = filename + java.io.File.separator;
}
filename = filename +
methodname.replace(java.io.File.separatorChar, '.') +
DotGraph.DOT_EXTENSION;
G.v().out.println("Generate dot file in "+filename);
canvas.plot(filename);
}
}