KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > soot > util > PhaseDumper


1 /* Soot - a J*va Optimization Framework
2  * Copyright (C) 2003 John Jorgensen
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */

19
20 /*
21  * Modified by the Sable Research Group and others 1997-2003.
22  * See the 'credits' file distributed with Soot for the complete list of
23  * contributors. (Soot is distributed at http://www.sable.mcgill.ca/soot)
24  */

25
26
27 package soot.util;
28
29 import java.io.File JavaDoc;
30 import java.io.IOException JavaDoc;
31 import java.io.PrintWriter JavaDoc;
32 import java.util.Collections JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Map JavaDoc;
37 import java.util.StringTokenizer JavaDoc;
38 import soot.Body;
39 import soot.G;
40 import soot.PhaseOptions;
41 import soot.Printer;
42 import soot.Scene;
43 import soot.Singletons;
44 import soot.SootClass;
45 import soot.SootMethod;
46 import soot.options.Options;
47 import soot.toolkits.graph.ExceptionalGraph;
48 import soot.toolkits.graph.DirectedGraph;
49 import soot.util.Chain;
50 import soot.util.cfgcmd.CFGToDotGraph;
51 import soot.util.dot.DotGraph;
52
53
54 /**
55  * The <tt>PhaseDumper</tt> is a debugging aid. It maintains two
56  * lists of phases to be debugged. If a phase is on the
57  * <code>bodyDumpingPhases</code> list, then the intermediate
58  * representation of the bodies being manipulated by the phase is
59  * dumped before and after the phase is applied. If a phase is on the
60  * <code>cfgDumpingPhases</code> list, then whenever a CFG is
61  * constructed during the phase, a dot file is dumped representing the
62  * CFG constructed.
63  */

