Commit 8bf64119 authored by Carlos Galindo's avatar Carlos Galindo
Browse files

Add exception-sensitive slicing

Other changes: corrections, improvements and API simplification
parent e3feee19
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@

    <groupId>tfm</groupId>
    <artifactId>tfm</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <properties>
@@ -16,5 +17,6 @@
    <modules>
        <module>sdg-core</module>
        <module>sdg-cli</module>
        <module>sdg-gui</module>
    </modules>
</project>
+2 −2
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@

    <groupId>tfm</groupId>
    <artifactId>sdg-cli</artifactId>
    <version>1.0-SNAPSHOT</version>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
@@ -27,7 +27,7 @@
        <dependency>
            <groupId>tfm</groupId>
            <artifactId>sdg-core</artifactId>
            <version>1.0-SNAPSHOT</version>
            <version>1.0.0</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
+24 −14
Original line number Diff line number Diff line
package tfm.cli;

import org.jgrapht.io.DOTExporter;
import tfm.arcs.Arc;
import tfm.graphs.Graph;
import tfm.nodes.GraphNode;
import tfm.utils.FileUtil;
import tfm.utils.Logger;

@@ -21,15 +24,12 @@ public abstract class GraphLog<G extends Graph> {
        }
    }

    static final String CFG = "cfg";
    static final String PDG = "pdg";
    static final String SDG = "sdg";

    G graph;
    protected G graph;

    protected String imageName;
    protected Format format;
    protected String format;
    protected boolean generated = false;
    protected File outputDir = new File("./out/");

    public GraphLog() {
        this(null);
@@ -39,6 +39,10 @@ public abstract class GraphLog<G extends Graph> {
        this.graph = graph;
    }

    public void setDirectory(File outputDir) {
        this.outputDir = outputDir;
    }

    public void log() throws IOException {
        Logger.log(
                "****************************\n" +
@@ -52,7 +56,7 @@ public abstract class GraphLog<G extends Graph> {
                "****************************"
        );
        try (StringWriter stringWriter = new StringWriter()) {
            graph.getDOTExporter().exportGraph(graph, stringWriter);
            getDOTExporter(graph).exportGraph(graph, stringWriter);
            stringWriter.append('\n');
            Logger.log(stringWriter.toString());
        }
@@ -63,22 +67,24 @@ public abstract class GraphLog<G extends Graph> {
    }

    public void generateImages(String imageName) throws IOException {
        generateImages(imageName, Format.PNG);
        generateImages(imageName, "pdf");
    }

    public void generateImages(String imageName, Format format) throws IOException {
        this.imageName = imageName + "-" + graph.getClass().getName();
    public void generateImages(String imageName, String format) throws IOException {
        this.imageName = imageName + "-" + graph.getClass().getSimpleName();
        this.format = format;
        generated = true;
        File tmpDot = File.createTempFile("graph-source-", ".dot");
        tmpDot.getParentFile().mkdirs();
        getImageFile().getParentFile().mkdirs();

        // Graph -> DOT -> file
        try (Writer w = new FileWriter(tmpDot)) {
            graph.getDOTExporter().exportGraph(graph, w);
            getDOTExporter(graph).exportGraph(graph, w);
        }
        // Execute dot
        ProcessBuilder pb = new ProcessBuilder("dot",
            tmpDot.getAbsolutePath(), "-T" + format.getExt(),
            tmpDot.getAbsolutePath(), "-T" + format,
            "-o", getImageFile().getAbsolutePath());
        try {
            int result = pb.start().waitFor();
@@ -96,7 +102,11 @@ public abstract class GraphLog<G extends Graph> {
        FileUtil.open(getImageFile());
    }

    protected File getImageFile() {
        return new File("./out/" + imageName + "." + format.getExt());
    public File getImageFile() {
        return new File(outputDir, imageName + "." + format);
    }

    protected DOTExporter<GraphNode<?>, Arc> getDOTExporter(G graph) {
        return graph.getDOTExporter();
    }
}
+3 −5
Original line number Diff line number Diff line
@@ -31,18 +31,16 @@ public class PDGLog extends GraphLog<PDG> {
        Logger.log(graph.vertexSet().stream()
                .sorted(Comparator.comparingLong(GraphNode::getId))
                .map(node ->
                        String.format("GraphNode { id: %s, instruction: %s, declared: %s, defined: %s, used: %s }",
                        String.format("GraphNode { id: %s, instruction: %s, variables: %s}",
                                node.getId(),
                                node.getInstruction(),
                                node.getDeclaredVariables(),
                                node.getDefinedVariables(),
                                node.getUsedVariables())
                                node.getVariableActions())
                ).collect(Collectors.joining(System.lineSeparator()))
        );
    }

    @Override
    public void generateImages(String imageName, Format format) throws IOException {
    public void generateImages(String imageName, String format) throws IOException {
        super.generateImages(imageName, format);
        if (cfgLog != null)
            cfgLog.generateImages(imageName, format);
+143 −0
Original line number Diff line number Diff line
package tfm.cli;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.comments.BlockComment;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import org.apache.commons.cli.*;
import tfm.graphs.cfg.CFG;
import tfm.graphs.exceptionsensitive.ESSDG;
import tfm.graphs.sdg.SDG;
import tfm.nodes.GraphNode;
import tfm.slicing.NodeIdSlicingCriterion;
import tfm.slicing.Slice;
import tfm.slicing.SlicingCriterion;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;

public class PHPSlice {
    protected static final Options OPTIONS = new Options();

    static {
        OPTIONS.addOption(Option
                .builder("f")
                .hasArg().argName("CriterionFile.java").type(File.class)
                .required()
                .desc("The file that contains the slicing criterion.")
                .build());
        OPTIONS.addOption(Option
                .builder("i")
                .hasArg().argName("node_id")
                .required()
                .desc("The slicing criterion, in the form of a node id (a positive integer).")
                .build());
        OPTIONS.addOption(Option
                .builder("o")
                .hasArg().argName("output_file")
                .required()
                .desc("The folder where the slice and the graphs should be placed")
                .build());
        OPTIONS.addOption("es", "exception-sensitive", false, "Enable exception-sensitive analysis");
        OPTIONS.addOption(Option
                .builder("h").longOpt("help")
                .desc("Shows this text")
                .build());
    }

    private final File outputDir;
    private File scFile;
    private int scId;
    private final CommandLine cliOpts;

    public PHPSlice(String... cliArgs) throws ParseException {
        cliOpts = new DefaultParser().parse(OPTIONS, cliArgs);
        if (cliOpts.hasOption('h'))
            throw new ParseException(OPTIONS.toString());
        setScId(Integer.parseInt(cliOpts.getOptionValue("i")));
        setScFile(cliOpts.getOptionValue("f"));
        outputDir = new File(cliOpts.getOptionValue("o"));
        if (!outputDir.isDirectory())
            throw new ParseException("The output directory is not a directory or not readable by us!");
    }

    private void setScFile(String fileName) throws ParseException {
        File file = new File(fileName);
        if (!(file.exists() && file.isFile()))
            throw new ParseException("Slicing criterion file is not an existing file.");
        scFile = file;
    }

    private void setScId(int line) throws ParseException {
        if (line < 0)
            throw new ParseException("The line of the slicing criterion must be strictly greater than zero.");
        scId = line;
    }

    public void slice() throws ParseException, IOException {
        // Configure JavaParser
        CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver();
        combinedTypeSolver.add(new ReflectionTypeSolver(true));
        JavaParser.getStaticConfiguration().setSymbolResolver(new JavaSymbolSolver(combinedTypeSolver));
        JavaParser.getStaticConfiguration().setAttributeComments(false);

        // Build the SDG
        NodeList<CompilationUnit> units = new NodeList<>();
        try {
            units.add(JavaParser.parse(scFile));
        } catch (FileNotFoundException e) {
            throw new ParseException(e.getMessage());
        }

        SDG sdg = cliOpts.hasOption("exception-sensitive") ? new ESSDG() : new SDG();
        sdg.build(units);

        // Slice the SDG
        SlicingCriterion sc = new NodeIdSlicingCriterion(scId, "");
        Slice slice = sdg.slice(sc);

        // Convert the slice to code and output the result to `outputDir`
        for (CompilationUnit cu : slice.toAst()) {
            if (cu.getStorage().isEmpty())
                throw new IllegalStateException("A synthetic CompilationUnit was discovered, with no file associated to it.");
            File javaFile = new File(outputDir, cu.getStorage().get().getFileName());
            try (PrintWriter pw = new PrintWriter(javaFile)) {
                pw.print(new BlockComment(getDisclaimer(cu.getStorage().get())));
                pw.print(cu);
            } catch (FileNotFoundException e) {
                System.err.println("Could not write file " + javaFile);
            }
        }

        File imageDir = new File(outputDir, "images");
        imageDir.mkdir();
        // Output the sliced graph to the output directory
        SDGLog sdgLog = new SlicedSDGLog(sdg, slice, sc);
        sdgLog.setDirectory(outputDir);
        sdgLog.generateImages("graph", "svg");
        for (CFG cfg : sdg.getCFGs()) {
            CFGLog log = new CFGLog(cfg);
            log.setDirectory(imageDir);
            log.generateImages("root" + cfg.getRootNode().map(GraphNode::getId).orElse(-1L), "svg");
        }
    }

    protected String getDisclaimer(CompilationUnit.Storage s) {
        return String.format("\n\tThis file was automatically generated as part of a slice with criterion" +
                        "\n\tnode id: %d\n\tOriginal file: %s\n", scId, s.getPath());
    }

    public static void main(String... args) {
        try {
            new PHPSlice(args).slice();
        } catch (Exception e) {
            System.err.println("Error!\n" + e.getMessage());
        }
    }

}
Loading