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

boomerang.callgraph.ObservableDynamicICFG Maven / Gradle / Ivy

There is a newer version: 3.2.2
Show newest version
package boomerang.callgraph;

import boomerang.controlflowgraph.ObservableControlFlowGraph;
import boomerang.scene.CallGraph;
import boomerang.scene.CallGraph.Edge;
import boomerang.scene.InvokeExpr;
import boomerang.scene.Method;
import boomerang.scene.Statement;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import java.util.Collection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An interprocedural control-flow graph, for which caller-callee edges can be observed using {@link
 * CalleeListener} and {@link CallerListener}. Used for demand-driven call graph generation.
 *
 * 

Starts with an graph only containing intraprocedual edges and uses a precomputed call graph to * derive callers. * * @author Melanie Bruns on 04.05.2018 */ public class ObservableDynamicICFG implements ObservableICFG { private static final Logger logger = LoggerFactory.getLogger(ObservableDynamicICFG.class); private static final int IMPRECISE_CALL_GRAPH_WARN_THRESHOLD = 30; private int numberOfEdgesTakenFromPrecomputedCallGraph = 0; private CallGraphOptions options = new CallGraphOptions(); private CallGraph demandDrivenCallGraph = new CallGraph(); private final Multimap> calleeListeners = HashMultimap.create(); private final Multimap> callerListeners = HashMultimap.create(); private final ObservableControlFlowGraph cfg; private final ICallerCalleeResolutionStrategy resolutionStrategy; public ObservableDynamicICFG( ObservableControlFlowGraph cfg, ICallerCalleeResolutionStrategy resolutionStrategy) { this.cfg = cfg; this.resolutionStrategy = resolutionStrategy; } @Override public void addCalleeListener(CalleeListener listener) { if (!calleeListeners.put(listener.getObservedCaller(), listener)) { return; } // Notify the new listener about edges we already know Statement stmt = listener.getObservedCaller(); Collection edges = demandDrivenCallGraph.edgesOutOf(stmt); if (edges.size() > IMPRECISE_CALL_GRAPH_WARN_THRESHOLD) { logger.debug( "Call graph has more than {} callees at {}", IMPRECISE_CALL_GRAPH_WARN_THRESHOLD, listener.getObservedCaller()); for (Edge e : Lists.newArrayList(edges)) { logger.trace("\t callee {}", e.tgt()); } } for (Edge e : Lists.newArrayList(edges)) { listener.onCalleeAdded(stmt, e.tgt()); } InvokeExpr ie = stmt.getInvokeExpr(); // Now check if we need to find new edges if ((ie.isInstanceInvokeExpr())) { // If it was invoked on an object we might find new instances if (ie.isSpecialInvokeExpr()) { addCallIfNotInGraph(stmt, resolutionStrategy.resolveSpecialInvoke(ie)); } else { // Query for callees of the unit and add edges to the graph for (Method method : resolutionStrategy.resolveInstanceInvoke(stmt)) { addCallIfNotInGraph(stmt, method); } } } else { // Call was not invoked on an object. Must be static addCallIfNotInGraph(stmt, resolutionStrategy.resolveStaticInvoke(ie)); } } @Override public void addCallerListener(CallerListener listener) { if (!callerListeners.put(listener.getObservedCallee(), listener)) { return; } Method method = listener.getObservedCallee(); logger.debug("Queried for callers of {}.", method); // Notify the new listener about what we already now Collection edges = demandDrivenCallGraph.edgesInto(method); if (edges.size() > IMPRECISE_CALL_GRAPH_WARN_THRESHOLD) { logger.debug( "Call graph has more than {} caller of {}", IMPRECISE_CALL_GRAPH_WARN_THRESHOLD, listener.getObservedCallee()); for (Edge e : edges) { logger.trace("\t callsite {}", e.src()); } } for (Edge e : Lists.newArrayList(edges)) { listener.onCallerAdded(e.src(), method); } } /** * Returns true if the call was added to the call graph, false if it was already present and the * call graph did not change */ protected boolean addCallIfNotInGraph(Statement caller, Method callee) { Edge edge = new Edge(caller, callee); if (!demandDrivenCallGraph.addEdge(edge)) { return false; } logger.debug("Added call from unit '{}' to method '{}'", caller, callee); // Notify all interested listeners, so .. // .. CalleeListeners interested in callees of the caller or the CallGraphExtractor that is // interested in any for (CalleeListener listener : Lists.newArrayList(calleeListeners.get(caller))) { listener.onCalleeAdded(caller, callee); } // .. CallerListeners interested in callers of the callee or the CallGraphExtractor that is // interested in any for (CallerListener listener : Lists.newArrayList(callerListeners.get(callee))) { listener.onCallerAdded(caller, callee); } return true; } protected void notifyNoCalleeFound(Statement s) { for (CalleeListener l : Lists.newArrayList(calleeListeners.get(s))) { l.onNoCalleeFound(); } } @Override public boolean isCallStmt(Statement unit) { return unit.containsInvokeExpr(); } @Override public boolean isExitStmt(Statement unit) { return unit.getMethod().getControlFlowGraph().getEndPoints().contains(unit); } @Override public boolean isStartPoint(Statement unit) { return unit.getMethod().getControlFlowGraph().getStartPoints().contains(unit); } @Override public Collection getStartPointsOf(Method m) { return m.getControlFlowGraph().getStartPoints(); } @Override public Collection getEndPointsOf(Method m) { return m.getControlFlowGraph().getEndPoints(); } @Override public int getNumberOfEdgesTakenFromPrecomputedGraph() { return numberOfEdgesTakenFromPrecomputedCallGraph; } @Override public void resetCallGraph() { demandDrivenCallGraph = new CallGraph(); numberOfEdgesTakenFromPrecomputedCallGraph = 0; calleeListeners.clear(); callerListeners.clear(); } @Override public void computeFallback() { resolutionStrategy.computeFallback(this); } @Override public void addEdges(Edge e) { demandDrivenCallGraph.addEdge(e); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy