All Downloads are FREE. Search and download functionalities are using the official Maven repository.

soot.jimple.infoflow.solver.gcSolver.AbstractReferenceCountingGarbageCollector Maven / Gradle / Ivy

package soot.jimple.infoflow.solver.gcSolver;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import heros.solver.PathEdge;
import soot.SootMethod;
import soot.jimple.infoflow.collect.ConcurrentCountingMap;
import soot.jimple.infoflow.collect.ConcurrentHashSet;
import soot.jimple.infoflow.util.ExtendedAtomicInteger;
import soot.jimple.toolkits.ide.icfg.BiDiInterproceduralCFG;
import soot.util.ConcurrentHashMultiMap;

/**
 * Abstract base class for garbage collectors based on reference counting
 * 
 * @author Steven Arzt
 *
 */
public abstract class AbstractReferenceCountingGarbageCollector extends AbstractGarbageCollector
		implements IGarbageCollectorPeer {

	private ConcurrentCountingMap jumpFnCounter = new ConcurrentCountingMap<>();
	private final Set gcScheduleSet = new ConcurrentHashSet<>();
	private final AtomicInteger gcedMethods = new AtomicInteger();
	private final AtomicInteger gcedEdges = new AtomicInteger();
	private final ExtendedAtomicInteger edgeCounterForThreshold = new ExtendedAtomicInteger();
	private GarbageCollectionTrigger trigger = GarbageCollectionTrigger.Immediate;
	private GarbageCollectorPeerGroup peerGroup = null;
	private boolean checkChangeCounter = false;

	protected boolean validateEdges = false;
	protected Set> oldEdges = new HashSet<>();

	/**
	 * The number of methods to collect as candidates for garbage collection, before
	 * halting the taint propagation and actually cleaning them up
	 */
	protected int methodThreshold = 0;
	/**
	 * Wait for at least this number of edges before starting the garbage collection
	 */
	protected int edgeThreshold = 0;

	public AbstractReferenceCountingGarbageCollector(BiDiInterproceduralCFG icfg,
			ConcurrentHashMultiMap> jumpFunctions,
			IGCReferenceProvider referenceProvider) {
		super(icfg, jumpFunctions, referenceProvider);
	}

	public AbstractReferenceCountingGarbageCollector(BiDiInterproceduralCFG icfg,
			ConcurrentHashMultiMap> jumpFunctions) {
		super(icfg, jumpFunctions);
	}

	@Override
	public void notifyEdgeSchedule(PathEdge edge) {
		SootMethod sm = icfg.getMethodOf(edge.getTarget());
		jumpFnCounter.increment(sm);
		gcScheduleSet.add(sm);
		if (trigger == GarbageCollectionTrigger.EdgeThreshold)
			edgeCounterForThreshold.incrementAndGet();

		if (validateEdges) {
			if (oldEdges.contains(edge))
				System.out.println("Edge re-scheduled");
		}
	}

	@Override
	public void notifyTaskProcessed(PathEdge edge) {
		jumpFnCounter.decrement(icfg.getMethodOf(edge.getTarget()));
	}

	/**
	 * Checks whether the given method has any open dependencies that prevent its
	 * jump functions from being garbage collected
	 * 
	 * @param method           The method to check
	 * @param referenceCounter The counter that keeps track of active references to
	 *                         taint abstractions
	 * @return True it the method has active dependencies and thus cannot be
	 *         garbage-collected, false otherwise
	 */
	private boolean hasActiveDependencies(SootMethod method, ConcurrentCountingMap referenceCounter) {
		int changeCounter = -1;
		do {
			// Update the change counter for the next round
			changeCounter = referenceCounter.getChangeCounter();

			// Check the method itself
			if (referenceCounter.get(method) > 0)
				return true;

			// Check the transitive callees
			Set references = referenceProvider.getMethodReferences(method, null);
			for (SootMethod ref : references) {
				if (referenceCounter.get(ref) > 0)
					return true;
			}
		} while (checkChangeCounter && changeCounter != referenceCounter.getChangeCounter());
		return false;
	}

	@Override
	public boolean hasActiveDependencies(SootMethod method) {
		return hasActiveDependencies(method, jumpFnCounter);
	}

	/**
	 * Immediately performs garbage collection
	 */
	protected void gcImmediate() {
		if (gcScheduleSet != null && !gcScheduleSet.isEmpty()) {
			// Check our various triggers for garbage collection
			boolean gc = trigger == GarbageCollectionTrigger.Immediate;
			gc |= trigger == GarbageCollectionTrigger.MethodThreshold && gcScheduleSet.size() > methodThreshold;
			gc |= trigger == GarbageCollectionTrigger.EdgeThreshold && edgeCounterForThreshold.get() > edgeThreshold;

			// Perform the garbage collection if required
			if (gc) {
				int tempMethods = 0;
				onBeforeRemoveEdges();
				for (SootMethod sm : gcScheduleSet) {
					// Is it safe to remove this method?
					if (peerGroup != null) {
						if (peerGroup.hasActiveDependencies(sm))
							continue;
					} else if (hasActiveDependencies(sm))
						continue;

					// Get stats for the stuff we are about to remove
					Set> oldFunctions = jumpFunctions.get(sm);
					if (oldFunctions != null) {
						int gcedSize = oldFunctions.size();
						gcedEdges.addAndGet(gcedSize);
						if (trigger == GarbageCollectionTrigger.EdgeThreshold)
							edgeCounterForThreshold.subtract(gcedSize);
					}

					// First unregister the method, then delete the edges. In case some other thread
					// concurrently schedules a new edge, the method gets back into the GC work list
					// this way.
					gcScheduleSet.remove(sm);
					if (jumpFunctions.remove(sm)) {
						gcedMethods.incrementAndGet();
						tempMethods++;
						if (validateEdges)
							oldEdges.addAll(oldFunctions);
					}
				}
				onAfterRemoveEdges(tempMethods);
			}
		}
	}

	/**
	 * Method that is called before the first edge is removed from the jump
	 * functions
	 */
	protected void onBeforeRemoveEdges() {
	}

	/**
	 * Method that is called after the last edge has been removed from the jump
	 * functions
	 * 
	 * @param gcedMethods The number of methods for which edges have been removed
	 */
	protected void onAfterRemoveEdges(int gcedMethods) {
	}

	@Override
	public int getGcedMethods() {
		return gcedMethods.get();
	}

	@Override
	public int getGcedEdges() {
		return gcedEdges.get();
	}

	/**
	 * Sets the number of methods for which edges must have been added before
	 * garbage collection is started
	 * 
	 * @param threshold The threshold of new methods required to trigger garbage
	 *                  collection
	 */
	public void setMethodThreshold(int threshold) {
		this.methodThreshold = threshold;
	}

	/**
	 * Sets the minimum number of edges that shall be propagated before triggering
	 * the garbage collection
	 * 
	 * @param threshold The minimum number of edges that shall be propagated before
	 *                  triggering the garbage collection
	 */
	public void setEdgeThreshold(int threshold) {
		this.edgeThreshold = threshold;
	}

	/**
	 * Set the trigger that defines when garbage collection shall be started
	 * 
	 * @param trigger The trigger that defines when garbage collection shall be
	 *                started
	 */
	public void setTrigger(GarbageCollectionTrigger trigger) {
		this.trigger = trigger;
	}

	/**
	 * Sets the peer group in which this solver operates. Peer groups are used to
	 * synchronize active dependencies between multiple solvers.
	 * 
	 * @param peerGroup The peer group
	 */
	public void setPeerGroup(GarbageCollectorPeerGroup peerGroup) {
		this.peerGroup = peerGroup;
		peerGroup.addGarbageCollector(this);
	}

	/**
	 * Sets whether the change counter shall be checked when identifying active
	 * method dependencies
	 * 
	 * @param checkChangeCounter True to ensure consistency using change counters,
	 *                           false otherwise
	 */
	public void setCheckChangeCounter(boolean checkChangeCounter) {
		this.checkChangeCounter = checkChangeCounter;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy