add option to graph visualization that supresses reachability subsets, for improved...
[IRC.git] / Robust / src / Analysis / OwnershipAnalysis / OwnershipAnalysis.java
1 package Analysis.OwnershipAnalysis;
2
3 import Analysis.CallGraph.*;
4 import IR.*;
5 import IR.Flat.*;
6 import IR.Tree.Modifiers;
7 import java.util.*;
8 import java.io.*;
9
10
11 public class OwnershipAnalysis {
12
13
14   ///////////////////////////////////////////
15   //
16   //  Public interface to discover possible
17   //  aliases in the program under analysis
18   //
19   ///////////////////////////////////////////
20
21   public HashSet<AllocationSite>
22   getFlaggedAllocationSitesReachableFromTask(TaskDescriptor td) {
23     checkAnalysisComplete();
24     return getFlaggedAllocationSitesReachableFromTaskPRIVATE(td);
25   }
26
27   public AllocationSite getAllocationSiteFromFlatNew(FlatNew fn) {
28     checkAnalysisComplete();
29     return getAllocationSiteFromFlatNewPRIVATE(fn);
30   }
31
32   public AllocationSite getAllocationSiteFromHeapRegionNodeID(Integer id) {
33     checkAnalysisComplete();
34     return mapHrnIdToAllocationSite.get(id);
35   }
36
37   public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
38                                          int paramIndex1,
39                                          int paramIndex2) {
40     checkAnalysisComplete();
41     OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
42     assert(og != null);
43     return og.hasPotentialAlias(paramIndex1, paramIndex2);
44   }
45
46   public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
47                                          int paramIndex,
48                                          AllocationSite alloc) {
49     checkAnalysisComplete();
50     OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
51     assert(og != null);
52     return og.hasPotentialAlias(paramIndex, alloc);
53   }
54
55   public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
56                                          AllocationSite alloc,
57                                          int paramIndex) {
58     checkAnalysisComplete();
59     OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
60     assert(og != null);
61     return og.hasPotentialAlias(paramIndex, alloc);
62   }
63
64   public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
65                                          AllocationSite alloc1,
66                                          AllocationSite alloc2) {
67     checkAnalysisComplete();
68     OwnershipGraph og = getGraphOfAllContextsFromDescriptor(taskOrMethod);
69     assert(og != null);
70     return og.hasPotentialAlias(alloc1, alloc2);
71   }
72
73
74   protected OwnershipGraph getGraphOfAllContextsFromDescriptor(Descriptor d) {
75     checkAnalysisComplete();
76
77     assert d != null;
78
79     OwnershipGraph og = new OwnershipGraph( allocationDepth, typeUtil );
80
81     assert mapDescriptorToAllMethodContexts.containsKey( d );
82     HashSet<MethodContext> contexts = mapDescriptorToAllMethodContexts.get( d );
83     Iterator<MethodContext> mcItr = contexts.iterator();
84     while( mcItr.hasNext() ) {
85       MethodContext mc = mcItr.next();
86
87       OwnershipGraph ogContext = mapMethodContextToCompleteOwnershipGraph.get(mc);
88       assert ogContext != null;
89
90       og.merge( ogContext );
91     }
92
93     return og;
94   }
95
96
97   public String prettyPrintNodeSet( Set<HeapRegionNode> s ) {    
98     checkAnalysisComplete();
99
100     String out = "{\n";
101
102     Iterator<HeapRegionNode> i = s.iterator();
103     while( i.hasNext() ) {
104       HeapRegionNode n = i.next();
105
106       AllocationSite as = n.getAllocationSite();
107       if( as == null ) {
108         out += "  "+n.toString()+",\n";
109       } else {
110         out += "  "+n.toString()+": "+as.toStringVerbose()+",\n";
111       }
112     }
113
114     out += "}\n";
115     return out;
116   }
117
118
119   // use the methods given above to check every possible alias
120   // between task parameters and flagged allocation sites reachable
121   // from the task
122   public void writeAllAliases(String outputFile, String timeReport) throws java.io.IOException {
123     checkAnalysisComplete();
124
125     BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
126
127     bw.write("Conducting ownership analysis with allocation depth = "+allocationDepth+"\n");
128     bw.write(timeReport+"\n");
129
130     // look through every task for potential aliases
131     Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
132     while( taskItr.hasNext() ) {
133       TaskDescriptor td = (TaskDescriptor) taskItr.next();
134
135       bw.write("\n---------"+td+"--------\n");
136
137       HashSet<AllocationSite> allocSites = getFlaggedAllocationSitesReachableFromTask(td);
138
139       Set<HeapRegionNode> common;
140
141       // for each task parameter, check for aliases with
142       // other task parameters and every allocation site
143       // reachable from this task
144       boolean foundSomeAlias = false;
145
146       FlatMethod fm = state.getMethodFlat(td);
147       for( int i = 0; i < fm.numParameters(); ++i ) {
148
149         // for the ith parameter check for aliases to all
150         // higher numbered parameters
151         for( int j = i + 1; j < fm.numParameters(); ++j ) {
152           common = createsPotentialAliases(td, i, j);
153           if( !common.isEmpty() ) {
154             foundSomeAlias = true;
155             bw.write("Potential alias between parameters "+i+" and "+j+".\n");
156             bw.write(prettyPrintNodeSet( common )+"\n" );
157           }
158         }
159
160         // for the ith parameter, check for aliases against
161         // the set of allocation sites reachable from this
162         // task context
163         Iterator allocItr = allocSites.iterator();
164         while( allocItr.hasNext() ) {
165           AllocationSite as = (AllocationSite) allocItr.next();
166           common = createsPotentialAliases(td, i, as);
167           if( !common.isEmpty() ) {
168             foundSomeAlias = true;
169             bw.write("Potential alias between parameter "+i+" and "+as.getFlatNew()+".\n");
170             bw.write(prettyPrintNodeSet( common )+"\n" );
171           }
172         }
173       }
174
175       // for each allocation site check for aliases with
176       // other allocation sites in the context of execution
177       // of this task
178       HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
179       Iterator allocItr1 = allocSites.iterator();
180       while( allocItr1.hasNext() ) {
181         AllocationSite as1 = (AllocationSite) allocItr1.next();
182
183         Iterator allocItr2 = allocSites.iterator();
184         while( allocItr2.hasNext() ) {
185           AllocationSite as2 = (AllocationSite) allocItr2.next();
186           
187           if( !outerChecked.contains(as2) ) {
188             common = createsPotentialAliases(td, as1, as2);
189
190             if( !common.isEmpty() ) {
191               foundSomeAlias = true;
192               bw.write("Potential alias between "+as1.getFlatNew()+" and "+as2.getFlatNew()+".\n");
193               bw.write(prettyPrintNodeSet( common )+"\n" );
194             }
195           }
196         }
197
198         outerChecked.add(as1);
199       }
200
201       if( !foundSomeAlias ) {
202         bw.write("No aliases between flagged objects in Task "+td+".\n");
203       }
204     }
205
206     bw.write( "\n"+computeAliasContextHistogram() );
207     bw.close();
208   }
209
210
211   // this version of writeAllAliases is for Java programs that have no tasks
212   public void writeAllAliasesJava(String outputFile, String timeReport) throws java.io.IOException {
213     checkAnalysisComplete();
214
215     assert !state.TASK;    
216
217     BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile) );
218
219     bw.write("Conducting ownership analysis with allocation depth = "+allocationDepth+"\n");
220     bw.write(timeReport+"\n\n");
221
222     boolean foundSomeAlias = false;
223
224     Descriptor d = typeUtil.getMain();
225     HashSet<AllocationSite> allocSites = getFlaggedAllocationSites(d);
226
227     // for each allocation site check for aliases with
228     // other allocation sites in the context of execution
229     // of this task
230     HashSet<AllocationSite> outerChecked = new HashSet<AllocationSite>();
231     Iterator allocItr1 = allocSites.iterator();
232     while( allocItr1.hasNext() ) {
233       AllocationSite as1 = (AllocationSite) allocItr1.next();
234       
235       Iterator allocItr2 = allocSites.iterator();
236       while( allocItr2.hasNext() ) {
237         AllocationSite as2 = (AllocationSite) allocItr2.next();
238         
239         if( !outerChecked.contains(as2) ) {       
240           Set<HeapRegionNode> common = createsPotentialAliases(d, as1, as2);
241
242           if( !common.isEmpty() ) {
243             foundSomeAlias = true;
244             bw.write("Potential alias between "+as1.getDisjointId()+" and "+as2.getDisjointId()+".\n");
245             bw.write( prettyPrintNodeSet( common )+"\n" );
246           }
247         }
248       }
249       
250       outerChecked.add(as1);
251     }
252     
253     if( !foundSomeAlias ) {
254       bw.write("No aliases between flagged objects found.\n");
255     }
256
257     bw.write( "\n"+computeAliasContextHistogram() );
258     bw.close();
259   }
260   ///////////////////////////////////////////
261   //
262   // end public interface
263   //
264   ///////////////////////////////////////////
265
266   protected void checkAnalysisComplete() {
267     if( !analysisComplete ) {
268       throw new Error("Warning: public interface method called while analysis is running.");
269     }
270   }
271
272
273
274
275
276   // data from the compiler
277   public State     state;
278   public CallGraph callGraph;
279   public TypeUtil  typeUtil;
280   public int       allocationDepth;
281
282   // for public interface methods to warn that they
283   // are grabbing results during analysis
284   private boolean analysisComplete;
285
286   // used to identify HeapRegionNode objects
287   // A unique ID equates an object in one
288   // ownership graph with an object in another
289   // graph that logically represents the same
290   // heap region
291   // start at 10 and increment to leave some
292   // reserved IDs for special purposes
293   static private int uniqueIDcount = 10;
294
295   // Use these data structures to track progress of
296   // processing all methods in the program, and by methods
297   // TaskDescriptor and MethodDescriptor are combined
298   // together, with a common parent class Descriptor
299   private Hashtable<MethodContext, OwnershipGraph>           mapMethodContextToInitialParamAllocGraph;
300   public  Hashtable<MethodContext, OwnershipGraph>           mapMethodContextToCompleteOwnershipGraph;
301   private Hashtable<FlatNew,       AllocationSite>           mapFlatNewToAllocationSite;
302   private Hashtable<Descriptor,    HashSet<AllocationSite> > mapDescriptorToAllocationSiteSet;
303   private Hashtable<MethodContext, Integer>                  mapMethodContextToNumUpdates;
304   private Hashtable<Descriptor,    HashSet<MethodContext> >  mapDescriptorToAllMethodContexts;
305   private Hashtable<MethodContext, HashSet<MethodContext> >  mapMethodContextToDependentContexts;
306   private Hashtable<Integer,       AllocationSite>           mapHrnIdToAllocationSite;
307
308   // Use these data structures to track progress of one pass of
309   // processing the FlatNodes of a particular method
310   private HashSet  <FlatNode>                 flatNodesToVisit;
311   private Hashtable<FlatNode, OwnershipGraph> mapFlatNodeToOwnershipGraph;
312   private HashSet  <FlatReturnNode>           returnNodesToCombineForCompleteOwnershipGraph;
313
314   // descriptorsToAnalyze identifies the set of tasks and methods
315   // that are reachable from the program tasks, this set is initialized
316   // and then remains static
317   public HashSet<Descriptor> descriptorsToAnalyze;
318
319   // descriptorsToVisit is initialized to descriptorsToAnalyze and is
320   // reduced by visiting a descriptor during analysis.  When dependents
321   // must be scheduled, only those contained in descriptorsToAnalyze
322   // should be re-added to this queue
323   private PriorityQueue<MethodContextQWrapper> methodContextsToVisitQ;
324   private Set          <MethodContext>         methodContextsToVisitSet;
325   private Hashtable<Descriptor, Integer> mapDescriptorToPriority;
326
327
328   // special field descriptors for array elements
329   public static final String arrayElementFieldName = "___element_";
330   private static Hashtable<TypeDescriptor, FieldDescriptor> mapTypeToArrayField =
331     new Hashtable<TypeDescriptor, FieldDescriptor>();
332
333
334   // for controlling DOT file output
335   private boolean writeDOTs;
336   private boolean writeAllDOTs;
337   
338   // for controlling method effects
339   private boolean methodEffects;
340   
341     //map each FlatNode to its own internal ownership graph
342         private MethodEffectsAnalysis meAnalysis;
343
344
345
346   // this analysis generates an ownership graph for every task
347   // in the program
348   public OwnershipAnalysis(State state,
349                            TypeUtil tu,
350                            CallGraph callGraph,
351                            int allocationDepth,
352                            boolean writeDOTs,
353                            boolean writeAllDOTs,
354                            String aliasFile) throws java.io.IOException {
355           
356           this.methodEffects = false;
357           init(state,tu,callGraph,allocationDepth,writeDOTs,writeAllDOTs,aliasFile);
358           
359   }
360   
361   public OwnershipAnalysis(State state,
362           TypeUtil tu,
363           CallGraph callGraph,
364           int allocationDepth,
365           boolean writeDOTs,
366           boolean writeAllDOTs,
367           String aliasFile,
368           boolean methodEffects) throws java.io.IOException {
369           
370           this.methodEffects = methodEffects;
371           init(state,tu,callGraph,allocationDepth,writeDOTs,writeAllDOTs,aliasFile);
372           
373   }
374   
375   private void init(State state,
376           TypeUtil tu,
377           CallGraph callGraph,
378           int allocationDepth,
379           boolean writeDOTs,
380           boolean writeAllDOTs,
381           String aliasFile) throws java.io.IOException {
382
383             analysisComplete = false;
384
385             this.state           = state;
386             this.typeUtil        = tu;
387             this.callGraph       = callGraph;
388             this.allocationDepth = allocationDepth;
389             this.writeDOTs       = writeDOTs;
390             this.writeAllDOTs    = writeAllDOTs;
391
392             descriptorsToAnalyze = new HashSet<Descriptor>();
393
394             mapMethodContextToInitialParamAllocGraph =
395               new Hashtable<MethodContext, OwnershipGraph>();
396
397             mapMethodContextToCompleteOwnershipGraph =
398               new Hashtable<MethodContext, OwnershipGraph>();
399
400             mapFlatNewToAllocationSite =
401               new Hashtable<FlatNew, AllocationSite>();
402
403             mapDescriptorToAllocationSiteSet =
404               new Hashtable<Descriptor, HashSet<AllocationSite> >();
405
406             mapDescriptorToAllMethodContexts = 
407               new Hashtable<Descriptor, HashSet<MethodContext> >();
408
409             mapMethodContextToDependentContexts =
410               new Hashtable<MethodContext, HashSet<MethodContext> >();
411
412             mapDescriptorToPriority = 
413               new Hashtable<Descriptor, Integer>();
414
415             mapHrnIdToAllocationSite =
416               new Hashtable<Integer, AllocationSite>();
417             
418             meAnalysis=new MethodEffectsAnalysis(methodEffects);
419
420
421             if( writeAllDOTs ) {
422               mapMethodContextToNumUpdates = new Hashtable<MethodContext, Integer>();
423             }
424
425
426             double timeStartAnalysis = (double) System.nanoTime();
427
428
429             if( state.TASK ) {
430               // initialize methods to visit as the set of all tasks in the
431               // program and then any method that could be called starting
432               // from those tasks
433               Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
434               while( taskItr.hasNext() ) {
435                 Descriptor d = (Descriptor) taskItr.next();
436                 scheduleAllCallees(d);
437               }
438
439             } else {
440               // we are not in task mode, just normal Java, so start with
441               // the main method
442               Descriptor d = typeUtil.getMain();
443               scheduleAllCallees(d);
444             }
445
446
447             // before beginning analysis, initialize every scheduled method
448             // with an ownership graph that has populated parameter index tables
449             // by analyzing the first node which is always a FlatMethod node
450             Iterator<Descriptor> dItr = descriptorsToAnalyze.iterator();
451             while( dItr.hasNext() ) {
452               Descriptor d  = dItr.next();
453               OwnershipGraph og = new OwnershipGraph(allocationDepth, typeUtil);
454
455               FlatMethod fm;
456               if( d instanceof MethodDescriptor ) {
457                 fm = state.getMethodFlat( (MethodDescriptor) d);
458               } else {
459                 assert d instanceof TaskDescriptor;
460                 fm = state.getMethodFlat( (TaskDescriptor) d);
461               }
462
463               MethodContext mc = new MethodContext( d );
464               assert !mapDescriptorToAllMethodContexts.containsKey( d );
465               HashSet<MethodContext> s = new HashSet<MethodContext>();
466               s.add( mc );
467               mapDescriptorToAllMethodContexts.put( d, s );
468
469               //System.out.println("Previsiting " + mc);
470
471               meAnalysis.createNewMapping(mc);
472
473               og = analyzeFlatNode(mc, fm, null, og);
474               setGraphForMethodContext(mc, og);
475             }
476
477             // as mentioned above, analyze methods one-by-one, possibly revisiting
478             // a method if the methods that it calls are updated
479             analyzeMethods();
480             analysisComplete = true;
481
482
483             double timeEndAnalysis = (double) System.nanoTime();
484             double dt = (timeEndAnalysis - timeStartAnalysis)/(Math.pow( 10.0, 9.0 ) );
485             String treport = String.format( "The reachability analysis took %.3f sec.", dt );
486             System.out.println( treport );
487
488             if( writeDOTs && !writeAllDOTs ) {
489               writeFinalContextGraphs();      
490             }  
491
492             if(methodEffects){
493                 meAnalysis.writeMethodEffectsResult();
494             }
495
496             if( aliasFile != null ) {
497               if( state.TASK ) {
498                 writeAllAliases(aliasFile, treport);
499               } else {
500                 writeAllAliasesJava(aliasFile, treport);
501               }
502             }
503           
504   }
505
506   // called from the constructor to help initialize the set
507   // of methods that needs to be analyzed by ownership analysis
508   private void scheduleAllCallees(Descriptor d) {
509     if( descriptorsToAnalyze.contains(d) ) {
510       return;
511     }
512     descriptorsToAnalyze.add(d);
513
514     // start with all method calls to further schedule
515     Set moreMethodsToCheck = moreMethodsToCheck = callGraph.getMethodCalls(d);
516
517     if( d instanceof MethodDescriptor ) {
518       // see if this method has virtual dispatch
519       Set virtualMethods = callGraph.getMethods( (MethodDescriptor)d);
520       moreMethodsToCheck.addAll(virtualMethods);
521     }
522
523     // keep following any further methods identified in
524     // the call chain
525     Iterator methItr = moreMethodsToCheck.iterator();
526     while( methItr.hasNext() ) {
527       Descriptor m = (Descriptor) methItr.next();
528       scheduleAllCallees(m);
529     }
530   }
531
532
533   // manage the set of tasks and methods to be analyzed
534   // and be sure to reschedule tasks/methods when the methods
535   // they call are updated
536   private void analyzeMethods() throws java.io.IOException {  
537
538     // first gather all of the method contexts to analyze
539     HashSet<MethodContext> allContexts = new HashSet<MethodContext>();
540     Iterator<Descriptor> itrd2a = descriptorsToAnalyze.iterator();
541     while( itrd2a.hasNext() ) {
542       HashSet<MethodContext> mcs = mapDescriptorToAllMethodContexts.get( itrd2a.next() );
543       assert mcs != null;
544
545       Iterator<MethodContext> itrmc = mcs.iterator();
546       while( itrmc.hasNext() ) {
547         allContexts.add( itrmc.next() );
548       }
549     }
550
551     // topologically sort them according to the caller graph so leaf calls are
552     // ordered first; use that ordering to give method contexts priorities
553     LinkedList<MethodContext> sortedMethodContexts = topologicalSort( allContexts );   
554
555     methodContextsToVisitQ   = new PriorityQueue<MethodContextQWrapper>();
556     methodContextsToVisitSet = new HashSet<MethodContext>();
557
558     int p = 0;
559     Iterator<MethodContext> mcItr = sortedMethodContexts.iterator();
560     while( mcItr.hasNext() ) {
561       MethodContext mc = mcItr.next();
562       mapDescriptorToPriority.put( mc.getDescriptor(), new Integer( p ) );
563       methodContextsToVisitQ.add( new MethodContextQWrapper( p, mc ) );
564       methodContextsToVisitSet.add( mc );
565       ++p;
566     }
567
568     // analyze methods from the priority queue until it is empty
569     while( !methodContextsToVisitQ.isEmpty() ) {
570       MethodContext mc = methodContextsToVisitQ.poll().getMethodContext();
571       assert methodContextsToVisitSet.contains( mc );
572       methodContextsToVisitSet.remove( mc );
573
574       // because the task or method descriptor just extracted
575       // was in the "to visit" set it either hasn't been analyzed
576       // yet, or some method that it depends on has been
577       // updated.  Recompute a complete ownership graph for
578       // this task/method and compare it to any previous result.
579       // If there is a change detected, add any methods/tasks
580       // that depend on this one to the "to visit" set.
581
582       System.out.println("Analyzing " + mc);
583
584       Descriptor d = mc.getDescriptor();
585       FlatMethod fm;
586       if( d instanceof MethodDescriptor ) {
587         fm = state.getMethodFlat( (MethodDescriptor) d);
588       } else {
589         assert d instanceof TaskDescriptor;
590         fm = state.getMethodFlat( (TaskDescriptor) d);
591       }
592
593       OwnershipGraph og = analyzeFlatMethod(mc, fm);
594       OwnershipGraph ogPrev = mapMethodContextToCompleteOwnershipGraph.get(mc);
595       if( !og.equals(ogPrev) ) {
596         setGraphForMethodContext(mc, og);
597
598         Iterator<MethodContext> depsItr = iteratorDependents( mc );
599         while( depsItr.hasNext() ) {
600           MethodContext mcNext = depsItr.next();
601
602           if( !methodContextsToVisitSet.contains( mcNext ) ) {
603             methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( mcNext.getDescriptor() ), 
604                                                                    mcNext ) );
605             methodContextsToVisitSet.add( mcNext );
606           }
607         }
608       }
609     }
610
611   }
612
613
614   // keep passing the Descriptor of the method along for debugging
615   // and dot file writing
616   private OwnershipGraph
617   analyzeFlatMethod(MethodContext mc,
618                     FlatMethod flatm) throws java.io.IOException {
619
620     // initialize flat nodes to visit as the flat method
621     // because it is the entry point
622
623     flatNodesToVisit = new HashSet<FlatNode>();
624     flatNodesToVisit.add(flatm);
625
626     // initilize the mapping of flat nodes in this flat method to
627     // ownership graph results to an empty mapping
628     mapFlatNodeToOwnershipGraph = new Hashtable<FlatNode, OwnershipGraph>();
629
630     // initialize the set of return nodes that will be combined as
631     // the final ownership graph result to return as an empty set
632     returnNodesToCombineForCompleteOwnershipGraph = new HashSet<FlatReturnNode>();
633
634
635     while( !flatNodesToVisit.isEmpty() ) {
636       FlatNode fn = (FlatNode) flatNodesToVisit.iterator().next();
637       flatNodesToVisit.remove(fn);
638
639       //System.out.println( "  "+fn );
640
641       // perform this node's contributions to the ownership
642       // graph on a new copy, then compare it to the old graph
643       // at this node to see if anything was updated.
644       OwnershipGraph og = new OwnershipGraph(allocationDepth, typeUtil);
645
646       // start by merging all node's parents' graphs
647       for( int i = 0; i < fn.numPrev(); ++i ) {
648         FlatNode pn = fn.getPrev(i);
649         if( mapFlatNodeToOwnershipGraph.containsKey(pn) ) {
650           OwnershipGraph ogParent = mapFlatNodeToOwnershipGraph.get(pn);
651           og.merge(ogParent);
652         }
653       }
654
655       // apply the analysis of the flat node to the
656       // ownership graph made from the merge of the
657       // parent graphs
658       og = analyzeFlatNode(mc,
659                            fn,
660                            returnNodesToCombineForCompleteOwnershipGraph,
661                            og);
662
663       
664
665      
666       if( takeDebugSnapshots && 
667           mc.getDescriptor().getSymbol().equals( mcDescSymbolDebug ) ) {
668         debugSnapshot(og,fn);
669       }
670      
671
672
673       // if the results of the new graph are different from
674       // the current graph at this node, replace the graph
675       // with the update and enqueue the children for
676       // processing
677       OwnershipGraph ogPrev = mapFlatNodeToOwnershipGraph.get(fn);
678       if( !og.equals(ogPrev) ) {
679         mapFlatNodeToOwnershipGraph.put(fn, og);
680
681         for( int i = 0; i < fn.numNext(); i++ ) {
682           FlatNode nn = fn.getNext(i);
683           flatNodesToVisit.add(nn);
684         }
685       }
686     }
687
688     // end by merging all return nodes into a complete
689     // ownership graph that represents all possible heap
690     // states after the flat method returns
691     OwnershipGraph completeGraph = new OwnershipGraph(allocationDepth, typeUtil);
692     Iterator retItr = returnNodesToCombineForCompleteOwnershipGraph.iterator();
693     while( retItr.hasNext() ) {
694       FlatReturnNode frn = (FlatReturnNode) retItr.next();
695       assert mapFlatNodeToOwnershipGraph.containsKey(frn);
696       OwnershipGraph ogr = mapFlatNodeToOwnershipGraph.get(frn);
697       completeGraph.merge(ogr);
698     }
699
700     return completeGraph;
701   }
702
703
704   private OwnershipGraph
705   analyzeFlatNode(MethodContext mc,
706                   FlatNode fn,
707                   HashSet<FlatReturnNode> setRetNodes,
708                   OwnershipGraph og) throws java.io.IOException {
709           
710     TempDescriptor lhs;
711     TempDescriptor rhs;
712     FieldDescriptor fld;
713
714     // use node type to decide what alterations to make
715     // to the ownership graph
716     switch( fn.kind() ) {
717
718     case FKind.FlatMethod:
719       FlatMethod fm = (FlatMethod) fn;
720
721       // there should only be one FlatMethod node as the
722       // parent of all other FlatNode objects, so take
723       // the opportunity to construct the initial graph by
724       // adding parameters labels to new heap regions
725       // AND this should be done once globally so that the
726       // parameter IDs are consistent between analysis
727       // iterations, so if this step has been done already
728       // just merge in the cached version
729       OwnershipGraph ogInitParamAlloc = mapMethodContextToInitialParamAllocGraph.get(mc);
730       if( ogInitParamAlloc == null ) {
731
732         // if the method context has aliased parameters, make sure
733         // there is a blob region for all those param to reference
734         Set<Integer> aliasedParamIndices = mc.getAliasedParamIndices();
735
736         if( !aliasedParamIndices.isEmpty() ) {
737           og.makeAliasedParamHeapRegionNode();
738         }
739
740         // set up each parameter
741         for( int i = 0; i < fm.numParameters(); ++i ) {
742           TempDescriptor tdParam    = fm.getParameter( i );
743           TypeDescriptor typeParam  = tdParam.getType();
744           Integer        paramIndex = new Integer( i );
745
746           if( typeParam.isImmutable() && !typeParam.isArray() ) {
747             // don't bother with this primitive parameter, it
748             // cannot affect reachability
749             continue;
750           }
751
752           if( aliasedParamIndices.contains( paramIndex ) ) {
753             // use the alias blob but give parameters their
754             // own primary obj region
755             og.assignTempEqualToAliasedParam( tdParam,
756                                               paramIndex );         
757           } else {
758             // this parameter is not aliased to others, give it
759             // a fresh primary obj and secondary object
760             og.assignTempEqualToParamAlloc( tdParam,
761                                             mc.getDescriptor() instanceof TaskDescriptor,
762                                             paramIndex );
763           }
764         }
765         
766         // add additional edges for aliased regions if necessary
767         if( !aliasedParamIndices.isEmpty() ) {
768           og.addParam2ParamAliasEdges( fm, aliasedParamIndices );
769         }
770         
771         // clean up reachability on initial parameter shapes
772         og.globalSweep();
773
774         // this maps tokens to parameter indices and vice versa
775         // for when this method is a callee
776         og.prepareParamTokenMaps( fm );
777
778         // cache the graph
779         OwnershipGraph ogResult = new OwnershipGraph(allocationDepth, typeUtil);
780         ogResult.merge(og);
781         mapMethodContextToInitialParamAllocGraph.put(mc, ogResult);
782
783       } else {
784         // or just leverage the cached copy
785         og.merge(ogInitParamAlloc);
786       }
787       break;
788       
789     case FKind.FlatOpNode:
790       FlatOpNode fon = (FlatOpNode) fn;
791       if( fon.getOp().getOp() == Operation.ASSIGN ) {
792         lhs = fon.getDest();
793         rhs = fon.getLeft();
794         og.assignTempXEqualToTempY(lhs, rhs);
795       }
796       break;
797
798     case FKind.FlatCastNode:
799       FlatCastNode fcn = (FlatCastNode) fn;
800       lhs = fcn.getDst();
801       rhs = fcn.getSrc();
802
803       TypeDescriptor td = fcn.getType();
804       assert td != null;
805       
806       og.assignTypedTempXEqualToTempY(lhs, rhs, td);
807       break;
808
809     case FKind.FlatFieldNode:
810       FlatFieldNode ffn = (FlatFieldNode) fn;
811       lhs = ffn.getDst();
812       rhs = ffn.getSrc();
813       fld = ffn.getField();
814       if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
815         og.assignTempXEqualToTempYFieldF(lhs, rhs, fld);
816       }
817       
818       meAnalysis.analyzeFlatFieldNode(mc, og, rhs, fld);
819       
820       break;
821
822     case FKind.FlatSetFieldNode:
823       FlatSetFieldNode fsfn = (FlatSetFieldNode) fn;
824       lhs = fsfn.getDst();
825       fld = fsfn.getField();
826       rhs = fsfn.getSrc();
827       if( !fld.getType().isImmutable() || fld.getType().isArray() ) {
828         og.assignTempXFieldFEqualToTempY(lhs, fld, rhs);
829       }
830       
831       meAnalysis.analyzeFlatSetFieldNode(mc, og, lhs, fld);
832       
833       break;
834
835     case FKind.FlatElementNode:
836       FlatElementNode fen = (FlatElementNode) fn;
837       lhs = fen.getDst();
838       rhs = fen.getSrc();
839       if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
840
841         assert rhs.getType() != null;
842         assert rhs.getType().isArray();
843         
844         TypeDescriptor  tdElement = rhs.getType().dereference();
845         FieldDescriptor fdElement = getArrayField( tdElement );
846   
847         og.assignTempXEqualToTempYFieldF(lhs, rhs, fdElement);
848       }
849       break;
850
851     case FKind.FlatSetElementNode:
852       FlatSetElementNode fsen = (FlatSetElementNode) fn;
853       lhs = fsen.getDst();
854       rhs = fsen.getSrc();
855       if( !rhs.getType().isImmutable() || rhs.getType().isArray() ) {
856
857         assert lhs.getType() != null;
858         assert lhs.getType().isArray();
859         
860         TypeDescriptor  tdElement = lhs.getType().dereference();
861         FieldDescriptor fdElement = getArrayField( tdElement );
862
863         og.assignTempXFieldFEqualToTempY(lhs, fdElement, rhs);
864       }
865       break;
866
867     case FKind.FlatNew:
868       FlatNew fnn = (FlatNew) fn;
869       lhs = fnn.getDst();
870       if( !lhs.getType().isImmutable() || lhs.getType().isArray() ) {
871         AllocationSite as = getAllocationSiteFromFlatNewPRIVATE(fnn);
872         og.assignTempEqualToNewAlloc(lhs, as);
873       }
874       break;
875
876     case FKind.FlatCall:
877       FlatCall fc = (FlatCall) fn;
878       MethodDescriptor md = fc.getMethod();
879       FlatMethod flatm = state.getMethodFlat(md);
880       OwnershipGraph ogMergeOfAllPossibleCalleeResults = new OwnershipGraph(allocationDepth, typeUtil);
881
882       if( md.isStatic() ) {
883         // a static method is simply always the same, makes life easy
884         ogMergeOfAllPossibleCalleeResults = og;
885
886         Set<Integer> aliasedParamIndices = 
887           ogMergeOfAllPossibleCalleeResults.calculateAliasedParamSet(fc, md.isStatic(), flatm);
888
889         MethodContext mcNew = new MethodContext( md, aliasedParamIndices );
890         Set contexts = mapDescriptorToAllMethodContexts.get( md );
891         assert contexts != null;
892         contexts.add( mcNew );
893
894         addDependent( mc, mcNew );
895
896         OwnershipGraph onlyPossibleCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
897
898         if( onlyPossibleCallee == null ) {
899           // if this method context has never been analyzed just schedule it for analysis
900           // and skip over this call site for now
901           if( !methodContextsToVisitSet.contains( mcNew ) ) {
902             methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( md ), 
903                                                                    mcNew ) );
904             methodContextsToVisitSet.add( mcNew );
905           }
906           
907         } else {
908           ogMergeOfAllPossibleCalleeResults.resolveMethodCall(fc, md.isStatic(), flatm, onlyPossibleCallee, mc, null);
909         }
910         
911         meAnalysis.createNewMapping(mcNew);
912         meAnalysis.analyzeFlatCall(ogMergeOfAllPossibleCalleeResults, mcNew, mc, fc);
913         
914
915       } else {
916         // if the method descriptor is virtual, then there could be a
917         // set of possible methods that will actually be invoked, so
918         // find all of them and merge all of their results together
919         TypeDescriptor typeDesc = fc.getThis().getType();
920         Set possibleCallees = callGraph.getMethods(md, typeDesc);
921
922         Iterator i = possibleCallees.iterator();
923         while( i.hasNext() ) {
924           MethodDescriptor possibleMd = (MethodDescriptor) i.next();
925           FlatMethod pflatm = state.getMethodFlat(possibleMd);
926
927           // don't alter the working graph (og) until we compute a result for every
928           // possible callee, merge them all together, then set og to that
929           OwnershipGraph ogCopy = new OwnershipGraph(allocationDepth, typeUtil);
930           ogCopy.merge(og);
931
932           Set<Integer> aliasedParamIndices = 
933             ogCopy.calculateAliasedParamSet(fc, possibleMd.isStatic(), pflatm);
934
935           MethodContext mcNew = new MethodContext( possibleMd, aliasedParamIndices );
936           Set contexts = mapDescriptorToAllMethodContexts.get( md );
937           assert contexts != null;
938           contexts.add( mcNew );
939           
940                 
941         meAnalysis.createNewMapping(mcNew);
942                 
943           
944           addDependent( mc, mcNew );
945
946           OwnershipGraph ogPotentialCallee = mapMethodContextToCompleteOwnershipGraph.get( mcNew );
947
948           if( ogPotentialCallee == null ) {
949             // if this method context has never been analyzed just schedule it for analysis
950             // and skip over this call site for now
951             if( !methodContextsToVisitSet.contains( mcNew ) ) {
952               methodContextsToVisitQ.add( new MethodContextQWrapper( mapDescriptorToPriority.get( md ), 
953                                                                      mcNew ) );
954               methodContextsToVisitSet.add( mcNew );
955             }
956             
957           } else {
958             ogCopy.resolveMethodCall(fc, possibleMd.isStatic(), pflatm, ogPotentialCallee, mc, null);
959           }
960                 
961           ogMergeOfAllPossibleCalleeResults.merge(ogCopy);
962           
963           meAnalysis.analyzeFlatCall(ogMergeOfAllPossibleCalleeResults, mcNew, mc, fc);
964         }
965         
966       }
967
968       og = ogMergeOfAllPossibleCalleeResults;
969       break;
970
971     case FKind.FlatReturnNode:
972       FlatReturnNode frn = (FlatReturnNode) fn;
973       rhs = frn.getReturnTemp();
974       if( rhs != null && !rhs.getType().isImmutable() ) {
975         og.assignReturnEqualToTemp(rhs);
976       }
977       setRetNodes.add(frn);
978       break;
979     }
980     
981     return og;
982   }
983
984
985   // this method should generate integers strictly greater than zero!
986   // special "shadow" regions are made from a heap region by negating
987   // the ID
988   static public Integer generateUniqueHeapRegionNodeID() {
989     ++uniqueIDcount;
990     return new Integer(uniqueIDcount);
991   }
992
993
994   static public FieldDescriptor getArrayField( TypeDescriptor tdElement ) {
995     FieldDescriptor fdElement = mapTypeToArrayField.get( tdElement );
996     if( fdElement == null ) {
997       fdElement = new FieldDescriptor(new Modifiers(Modifiers.PUBLIC),
998                                       tdElement,
999                                       arrayElementFieldName,
1000                                       null,
1001                                       false);
1002       mapTypeToArrayField.put( tdElement, fdElement );
1003     }
1004     return fdElement;
1005   }
1006
1007   
1008   private void setGraphForMethodContext(MethodContext mc, OwnershipGraph og) {
1009
1010     mapMethodContextToCompleteOwnershipGraph.put(mc, og);
1011
1012     if( writeDOTs && writeAllDOTs ) {
1013       if( !mapMethodContextToNumUpdates.containsKey(mc) ) {
1014         mapMethodContextToNumUpdates.put(mc, new Integer(0) );
1015       }
1016       Integer n = mapMethodContextToNumUpdates.get(mc);
1017       try {
1018         og.writeGraph(mc, n, true, true, true, false, false, true);
1019       } catch( IOException e ) {}
1020       mapMethodContextToNumUpdates.put(mc, n + 1);
1021     }
1022   }
1023
1024
1025   private void addDependent( MethodContext caller, MethodContext callee ) {
1026     HashSet<MethodContext> deps = mapMethodContextToDependentContexts.get( callee );
1027     if( deps == null ) {
1028       deps = new HashSet<MethodContext>();
1029     }
1030     deps.add( caller );
1031     mapMethodContextToDependentContexts.put( callee, deps );
1032   }
1033
1034   private Iterator<MethodContext> iteratorDependents( MethodContext callee ) {
1035     HashSet<MethodContext> deps = mapMethodContextToDependentContexts.get( callee );
1036     if( deps == null ) {
1037       deps = new HashSet<MethodContext>();
1038       mapMethodContextToDependentContexts.put( callee, deps );
1039     }
1040     return deps.iterator();
1041   }
1042
1043
1044   private void writeFinalContextGraphs() {
1045     // arguments to writeGraph are:
1046     // boolean writeLabels,
1047     // boolean labelSelect,
1048     // boolean pruneGarbage,
1049     // boolean writeReferencers
1050     // boolean writeParamMappings
1051     // boolean hideSubsetReachability
1052
1053     Set entrySet = mapMethodContextToCompleteOwnershipGraph.entrySet();
1054     Iterator itr = entrySet.iterator();
1055     while( itr.hasNext() ) {
1056       Map.Entry      me = (Map.Entry)      itr.next();
1057       MethodContext  mc = (MethodContext)  me.getKey();
1058       OwnershipGraph og = (OwnershipGraph) me.getValue();
1059
1060       try {
1061         og.writeGraph(mc, true, true, true, false, false, true);
1062       } catch( IOException e ) {}    
1063     }
1064   }
1065   
1066   
1067
1068   // return just the allocation site associated with one FlatNew node
1069   private AllocationSite getAllocationSiteFromFlatNewPRIVATE(FlatNew fn) {
1070
1071     if( !mapFlatNewToAllocationSite.containsKey(fn) ) {
1072       AllocationSite as = new AllocationSite(allocationDepth, fn, fn.getDisjointId());
1073
1074       // the newest nodes are single objects
1075       for( int i = 0; i < allocationDepth; ++i ) {
1076         Integer id = generateUniqueHeapRegionNodeID();
1077         as.setIthOldest(i, id);
1078         mapHrnIdToAllocationSite.put( id, as );
1079       }
1080
1081       // the oldest node is a summary node
1082       Integer idSummary = generateUniqueHeapRegionNodeID();
1083       as.setSummary(idSummary);
1084
1085       mapFlatNewToAllocationSite.put(fn, as);
1086     }
1087
1088     return mapFlatNewToAllocationSite.get(fn);
1089   }
1090
1091
1092   // return all allocation sites in the method (there is one allocation
1093   // site per FlatNew node in a method)
1094   private HashSet<AllocationSite> getAllocationSiteSet(Descriptor d) {
1095     if( !mapDescriptorToAllocationSiteSet.containsKey(d) ) {
1096       buildAllocationSiteSet(d);
1097     }
1098
1099     return mapDescriptorToAllocationSiteSet.get(d);
1100
1101   }
1102
1103   private void buildAllocationSiteSet(Descriptor d) {
1104     HashSet<AllocationSite> s = new HashSet<AllocationSite>();
1105
1106     FlatMethod fm;
1107     if( d instanceof MethodDescriptor ) {
1108       fm = state.getMethodFlat( (MethodDescriptor) d);
1109     } else {
1110       assert d instanceof TaskDescriptor;
1111       fm = state.getMethodFlat( (TaskDescriptor) d);
1112     }
1113
1114     // visit every node in this FlatMethod's IR graph
1115     // and make a set of the allocation sites from the
1116     // FlatNew node's visited
1117     HashSet<FlatNode> visited = new HashSet<FlatNode>();
1118     HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
1119     toVisit.add(fm);
1120
1121     while( !toVisit.isEmpty() ) {
1122       FlatNode n = toVisit.iterator().next();
1123
1124       if( n instanceof FlatNew ) {
1125         s.add(getAllocationSiteFromFlatNewPRIVATE( (FlatNew) n) );
1126       }
1127
1128       toVisit.remove(n);
1129       visited.add(n);
1130
1131       for( int i = 0; i < n.numNext(); ++i ) {
1132         FlatNode child = n.getNext(i);
1133         if( !visited.contains(child) ) {
1134           toVisit.add(child);
1135         }
1136       }
1137     }
1138
1139     mapDescriptorToAllocationSiteSet.put(d, s);
1140   }
1141
1142
1143   private HashSet<AllocationSite> getFlaggedAllocationSites(Descriptor dIn) {
1144     
1145     HashSet<AllocationSite> out     = new HashSet<AllocationSite>();
1146     HashSet<Descriptor>     toVisit = new HashSet<Descriptor>();
1147     HashSet<Descriptor>     visited = new HashSet<Descriptor>();
1148
1149     toVisit.add(dIn);
1150
1151     while( !toVisit.isEmpty() ) {
1152       Descriptor d = toVisit.iterator().next();
1153       toVisit.remove(d);
1154       visited.add(d);
1155
1156       HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
1157       Iterator asItr = asSet.iterator();
1158       while( asItr.hasNext() ) {
1159         AllocationSite as = (AllocationSite) asItr.next();
1160         if( as.getDisjointId() != null ) {
1161           out.add(as);
1162         }
1163       }
1164
1165       // enqueue callees of this method to be searched for
1166       // allocation sites also
1167       Set callees = callGraph.getCalleeSet(d);
1168       if( callees != null ) {
1169         Iterator methItr = callees.iterator();
1170         while( methItr.hasNext() ) {
1171           MethodDescriptor md = (MethodDescriptor) methItr.next();
1172
1173           if( !visited.contains(md) ) {
1174             toVisit.add(md);
1175           }
1176         }
1177       }
1178     }
1179     
1180     return out;
1181   }
1182
1183
1184   private HashSet<AllocationSite>
1185   getFlaggedAllocationSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
1186
1187     HashSet<AllocationSite> asSetTotal = new HashSet<AllocationSite>();
1188     HashSet<Descriptor>     toVisit    = new HashSet<Descriptor>();
1189     HashSet<Descriptor>     visited    = new HashSet<Descriptor>();
1190
1191     toVisit.add(td);
1192
1193     // traverse this task and all methods reachable from this task
1194     while( !toVisit.isEmpty() ) {
1195       Descriptor d = toVisit.iterator().next();
1196       toVisit.remove(d);
1197       visited.add(d);
1198
1199       HashSet<AllocationSite> asSet = getAllocationSiteSet(d);
1200       Iterator asItr = asSet.iterator();
1201       while( asItr.hasNext() ) {
1202         AllocationSite as = (AllocationSite) asItr.next();
1203         TypeDescriptor typed = as.getType();
1204         if( typed != null ) {
1205           ClassDescriptor cd = typed.getClassDesc();
1206           if( cd != null && cd.hasFlags() ) {
1207             asSetTotal.add(as);
1208           }
1209         }
1210       }
1211
1212       // enqueue callees of this method to be searched for
1213       // allocation sites also
1214       Set callees = callGraph.getCalleeSet(d);
1215       if( callees != null ) {
1216         Iterator methItr = callees.iterator();
1217         while( methItr.hasNext() ) {
1218           MethodDescriptor md = (MethodDescriptor) methItr.next();
1219
1220           if( !visited.contains(md) ) {
1221             toVisit.add(md);
1222           }
1223         }
1224       }
1225     }
1226
1227
1228     return asSetTotal;
1229   }
1230
1231
1232   private LinkedList<MethodContext> topologicalSort( HashSet<MethodContext> set ) {
1233     HashSet   <MethodContext> discovered = new HashSet   <MethodContext>();
1234     LinkedList<MethodContext> sorted     = new LinkedList<MethodContext>();
1235   
1236     Iterator<MethodContext> itr = set.iterator();
1237     while( itr.hasNext() ) {
1238       MethodContext mc = itr.next();
1239           
1240       if( !discovered.contains( mc ) ) {
1241         dfsVisit( set, mc, sorted, discovered );
1242       }
1243     }
1244     
1245     return sorted;
1246   }
1247   
1248   private void dfsVisit( HashSet<MethodContext> set,
1249                          MethodContext mc,
1250                          LinkedList<MethodContext> sorted,
1251                          HashSet   <MethodContext> discovered ) {
1252     discovered.add( mc );
1253     
1254     Descriptor d = mc.getDescriptor();
1255     if( d instanceof MethodDescriptor ) {
1256       MethodDescriptor md = (MethodDescriptor) d;      
1257       Iterator itr = callGraph.getCallerSet( md ).iterator();
1258       while( itr.hasNext() ) {
1259         Descriptor dCaller = (Descriptor) itr.next();
1260         
1261         // only consider the callers in the original set to analyze
1262         Set<MethodContext> callerContexts = mapDescriptorToAllMethodContexts.get( dCaller );
1263         if( callerContexts == null )
1264           continue;     
1265         
1266         // since the analysis hasn't started, there should be exactly one
1267         // context if there are any at all
1268         assert callerContexts.size() == 1;      
1269         MethodContext mcCaller = callerContexts.iterator().next();
1270         assert set.contains( mcCaller );
1271
1272         if( !discovered.contains( mcCaller ) ) {
1273           dfsVisit( set, mcCaller, sorted, discovered );
1274         }
1275       }
1276     }
1277
1278     sorted.addFirst( mc );
1279   }
1280
1281
1282
1283   private String computeAliasContextHistogram() {
1284     
1285     Hashtable<Integer, Integer> mapNumContexts2NumDesc = 
1286       new Hashtable<Integer, Integer>();
1287   
1288     Iterator itr = mapDescriptorToAllMethodContexts.entrySet().iterator();
1289     while( itr.hasNext() ) {
1290       Map.Entry me = (Map.Entry) itr.next();
1291       HashSet<MethodContext> s = (HashSet<MethodContext>) me.getValue();
1292       
1293       Integer i = mapNumContexts2NumDesc.get( s.size() );
1294       if( i == null ) {
1295         i = new Integer( 0 );
1296       }
1297       mapNumContexts2NumDesc.put( s.size(), i + 1 );
1298     }   
1299
1300     String s = "";
1301     int total = 0;
1302
1303     itr = mapNumContexts2NumDesc.entrySet().iterator();
1304     while( itr.hasNext() ) {
1305       Map.Entry me = (Map.Entry) itr.next();
1306       Integer c0 = (Integer) me.getKey();
1307       Integer d0 = (Integer) me.getValue();
1308       total += d0;
1309       s += String.format( "%4d methods had %4d unique alias contexts.\n", d0, c0 );
1310     }
1311
1312     s += String.format( "\n%4d total methods analayzed.\n", total );
1313
1314     return s;
1315   }
1316   
1317
1318
1319
1320   // insert a call to debugSnapshot() somewhere in the analysis 
1321   // to get successive captures of the analysis state
1322   boolean takeDebugSnapshots = false;
1323   String mcDescSymbolDebug = "addFirst";
1324   boolean stopAfterCapture = true;
1325
1326   // increments every visit to debugSnapshot, don't fiddle with it
1327   int debugCounter = 0;
1328
1329   // the value of debugCounter to start reporting the debugCounter
1330   // to the screen to let user know what debug iteration we're at
1331   int numStartCountReport = 0;
1332
1333   // the frequency of debugCounter values to print out, 0 no report
1334   int freqCountReport = 0;
1335
1336   // the debugCounter value at which to start taking snapshots
1337   int iterStartCapture = 0;
1338
1339   // the number of snapshots to take
1340   int numIterToCapture = 40;
1341
1342   void debugSnapshot(OwnershipGraph og, FlatNode fn) {
1343     if( debugCounter > iterStartCapture + numIterToCapture ) {
1344       return;
1345     }
1346
1347     ++debugCounter;
1348     if( debugCounter > numStartCountReport &&
1349         freqCountReport > 0 &&
1350         debugCounter % freqCountReport == 0 ) {
1351       System.out.println("    @@@ debug counter = "+debugCounter);
1352     }
1353     if( debugCounter > iterStartCapture ) {
1354       System.out.println("    @@@ capturing debug "+(debugCounter-iterStartCapture)+" @@@");
1355       String graphName = String.format("snap%04d",debugCounter-iterStartCapture);
1356       if( fn != null ) {
1357         graphName = graphName+fn;
1358       }
1359       try {
1360         // arguments to writeGraph are:
1361         // boolean writeLabels,
1362         // boolean labelSelect,
1363         // boolean pruneGarbage,
1364         // boolean writeReferencers
1365         // boolean writeParamMappings
1366
1367         //og.writeGraph(graphName, true, true, true, false, false);
1368         og.writeGraph(graphName, true, true, true, false, false);
1369       } catch( Exception e ) {
1370         System.out.println("Error writing debug capture.");
1371         System.exit(0);
1372       }
1373     }
1374
1375     if( debugCounter == iterStartCapture + numIterToCapture && stopAfterCapture ) {
1376       System.out.println("Stopping analysis after debug captures.");
1377       System.exit(0);
1378     }
1379   }
1380 }