public class DisjointAnalysis {
+
+ ///////////////////////////////////////////
+ //
+ // Public interface to discover possible
+ // aliases in the program under analysis
+ //
+ ///////////////////////////////////////////
+
+ public HashSet<AllocSite>
+ getFlaggedAllocationSitesReachableFromTask(TaskDescriptor td) {
+ checkAnalysisComplete();
+ return getFlaggedAllocationSitesReachableFromTaskPRIVATE(td);
+ }
+
+ public AllocSite getAllocationSiteFromFlatNew(FlatNew fn) {
+ checkAnalysisComplete();
+ return getAllocSiteFromFlatNewPRIVATE(fn);
+ }
+
+ public AllocSite getAllocationSiteFromHeapRegionNodeID(Integer id) {
+ checkAnalysisComplete();
+ return mapHrnIdToAllocSite.get(id);
+ }
+
+ public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
+ int paramIndex1,
+ int paramIndex2) {
+ checkAnalysisComplete();
+ ReachGraph rg=mapDescriptorToCompleteReachGraph.get(taskOrMethod);
+ FlatMethod fm=state.getMethodFlat(taskOrMethod);
+ assert(rg != null);
+ return rg.mayReachSharedObjects(fm, paramIndex1, paramIndex2);
+ }
+
+ public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
+ int paramIndex, AllocSite alloc) {
+ checkAnalysisComplete();
+ ReachGraph rg = mapDescriptorToCompleteReachGraph.get(taskOrMethod);
+ FlatMethod fm=state.getMethodFlat(taskOrMethod);
+ assert (rg != null);
+ return rg.mayReachSharedObjects(fm, paramIndex, alloc);
+ }
+
+ public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
+ AllocSite alloc, int paramIndex) {
+ checkAnalysisComplete();
+ ReachGraph rg = mapDescriptorToCompleteReachGraph.get(taskOrMethod);
+ FlatMethod fm=state.getMethodFlat(taskOrMethod);
+ assert (rg != null);
+ return rg.mayReachSharedObjects(fm, paramIndex, alloc);
+ }
+
+ public Set<HeapRegionNode> createsPotentialAliases(Descriptor taskOrMethod,
+ AllocSite alloc1, AllocSite alloc2) {
+ checkAnalysisComplete();
+ ReachGraph rg = mapDescriptorToCompleteReachGraph.get(taskOrMethod);
+ assert (rg != null);
+ return rg.mayReachSharedObjects(alloc1, alloc2);
+ }
+
+ public String prettyPrintNodeSet(Set<HeapRegionNode> s) {
+ checkAnalysisComplete();
+
+ String out = "{\n";
+
+ Iterator<HeapRegionNode> i = s.iterator();
+ while (i.hasNext()) {
+ HeapRegionNode n = i.next();
+
+ AllocSite as = n.getAllocSite();
+ if (as == null) {
+ out += " " + n.toString() + ",\n";
+ } else {
+ out += " " + n.toString() + ": " + as.toStringVerbose()
+ + ",\n";
+ }
+ }
+
+ out += "}\n";
+ return out;
+ }
+
+ // use the methods given above to check every possible alias
+ // between task parameters and flagged allocation sites reachable
+ // from the task
+ public void writeAllAliases(String outputFile,
+ String timeReport,
+ String justTime,
+ boolean tabularOutput,
+ int numLines
+)
+ throws java.io.IOException {
+ checkAnalysisComplete();
+
+ BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile));
+
+ if (!tabularOutput) {
+ bw.write("Conducting ownership analysis with allocation depth = "
+ + allocationDepth + "\n");
+ bw.write(timeReport + "\n");
+ }
+
+ int numAlias = 0;
+
+ // look through every task for potential aliases
+ Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
+ while (taskItr.hasNext()) {
+ TaskDescriptor td = (TaskDescriptor) taskItr.next();
+
+ if (!tabularOutput) {
+ bw.write("\n---------" + td + "--------\n");
+ }
+
+ HashSet<AllocSite> allocSites = getFlaggedAllocationSitesReachableFromTask(td);
+
+ Set<HeapRegionNode> common;
+
+ // for each task parameter, check for aliases with
+ // other task parameters and every allocation site
+ // reachable from this task
+ boolean foundSomeAlias = false;
+
+ FlatMethod fm = state.getMethodFlat(td);
+ for (int i = 0; i < fm.numParameters(); ++i) {
+
+ // for the ith parameter check for aliases to all
+ // higher numbered parameters
+ for (int j = i + 1; j < fm.numParameters(); ++j) {
+ common = createsPotentialAliases(td, i, j);
+ if (!common.isEmpty()) {
+ foundSomeAlias = true;
+ if (!tabularOutput) {
+ bw.write("Potential alias between parameters " + i
+ + " and " + j + ".\n");
+ bw.write(prettyPrintNodeSet(common) + "\n");
+ } else {
+ ++numAlias;
+ }
+ }
+ }
+
+ // for the ith parameter, check for aliases against
+ // the set of allocation sites reachable from this
+ // task context
+ Iterator allocItr = allocSites.iterator();
+ while (allocItr.hasNext()) {
+ AllocSite as = (AllocSite) allocItr.next();
+ common = createsPotentialAliases(td, i, as);
+ if (!common.isEmpty()) {
+ foundSomeAlias = true;
+ if (!tabularOutput) {
+ bw.write("Potential alias between parameter " + i
+ + " and " + as.getFlatNew() + ".\n");
+ bw.write(prettyPrintNodeSet(common) + "\n");
+ } else {
+ ++numAlias;
+ }
+ }
+ }
+ }
+
+ // for each allocation site check for aliases with
+ // other allocation sites in the context of execution
+ // of this task
+ HashSet<AllocSite> outerChecked = new HashSet<AllocSite>();
+ Iterator allocItr1 = allocSites.iterator();
+ while (allocItr1.hasNext()) {
+ AllocSite as1 = (AllocSite) allocItr1.next();
+
+ Iterator allocItr2 = allocSites.iterator();
+ while (allocItr2.hasNext()) {
+ AllocSite as2 = (AllocSite) allocItr2.next();
+
+ if (!outerChecked.contains(as2)) {
+ common = createsPotentialAliases(td, as1, as2);
+
+ if (!common.isEmpty()) {
+ foundSomeAlias = true;
+ if (!tabularOutput) {
+ bw.write("Potential alias between "
+ + as1.getFlatNew() + " and "
+ + as2.getFlatNew() + ".\n");
+ bw.write(prettyPrintNodeSet(common) + "\n");
+ } else {
+ ++numAlias;
+ }
+ }
+ }
+ }
+
+ outerChecked.add(as1);
+ }
+
+ if (!foundSomeAlias) {
+ if (!tabularOutput) {
+ bw.write("No aliases between flagged objects in Task " + td
+ + ".\n");
+ }
+ }
+ }
+
+ /*
+ if (!tabularOutput) {
+ bw.write("\n" + computeAliasContextHistogram());
+ } else {
+ bw.write(" & " + numAlias + " & " + justTime + " & " + numLines
+ + " & " + numMethodsAnalyzed() + " \\\\\n");
+ }
+ */
+
+ bw.close();
+ }
+
+ // this version of writeAllAliases is for Java programs that have no tasks
+ public void writeAllAliasesJava(String outputFile,
+ String timeReport,
+ String justTime,
+ boolean tabularOutput,
+ int numLines
+)
+ throws java.io.IOException {
+ checkAnalysisComplete();
+
+ assert !state.TASK;
+
+ BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile));
+
+ bw.write("Conducting ownership analysis with allocation depth = "
+ + allocationDepth + "\n");
+ bw.write(timeReport + "\n\n");
+
+ boolean foundSomeAlias = false;
+
+ Descriptor d = typeUtil.getMain();
+ HashSet<AllocSite> allocSites = getFlaggedAllocationSites(d);
+
+ // for each allocation site check for aliases with
+ // other allocation sites in the context of execution
+ // of this task
+ HashSet<AllocSite> outerChecked = new HashSet<AllocSite>();
+ Iterator allocItr1 = allocSites.iterator();
+ while (allocItr1.hasNext()) {
+ AllocSite as1 = (AllocSite) allocItr1.next();
+
+ Iterator allocItr2 = allocSites.iterator();
+ while (allocItr2.hasNext()) {
+ AllocSite as2 = (AllocSite) allocItr2.next();
+
+ if (!outerChecked.contains(as2)) {
+ Set<HeapRegionNode> common = createsPotentialAliases(d,
+ as1, as2);
+
+ if (!common.isEmpty()) {
+ foundSomeAlias = true;
+ bw.write("Potential alias between "
+ + as1.getDisjointAnalysisId() + " and "
+ + as2.getDisjointAnalysisId() + ".\n");
+ bw.write(prettyPrintNodeSet(common) + "\n");
+ }
+ }
+ }
+
+ outerChecked.add(as1);
+ }
+
+ if (!foundSomeAlias) {
+ bw.write("No aliases between flagged objects found.\n");
+ }
+
+// bw.write("\n" + computeAliasContextHistogram());
+ bw.close();
+ }
+
+ ///////////////////////////////////////////
+ //
+ // end public interface
+ //
+ ///////////////////////////////////////////
+
+ protected void checkAnalysisComplete() {
+ if( !analysisComplete ) {
+ throw new Error("Warning: public interface method called while analysis is running.");
+ }
+ }
// data from the compiler
public ArrayReferencees arrayReferencees;
public TypeUtil typeUtil;
public int allocationDepth;
+
+ // data structure for public interface
+ private Hashtable<Descriptor, HashSet<AllocSite> > mapDescriptorToAllocSiteSet;
+
+
+ // for public interface methods to warn that they
+ // are grabbing results during analysis
+ private boolean analysisComplete;
// used to identify HeapRegionNode objects
// the set of task and/or method descriptors
// reachable in call graph
- protected Set<Descriptor> descriptorsToAnalyze;
+ protected Set<Descriptor>
+ descriptorsToAnalyze;
+
+ // current descriptors to visit in fixed-point
+ // interprocedural analysis, prioritized by
+ // dependency in the call graph
+ protected PriorityQueue<DescriptorQWrapper>
+ descriptorsToVisitQ;
+
+ // a duplication of the above structure, but
+ // for efficient testing of inclusion
+ protected HashSet<Descriptor>
+ descriptorsToVisitSet;
+
+ // storage for priorities (doesn't make sense)
+ // to add it to the Descriptor class, just in
+ // this analysis
+ protected Hashtable<Descriptor, Integer>
+ mapDescriptorToPriority;
+
// maps a descriptor to its current partial result
// from the intraprocedural fixed-point analysis--
protected Hashtable<Integer, AllocSite>
mapHrnIdToAllocSite;
+ // maps a method to its initial heap model (IHM) that
+ // is the set of reachability graphs from every caller
+ // site, all merged together. The reason that we keep
+ // them separate is that any one call site's contribution
+ // to the IHM may changed along the path to the fixed point
+ protected Hashtable< Descriptor, Hashtable< FlatCall, ReachGraph > >
+ mapDescriptorToIHMcontributions;
+
// TODO -- CHANGE EDGE/TYPE/FIELD storage!
public static final String arrayElementFieldName = "___element_";
static protected Hashtable<TypeDescriptor, FieldDescriptor>
// unique filenames
protected Hashtable<Descriptor, Integer>
mapDescriptorToNumUpdates;
+
+ //map task descriptor to initial task parameter
+ protected Hashtable<Descriptor, ReachGraph>
+ mapDescriptorToReachGraph;
// allocate various structures that are not local
mapFlatNewToAllocSite =
new Hashtable<FlatNew, AllocSite>();
+ mapDescriptorToIHMcontributions =
+ new Hashtable< Descriptor, Hashtable< FlatCall, ReachGraph > >();
+
mapHrnIdToAllocSite =
new Hashtable<Integer, AllocSite>();
mapTypeToArrayField =
new Hashtable <TypeDescriptor, FieldDescriptor>();
+
+ descriptorsToVisitQ =
+ new PriorityQueue<DescriptorQWrapper>();
+
+ descriptorsToVisitSet =
+ new HashSet<Descriptor>();
+
+ mapDescriptorToPriority =
+ new Hashtable<Descriptor, Integer>();
+
+ mapDescriptorToAllocSiteSet =
+ new Hashtable<Descriptor, HashSet<AllocSite> >();
+
+ mapDescriptorToReachGraph =
+ new Hashtable<Descriptor, ReachGraph>();
}
// this analysis generates a disjoint reachability
// graph for every reachable method in the program
- public DisjointAnalysis( State s,
- TypeUtil tu,
- CallGraph cg,
- Liveness l,
+ public DisjointAnalysis( State s,
+ TypeUtil tu,
+ CallGraph cg,
+ Liveness l,
ArrayReferencees ar
) throws java.io.IOException {
init( s, tu, cg, l, ar );
}
- protected void init( State state,
- TypeUtil typeUtil,
- CallGraph callGraph,
- Liveness liveness,
+ protected void init( State state,
+ TypeUtil typeUtil,
+ CallGraph callGraph,
+ Liveness liveness,
ArrayReferencees arrayReferencees
) throws java.io.IOException {
+
+ analysisComplete = false;
this.state = state;
this.typeUtil = typeUtil;
// start interprocedural fixed-point computation
analyzeMethods();
+ analysisComplete=true;
double timeEndAnalysis = (double) System.nanoTime();
double dt = (timeEndAnalysis - timeStartAnalysis)/(Math.pow( 10.0, 9.0 ) );
System.out.println( treport );
if( writeFinalDOTs && !writeAllIncrementalDOTs ) {
- //writeFinalContextGraphs();
- }
+ writeFinalGraphs();
+ }
+
+ if( state.DISJOINTWRITEIHMS ) {
+ writeFinalIHMs();
+ }
if( state.DISJOINTALIASFILE != null ) {
if( state.TASK ) {
// not supporting tasks yet...
+ writeAllAliases(state.OWNERSHIPALIASFILE, treport, justtime, state.OWNERSHIPALIASTAB, state.lines);
} else {
/*
writeAllAliasesJava( aliasFile,
// set of descriptors to analyze as the program-reachable
// tasks and the methods callable by them. For Java,
// just methods reachable from the main method.
- System.out.println( "No Bamboo support yet..." );
- System.exit( -1 );
+ System.out.println( "Bamboo..." );
+ Iterator taskItr = state.getTaskSymbolTable().getDescriptorsIterator();
+
+ while (taskItr.hasNext()) {
+ TaskDescriptor td = (TaskDescriptor) taskItr.next();
+ if (!descriptorsToAnalyze.contains(td)) {
+ descriptorsToAnalyze.add(td);
+ descriptorsToAnalyze.addAll(callGraph.getAllMethods(td));
+ }
+ }
} else {
// add all methods transitively reachable from the
// add sorted descriptors to priority queue, and duplicate
// the queue as a set for efficiently testing whether some
// method is marked for analysis
- PriorityQueue<DescriptorQWrapper> descriptorsToVisitQ
- = new PriorityQueue<DescriptorQWrapper>();
-
- HashSet<Descriptor> descriptorsToVisitSet
- = new HashSet<Descriptor>();
-
- Hashtable<Descriptor, Integer> mapDescriptorToPriority
- = new Hashtable<Descriptor, Integer>();
-
int p = 0;
Iterator<Descriptor> dItr = sortedDescriptors.iterator();
while( dItr.hasNext() ) {
ReachGraph rg = analyzeMethod( d );
ReachGraph rgPrev = getPartial( d );
-
+
if( !rg.equals( rgPrev ) ) {
setPartial( d, rg );
- // results for d changed, so queue dependents
+ // results for d changed, so enqueue dependents
// of d for further analysis
Iterator<Descriptor> depsItr = getDependents( d ).iterator();
while( depsItr.hasNext() ) {
Descriptor dNext = depsItr.next();
-
- if( !descriptorsToVisitSet.contains( dNext ) ) {
- Integer priority = mapDescriptorToPriority.get( dNext );
- descriptorsToVisitQ.add( new DescriptorQWrapper( priority ,
- dNext )
- );
- descriptorsToVisitSet.add( dNext );
- }
+ enqueue( dNext );
}
}
}
}
-
protected ReachGraph analyzeMethod( Descriptor d )
throws java.io.IOException {
// to see if anything was updated.
ReachGraph rg = new ReachGraph();
+ TaskDescriptor taskDesc;
+ if(fn instanceof FlatMethod && (taskDesc=((FlatMethod)fn).getTask())!=null){
+ if(mapDescriptorToReachGraph.containsKey(taskDesc)){
+ // retrieve existing reach graph if it is not first time
+ rg=mapDescriptorToReachGraph.get(taskDesc);
+ }else{
+ // create initial reach graph for a task
+ rg=createInitialTaskReachGraph((FlatMethod)fn);
+ rg.globalSweep();
+ mapDescriptorToReachGraph.put(taskDesc, rg);
+ }
+ }
// start by merging all node's parents' graphs
for( int i = 0; i < fn.numPrev(); ++i ) {
FlatNode pn = fn.getPrev( i );
if( mapFlatNodeToReachGraph.containsKey( pn ) ) {
ReachGraph rgParent = mapFlatNodeToReachGraph.get( pn );
+// System.out.println("parent="+pn+"->"+rgParent);
rg.merge( rgParent );
}
}
+
+ if( takeDebugSnapshots &&
+ d.getSymbol().equals( descSymbolDebug )
+ ) {
+ debugSnapshot( rg, fn, true );
+ }
+
+
// modify rg with appropriate transfer function
- analyzeFlatNode( d, fm, fn, setReturns, rg );
-
- /*
+ rg = analyzeFlatNode( d, fm, fn, setReturns, rg );
+
+
if( takeDebugSnapshots &&
- d.getSymbol().equals( descSymbolDebug ) ) {
- debugSnapshot(og,fn);
+ d.getSymbol().equals( descSymbolDebug )
+ ) {
+ debugSnapshot( rg, fn, false );
}
- */
+
// if the results of the new graph are different from
// the current graph at this node, replace the graph
// states after the flat method returns
ReachGraph completeGraph = new ReachGraph();
+ assert !setReturns.isEmpty();
Iterator retItr = setReturns.iterator();
while( retItr.hasNext() ) {
FlatReturnNode frn = (FlatReturnNode) retItr.next();
+
assert mapFlatNodeToReachGraph.containsKey( frn );
ReachGraph rgRet = mapFlatNodeToReachGraph.get( frn );
+
completeGraph.merge( rgRet );
}
-
return completeGraph;
}
- protected void
- analyzeFlatNode( Descriptor d,
- FlatMethod fmContaining,
- FlatNode fn,
+ protected ReachGraph
+ analyzeFlatNode( Descriptor d,
+ FlatMethod fmContaining,
+ FlatNode fn,
HashSet<FlatReturnNode> setRetNodes,
- ReachGraph rg
+ ReachGraph rg
) throws java.io.IOException {
// any variables that are no longer live should be
// nullified in the graph to reduce edges
- // NOTE: it is not clear we need this. It costs a
- // liveness calculation for every method, so only
- // turn it on if we find we actually need it.
- // rg.nullifyDeadVars( liveness.getLiveInTemps( fmContaining, fn ) );
+ //rg.nullifyDeadVars( liveness.getLiveInTemps( fmContaining, fn ) );
- TempDescriptor lhs;
- TempDescriptor rhs;
+ TempDescriptor lhs;
+ TempDescriptor rhs;
FieldDescriptor fld;
// use node type to decide what transfer function
// to apply to the reachability graph
switch( fn.kind() ) {
- case FKind.FlatMethod:
- FlatMethod fm = (FlatMethod) fn;
- break;
+ case FKind.FlatMethod: {
+ // construct this method's initial heap model (IHM)
+ // since we're working on the FlatMethod, we know
+ // the incoming ReachGraph 'rg' is empty
+
+ Hashtable<FlatCall, ReachGraph> heapsFromCallers =
+ getIHMcontributions( d );
+
+ Set entrySet = heapsFromCallers.entrySet();
+ Iterator itr = entrySet.iterator();
+ while( itr.hasNext() ) {
+ Map.Entry me = (Map.Entry) itr.next();
+ FlatCall fc = (FlatCall) me.getKey();
+ ReachGraph rgContrib = (ReachGraph) me.getValue();
+
+ assert fc.getMethod().equals( d );
+
+ // some call sites are in same method context though,
+ // and all of them should be merged together first,
+ // then heaps from different contexts should be merged
+ // THIS ASSUMES DIFFERENT CONTEXTS NEED SPECIAL CONSIDERATION!
+ // such as, do allocation sites need to be aged?
+
+ rg.merge_diffMethodContext( rgContrib );
+ }
+ } break;
case FKind.FlatOpNode:
FlatOpNode fon = (FlatOpNode) fn;
}
break;
- case FKind.FlatCall:
- /*
- FlatCall fc = (FlatCall) fn;
- MethodDescriptor md = fc.getMethod();
- FlatMethod flatm = state.getMethodFlat(md);
- ReachGraph ogMergeOfAllPossibleCalleeResults = new ReachGraph();
-
- if( md.isStatic() ) {
- // a static method is simply always the same, makes life easy
- ogMergeOfAllPossibleCalleeResults = og;
-
- Set<Integer> aliasedParamIndices =
- ogMergeOfAllPossibleCalleeResults.calculateAliasedParamSet(fc, md.isStatic(), flatm);
-
- Descriptor mcNew = new Descriptor( md, aliasedParamIndices );
- Set contexts = mapDescriptorToAllDescriptors.get( md );
- assert contexts != null;
- contexts.add( mcNew );
-
- addDependent( mc, mcNew );
-
- ReachGraph onlyPossibleCallee = mapDescriptorToCompleteReachabilityGraph.get( mcNew );
-
- if( onlyPossibleCallee == null ) {
- // if this method context has never been analyzed just schedule it for analysis
- // and skip over this call site for now
- if( !methodContextsToVisitSet.contains( mcNew ) ) {
- methodContextsToVisitQ.add( new DescriptorQWrapper( mapDescriptorToPriority.get( md ),
- mcNew ) );
- methodContextsToVisitSet.add( mcNew );
- }
-
- } else {
- ogMergeOfAllPossibleCalleeResults.resolveMethodCall(fc, md.isStatic(), flatm, onlyPossibleCallee, mc, null);
- }
-
- meAnalysis.createNewMapping(mcNew);
- meAnalysis.analyzeFlatCall(ogMergeOfAllPossibleCalleeResults, mcNew, mc, fc);
-
-
- } else {
- // if the method descriptor is virtual, then there could be a
- // set of possible methods that will actually be invoked, so
- // find all of them and merge all of their results together
- TypeDescriptor typeDesc = fc.getThis().getType();
- Set possibleCallees = callGraph.getMethods(md, typeDesc);
+ case FKind.FlatCall: {
+ //TODO: temporal fix for task descriptor case
+ //MethodDescriptor mdCaller = fmContaining.getMethod();
+ Descriptor mdCaller;
+ if(fmContaining.getMethod()!=null){
+ mdCaller = fmContaining.getMethod();
+ }else{
+ mdCaller = fmContaining.getTask();
+ }
+ FlatCall fc = (FlatCall) fn;
+ MethodDescriptor mdCallee = fc.getMethod();
+ FlatMethod fmCallee = state.getMethodFlat( mdCallee );
+
+ boolean writeDebugDOTs =
+ mdCaller.getSymbol().equals( state.DISJOINTDEBUGCALLER ) &&
+ mdCallee.getSymbol().equals( state.DISJOINTDEBUGCALLEE );
+
+
+ // calculate the heap this call site can reach--note this is
+ // not used for the current call site transform, we are
+ // grabbing this heap model for future analysis of the callees,
+ // so if different results emerge we will return to this site
+ ReachGraph heapForThisCall_old =
+ getIHMcontribution( mdCallee, fc );
+
+ // the computation of the callee-reachable heap
+ // is useful for making the callee starting point
+ // and for applying the call site transfer function
+ Set<Integer> callerNodeIDsCopiedToCallee =
+ new HashSet<Integer>();
+
+ ReachGraph heapForThisCall_cur =
+ rg.makeCalleeView( fc,
+ fmCallee,
+ callerNodeIDsCopiedToCallee,
+ writeDebugDOTs
+ );
+
+ if( !heapForThisCall_cur.equals( heapForThisCall_old ) ) {
+ // if heap at call site changed, update the contribution,
+ // and reschedule the callee for analysis
+ addIHMcontribution( mdCallee, fc, heapForThisCall_cur );
+ enqueue( mdCallee );
+ }
- Iterator i = possibleCallees.iterator();
- while( i.hasNext() ) {
- MethodDescriptor possibleMd = (MethodDescriptor) i.next();
- FlatMethod pflatm = state.getMethodFlat(possibleMd);
- // don't alter the working graph (og) until we compute a result for every
- // possible callee, merge them all together, then set og to that
- ReachGraph ogCopy = new ReachGraph();
- ogCopy.merge(og);
- Set<Integer> aliasedParamIndices =
- ogCopy.calculateAliasedParamSet(fc, possibleMd.isStatic(), pflatm);
- Descriptor mcNew = new Descriptor( possibleMd, aliasedParamIndices );
- Set contexts = mapDescriptorToAllDescriptors.get( md );
- assert contexts != null;
- contexts.add( mcNew );
-
-
- meAnalysis.createNewMapping(mcNew);
-
-
- addDependent( mc, mcNew );
+ // the transformation for a call site should update the
+ // current heap abstraction with any effects from the callee,
+ // or if the method is virtual, the effects from any possible
+ // callees, so find the set of callees...
+ Set<MethodDescriptor> setPossibleCallees =
+ new HashSet<MethodDescriptor>();
- ReachGraph ogPotentialCallee = mapDescriptorToCompleteReachabilityGraph.get( mcNew );
+ if( mdCallee.isStatic() ) {
+ setPossibleCallees.add( mdCallee );
+ } else {
+ TypeDescriptor typeDesc = fc.getThis().getType();
+ setPossibleCallees.addAll( callGraph.getMethods( mdCallee,
+ typeDesc )
+ );
+ }
- if( ogPotentialCallee == null ) {
- // if this method context has never been analyzed just schedule it for analysis
- // and skip over this call site for now
- if( !methodContextsToVisitSet.contains( mcNew ) ) {
- methodContextsToVisitQ.add( new DescriptorQWrapper( mapDescriptorToPriority.get( md ),
- mcNew ) );
- methodContextsToVisitSet.add( mcNew );
- }
-
- } else {
- ogCopy.resolveMethodCall(fc, possibleMd.isStatic(), pflatm, ogPotentialCallee, mc, null);
- }
-
- ogMergeOfAllPossibleCalleeResults.merge(ogCopy);
-
- meAnalysis.analyzeFlatCall(ogMergeOfAllPossibleCalleeResults, mcNew, mc, fc);
- }
-
+ ReachGraph rgMergeOfEffects = new ReachGraph();
+
+ Iterator<MethodDescriptor> mdItr = setPossibleCallees.iterator();
+ while( mdItr.hasNext() ) {
+ MethodDescriptor mdPossible = mdItr.next();
+ FlatMethod fmPossible = state.getMethodFlat( mdPossible );
+
+ addDependent( mdPossible, // callee
+ d ); // caller
+
+ // don't alter the working graph (rg) until we compute a
+ // result for every possible callee, merge them all together,
+ // then set rg to that
+ ReachGraph rgCopy = new ReachGraph();
+ rgCopy.merge( rg );
+
+ ReachGraph rgEffect = getPartial( mdPossible );
+
+ if( rgEffect == null ) {
+ // if this method has never been analyzed just schedule it
+ // for analysis and skip over this call site for now
+ enqueue( mdPossible );
+ } else {
+ rgCopy.resolveMethodCall( fc,
+ fmPossible,
+ rgEffect,
+ callerNodeIDsCopiedToCallee,
+ writeDebugDOTs
+ );
+ }
+
+ rgMergeOfEffects.merge( rgCopy );
}
- og = ogMergeOfAllPossibleCalleeResults;
- */
- break;
+
+ // now that we've taken care of building heap models for
+ // callee analysis, finish this transformation
+ rg = rgMergeOfEffects;
+ } break;
case FKind.FlatReturnNode:
break;
} // end switch
+
+ // dead variables were removed before the above transfer function
+ // was applied, so eliminate heap regions and edges that are no
+ // longer part of the abstractly-live heap graph, and sweep up
+ // and reachability effects that are altered by the reduction
+ //rg.abstractGarbageCollect();
+ //rg.globalSweep();
+
+
// at this point rg should be the correct update
// by an above transfer function, or untouched if
// the flat node type doesn't affect the heap
+ return rg;
}
}
- /*
- private void writeFinalContextGraphs() {
- Set entrySet = mapDescriptorToCompleteReachabilityGraph.entrySet();
+
+ private void writeFinalGraphs() {
+ Set entrySet = mapDescriptorToCompleteReachGraph.entrySet();
Iterator itr = entrySet.iterator();
while( itr.hasNext() ) {
- Map.Entry me = (Map.Entry) itr.next();
- Descriptor mc = (Descriptor) me.getKey();
- ReachabilityGraph og = (ReachabilityGraph) me.getValue();
-
- try {
- og.writeGraph(mc+"COMPLETE",
- true, // write labels (variables)
- true, // selectively hide intermediate temp vars
- true, // prune unreachable heap regions
- false, // show back edges to confirm graph validity
- false, // show parameter indices (unmaintained!)
- true, // hide subset reachability states
- true); // hide edge taints
+ Map.Entry me = (Map.Entry) itr.next();
+ Descriptor d = (Descriptor) me.getKey();
+ ReachGraph rg = (ReachGraph) me.getValue();
+
+ try {
+ rg.writeGraph( "COMPLETE"+d,
+ true, // write labels (variables)
+ true, // selectively hide intermediate temp vars
+ true, // prune unreachable heap regions
+ false, // hide subset reachability states
+ true ); // hide edge taints
} catch( IOException e ) {}
}
}
-
- */
+
+ private void writeFinalIHMs() {
+ Iterator d2IHMsItr = mapDescriptorToIHMcontributions.entrySet().iterator();
+ while( d2IHMsItr.hasNext() ) {
+ Map.Entry me1 = (Map.Entry) d2IHMsItr.next();
+ Descriptor d = (Descriptor) me1.getKey();
+ Hashtable<FlatCall, ReachGraph> IHMs = (Hashtable<FlatCall, ReachGraph>) me1.getValue();
+
+ Iterator fc2rgItr = IHMs.entrySet().iterator();
+ while( fc2rgItr.hasNext() ) {
+ Map.Entry me2 = (Map.Entry) fc2rgItr.next();
+ FlatCall fc = (FlatCall) me2.getKey();
+ ReachGraph rg = (ReachGraph) me2.getValue();
+
+ try {
+ rg.writeGraph( "IHMPARTFOR"+d+"FROM"+fc,
+ true, // write labels (variables)
+ false, // selectively hide intermediate temp vars
+ false, // prune unreachable heap regions
+ false, // hide subset reachability states
+ true ); // hide edge taints
+ } catch( IOException e ) {}
+ }
+ }
+ }
+
+
+
// return just the allocation site associated with one FlatNew node
protected AllocSite getAllocSiteFromFlatNewPRIVATE( FlatNew fnew ) {
if( !mapFlatNewToAllocSite.containsKey( fnew ) ) {
AllocSite as =
- new AllocSite( allocationDepth, fnew, fnew.getDisjointId() );
+ (AllocSite) Canonical.makeCanonical( new AllocSite( allocationDepth,
+ fnew,
+ fnew.getDisjointId()
+ )
+ );
// the newest nodes are single objects
for( int i = 0; i < allocationDepth; ++i ) {
}
// the oldest node is a summary node
- Integer idSummary = generateUniqueHeapRegionNodeID();
- as.setSummary( idSummary );
+ as.setSummary( generateUniqueHeapRegionNodeID() );
mapFlatNewToAllocSite.put( fnew, as );
}
}
*/
-
- /*
- // insert a call to debugSnapshot() somewhere in the analysis
- // to get successive captures of the analysis state
- boolean takeDebugSnapshots = false;
- String mcDescSymbolDebug = "setRoute";
- boolean stopAfterCapture = true;
-
- // increments every visit to debugSnapshot, don't fiddle with it
- // IMPORTANT NOTE FOR SETTING THE FOLLOWING VALUES: this
- // counter increments just after every node is analyzed
- // from the body of the method whose symbol is specified
- // above.
- int debugCounter = 0;
-
- // the value of debugCounter to start reporting the debugCounter
- // to the screen to let user know what debug iteration we're at
- int numStartCountReport = 0;
-
- // the frequency of debugCounter values to print out, 0 no report
- int freqCountReport = 0;
-
- // the debugCounter value at which to start taking snapshots
- int iterStartCapture = 0;
-
- // the number of snapshots to take
- int numIterToCapture = 300;
-
- void debugSnapshot(ReachabilityGraph og, FlatNode fn) {
- if( debugCounter > iterStartCapture + numIterToCapture ) {
- return;
- }
-
- ++debugCounter;
- if( debugCounter > numStartCountReport &&
- freqCountReport > 0 &&
- debugCounter % freqCountReport == 0 ) {
- System.out.println(" @@@ debug counter = "+debugCounter);
- }
- if( debugCounter > iterStartCapture ) {
- System.out.println(" @@@ capturing debug "+(debugCounter-iterStartCapture)+" @@@");
- String graphName = String.format("snap%04d",debugCounter-iterStartCapture);
- if( fn != null ) {
- graphName = graphName+fn;
- }
- try {
- og.writeGraph(graphName,
- true, // write labels (variables)
- true, // selectively hide intermediate temp vars
- true, // prune unreachable heap regions
- false, // show back edges to confirm graph validity
- false, // show parameter indices (unmaintained!)
- true, // hide subset reachability states
- true); // hide edge taints
- } catch( Exception e ) {
- System.out.println("Error writing debug capture.");
- System.exit(0);
- }
- }
-
- if( debugCounter == iterStartCapture + numIterToCapture && stopAfterCapture ) {
- System.out.println("Stopping analysis after debug captures.");
- System.exit(0);
- }
- }
- */
+
// Take in source entry which is the program's compiled entry and
"analysisEntryMethod"
);
- TempDescriptor cmdLineArgs = new TempDescriptor( "args" );
-
- FlatNew fn = new FlatNew( mdSourceEntry.getParamType( 0 ),
- cmdLineArgs,
- false // is global
- );
+ TempDescriptor cmdLineArgs =
+ new TempDescriptor( "args",
+ mdSourceEntry.getParamType( 0 )
+ );
+
+ FlatNew fn =
+ new FlatNew( mdSourceEntry.getParamType( 0 ),
+ cmdLineArgs,
+ false // is global
+ );
TempDescriptor[] sourceEntryArgs = new TempDescriptor[1];
sourceEntryArgs[0] = cmdLineArgs;
- FlatCall fc = new FlatCall( mdSourceEntry,
- null, // dst temp
- null, // this temp
- sourceEntryArgs
- );
+ FlatCall fc =
+ new FlatCall( mdSourceEntry,
+ null, // dst temp
+ null, // this temp
+ sourceEntryArgs
+ );
+
+ FlatReturnNode frn = new FlatReturnNode( null );
FlatExit fe = new FlatExit();
- this.fmAnalysisEntry = new FlatMethod( mdAnalysisEntry, fe );
+ this.fmAnalysisEntry =
+ new FlatMethod( mdAnalysisEntry,
+ fe
+ );
this.fmAnalysisEntry.addNext( fn );
fn.addNext( fc );
- fc.addNext( fe );
+ fc.addNext( frn );
+ frn.addNext( fe );
}
}
+ protected void enqueue( Descriptor d ) {
+ if( !descriptorsToVisitSet.contains( d ) ) {
+ Integer priority = mapDescriptorToPriority.get( d );
+ descriptorsToVisitQ.add( new DescriptorQWrapper( priority,
+ d )
+ );
+ descriptorsToVisitSet.add( d );
+ }
+ }
+
+
protected ReachGraph getPartial( Descriptor d ) {
return mapDescriptorToCompleteReachGraph.get( d );
}
return deps;
}
+
+ public Hashtable<FlatCall, ReachGraph> getIHMcontributions( Descriptor d ) {
+
+ Hashtable<FlatCall, ReachGraph> heapsFromCallers =
+ mapDescriptorToIHMcontributions.get( d );
+
+ if( heapsFromCallers == null ) {
+ heapsFromCallers = new Hashtable<FlatCall, ReachGraph>();
+ mapDescriptorToIHMcontributions.put( d, heapsFromCallers );
+ }
+
+ return heapsFromCallers;
+ }
+
+ public ReachGraph getIHMcontribution( Descriptor d,
+ FlatCall fc
+ ) {
+ Hashtable<FlatCall, ReachGraph> heapsFromCallers =
+ getIHMcontributions( d );
+
+ if( !heapsFromCallers.containsKey( fc ) ) {
+ heapsFromCallers.put( fc, new ReachGraph() );
+ }
+
+ return heapsFromCallers.get( fc );
+ }
+
+ public void addIHMcontribution( Descriptor d,
+ FlatCall fc,
+ ReachGraph rg
+ ) {
+ Hashtable<FlatCall, ReachGraph> heapsFromCallers =
+ getIHMcontributions( d );
+
+ heapsFromCallers.put( fc, rg );
+ }
+
+private AllocSite createParameterAllocSite(ReachGraph rg, TempDescriptor tempDesc) {
+
+ // create temp descriptor for each parameter variable
+ FlatNew flatNew = new FlatNew(tempDesc.getType(), tempDesc, false);
+ // create allocation site
+ AllocSite as = (AllocSite) Canonical.makeCanonical(new AllocSite( allocationDepth, flatNew, flatNew.getDisjointId()));
+ for (int i = 0; i < allocationDepth; ++i) {
+ Integer id = generateUniqueHeapRegionNodeID();
+ as.setIthOldest(i, id);
+ mapHrnIdToAllocSite.put(id, as);
+ }
+ // the oldest node is a summary node
+ as.setSummary( generateUniqueHeapRegionNodeID() );
+
+ rg.age(as);
+
+ return as;
+
+}
+
+private ReachGraph createInitialTaskReachGraph(FlatMethod fm) {
+ ReachGraph rg = new ReachGraph();
+ TaskDescriptor taskDesc = fm.getTask();
+
+ for (int idx = 0; idx < taskDesc.numParameters(); idx++) {
+ Descriptor paramDesc = taskDesc.getParameter(idx);
+ TypeDescriptor paramTypeDesc = taskDesc.getParamType(idx);
+
+ // setup data structure
+ Set<HashMap<HeapRegionNode, FieldDescriptor>> workSet =
+ new HashSet<HashMap<HeapRegionNode, FieldDescriptor>>();
+ Hashtable<TypeDescriptor, HeapRegionNode> mapTypeToExistingSummaryNode =
+ new Hashtable<TypeDescriptor, HeapRegionNode>();
+ Set<String> doneSet = new HashSet<String>();
+
+ TempDescriptor tempDesc = fm.getParameter(idx);
+
+ AllocSite as = createParameterAllocSite(rg, tempDesc);
+ VariableNode lnX = rg.getVariableNodeFromTemp(tempDesc);
+ Integer idNewest = as.getIthOldest(0);
+ HeapRegionNode hrnNewest = rg.id2hrn.get(idNewest);
+ // make a new reference to allocated node
+ RefEdge edgeNew = new RefEdge(lnX, // source
+ hrnNewest, // dest
+ taskDesc.getParamType(idx), // type
+ null, // field name
+ hrnNewest.getAlpha(), // beta
+ ExistPredSet.factory(rg.predTrue) // predicates
+ );
+ rg.addRefEdge(lnX, hrnNewest, edgeNew);
+
+ // set-up a work set for class field
+ ClassDescriptor classDesc = paramTypeDesc.getClassDesc();
+ for (Iterator it = classDesc.getFields(); it.hasNext();) {
+ FieldDescriptor fd = (FieldDescriptor) it.next();
+ TypeDescriptor fieldType = fd.getType();
+ if (!fieldType.isImmutable() || fieldType.isArray()) {
+ HashMap<HeapRegionNode, FieldDescriptor> newMap = new HashMap<HeapRegionNode, FieldDescriptor>();
+ newMap.put(hrnNewest, fd);
+ workSet.add(newMap);
+ }
+ }
+
+ int uniqueIdentifier = 0;
+ while (!workSet.isEmpty()) {
+ HashMap<HeapRegionNode, FieldDescriptor> map = workSet
+ .iterator().next();
+ workSet.remove(map);
+
+ Set<HeapRegionNode> key = map.keySet();
+ HeapRegionNode srcHRN = key.iterator().next();
+ FieldDescriptor fd = map.get(srcHRN);
+ TypeDescriptor type = fd.getType();
+ String doneSetIdentifier = srcHRN.getIDString() + "_" + fd;
+
+ if (!doneSet.contains(doneSetIdentifier)) {
+ doneSet.add(doneSetIdentifier);
+ if (!mapTypeToExistingSummaryNode.containsKey(type)) {
+ // create new summary Node
+ TempDescriptor td = new TempDescriptor("temp"
+ + uniqueIdentifier, type);
+
+ AllocSite allocSite;
+ if(type.equals(paramTypeDesc)){
+ //corresponding allocsite has already been created for a parameter variable.
+ allocSite=as;
+ }else{
+ allocSite = createParameterAllocSite(rg, td);
+ }
+ String strDesc = allocSite.toStringForDOT()
+ + "\\nsummary";
+ HeapRegionNode hrnSummary =
+ rg.createNewHeapRegionNode(allocSite.getSummary(), // id or null to generate a new one
+ false, // single object?
+ true, // summary?
+ false, // flagged?
+ false, // out-of-context?
+ allocSite.getType(), // type
+ allocSite, // allocation site
+ null, // inherent reach
+ srcHRN.getAlpha(), // current reach
+ ExistPredSet.factory(), // predicates
+ strDesc // description
+ );
+ rg.id2hrn.put(allocSite.getSummary(),hrnSummary);
+
+ // make a new reference to summary node
+ RefEdge edgeToSummary = new RefEdge(srcHRN, // source
+ hrnSummary, // dest
+ fd.getType(), // type
+ fd.getSymbol(), // field name
+ srcHRN.getAlpha(), // beta
+ ExistPredSet.factory(rg.predTrue) // predicates
+ );
+
+ rg.addRefEdge(srcHRN, hrnSummary, edgeToSummary);
+
+ uniqueIdentifier++;
+
+ mapTypeToExistingSummaryNode.put(type, hrnSummary);
+
+ // set-up a work set for fields of the class
+ if(!type.isImmutable()){
+ classDesc = type.getClassDesc();
+ for (Iterator it = classDesc.getFields(); it.hasNext();) {
+ FieldDescriptor typeFieldDesc = (FieldDescriptor) it.next();
+ TypeDescriptor fieldType = typeFieldDesc.getType();
+ if (!fieldType.isImmutable()) {
+ doneSetIdentifier = hrnSummary.getIDString() + "_" + typeFieldDesc;
+ if(!doneSet.contains(doneSetIdentifier)){
+ // add new work item
+ HashMap<HeapRegionNode, FieldDescriptor> newMap =
+ new HashMap<HeapRegionNode, FieldDescriptor>();
+ newMap.put(hrnSummary, typeFieldDesc);
+ workSet.add(newMap);
+ }
+ }
+ }
+ }
+
+ }else{
+ // if there exists corresponding summary node
+ HeapRegionNode hrnDst=mapTypeToExistingSummaryNode.get(type);
+
+ RefEdge edgeToSummary = new RefEdge(srcHRN, // source
+ hrnDst, // dest
+ fd.getType(), // type
+ fd.getSymbol(), // field name
+ srcHRN.getAlpha(), // beta
+ ExistPredSet.factory(rg.predTrue) // predicates
+ );
+ rg.addRefEdge(srcHRN, hrnDst, edgeToSummary);
+
+ }
+ }
+ }
+ }
+// debugSnapshot(rg, fm, true);
+ return rg;
+}
+
+// return all allocation sites in the method (there is one allocation
+// site per FlatNew node in a method)
+private HashSet<AllocSite> getAllocationSiteSet(Descriptor d) {
+ if( !mapDescriptorToAllocSiteSet.containsKey(d) ) {
+ buildAllocationSiteSet(d);
+ }
+
+ return mapDescriptorToAllocSiteSet.get(d);
+
+}
+
+private void buildAllocationSiteSet(Descriptor d) {
+ HashSet<AllocSite> s = new HashSet<AllocSite>();
+
+ FlatMethod fm;
+ if( d instanceof MethodDescriptor ) {
+ fm = state.getMethodFlat( (MethodDescriptor) d);
+ } else {
+ assert d instanceof TaskDescriptor;
+ fm = state.getMethodFlat( (TaskDescriptor) d);
+ }
+
+ // visit every node in this FlatMethod's IR graph
+ // and make a set of the allocation sites from the
+ // FlatNew node's visited
+ HashSet<FlatNode> visited = new HashSet<FlatNode>();
+ HashSet<FlatNode> toVisit = new HashSet<FlatNode>();
+ toVisit.add(fm);
+
+ while( !toVisit.isEmpty() ) {
+ FlatNode n = toVisit.iterator().next();
+
+ if( n instanceof FlatNew ) {
+ s.add(getAllocSiteFromFlatNewPRIVATE( (FlatNew) n) );
+ }
+
+ toVisit.remove(n);
+ visited.add(n);
+
+ for( int i = 0; i < n.numNext(); ++i ) {
+ FlatNode child = n.getNext(i);
+ if( !visited.contains(child) ) {
+ toVisit.add(child);
+ }
+ }
+ }
+
+ mapDescriptorToAllocSiteSet.put(d, s);
+ }
+
+ private HashSet<AllocSite> getFlaggedAllocationSites(Descriptor dIn) {
+
+ HashSet<AllocSite> out = new HashSet<AllocSite>();
+ HashSet<Descriptor> toVisit = new HashSet<Descriptor>();
+ HashSet<Descriptor> visited = new HashSet<Descriptor>();
+
+ toVisit.add(dIn);
+
+ while (!toVisit.isEmpty()) {
+ Descriptor d = toVisit.iterator().next();
+ toVisit.remove(d);
+ visited.add(d);
+
+ HashSet<AllocSite> asSet = getAllocationSiteSet(d);
+ Iterator asItr = asSet.iterator();
+ while (asItr.hasNext()) {
+ AllocSite as = (AllocSite) asItr.next();
+ if (as.getDisjointAnalysisId() != null) {
+ out.add(as);
+ }
+ }
+
+ // enqueue callees of this method to be searched for
+ // allocation sites also
+ Set callees = callGraph.getCalleeSet(d);
+ if (callees != null) {
+ Iterator methItr = callees.iterator();
+ while (methItr.hasNext()) {
+ MethodDescriptor md = (MethodDescriptor) methItr.next();
+
+ if (!visited.contains(md)) {
+ toVisit.add(md);
+ }
+ }
+ }
+ }
+
+ return out;
+ }
+
+
+private HashSet<AllocSite>
+getFlaggedAllocationSitesReachableFromTaskPRIVATE(TaskDescriptor td) {
+
+ HashSet<AllocSite> asSetTotal = new HashSet<AllocSite>();
+ HashSet<Descriptor> toVisit = new HashSet<Descriptor>();
+ HashSet<Descriptor> visited = new HashSet<Descriptor>();
+
+ toVisit.add(td);
+
+ // traverse this task and all methods reachable from this task
+ while( !toVisit.isEmpty() ) {
+ Descriptor d = toVisit.iterator().next();
+ toVisit.remove(d);
+ visited.add(d);
+
+ HashSet<AllocSite> asSet = getAllocationSiteSet(d);
+ Iterator asItr = asSet.iterator();
+ while( asItr.hasNext() ) {
+ AllocSite as = (AllocSite) asItr.next();
+ TypeDescriptor typed = as.getType();
+ if( typed != null ) {
+ ClassDescriptor cd = typed.getClassDesc();
+ if( cd != null && cd.hasFlags() ) {
+ asSetTotal.add(as);
+ }
+ }
+ }
+
+ // enqueue callees of this method to be searched for
+ // allocation sites also
+ Set callees = callGraph.getCalleeSet(d);
+ if( callees != null ) {
+ Iterator methItr = callees.iterator();
+ while( methItr.hasNext() ) {
+ MethodDescriptor md = (MethodDescriptor) methItr.next();
+
+ if( !visited.contains(md) ) {
+ toVisit.add(md);
+ }
+ }
+ }
+ }
+
+ return asSetTotal;
+}
+
+
+
+
+ // get successive captures of the analysis state
+ boolean takeDebugSnapshots = false;
+ String descSymbolDebug = "addSomething";
+ boolean stopAfterCapture = true;
+
+ // increments every visit to debugSnapshot, don't fiddle with it
+ int debugCounter = 0;
+
+ // the value of debugCounter to start reporting the debugCounter
+ // to the screen to let user know what debug iteration we're at
+ int numStartCountReport = 0;
+
+ // the frequency of debugCounter values to print out, 0 no report
+ int freqCountReport = 0;
+
+ // the debugCounter value at which to start taking snapshots
+ int iterStartCapture = 25;
+
+ // the number of snapshots to take
+ int numIterToCapture = 300;
+
+
+ void debugSnapshot( ReachGraph rg, FlatNode fn, boolean in ) {
+ if( debugCounter > iterStartCapture + numIterToCapture ) {
+ return;
+ }
+
+ if( in ) {
+ ++debugCounter;
+ }
+
+ if( debugCounter > numStartCountReport &&
+ freqCountReport > 0 &&
+ debugCounter % freqCountReport == 0
+ ) {
+ System.out.println( " @@@ debug counter = "+
+ debugCounter );
+ }
+
+ if( debugCounter > iterStartCapture ) {
+ System.out.println( " @@@ capturing debug "+
+ (debugCounter - iterStartCapture)+
+ " @@@" );
+ String graphName;
+ if( in ) {
+ graphName = String.format( "snap%04din",
+ debugCounter - iterStartCapture );
+ } else {
+ graphName = String.format( "snap%04dout",
+ debugCounter - iterStartCapture );
+ }
+ if( fn != null ) {
+ graphName = graphName + fn;
+ }
+ try {
+ rg.writeGraph( graphName,
+ true, // write labels (variables)
+ true, // selectively hide intermediate temp vars
+ true, // prune unreachable heap regions
+ false, // hide subset reachability states
+ true );// hide edge taints
+ } catch( Exception e ) {
+ System.out.println( "Error writing debug capture." );
+ System.exit( 0 );
+ }
+ }
+
+ if( debugCounter == iterStartCapture + numIterToCapture &&
+ stopAfterCapture
+ ) {
+ System.out.println( "Stopping analysis after debug captures." );
+ System.exit( 0 );
+ }
+ }
}