/* * e-Knife, a program slicing tool for Erlang based on the EDG * Copyright (c) 2021. David Insa, Sergio PĂ©rez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package eknife; import edg.DotFactory; import edg.EDGFactory; import edg.PdfFactory; import edg.graph.EDG; import edg.graph.Edge; import edg.graph.LAST; import edg.graph.Node; import edg.slicing.*; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.*; import java.io.File; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.lang.reflect.InvocationTargetException; public class EKnife { public enum Language {Java, Erlang, Php} // TODO: OUTPUT FILES PATH // Graph Generation Time public static final String baseDir = "/Users/serperu/Desktop/SlicerOutput/JSS_Empirical_Evaluation/"; public static final String outputTimeFolder = baseDir + "Times/"; public static final String sliceTimeFolder = outputTimeFolder + "Slices/"; public static final String outputSliceGenerationFolder = baseDir + "Slices/"; public static void main(String[] args) { if (args.length == 0) { EKnife.printHelp(); return; } final Args arguments = EKnife.processArguments(args); if (!arguments.isValid()) { EKnife.printHelp(); System.exit(3); } EKnife.run(arguments); } public static void generateAllSlices(String[] args, String setName) { if (args.length == 0) { EKnife.printHelp(); return; } final Args arguments = EKnife.processArguments(args); if (!arguments.isValid()) { EKnife.printHelp(); System.exit(3); } EKnife.sliceGenerationRun(arguments, setName); } public static void timedRun(String[] args, String setName) { if (args.length == 0) { EKnife.printHelp(); return; } final Args arguments = EKnife.processArguments(args); if (!arguments.isValid()) { EKnife.printHelp(); System.exit(3); } EKnife.timedRun(arguments,setName); // EKnife.SCCounter(arguments); } private static Args processArguments(String[] args) { Args kArgs = new Args(); for (int argIndex = 0; argIndex < args.length; argIndex++) { final String arg = args[argIndex]; switch (arg) { case "-i": case "--input": case "-o": case "--output": case "-f": case "--file": case "-l": case "--line": case "-v": case "--var": case "-n": case "--occurrence": case "-G": case "--print-graph": if (argIndex == args.length - 1) { System.out.printf("Parameter %s requires an argument\n", arg); printHelp(); System.exit(1); } } switch (arg) { case "--help": printHelp(); System.exit(0); case "-i": case "--input": kArgs.inputPath = args[argIndex + 1]; break; case "-o": case "--output": kArgs.outputFile = new File(args[argIndex + 1]); break; case "-f": case "--file": kArgs.file = args[argIndex + 1]; break; case "-l": case "--line": kArgs.line = Integer.parseInt(args[argIndex + 1]); break; case "-v": case "--var": kArgs.name = args[argIndex + 1]; break; case "-n": case "--occurrence": kArgs.occurrence = Integer.parseInt(args[argIndex + 1]); break; case "-G": case "--print-graph": kArgs.setGraphFile(new File(args[argIndex + 1])); break; case "--ignore-constraints": kArgs.algClass = AdaptedStandardAlgorithm.class; break; } } kArgs.completeData(); return kArgs; } private static void printHelp() { String help = ""; help += "Use the following options:\n"; help += " -i,--input The file/folder where the source code is\n"; help += " -o,--output The file/folder where the slice will be stored\n"; help += " -f,--file The file (relative to -i) where the slicing criterion is\n"; help += " -l,--line The line of the slicing criterion\n"; help += " -v,--var The name of the slicing criterion (must be a variable)\n"; help += " -n,--occurrence The occurrence of the slicing criterion in that line\n"; help += " -G,--print-graph Exports the graph as a dot file\n"; help += " -G,--print-graph Exports the graph as a PDF file\n"; help += " --ignore-constraints Generates constraints but ignores when slicing\n"; help += " --help Show this message.\n"; System.out.print(help); } private static void run(Args a) { final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true); final EDG edg = new EDGFactory(last).createEDG(); final SlicingCriterion slicingCriterion = new SlicingCriterion(a.file, a.line, a.name, a.occurrence); final Node SC; try { SC = edg.getNode(slicingCriterion); if (SC == null) { System.out.println("Error: the slicing criterion could not be found! " + slicingCriterion); System.exit(1); } } catch (IllegalArgumentException e) { SC = null; System.out.println("Error: the slicing criterion could not be found! " + slicingCriterion); System.exit(1); } final SlicingAlgorithm slicingAlgorithm = a.getAlgorithm(edg); final Set slice = slicingAlgorithm.slice(SC); if (slice.isEmpty()) { System.out.println("Warning: no files will be written, as the slice is empty."); System.exit(2); } if (a.graphFile != null) { switch (a.graphFormat) { case DOT: DotFactory.createDot(a.graphFile, edg, SC, slice); break; case PDF: PdfFactory.createPdf(a.graphFile, edg, SC, slice); break; } } CodeFactory.createCode(Language.Erlang, a.outputFile, edg, slice); } /** * Executes and measures time for graph generation and slicing according to measured dimensions */ private static void timedRun(Args a, String setName) { // CONFIGURE MEASURED DIMENSIONS boolean measureGenerationTime = true; boolean performAllSCs = true; /* ***************************************************** */ /* ** MEASUREMENT OF GENERATION TIME (100 executions) ** */ /* ***************************************************** */ int numberOfIterationsGen = 2; int numberOfIterationsSlice = 5; // GET MODULE NAME: String moduleName = a.inputPath.lastIndexOf(File.separator) != -1 ? a.inputPath.substring(a.inputPath.lastIndexOf("/") + 1) : a.inputPath; moduleName = moduleName.lastIndexOf(".") != -1 ? moduleName.substring(0, moduleName.lastIndexOf(".")) : moduleName; if (measureGenerationTime) { double[] StructuralTime = new double[numberOfIterationsGen]; double[] CFGTime = new double[numberOfIterationsGen]; double[] ValueTime = new double[numberOfIterationsGen]; double[] CompositeTime = new double[numberOfIterationsGen]; double[] ControlTime = new double[numberOfIterationsGen]; double[] FlowTime = new double[numberOfIterationsGen]; double[] IPTime = new double[numberOfIterationsGen]; double[] standardTime = new double[numberOfIterationsGen]; double[] constrainedTime = new double[numberOfIterationsGen]; for (int i = -1; i < numberOfIterationsGen; i++) { final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true); final EDG edg = new EDGFactory(last).createEDG(); if (i == -1) continue; StructuralTime[i] = edg.getGenerationTime().getStructureTime(); CFGTime[i] = edg.getGenerationTime().getControlFlowTime(); ValueTime[i] = edg.getGenerationTime().getValueTime(); CompositeTime[i] = edg.getGenerationTime().getCompositeTime(); ControlTime[i] = edg.getGenerationTime().getControlTime(); FlowTime[i] = edg.getGenerationTime().getFlowTime(); IPTime[i] = edg.getGenerationTime().getInterproceduralTime(); standardTime[i] = StructuralTime[i] + CFGTime[i] + ValueTime[i] - CompositeTime[i] + ControlTime[i] + FlowTime[i] + IPTime[i]; constrainedTime[i] = StructuralTime[i] + CFGTime[i] + ValueTime[i] + ControlTime[i] + FlowTime[i] + IPTime[i]; // printArray("/Users/serperu/Desktop/SlicerOutput/Times/ArrayContent.txt",constrainedTime); } // Print RESULTS: try { new File(outputTimeFolder).mkdirs(); File txtSDG = new File(outputTimeFolder + "graphGenerationPDG.txt"); File txtEDG = new File(outputTimeFolder + "graphGenerationCE-PDG.txt"); if (!txtSDG.exists()) txtSDG.createNewFile(); if (!txtEDG.exists()) txtEDG.createNewFile(); FileWriter fw = new FileWriter(txtSDG, true); for (int i = 0; i < numberOfIterationsGen; i++) { fw.append(moduleName + ";" + standardTime[i] + "\n"); } fw.close(); FileWriter fw2 = new FileWriter(txtEDG, true); for (int i = 0; i < numberOfIterationsGen; i++) { fw2.append(moduleName + ";" + constrainedTime[i] + "\n"); } fw2.close(); } catch (IOException e) { System.out.println("An error occurred."); e.printStackTrace(); } // INTRAPROCEDURAL GENERATION TIME // TIME MEASSUREMENT: // printCSVGenerationTime(a.file, standardTime, constrainedTime); } final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true); final EDG edg = new EDGFactory(last).createEDG(); /* ************************ */ /* ** MEASUREMENT OF SCs ** */ /* ************************ */ if (performAllSCs) { // SC SELECTION List clauses = edg.getNodes(Node.Type.Clause); clauses.removeIf(c -> edg.getParent(c).getType() != Node.Type.Routine); // printHeadings("/Users/serperu/Desktop/SlicerOutput/Times/slicingStatistics.txt", a.file); int scCounter = 0; for (Node clause : clauses) { int arity = edg.getChildrenNonResult(edg.getChild(clause, Node.Type.Parameters)).size(); String functionName = edg.getParent(clause).getName() + "/" + arity; List resultNodes = extractSlicingCriteria(edg, clause, a.file); // CONFIGURE MEASURED DIMENSIONS boolean measureSlicingTime = true; final SlicingAlgorithm standardAlgorithm = new AdaptedStandardAlgorithm(edg); final SlicingAlgorithm constrainedAlgorithm = new OnePassConstrainedAlgorithm(edg); if (setName.equals("SetA")) scCounter = 0; for (Node SC : resultNodes) { // INITIALIZATIONS double[] standardTime = new double[numberOfIterationsSlice]; double[] constrainedTime = new double[numberOfIterationsSlice]; // MEASURE TIME FOR EACH SC if (measureSlicingTime) { for (int j = -1; j < numberOfIterationsSlice; j++) { long initialStandardTime = System.nanoTime(); standardAlgorithm.slice(SC); long finalStandardTime = System.nanoTime(); if (j == -1) continue; standardTime[j] = (finalStandardTime - initialStandardTime) / 1000.0; } for (int j = -1; j < numberOfIterationsSlice; j++) { long initialConstrainedTime = System.nanoTime(); constrainedAlgorithm.slice(SC); long finalConstrainedTime = System.nanoTime(); if (j == -1) continue; constrainedTime[j] = (finalConstrainedTime - initialConstrainedTime) / 1000.0; } printEachSCData(setName, moduleName, functionName, scCounter, standardTime, constrainedTime); } scCounter++; } } } } /** Extracts the possible SCs from a clause considering different factors commented inside the method body */ public static List extractSlicingCriteria(EDG edg, Node clause, String fileName) { List descendants = edg.getDescendants(clause); descendants.add(clause); List resultNodes = new LinkedList<>(); List nonResultNodes = new LinkedList<>(); for (Node n : descendants) if (n.getType() == Node.Type.Result) { if (edg.getNodeFromRes(n).getType() != Node.Type.Callee) resultNodes.add(n); } else nonResultNodes.add(n); nonResultNodes.add(clause); resultNodes.add(edg.getResFromNode(clause)); // IGNORE FUNCTIONS WITHOUT COMPOSITE STRUCTURES if (nonResultNodes.stream() .noneMatch( n -> { if (n.getType() == Node.Type.DataConstructor) return true; if (n.getType() == Node.Type.List) if (edg.getChildren(n).size() != 0 || edg.getParent(n).getType() == Node.Type.List) return true; return false; })) { // System.out.println("File Name: " + fileName); // int functionArity = edg.getChildrenNonResult(edg.getChild(clause, Node.Type.Parameters)).size(); // System.out.println("Function " + edg.getParent(clause).getName() + "/" + functionArity + " has no valid slicing criteria."); return new LinkedList<>(); } /* FILTER SC NODES BY A CRITERION (CONSIDER ONLY VARIABLES) */ resultNodes.removeIf(n -> edg.getNodeFromRes(n).getType() != Node.Type.Variable); /* REMOVE NODES GENERATED BY HIGH ORDER FUNS (e.g., fun g/2) */ resultNodes.removeIf(n -> edg.getAncestor(n, Node.Type.AnonymousRoutine) != null && edg.getNodeFromRes(n).getName().startsWith("FreshVar")); /* REMOVE DEAD CODE FROM BEING SLICING CRITERIA */ resultNodes.removeIf(n -> edg.incomingEdgesOf(edg.getNodeFromRes(n)).stream().noneMatch(Edge::isControlFlowEdge)); return resultNodes; } /** Generates the slice and store it for all possible slicing criteria */ private static void sliceGenerationRun(Args a, String setName) { final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true); final EDG edg = new EDGFactory(last).createEDG(); int scFunctionCounter = 0; /* ************************ */ /* ** MEASUREMENT OF SCs ** */ /* ************************ */ // SC SELECTION List clauses = edg.getNodes(Node.Type.Clause); clauses.removeIf(c -> edg.getParent(c).getType() != Node.Type.Routine); for (Node clause : clauses) { Map functionCriteria = new HashMap<>(); List descendants = edg.getDescendants(clause); descendants.add(clause); List resultNodes = extractSlicingCriteria(edg,clause,a.file); int arity = edg.getChildrenNonResult(edg.getChild(clause, Node.Type.Parameters)).size(); String functionName = edg.getParent(clause).getName() + "-" + arity; String moduleName = edg.getParent(edg.getParent(clause)).getName(); moduleName = setName.equals("SetA") ? setName+"/"+moduleName + "-" + functionName : setName+"/"+moduleName; if (!resultNodes.isEmpty()) new File(outputSliceGenerationFolder + moduleName).mkdirs(); final SlicingAlgorithm standardAlgorithm = new AdaptedStandardAlgorithm(edg); final SlicingAlgorithm constrainedAlgorithm = new OnePassConstrainedAlgorithm(edg); // WITH LOOPS if(setName.equals("SetA")) scFunctionCounter = 0; for (Node SC : resultNodes) { Node normalNodeSC = edg.getNodeFromRes(SC); String scString = "<"+normalNodeSC.getInfo().getLine()+","+normalNodeSC.getName()+">"; functionCriteria.put("#"+scFunctionCounter,scString); // MEASURE PERFORMARNCE final Set standardSlice = standardAlgorithm.slice(SC); final Set constrainedSlice = constrainedAlgorithm.slice(SC); if (constrainedSlice.size() > standardSlice.size()) throw new RuntimeException("The CE-PDG slice cannot be greater than the PDG slice"); // PDG PRINT CodeFactory.createCode(Language.Erlang, new File(outputSliceGenerationFolder+moduleName+"/PDG"+"#"+scFunctionCounter+".erl"), edg, standardSlice); // CE-PDG PRINT CodeFactory.createCode(Language.Erlang, new File(outputSliceGenerationFolder+moduleName+"/CE-PDG"+"#"+scFunctionCounter+".erl"), edg, constrainedSlice); scFunctionCounter++; } if (scFunctionCounter > 0) { try { File txt = new File(outputSliceGenerationFolder + moduleName + "/criteria_map.txt"); txt.createNewFile(); FileWriter fw = new FileWriter(txt, true); for (String k : functionCriteria.keySet()) { fw.append(k + " => " + functionCriteria.get(k) + "\n"); } fw.close(); } catch (IOException e) { System.out.println("An error occurred."); e.printStackTrace(); } } } } /** Method to count the number of SCs in each program or pair program-function */ public static void SCCounter(Args a) { final LAST last = LASTFactory.createLAST(Language.Erlang, a.inputPath, true); final EDG edg = new EDGFactory(last).createEDG(); // SC SELECTION List clauses = edg.getNodes(Node.Type.Clause); clauses.removeIf(c -> edg.getParent(c).getType() != Node.Type.Routine); int scFunctionCounter = 0; for (Node clause : clauses) { Map functionCriteria = new HashMap<>(); int arity = edg.getChildrenNonResult(edg.getChild(clause, Node.Type.Parameters)).size(); String functionName = edg.getParent(clause).getName() + "-" + arity; String moduleName = edg.getParent(edg.getParent(clause)).getName(); List descendants = edg.getDescendants(clause); descendants.add(clause); List resultNodes = extractSlicingCriteria(edg,clause,a.file); for (Node SC : resultNodes ){ Node normalNodeSC = edg.getNodeFromRes(SC); String scString = "<"+normalNodeSC.getInfo().getLine()+","+normalNodeSC.getName()+">"; functionCriteria.put("#"+scFunctionCounter,scString); scFunctionCounter++; } // // Map functionCriteriaIncomplete = new HashMap<>(); // Map InversefunctionCriteriaIncomplete = new HashMap<>(); // List resultNodesIncomplete = extractSlicingCriteriaIncomplete(edg,clause,a.file); // // for (Node SC : resultNodesIncomplete ){ // Node normalNodeSC = edg.getNodeFromRes(SC); // String scString = "<"+normalNodeSC.getInfo().getLine()+","+normalNodeSC.getName()+">"; // functionCriteriaIncomplete.put("#"+scFunctionCounterIncomplete,scString); // InversefunctionCriteriaIncomplete.put(scString,"#"+scFunctionCounterIncomplete); // scFunctionCounterIncomplete++; // } // // Collection okValues = functionCriteria.values(); // // for (String s : functionCriteriaIncomplete.values()) { // if (!okValues.contains(s)) { // System.out.println("Benchmark"+moduleName+"_"+functionName+"Remove SC "+InversefunctionCriteriaIncomplete.get(s)+" => "+s); // } // } try { new File(outputSliceGenerationFolder+"SlicingCriteriaMaps/"+moduleName+"/").mkdirs(); File txt = new File(outputSliceGenerationFolder+"SlicingCriteriaMaps/"+moduleName+"/criteria.txt"); txt.createNewFile(); FileWriter fw = new FileWriter(txt,true); for(String k : functionCriteria.keySet()){ fw.append(k +" => "+ functionCriteria.get(k)+"\n"); } fw.close(); } catch (IOException e) { System.out.println("An error occurred."); e.printStackTrace(); } } System.out.println("\tBenchmark: "+ a.file + " => " + scFunctionCounter +" SCs"); } /* *********************** *********************** *********************** *********************** */ /* *********************** ****************** PRINT METHODS ************** *********************** */ /* *********************** *********************** *********************** *********************** */ public static void printEachSCData(String setName, String file, String funName, int iteration,double[] stdTime, double[] consTime) { new File(sliceTimeFolder + setName + "/").mkdirs(); File outputFile = new File(sliceTimeFolder + setName + "/" + file + ".txt"); FileWriter timeFileWriter; int numberOfElems = stdTime.length; try { if (!outputFile.exists()) outputFile.createNewFile(); timeFileWriter = new FileWriter(outputFile, true); // FILE; FunName; #SC; StandardTIME; ConstrainedTIME => ROW FORMAT for (int i = 0; i < numberOfElems; i++) { timeFileWriter.append(file + ";" + funName + ";" + "#"+ iteration + ";" + stdTime[i] + ";" + consTime[i] + "\n"); } timeFileWriter.close(); } catch (IOException e) { System.out.println("FILE NOT FOUND ERROR"); } } /* *********************** *********************** *********************** *********************** */ /* *********************** *********************** *********************** *********************** */ /* *********************** *********************** *********************** *********************** */ static class Args { enum GraphFormat { PDF, DOT } String inputPath; File outputFile; String file; int line; String name; int occurrence = 1; File graphFile; GraphFormat graphFormat; Class algClass = OnePassConstrainedAlgorithm.class; void setGraphFile(File graphFile) { if (graphFile.getName().endsWith(".dot")) graphFormat = GraphFormat.DOT; else if (graphFile.getName().endsWith(".pdf")) graphFormat = GraphFormat.PDF; else { System.out.println("The graph file must end in .dot or .pdf, you set it to " + graphFile); System.exit(1); } this.graphFile = graphFile; } void completeData() { if (file == null && inputPath != null && new File(inputPath).isFile()) file = new File(inputPath).getName(); } boolean isValid() { List errors = new LinkedList<>(); if (inputPath == null) errors.add("You must specify a file to analyze with '-i'."); else if (!new File(inputPath).exists()) errors.add("The input file you've specified does not exist or isn't readable(" + inputPath + ")."); else if (file == null) errors.add("The input file is a folder, so you must specify a the file that contains the slicing criterion with '-f'."); if (outputFile == null) errors.add("You must specify a location for the output with '-o'."); else if (!outputFile.exists() && !outputFile.getAbsoluteFile().getParentFile().isDirectory()) errors.add("The output file's parent folder does not exist, or the output folder does not exist."); if (line <= 0) errors.add("You must specify a line number greater than 0 with '-l'."); if (name == null || name.isEmpty()) errors.add("You must specify a valid variable name with '-v'."); if (occurrence <= 0) errors.add("You must specify an occurrence greater than 0 with '-n'."); for (String error : errors) System.out.println(error); return errors.isEmpty(); } SlicingAlgorithm getAlgorithm(EDG edg) { try { return algClass.getConstructor(EDG.class).newInstance(edg); } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } } public static class SlicingCriterionState{ private final double slicingPerformance; private final double slicingTime; public SlicingCriterionState(double performance, double time) { slicingPerformance = performance; slicingTime = time; } public double getPerformance() { return this.slicingPerformance; } public double getTime() { return this.slicingTime; } } }