64
65 public class PhaseDumper {
66     private List JavaDoc bodyDumpingPhases;
67     private List JavaDoc cfgDumpingPhases;
68
69     private class PhaseStack extends ArrayList JavaDoc {
70     // We eschew java.util.Stack to avoid synchronization overhead.
71

72     private final static int initialCapacity = 4;
73     final static String JavaDoc EMPTY_STACK_PHASE_NAME = "NOPHASE";
74
75     PhaseStack() {
76         super(initialCapacity);
77     }
78
79     boolean empty() {
80         return (this.size() == 0);
81     }
82
83     String JavaDoc currentPhase() {
84         if (this.size() <= 0) {
85         return EMPTY_STACK_PHASE_NAME;
86         } else {
87         return (String JavaDoc) this.get(this.size() - 1);
88         }
89     }
90
91     String JavaDoc pop() {
92         return (String JavaDoc) this.remove(this.size() - 1);
93     }
94
95     String JavaDoc push(String JavaDoc phaseName) {
96         this.add(phaseName);
97         return phaseName;
98     }
99     }
100     private PhaseStack phaseStack = new PhaseStack();
101     final static String JavaDoc allWildcard = "ALL";
102
103
104     public PhaseDumper(Singletons.Global g) {
105     bodyDumpingPhases = Options.v().dump_body();
106     cfgDumpingPhases = Options.v().dump_cfg();
107     }
108
109
110     /**
111      * Returns the single instance of <code>PhaseDumper</code>.
112      *
113      * @return Soot's <code>PhaseDumper</code>.
114      */

115     public static PhaseDumper v() {
116     return G.v().soot_util_PhaseDumper();
117     }
118
119
120     private boolean isBodyDumpingPhase(String JavaDoc phaseName) {
121     return bodyDumpingPhases.contains(phaseName) ||
122         bodyDumpingPhases.contains(allWildcard);
123     }
124
125
126     private boolean isCFGDumpingPhase(String JavaDoc phaseName) {
127     return cfgDumpingPhases.contains(phaseName) ||
128         cfgDumpingPhases.contains(allWildcard);
129     }
130
131
132     private static java.io.File JavaDoc makeDirectoryIfMissing(Body b)
133     throws java.io.IOException JavaDoc {
134     StringBuffer JavaDoc buf =
135         new StringBuffer JavaDoc(soot.SourceLocator.v().getOutputDir());
136     buf.append(File.separatorChar);
137     String JavaDoc className = b.getMethod().getDeclaringClass().getName();
138     buf.append(className);
139     buf.append(File.separatorChar);
140     buf.append(b.getMethod().getSubSignature());
141     java.io.File JavaDoc dir = new java.io.File JavaDoc(buf.toString());
142     if (dir.exists()) {
143         if (! dir.isDirectory()) {
144         throw new java.io.IOException JavaDoc(dir.getPath() + " exists but is not a directory.");
145         }
146     } else {
147         if (! dir.mkdirs()) {
148         throw new java.io.IOException JavaDoc("Unable to mkdirs " + dir.getPath());
149         }
150     }
151     return dir;
152     }
153
154
155     private static PrintWriter JavaDoc openBodyFile(Body b, String JavaDoc baseName)
156     throws java.io.IOException JavaDoc {
157     File JavaDoc dir = makeDirectoryIfMissing(b);
158     String JavaDoc filePath = dir.toString() + File.separatorChar + baseName;
159     return
160         new PrintWriter JavaDoc(new java.io.FileOutputStream JavaDoc(filePath));
161     }
162
163
164     /**
165      * Returns the next available name for a graph file.
166      */

167
168     private static String JavaDoc nextGraphFileName(Body b, String JavaDoc baseName)
169     throws java.io.IOException JavaDoc {
170     // We number output files to allow multiple graphs per phase.
171
File JavaDoc dir = makeDirectoryIfMissing(b);
172     final String JavaDoc prefix = dir.toString() + File.separatorChar + baseName;
173     String JavaDoc filename = null;
174     File JavaDoc file = null;
175     int fileNumber = 0;
176     do {
177         file = new File JavaDoc(prefix + fileNumber + DotGraph.DOT_EXTENSION);
178         fileNumber++;
179     } while (file.exists());
180     return file.toString();
181     }
182
183
184     private static void deleteOldGraphFiles(final Body b,
185                         final String JavaDoc phaseName) {
186     try {
187         final File JavaDoc dir = makeDirectoryIfMissing(b);
188         final File JavaDoc[] toDelete = dir.listFiles(new java.io.FilenameFilter JavaDoc() {
189             public boolean accept(File JavaDoc dir, String JavaDoc name) {
190             return name.startsWith(phaseName) &&
191                 name.endsWith(DotGraph.DOT_EXTENSION);
192             }
193         });
194         for (int i = 0; i < toDelete.length ; i++) {
195         toDelete[i].delete();
196         }
197     } catch (java.io.IOException JavaDoc e) {
198         // Don't abort execution because of an I/O error, but report
199
// the error.
200
G.v().out.println("PhaseDumper.dumpBody() caught: " + e.toString());
201         e.printStackTrace(G.v().out);
202     }
203     }
204
205
206     // soot.Printer itself needs to create a BriefUnitGraph in order
207
// to format the text for a method's instructions, so this flag is
208
// a hack to avoid dumping graphs that we create in the course of
209
// dumping bodies or other graphs.
210
//
211
// Note that this hack would not work if a PhaseDumper might be
212
// accessed by multiple threads. So long as there is a single
213
// active PhaseDumper accessed through soot.G, it seems
214
// safe to assume it will be accessed by only a single thread.
215
private boolean alreadyDumping = false;
216     
217     private void dumpBody(Body b, String JavaDoc baseName) {
218     try {
219         alreadyDumping = true;
220         java.io.PrintWriter JavaDoc out = openBodyFile(b, baseName);
221         soot.Printer.v().setOption(Printer.USE_ABBREVIATIONS);
222         soot.Printer.v().printTo(b, out);
223         out.close();
224     } catch (java.io.IOException JavaDoc e) {
225         // Don't abort execution because of an I/O error, but let
226
// the user know.
227
G.v().out.println("PhaseDumper.dumpBody() caught: " + e.toString());
228         e.printStackTrace(G.v().out);
229     } finally {
230         alreadyDumping = false;
231     }
232     }
233
234     private void dumpAllBodies(String JavaDoc baseName,
235                       boolean deleteGraphFiles) {
236     List JavaDoc classes = Scene.v().getClasses(SootClass.BODIES);
237     for (Iterator JavaDoc c = classes.iterator(); c.hasNext(); ) {
238         SootClass cls = (SootClass) c.next();
239         for (Iterator JavaDoc m = cls.getMethods().iterator(); m.hasNext(); ) {
240         SootMethod method = (SootMethod) m.next();
241         if (method.hasActiveBody()) {
242             Body body = method.getActiveBody();
243             if (deleteGraphFiles) {
244             deleteOldGraphFiles(body, baseName);
245             }
246             dumpBody(body, baseName);
247         }
248         }
249     }
250     }
251
252
253     /**
254      * Tells the <code>PhaseDumper</code> that a {@link Body}
255      * transforming phase has started, so that it can dump the
256      * phases's &ldquo;before&rdquo; file. If the phase is to be
257      * dumped, <code>dumpBefore</code> deletes any old
258      * graph files dumped during previous runs of the phase.
259      *
260      * @param b the {@link Body} being transformed.
261      * @param phaseName the name of the phase that has just started.
262      */

263     public void dumpBefore(Body b, String JavaDoc phaseName) {
264     phaseStack.push(phaseName);
265     if (isBodyDumpingPhase(phaseName)) {
266         deleteOldGraphFiles(b, phaseName);
267         dumpBody(b, phaseName + ".in");
268     }
269     }
270
271
272     /**
273      * Tells the <code>PhaseDumper</code> that a {@link Body}
274      * transforming phase has ended, so that it can dump the
275      * phases's &ldquo;after&rdquo; file.
276      *
277      * @param b the {@link Body} being transformed.
278      *
279      * @param phaseName the name of the phase that has just ended.
280      *
281      * @throws IllegalArgumentException if <code>phaseName</code> does not
282      * match the <code>PhaseDumper</code>'s record of the current phase.
283      */

284     public void dumpAfter(Body b, String JavaDoc phaseName) {
285     String JavaDoc poppedPhaseName = phaseStack.pop();
286     if (poppedPhaseName != phaseName) {
287         throw new IllegalArgumentException JavaDoc("dumpAfter(" + phaseName +
288                            ") when poppedPhaseName == " +
289                            poppedPhaseName);
290     }
291     if (isBodyDumpingPhase(phaseName)) {
292         dumpBody(b, phaseName + ".out");
293     }
294     }
295
296
297     /**
298      * Tells the <code>PhaseDumper</code> that a {@link Scene}
299      * transforming phase has started, so that it can dump the
300      * phases's &ldquo;before&rdquo; files. If the phase is to be
301      * dumped, <code>dumpBefore</code> deletes any old
302      * graph files dumped during previous runs of the phase.
303      *
304      * @param phaseName the name of the phase that has just started.
305      */

306     public void dumpBefore(String JavaDoc phaseName) {
307     phaseStack.push(phaseName);
308     if (isBodyDumpingPhase(phaseName)) {
309         dumpAllBodies(phaseName + ".in", true);
310     }
311     }
312
313
314     /**
315      * Tells the <code>PhaseDumper</code> that a {@link Scene}
316      * transforming phase has ended, so that it can dump the
317      * phases's &ldquo;after&rdquo; files.
318      *
319      * @param phaseName the name of the phase that has just ended.
320      *
321      * @throws IllegalArgumentException if <code>phaseName</code> does not
322      * match the <code>PhaseDumper</code>'s record of the current phase.
323      */

324     public void dumpAfter(String JavaDoc phaseName) {
325     String JavaDoc poppedPhaseName = phaseStack.pop();
326     if (poppedPhaseName != phaseName) {
327         throw new IllegalArgumentException JavaDoc("dumpAfter(" + phaseName +
328                            ") when poppedPhaseName == " +
329                            poppedPhaseName);
330     }
331     if (isBodyDumpingPhase(phaseName)) {
332         dumpAllBodies(phaseName + ".out", false);
333     }
334     }
335
336
337     /**
338      * Asks the <code>PhaseDumper</code> to dump the passed {@link
339      * DirectedGraph} if the current phase is being dumped.
340      *
341      * @param g the graph to dump.
342      *
343      * @param body the {@link Body} represented by <code>g</code>.
344      */

345     public void dumpGraph(DirectedGraph g, Body b) {
346     if (alreadyDumping) {
347         return;
348     }
349     try {
350         alreadyDumping = true;
351         String JavaDoc phaseName = phaseStack.currentPhase();
352         if (isCFGDumpingPhase(phaseName)) {
353         try {
354             String JavaDoc outputFile = nextGraphFileName(b, phaseName + "-" +
355                               getClassIdent(g) + "-");
356             DotGraph dotGraph = new CFGToDotGraph().drawCFG(g, b);
357             dotGraph.plot(outputFile);
358
359         } catch (java.io.IOException JavaDoc e) {
360             // Don't abort execution because of an I/O error, but
361
// report the error.
362
G.v().out.println("PhaseDumper.dumpBody() caught: " +
363                       e.toString());
364             e.printStackTrace(G.v().out);
365         }
366         }
367     } finally {
368         alreadyDumping = false;
369     }
370     }
371
372
373     /**
374      * Asks the <code>PhaseDumper</code> to dump the passed {@link
375      * ExceptionalGraph} if the current phase is being dumped.
376      *
377      * @param g the graph to dump.
378      */

379     public void dumpGraph(ExceptionalGraph g) {
380     if (alreadyDumping) {
381         return;
382     }
383     try {
384         alreadyDumping = true;
385         String JavaDoc phaseName = phaseStack.currentPhase();
386         if (isCFGDumpingPhase(phaseName)) {
387         try {
388             String JavaDoc outputFile = nextGraphFileName(g.getBody(),
389                               phaseName + "-" +
390                               getClassIdent(g) + "-");
391             CFGToDotGraph drawer = new CFGToDotGraph();
392             drawer.setShowExceptions(Options.v().show_exception_dests());
393             DotGraph dotGraph = drawer.drawCFG(g);
394             dotGraph.plot(outputFile);
395
396         } catch (java.io.IOException JavaDoc e) {
397             // Don't abort execution because of an I/O error, but
398
// report the error.
399
G.v().out.println("PhaseDumper.dumpBody() caught: " +
400                       e.toString());
401             e.printStackTrace(G.v().out);
402         }
403         }
404     } finally {
405         alreadyDumping = false;
406     }
407     }
408
409     /**
410      * A utility routine that returns the unqualified identifier
411      * naming the class of an object.
412      *
413      * @param obj The object whose class name is to be returned.
414      */

415     private String JavaDoc getClassIdent(Object JavaDoc obj) {
416     String JavaDoc qualifiedName = obj.getClass().getName();
417     int lastDotIndex = qualifiedName.lastIndexOf('.');
418     return qualifiedName.substring(lastDotIndex+1);
419     }
420
421     
422     /**
423      * Prints the current stack trace, as a brute force tool for
424      * program understanding. This method appeared in response to the
425      * many times dumpGraph() was being called while the phase stack
426      * was empty. Turned out that the Printer needs to
427      * build a BriefUnitGraph in order to print a graph. Doh!
428      */

429     public void printCurrentStackTrace() {
430     try {
431         throw new java.io.IOException JavaDoc("FAKE");
432     } catch (java.io.IOException JavaDoc e) {
433         e.printStackTrace(G.v().out);
434     }
435     }
436 }
437
Popular Tags