Commit 2f3d9abf authored by Carlos Galindo's avatar Carlos Galindo
Browse files

Bug-fixing on tabular slicing algorithms and improvements for benchmarks.

- Tabular algorithm: only traverse intraprocedural edges by default.
- Improved subsumption checks in tabular algorithm.
- Corrected actual-out detection.
- Added assertions to check for incorrect interprocedural edges.
- Control-flow is incorrect in anonymous routines: it produces interprocedural control dependence.
- Parametrized erlang server connection settings.
- Added new comparisons for benchmarks.
- New script to run benchmarks and analyze results with SQLite.
parent 58d95b2c
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ import edg.graph.EDG;
 * Constraind tabular algorithm with subsumption.
 * It uses stack ordering to disallow insertion into pathEdge and workList if a subsuming
 * stack is included in them already.
 * @see ConstrainedTabularAlgorithm
 */
public class ConstrainedSubsumedTabularAlgorithm extends ConstrainedTabularAlgorithm {
    public ConstrainedSubsumedTabularAlgorithm(EDG edg) {
@@ -14,9 +15,11 @@ public class ConstrainedSubsumedTabularAlgorithm extends ConstrainedTabularAlgor

    @Override
    protected void propagate(Work work) {
        if (!pathEdge.contains(work) && pathEdge.stream().noneMatch(work::isSubsumedBy)) {
        if (!pathEdge.contains(work)) {
            if (work.current().stack().isEdgeConstraintsEmpty() || pathEdge.stream().noneMatch(work::isSubsumedBy)) {
                pathEdge.add(work);
                workList.add(work);
            }
        }
    }
}
+23 −8
Original line number Diff line number Diff line
@@ -88,7 +88,7 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
                traverseSummaries(w);
                contextSwitch(w.current);
            } else {
                traverseEdges(w, e -> true);
                traverseEdges(w, this::edgeIsIntraprocedural);
            }
        }
    }
@@ -115,6 +115,16 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
                continue;
            // Processing EdgeWork
            Node nextNode = edg.getEdgeSource(edge);
            assert work.context == null
                    || edg.getAncestor(nextNode, Node.Type.AnonymousRoutine) == null
                    || edg.getAncestor(work.context.node, Node.Type.AnonymousRoutine) == nextNode
                    || edg.getAncestor(work.context.node, Node.Type.AnonymousRoutine) == edg.getAncestor(nextNode, Node.Type.AnonymousRoutine):
                    String.format("Illegal context switch in edge %d -> %d (%s)", node.getId(), nextNode.getId(), edge.getType());
            assert work.context == null
                    || edg.getAncestor(work.context.node, Node.Type.Routine) == nextNode
                    || edg.getAncestor(nextNode, Node.Type.Routine) == null
                    || edg.getAncestor(work.context.node, Node.Type.Routine) == edg.getAncestor(nextNode, Node.Type.Routine):
                    String.format("Illegal context switch in edge %d -> %d (%s)", node.getId(), nextNode.getId(), edge.getType());
            EdgeConstraint edgeCons = edge.getConstraint();
            EdgeConstraint topConstraint = work.current.stack.isEdgeConstraintsEmpty() ? null : work.current.stack.peekEdgeConstraint();
            List<Constraints> resolvedList = edgeCons.resolve(Phase.Tabular, edg, edge, (Constraints) work.current.stack.clone(), topConstraint, 0);
