1 /*******************************************************************************
2 * This program and the accompanying materials
3 * are made available under the terms of the Eclipse Public License v1.0
4 * which accompanies this distribution, and is available at
5 * http://www.eclipse.org/legal/epl-v10.html.
7 * This file is a derivative of code released by the University of
8 * California under the terms listed below.
10 * Refinement Analysis Tools is Copyright ©2007 The Regents of the
11 * University of California (Regents). Provided that this notice and
12 * the following two paragraphs are included in any distribution of
13 * Refinement Analysis Tools or its derivative work, Regents agrees
14 * not to assert any of Regents' copyright rights in Refinement
15 * Analysis Tools against recipient for recipients reproduction,
16 * preparation of derivative works, public display, public
17 * performance, distribution or sublicensing of Refinement Analysis
18 * Tools and derivative works, in source code and object code form.
19 * This agreement not to assert does not confer, by implication,
20 * estoppel, or otherwise any license or rights in any intellectual
21 * property of Regents, including, but not limited to, any patents
22 * of Regents or Regents employees.
24 * IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT,
25 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
26 * INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE
27 * AND ITS DOCUMENTATION, EVEN IF REGENTS HAS BEEN ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
30 * REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
32 * FOR A PARTICULAR PURPOSE AND FURTHER DISCLAIMS ANY STATUTORY
33 * WARRANTY OF NON-INFRINGEMENT. THE SOFTWARE AND ACCOMPANYING
34 * DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS
35 * IS". REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
36 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
38 package edu
.berkeley
.cs
.bodik
.svelte
.plugin
.cgview
;
40 import java
.util
.ArrayList
;
41 import java
.util
.Collection
;
42 import java
.util
.HashSet
;
43 import java
.util
.Hashtable
;
44 import java
.util
.Iterator
;
46 import com
.ibm
.wala
.ipa
.callgraph
.CGNode
;
47 import com
.ibm
.wala
.ipa
.slicer
.NormalReturnCallee
;
48 import com
.ibm
.wala
.ipa
.slicer
.NormalReturnCaller
;
49 import com
.ibm
.wala
.ipa
.slicer
.NormalStatement
;
50 import com
.ibm
.wala
.ipa
.slicer
.ParamCallee
;
51 import com
.ibm
.wala
.ipa
.slicer
.ParamCaller
;
52 import com
.ibm
.wala
.ipa
.slicer
.Statement
;
53 import com
.ibm
.wala
.types
.TypeReference
;
54 import com
.ibm
.wala
.util
.graph
.Graph
;
56 import edu
.berkeley
.cs
.bodik
.svelte
.Slice
;
57 import edu
.berkeley
.cs
.bodik
.svelte
.StatementUtils
;
58 import edu
.berkeley
.cs
.bodik
.svelte
.plugin
.SliceEnvironment
;
60 public class CallGraphSlice
{
62 // if you want to work on performance, you could try having using CGNode for this instead (need
63 // another hash for string lookups)
64 private Hashtable
<String
, Node
> allnodes
= null;
66 // fakenewnodes are added to represent new X() calls which assign a default value of x.property.
67 // for instance, if the function foo() contains the line "b = new Bar()", which causes
68 // b.myObject to have a default
69 // value of null, and b.myObject is used later in function hello(), we don't want to show foo()
70 // having a direct edge to hello()
71 // rather we want to show an edge from foo to a "fake new node", and from this to hello()
72 // private Hashtable<TypeReference, FakeNewNode> fakenewnodes = null;
73 // 2008-05-06: wasn't read so was giving a warning, taken out. I don't know why?
76 private Hashtable
<Edge
, Edge
> alledges
= null;
78 // a mapping from class -> unique index, 1,2,3,... in the order came upon by the BFS traversal
80 Hashtable
<String
, Integer
> classesToIndices
;
82 private Hashtable
<Node
, ArrayList
<Edge
>> predEdges
= null;
85 public CallGraphSlice(Slice seed
, int depth
) {
91 boolean drawPredNodes
= false;
94 class RegularNode
extends Node
{
97 public RegularNode(CGNode node
) {
102 public int hashCode() {
103 return node
.getMethod().getSignature().hashCode();
107 public boolean equals(Object xx
) {
109 return (xx
instanceof RegularNode
) && ((x
= (RegularNode
) xx
) != null)
110 && x
.node
.getMethod().getSignature().equals(node
.getMethod().getSignature());
111 // compare based on signature.
115 public String
toString() {
116 return node
.getMethod().getSignature();
120 class FakeNewNode
extends Node
{
121 TypeReference typref
; // reference to the constructor function
123 public FakeNewNode(TypeReference newInstType
) {
124 this.typref
= newInstType
;
128 public int hashCode() {
129 return typref
.toString().hashCode();
133 public boolean equals(Object xx
) {
135 return (xx
instanceof FakeNewNode
) && ((x
= (FakeNewNode
) xx
) != null)
136 && x
.typref
.toString().equals(typref
.toString());
137 // compare based on signature.
141 public String
toString() {
142 return typref
.toString();
147 public static final int TYPE_THRU_PARAM
= 1;
148 public static final int TYPE_THRU_RETVAL
= 2;
149 public static final int TYPE_THRU_ALIASING
= 3;
152 public int duplicity
;
156 public Edge(int type
, Node from
, Node to
) {
164 public int hashCode() {
165 return from
.hashCode() + to
.hashCode() + type
;
169 public boolean equals(Object xx
) {
171 return (xx
instanceof Edge
) && ((x
= (Edge
) xx
) != null) && from
.equals(x
.from
)
172 && to
.equals(x
.to
) && type
== x
.type
;
177 private Node
getNodeFromCGNode(CGNode cgn
) {
178 assert (allnodes
!= null);
179 Node node
= allnodes
.get(cgn
.getMethod().getSignature());
181 node
= new RegularNode(cgn
);
182 allnodes
.put(cgn
.getMethod().getSignature(), node
);
184 // if this class not listed in classesToColor, add this class.
185 String sig
= node
.toString();
186 int lastdot
= sig
.lastIndexOf('.');
187 String classname
= sig
.substring(0, lastdot
);
188 if (classesToIndices
.get(classname
) == null)
189 classesToIndices
.put(classname
, new Integer(classesToIndices
.size() + 1));
195 * Add edge to alledges and tosPredEdges. If it is already in (check all edges but should be in
196 * both in), just increments the duplicity (if the second arg is true)
200 private void addEdge(Edge edgeToAdd
, boolean ignoreIfAlreadyIn
) {
201 if (edgeToAdd
!= null) {
202 Edge alreadyIn
= alledges
.get(edgeToAdd
);
203 if (alreadyIn
== null) {
204 alledges
.put(edgeToAdd
, edgeToAdd
);
206 ArrayList
<Edge
> tosPredEdges
= predEdges
.get(edgeToAdd
.to
);
207 if (tosPredEdges
== null) {
208 tosPredEdges
= new ArrayList
<Edge
>();
209 predEdges
.put(edgeToAdd
.to
, tosPredEdges
);
211 tosPredEdges
.add(edgeToAdd
);
213 if (!ignoreIfAlreadyIn
)
214 alreadyIn
.duplicity
+= 1;
219 private Node
getOrMakeFakeNewNode(TypeReference newInstType
) {
220 assert (allnodes
!= null);
221 Node node
= allnodes
.get(newInstType
.toString());
223 node
= new FakeNewNode(newInstType
);
224 allnodes
.put(newInstType
.toString(), node
);
226 // if this class not listed in classesToColor, add this class.
227 String classname
= newInstType
.getClass().toString();
228 System
.out
.println("DEBUG fakenewnode class = " + classname
);
229 if (classesToIndices
.get(classname
) == null)
230 classesToIndices
.put(classname
, new Integer(classesToIndices
.size() + 1));
235 // this also calculates the colors (via getNodeFromCGNode)
236 public void recalculateGraph() {
237 // clear nodes & edges
238 allnodes
= new Hashtable
<String
, Node
>();
239 // fakenewnodes = new Hashtable<TypeReference, FakeNewNode>();
240 alledges
= new Hashtable
<Edge
, Edge
>();
241 predEdges
= new Hashtable
<Node
, ArrayList
<Edge
>>();
242 classesToIndices
= new Hashtable
<String
, Integer
>();
244 // do the cgview stuff and generate the dot code
246 // NEVER fix method seed statements here. They should have already been fixed in the seed.
248 Graph
<Statement
> depGraph
= SliceEnvironment
.getDepGraph();
250 ArrayList
<Statement
> worklist
= new ArrayList
<Statement
>();
251 HashSet
<Statement
> serviced
= new HashSet
<Statement
>();
253 worklist
.addAll(seed
.getStatements());
256 int nextlevelindex
= worklist
.size();
259 while ((index
+ 1) < worklist
.size()) {
261 if (index
== nextlevelindex
) {
262 nextlevelindex
= worklist
.size();
267 Statement s
= worklist
.get(index
);
270 if (serviced
.contains(s
))
274 // look thru the succ nodes, if theres any 'return val' ones or something, build the
276 Iterator
<?
extends Statement
> succNodes
= depGraph
.getSuccNodes(s
);
278 while (succNodes
.hasNext()) {
279 ss
= succNodes
.next();
281 Node fromnode
= null, tonode
= null;
283 if (s
instanceof ParamCallee
&& ss
instanceof ParamCaller
) {
284 // node of ss calls node of s, passing in the data as a parameter
285 tonode
= getNodeFromCGNode(((ParamCallee
) s
).getNode());
286 fromnode
= getNodeFromCGNode(((ParamCaller
) ss
).getNode());
287 addEdge(new Edge(Edge
.TYPE_THRU_PARAM
, fromnode
, tonode
), false);
288 } else if (s
instanceof NormalReturnCaller
&& ss
instanceof NormalReturnCallee
) {
289 // node of s calls node of ss, gets data from node of ss as a return value
290 tonode
= getNodeFromCGNode(((NormalReturnCaller
) s
).getNode());
291 fromnode
= getNodeFromCGNode(((NormalReturnCallee
) ss
).getNode());
292 addEdge(new Edge(Edge
.TYPE_THRU_RETVAL
, fromnode
, tonode
), false);
293 } else if (s
instanceof NormalStatement
&& ss
instanceof NormalStatement
294 && ((NormalStatement
) s
).getNode() != ((NormalStatement
) ss
).getNode()) {
295 // ss writes to some pointerkey which s reads from, thereby passing data
298 tonode
= getNodeFromCGNode(((NormalStatement
) s
).getNode());
299 fromnode
= getNodeFromCGNode(((NormalStatement
) ss
).getNode());
301 TypeReference newInstType
= StatementUtils
.getNewInstructionDeclaredType(ss
);
302 if (newInstType
!= null) { // if it's a SSANewInstruction
303 // System.out.println("fakenewnode from " + ss + " --to-- " + s);
304 Node fakenewnode
= getOrMakeFakeNewNode(newInstType
);
305 addEdge(new Edge(Edge
.TYPE_THRU_ALIASING
, fakenewnode
, tonode
), true); // don't
311 addEdge(new Edge(Edge
.TYPE_THRU_PARAM
, fromnode
, fakenewnode
), false);
313 addEdge(new Edge(Edge
.TYPE_THRU_ALIASING
, fromnode
, tonode
), false);
316 // add succ nodes to worklist if not already serviced.
317 if (!serviced
.contains(ss
))
322 System
.out
.println("! total # of nodes: " + allnodes
.size());
323 System
.out
.println("! total # of edges: " + alledges
.size());
324 System
.out
.println("! total # of statements: " + index
);
328 public void toggleCollapsed(String nodename
) {
329 if (allnodes
!= null) {
330 Node node
= allnodes
.get(nodename
);
332 node
.drawPredNodes
= !node
.drawPredNodes
;
334 System
.err
.println("can't find node " + nodename
+ "!");
339 public StringBuffer
generateDot() {
340 if (predEdges
== null)
343 // do a BFS of the CG slice graph and find which nodes and edges to draw.
344 HashSet
<Node
> nodesToDraw
= new HashSet
<Node
>();
345 // HashSet<Edge> edgesToDraw = new HashSet<Edge>();
347 ArrayList
<Node
> orderedNodesToDraw
= new ArrayList
<Node
>(); // preserver order of BFS to not
348 // surprise users when they
349 // expand/collapse things
350 ArrayList
<Edge
> orderedEdgesToDraw
= new ArrayList
<Edge
>();
352 ArrayList
<Node
> worklist
= new ArrayList
<Node
>();
355 // start at the seed, as always
356 for (Statement s
: seed
) {
357 if (s
instanceof NormalStatement
) {
358 worklist
.add(getNodeFromCGNode(((NormalStatement
) s
).getNode()));
363 while ((index
+ 1) < worklist
.size()) {
365 Node n
= worklist
.get(index
);
368 if (nodesToDraw
.contains(n
))
371 orderedNodesToDraw
.add(n
);
373 // look thru the nodes pointing to it and add them
374 if (n
.drawPredNodes
) {
375 ArrayList
<Edge
> nsPredEdges
= predEdges
.get(n
);
376 if (nsPredEdges
!= null) {
377 for (Edge e
: nsPredEdges
) {
378 worklist
.add(e
.from
);
379 // edgesToDraw.add(e);
380 orderedEdgesToDraw
.add(e
);
385 return generateDot(orderedNodesToDraw
, orderedEdgesToDraw
);
388 public StringBuffer
generateDot(Collection
<Node
> nodes
, Collection
<Edge
> edges
) {
389 StringBuffer buf
= new StringBuffer();
392 buf
.append("digraph callgraphslice {\n");
393 buf
.append("rankdir=RL;\n");
395 .append("node [shape=rect,height=0,width=0,fontsize=12,fillcolor=mediumaquamarine,style=filled]\n");
397 // nodes (need URL property)
399 String
[] colors
= { "greenyellow", "aliceblue", "salmon", "lightskyblue", "lightslateblue",
402 for (Node n
: nodes
) {
403 String sig
= n
.toString();
405 Integer colorindex
= null;
407 int lastdot
= sig
.lastIndexOf('.');
408 String classname
= sig
.substring(0, lastdot
);
409 String methodname
= sig
.substring(lastdot
+ 1, sig
.length());
410 lastdot
= classname
.lastIndexOf('.');
411 String classclassname
= classname
.substring(lastdot
+ 1, classname
.length());
413 label
= methodname
+ " -- " + classclassname
;
414 colorindex
= classesToIndices
.get(classname
);
415 } catch (Exception e
) {
418 String color
= "lightgrey";
419 if (colorindex
!= null)
420 color
= colors
[colorindex
.intValue() % colors
.length
];
422 buf
.append(String
.format(
423 "\"%s\" [URL=\"%s\",fillcolor=\"%s\",label=\"%s\",color=\"%s\"]\n", n
, n
,
424 color
, label
, ((!n
.drawPredNodes
) && predEdges
.get(n
) != null) ?
"black"
429 for (Edge e
: edges
) {
430 String s
= String
.format("\"%s\" -> \"%s\" [", e
.from
, e
.to
);
432 s
+= String
.format("label=\"x%2d\",", e
.duplicity
);
434 if (e
.type
== Edge
.TYPE_THRU_PARAM
)
436 else if (e
.type
== Edge
.TYPE_THRU_RETVAL
)
437 s
+= "color=red,arrowhead=inv";
438 else if (e
.type
== Edge
.TYPE_THRU_ALIASING
)