@@ -137,10 +147,18 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
     * all actual-out nodes that have been reached immediately traverse the new summary edge.
     */
    protected void createSummaryEdges(MState formalIn, State formalOut) {
        assert edg.getAncestor(formalIn.node, Node.Type.AnonymousRoutine) == formalOut.node
                || edg.getAncestor(formalOut.node, Node.Type.AnonymousRoutine) == null
                || edg.getAncestor(formalIn.node, Node.Type.AnonymousRoutine) == edg.getAncestor(formalOut.node, Node.Type.AnonymousRoutine):
                String.format("Illegal context switch in potential summary edge %d -> %d", formalIn.node.getId(), formalOut.node.getId());
        assert edg.getAncestor(formalIn.node, Node.Type.Routine) == formalOut.node
                || edg.getAncestor(formalOut.node, Node.Type.Routine) == null
                || edg.getAncestor(formalIn.node, Node.Type.Routine) == edg.getAncestor(formalOut.node, Node.Type.Routine):
                String.format("Illegal context switch in potential summary edge %d -> %d", formalIn.node.getId(), formalOut.node.getId());
        List<Node> actualOutNodes = edg.getNodes(formalOut.node, LAST.Direction.Forwards, Edge.Type.Output);
        for (Node aInNode : edg.getNodes(formalIn.node, LAST.Direction.Backwards, Edge.Type.Input)) {
            Node call = edg.getNodeFromRes(aInNode).getType() == Node.Type.Call ?
                    edg.getNodeFromRes(aInNode) : edg.getAncestor(aInNode, Node.Type.Call);
            Node call = edg.getParent(aInNode).getType() == Node.Type.Arguments ?
                    edg.getAncestor(aInNode, Node.Type.Call) : edg.getNodeFromRes(aInNode);
            int toBeRemoved = -1;
            for (int i = 0; i < actualOutNodes.size(); i++) {
                Node aOutNode = actualOutNodes.get(i);
@@ -200,10 +218,7 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {

    /** Checks whether a given node is an actual-out node in a call. */
    protected boolean isActualOut(Node node) {
        Node call = edg.getNodeFromRes(node);
        if (node.getType() == Node.Type.Result && call.getType() == Node.Type.Call)
            return edg.getNodes(node, LAST.Direction.Backwards, Edge.Type.Value).contains(call);
        return false;
        return !edg.getEdges(node, LAST.Direction.Backwards, Edge.Type.Output).isEmpty();
    }

    /**
@@ -228,7 +243,7 @@ public class ConstrainedTabularAlgorithm implements SlicingAlgorithm {
         * for which we check whether this current stack is subsumed by the other's.
         */
        public boolean isSubsumedBy(Work work) {
            return context.equals(work.context) && current.node.equals(work.current.node)
            return Objects.equals(context, work.context) && current.node.equals(work.current.node)
                    && work.current.stack.isSuffix(current.stack);
        }

+30 −28
Original line number Diff line number Diff line
@@ -18,31 +18,34 @@

package eknife.erlang;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;

import com.ericsson.otp.erlang.OtpConnection;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpPeer;
import com.ericsson.otp.erlang.OtpSelf;

import eknife.config.Config;
import misc.util.Flusher;
import misc.util.Flusher.Output;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

public class ErlConnection
{
	/** Starting the server can be time-consuming. For anything but standalone use, it might be better to start it
	 *  in the background and re-using the same server over different invocations of e-Knife. */
	private final static boolean START_SERVER = Boolean.parseBoolean(System.getProperty("erlServer.autoStart", "true"));
	/** If {@link #START_SERVER} is {@code false}, set this value to the one given on server start. */
	private static final String SERVER_COOKIE = System.getProperty("erlServer.cookie", "erlang");
	/** If {@link #START_SERVER} is {@code false}, set this value to the one given on server start. */
	private static final String SERVER_NAME = System.getProperty("erlServer.name", "server");
	/** If multiple instances of e-Knife access the same server, they need different client names. */
	private static final boolean RANDOMIZE_CLIENT_NAME = Boolean.parseBoolean(System.getProperty("erlServer.randomizeClientName", "false"));
	private static final String defaultClientNodeName = "client";

	private final Config config = Config.getConfig();
	private final String defaultClientNodeName = "client";
	private final String defaultServerNodeName = "server";
	private final String defaultCookie = "erlang";

	private String clientNodeName;
	private String serverNodeName;
	private String cookie;
	private OtpSelf clientNode;
	private OtpPeer serverNode;
	private OtpConnection connection;
	private Process serverProcess;

@@ -62,14 +65,13 @@ public class ErlConnection
		for (int id = 0; id < 10 && !connected; id++)
			try
			{
				this.clientNodeName = this.defaultClientNodeName + id;
				this.serverNodeName = this.defaultServerNodeName + id;
				this.cookie = this.defaultCookie + id;
				String clientNodeName = RANDOMIZE_CLIENT_NAME ? UUID.randomUUID().toString() : defaultClientNodeName + id;
				this.serverNodeName = START_SERVER ? SERVER_NAME + id : SERVER_NAME;
				if (START_SERVER)
					this.openServer();
				this.clientNode = new OtpSelf(this.clientNodeName + "@localhost", this.cookie);
				this.serverNode = new OtpPeer(this.serverNodeName + "@localhost");
				this.serverNode.setCookie(this.cookie);
				this.connection = this.clientNode.connect(this.serverNode);
				OtpSelf clientNode = new OtpSelf(clientNodeName + "@localhost", SERVER_COOKIE);
				OtpPeer serverNode = new OtpPeer(this.serverNodeName + "@localhost");
				this.connection = clientNode.connect(serverNode);
				connected = true;
			}
			catch (Exception e)
@@ -85,23 +87,23 @@ public class ErlConnection
		if (!this.isConnected())
			throw new RuntimeException("The connection is not established");

		try
		{
//		try
//		{
			if (this.connection != null)
				this.connection.close();
			this.closeServer();
		}
		catch (Exception e)
		{
			throw new RuntimeException(e);
		}
//		}
//		catch (Exception e)
//		{
//			throw new RuntimeException(e);
//		}
	}
	private void openServer() throws Exception {
		// Start an `erl` process with a shell, so that it has some output, and we can
		// measure using Flusher that it has started.
		final File scriptsFile = this.config.getScriptsFile();
		serverProcess = new ProcessBuilder("erl", "-pa", scriptsFile.getAbsolutePath(),
				"-name", this.serverNodeName + "@localhost", "-setcookie", this.cookie).start();
				"-name", this.serverNodeName + "@localhost", "-setcookie", SERVER_COOKIE).start();
		final Flusher flusher = new Flusher(this.serverProcess, true, true);
		flusher.start();
		while (flusher.getOutput(Output.Standard).isEmpty() && flusher.getOutput(Output.Error).isEmpty());
+6 −1
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@ import java.util.Map;
 */
public class InterprocSummaryNonRecursiveComparerBenchmark extends InterproceduralTerminatingBenchmark {

    private static final int CONSTRAINED = 0, TABULAR = 1, STANDARD = 2, TABULAR_CONSTRAINED = 3;
    private static final int CONSTRAINED = 0, TABULAR = 1, STANDARD = 2, TABULAR_CONSTRAINED = 3, TABULAR_SUBSUMED = 4;
    private int algorithm;

    public static void main(String[] args) {
@@ -43,6 +43,10 @@ public class InterprocSummaryNonRecursiveComparerBenchmark extends Interprocedur
        this.setName = setName + "_tabular";
        super.benchmarkProgram(programFile);
        logger.log(System.Logger.Level.INFO, "Tabular constrained finished");
        algorithm = TABULAR_SUBSUMED;
        this.setName = setName + "_tabularsub";
        super.benchmarkProgram(programFile);
        logger.log(System.Logger.Level.INFO, "Tabular subsumed finished");
        // Work-list approach
        algorithm = CONSTRAINED;
        Config.programIsRecursive = true;
@@ -64,6 +68,7 @@ public class InterprocSummaryNonRecursiveComparerBenchmark extends Interprocedur
            case TABULAR:     return new TabularAlgorithm(edg);
            case STANDARD:    return new StandardAlgorithm(edg);
            case TABULAR_CONSTRAINED: return new ConstrainedTabularAlgorithm(edg);
            case TABULAR_SUBSUMED:    return new ConstrainedSubsumedTabularAlgorithm(edg);
            default: throw new IllegalStateException();
        }
    }
+62 −0
Original line number Diff line number Diff line
package eknife.benchmark;

import edg.config.Config;
import edg.graph.EDG;
import edg.graph.Node;
import edg.slicing.ConstrainedSubsumedTabularAlgorithm;
import edg.slicing.ConstrainedTabularAlgorithm;
import edg.slicing.SlicingAlgorithm;

import java.io.File;
import java.util.List;
import java.util.Map;

public class InterprocSummaryRecursiveComparerBenchmark extends InterproceduralTerminatingBenchmark {

    protected static final int NO_SUBSUMPTION = 0, SUBSUMPTION = 1;
    protected int algorithm;

    public static void main(String[] args) {
        new InterprocSummaryRecursiveComparerBenchmark("bencher-no-lambdas_" + Config.MAX_STACK_SIZE)
                .benchmarkAll(new File("e-Knife/src/test/resources/bencher-no-lambdas"));
    }

    public InterprocSummaryRecursiveComparerBenchmark(String setName) {
        super(setName);
    }

    @Override
    public void benchmarkProgram(File programFile) {
        System.getLogger("bench").log(System.Logger.Level.INFO, "Started benching " + setName + "#" + programFile.getName());
        String setName = this.setName;

        algorithm = NO_SUBSUMPTION;
        this.setName = setName + "_subsumption";
        super.benchmarkProgram(programFile);
        algorithm = SUBSUMPTION;
        this.setName = setName + "_tabular";
        super.benchmarkProgram(programFile);

        this.setName = setName;
        System.getLogger("bench").log(System.Logger.Level.INFO, "Finished benching " + setName + "#" + programFile.getName());
    }


    protected SlicingAlgorithm createAlg(EDG edg) {
        switch (algorithm) {
            case NO_SUBSUMPTION: return new ConstrainedTabularAlgorithm(edg);
            case    SUBSUMPTION: return new ConstrainedSubsumedTabularAlgorithm(edg);
            default: throw new IllegalStateException();
        }
    }

    @Override
    protected void measureSlicing(String moduleName, EDG edg, Map<Node, List<Node>> clause2resNodes, Map<Node, String> clause2name, SlicingAlgorithm algorithm, boolean clearCache, String benchName) {
        super.measureSlicing(moduleName, edg, clause2resNodes, clause2name, createAlg(edg), clearCache, benchName);
    }

    @Override
    protected void measureSlicingIncrementalCache(String moduleName, EDG edg, Map<Node, List<Node>> clause2resNodes, SlicingAlgorithm algorithm) {
        super.measureSlicingIncrementalCache(moduleName, edg, clause2resNodes, createAlg(edg));
    }
}
Loading