From afdcccc6aeec915f786770d5412e570f539aa8cb Mon Sep 17 00:00:00 2001 From: mguerrinha Date: Sat, 31 Aug 2024 18:39:41 +0100 Subject: [PATCH 1/2] identify math operations --- com.gzoltar.cli.examples/run.sh | 1 + .../cli/commands/FaultLocalizationReport.java | 4 + .../java/com/gzoltar/core/AgentConfigs.java | 24 ++++++ .../java/com/gzoltar/core/model/Node.java | 10 +++ .../core/spectrum/FilteredSpectrum.java | 83 +++++++++++++++---- .../java/com/gzoltar/core/util/FileUtils.java | 19 +++++ 6 files changed, 125 insertions(+), 16 deletions(-) diff --git a/com.gzoltar.cli.examples/run.sh b/com.gzoltar.cli.examples/run.sh index 7244217f..feb05d28 100755 --- a/com.gzoltar.cli.examples/run.sh +++ b/com.gzoltar.cli.examples/run.sh @@ -189,6 +189,7 @@ java -cp $BUILD_DIR:$JUNIT_JAR:$HAMCREST_JAR:$GZOLTAR_CLI_JAR \ --family "sfl" \ --formula "ochiai" \ --metric "entropy" \ + --weightedElements \ --formatter "txt" || die "Generation of fault-localization report has failed!" [ -s "$SPECTRA_FILE" ] || die "$SPECTRA_FILE does not exist or it is empty!" diff --git a/com.gzoltar.cli/src/main/java/com/gzoltar/cli/commands/FaultLocalizationReport.java b/com.gzoltar.cli/src/main/java/com/gzoltar/cli/commands/FaultLocalizationReport.java index c8e291a4..b5bbd6b6 100644 --- a/com.gzoltar.cli/src/main/java/com/gzoltar/cli/commands/FaultLocalizationReport.java +++ b/com.gzoltar.cli/src/main/java/com/gzoltar/cli/commands/FaultLocalizationReport.java @@ -71,6 +71,9 @@ public class FaultLocalizationReport extends AbstractReport { metaVar = "", required = false) private String formatter = ReportFormatter.TXT.name(); + @Option(name = "--weightedElements", usage = "weighted elements algorithm to improve general score", metaVar = "", required = false) + private Boolean weightedElements = false; + /** * {@inheritDoc} */ @@ -98,6 +101,7 @@ protected void generateReport(final Locale locale) throws Exception { this.agentConfigs.setInclDeprecatedMethods(this.inclDeprecatedMethods); this.agentConfigs.setIncludes(this.includes); this.agentConfigs.setExcludes(this.excludes); + this.agentConfigs.setWeightedElements(this.weightedElements); final ConfigFaultLocalizationFamily configFlFamily = new ConfigFaultLocalizationFamily(); diff --git a/com.gzoltar.core/src/main/java/com/gzoltar/core/AgentConfigs.java b/com.gzoltar.core/src/main/java/com/gzoltar/core/AgentConfigs.java index 008da285..022f45a8 100644 --- a/com.gzoltar.core/src/main/java/com/gzoltar/core/AgentConfigs.java +++ b/com.gzoltar.core/src/main/java/com/gzoltar/core/AgentConfigs.java @@ -155,6 +155,10 @@ public final class AgentConfigs { public static final InstrumentationLevel DEFAULT_INSTRUMENTATION_LEVEL = InstrumentationLevel.FULL; + public static final String WEIGHTED_ELEMENTS_KEY = "weightedElements"; + + public static final boolean DEFAULT_WEIGHTED_ELEMENTS = false; + private final Map configs; private static final Collection VALID_CONFIGS = @@ -494,6 +498,26 @@ public void setInstrumentationLevel(final InstrumentationLevel instrumentationLe this.setConfig(INSTRUMENTATION_LEVEL_KEY, instrumentationLevel.name()); } + /** + * Returns whether weighted elements algorithm should be executed. + * + * @return true if weighted elements algorithm should be executed + */ + public Boolean getWeightedElements() { + return this.getConfig(WEIGHTED_ELEMENTS_KEY, DEFAULT_WEIGHTED_ELEMENTS); + } + + /** + * Sets whether weighted elements algorithm should be executed. + * + * @param weightedElements true if weighted elements algorithm should be executed + */ + public void setWeightedElements(final boolean weightedElements) { + this.setConfig(WEIGHTED_ELEMENTS_KEY, weightedElements); + } + + + /** * Generate required JVM argument based on current configuration and supplied agent jar location. * diff --git a/com.gzoltar.core/src/main/java/com/gzoltar/core/model/Node.java b/com.gzoltar.core/src/main/java/com/gzoltar/core/model/Node.java index ca0e9ee1..1d89231d 100644 --- a/com.gzoltar.core/src/main/java/com/gzoltar/core/model/Node.java +++ b/com.gzoltar.core/src/main/java/com/gzoltar/core/model/Node.java @@ -42,6 +42,8 @@ public class Node { private Map suspiciousnessValues = null; + private Boolean containsMathOperator = false; + /** * * @param name @@ -360,4 +362,12 @@ public boolean equals(Object obj) { return builder.isEquals(); } + public Boolean getContainsMathOperator() { + return containsMathOperator; + } + + public void setContainsMathOperator(Boolean containsMathOperator) { + this.containsMathOperator = containsMathOperator; + } + } diff --git a/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/FilteredSpectrum.java b/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/FilteredSpectrum.java index dc3934dd..8b89a913 100644 --- a/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/FilteredSpectrum.java +++ b/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/FilteredSpectrum.java @@ -1,21 +1,22 @@ /** * Copyright (C) 2020 GZoltar contributors. - * + * * This file is part of GZoltar. - * + * * GZoltar is free software: you can redistribute it and/or modify it under the terms of the GNU * Lesser General Public License as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. - * + * * GZoltar 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 Lesser * General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License along with GZoltar. If * not, see . */ package com.gzoltar.core.spectrum; +import com.gzoltar.core.util.FileUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import com.gzoltar.core.AgentConfigs; import com.gzoltar.core.instr.Outcome; @@ -35,6 +36,11 @@ import com.gzoltar.core.util.ArrayUtils; import javassist.Modifier; +import java.io.*; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.List; + public class FilteredSpectrum { private final GranularityLevel granularity; @@ -43,14 +49,20 @@ public class FilteredSpectrum { private final Filter methodFilter = new Filter(); + private boolean WeightedElements = false; + + HashMap> classesContent = new HashMap<>(); + /** - * + * * @param source */ public FilteredSpectrum(AgentConfigs configs) { this.granularity = configs.getGranularity(); + this.WeightedElements = configs.getWeightedElements() != null; + // === Class level filters === // instrument some classes @@ -73,18 +85,18 @@ public FilteredSpectrum(AgentConfigs configs) { if (!configs.getInclDeprecatedMethods()) { this.methodFilter - .add(new BlackList(new MethodAnnotationMatcher(Deprecated.class.getCanonicalName()))); + .add(new BlackList(new MethodAnnotationMatcher(Deprecated.class.getCanonicalName()))); } } /** * Returns a filtered {@link com.gzoltar.core.spectrum.ISpectrum} object according to user's * preferences. - * + * * @param source * @return */ - public ISpectrum filter(ISpectrum source) { + public ISpectrum filter(ISpectrum source) throws IOException { if (source == null) { return null; } @@ -115,8 +127,10 @@ public ISpectrum filter(ISpectrum source) { } if (this.granularity == GranularityLevel.LINE) { + Node node = WeightedElements ? verifyMathOperations(probe) : probe.getNode(); // register Line probe - newProbeGroup.registerProbe(probe.getNode(), probe.getCtBehavior()); + newProbeGroup.registerProbe(node, probe.getCtBehavior()); + } else if (this.granularity == GranularityLevel.CLASS) { // register Class probe newProbeGroup.registerProbe(probe.getNode(), probe.getCtBehavior()); @@ -127,8 +141,8 @@ public ISpectrum filter(ISpectrum source) { Node node = probe.getNode(); String methodName = - node.getName().substring(node.getName().indexOf(NodeType.METHOD.getSymbol()) + 1, - node.getName().indexOf(NodeType.LINE.getSymbol())); + node.getName().substring(node.getName().indexOf(NodeType.METHOD.getSymbol()) + 1, + node.getName().indexOf(NodeType.LINE.getSymbol())); granularityMethodFilter.add(new BlackList(new MethodNameMatcher(methodName))); } else if (this.granularity == GranularityLevel.BASICBLOCK && probe.getNode().isStartBlock()) { @@ -146,8 +160,8 @@ public ISpectrum filter(ISpectrum source) { for (Transaction transaction : source.getTransactions()) { Transaction newTransaction = - new Transaction(transaction.getName(), transaction.getTransactionOutcome(), - transaction.getRuntime(), transaction.getStackTrace()); + new Transaction(transaction.getName(), transaction.getTransactionOutcome(), + transaction.getRuntime(), transaction.getStackTrace()); for (String hash : transaction.getProbeGroupsHash()) { if (!filteredSpectrum.containsProbeGroupByHash(hash)) { @@ -175,7 +189,7 @@ public ISpectrum filter(ISpectrum source) { if (ArrayUtils.containsValue(newHitArray, true)) { newTransaction.addActivity(hash, - new ImmutablePair(newProbeGroup.getName(), newHitArray)); + new ImmutablePair(newProbeGroup.getName(), newHitArray)); } } @@ -196,12 +210,12 @@ public ISpectrum filter(ISpectrum source) { switch (this.granularity) { case CLASS: newNodeName = - node.getName().substring(0, node.getName().indexOf(NodeType.METHOD.getSymbol())); + node.getName().substring(0, node.getName().indexOf(NodeType.METHOD.getSymbol())); newNodeType = NodeType.CLASS; break; case METHOD: newNodeName = - node.getName().substring(0, node.getName().indexOf(NodeType.LINE.getSymbol())); + node.getName().substring(0, node.getName().indexOf(NodeType.LINE.getSymbol())); newNodeType = NodeType.METHOD; break; case BASICBLOCK: @@ -221,4 +235,41 @@ public ISpectrum filter(ISpectrum source) { return filteredSpectrum; } + /** + * Only called when node in probe is a statement and weighted elements is activated + * @param probe + */ + private Node verifyMathOperations(Probe probe) throws IOException { + Node node = probe.getNode(); + String className = probe.getCtBehavior().getDeclaringClass().getName(); + if (!classesContent.containsKey(className)) { + // build file path + String classPath = className.replace('.', '/') + ".java"; + String baseDir = System.getProperty("user.dir") + "/src"; + String filePath = baseDir + "/" + classPath; + File file = new File(filePath); + InputStream inputStream = Files.newInputStream(file.toPath()); + List classContent = FileUtils.loadFileByLine(inputStream); + + classesContent.put(className, classContent); + } + String[] words = classesContent.get(className).get(node.getLineNumber() - 1).split(" "); + for (String word : words) { + if (containsMathOperator(word)) { + node.setContainsMathOperator(true); + } + } + return node; + } + + private static Boolean containsMathOperator(String word) { + char[] operators = {'+', '-', '*', '/', '%', '^'}; + + for (char operator : operators) { + if (word.indexOf(operator) != -1) { + return true; + } + } + return false; + } } diff --git a/com.gzoltar.core/src/main/java/com/gzoltar/core/util/FileUtils.java b/com.gzoltar.core/src/main/java/com/gzoltar/core/util/FileUtils.java index 3e74df26..09bfe368 100644 --- a/com.gzoltar.core/src/main/java/com/gzoltar/core/util/FileUtils.java +++ b/com.gzoltar.core/src/main/java/com/gzoltar/core/util/FileUtils.java @@ -17,8 +17,10 @@ package com.gzoltar.core.util; import java.io.File; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import java.util.Scanner; public final class FileUtils { @@ -44,4 +46,21 @@ public static List listFiles(final File dir, final String ext, final boole return files; } + /** + * Returns a list with all the lines in a given file. + * + * @param stream of the file to be read + * @return a {@link java.util.List} of all lines in the file + */ + public static List loadFileByLine(InputStream stream) { + List lines = new ArrayList<>(); + try (Scanner scanner = new Scanner(stream)) { + while (scanner.hasNextLine()) { + String line = scanner.nextLine().trim(); + lines.add(line); + } + } + return lines; + } + } From eb006743481d8434913587057a59b8bb63515818 Mon Sep 17 00:00:00 2001 From: mguerrinha Date: Sun, 1 Sep 2024 18:25:46 +0100 Subject: [PATCH 2/2] weighted elements method complete --- com.gzoltar.core/pom.xml | 4 ++++ .../main/java/com/gzoltar/core/model/Node.java | 9 +++++++++ .../com/gzoltar/core/spectrum/ISpectrum.java | 4 ++++ .../com/gzoltar/core/spectrum/Spectrum.java | 13 +++++++++++++ .../java/com/gzoltar/fl/FaultLocalization.java | 2 +- .../com/gzoltar/fl/IFaultLocalization.java | 2 +- .../src/main/java/com/gzoltar/sfl/SFL.java | 18 +++++++++++++++++- .../sfl/formulas/AbstractSFLFormula.java | 9 ++++++++- .../com/gzoltar/maven/AbstractReportMojo.java | 7 +++++++ 9 files changed, 64 insertions(+), 4 deletions(-) diff --git a/com.gzoltar.core/pom.xml b/com.gzoltar.core/pom.xml index 0f706c27..912bec1b 100644 --- a/com.gzoltar.core/pom.xml +++ b/com.gzoltar.core/pom.xml @@ -71,6 +71,10 @@ org.apache.maven.plugins maven-compiler-plugin + + 8 + 8 + org.apache.maven.plugins diff --git a/com.gzoltar.core/src/main/java/com/gzoltar/core/model/Node.java b/com.gzoltar.core/src/main/java/com/gzoltar/core/model/Node.java index 1d89231d..71e55b08 100644 --- a/com.gzoltar.core/src/main/java/com/gzoltar/core/model/Node.java +++ b/com.gzoltar.core/src/main/java/com/gzoltar/core/model/Node.java @@ -44,6 +44,8 @@ public class Node { private Boolean containsMathOperator = false; + private Boolean haveFailedTest = false; + /** * * @param name @@ -370,4 +372,11 @@ public void setContainsMathOperator(Boolean containsMathOperator) { this.containsMathOperator = containsMathOperator; } + public Boolean getHaveFailedTest() { + return haveFailedTest; + } + + public void setHaveFailedTest(Boolean haveFailedTest) { + this.haveFailedTest = haveFailedTest; + } } diff --git a/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/ISpectrum.java b/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/ISpectrum.java index cb5f2b8b..6e27ede1 100644 --- a/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/ISpectrum.java +++ b/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/ISpectrum.java @@ -24,6 +24,10 @@ public interface ISpectrum { + public void maxScore(String formula, double score); + + public double getMaxScore(String formula); + // === ProbeGroups === /** diff --git a/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/Spectrum.java b/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/Spectrum.java index 749cef16..8916b527 100644 --- a/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/Spectrum.java +++ b/com.gzoltar.core/src/main/java/com/gzoltar/core/spectrum/Spectrum.java @@ -30,6 +30,8 @@ public class Spectrum implements ISpectrum { + private final Map maxScore; + /** */ private final Map probeGroups; @@ -39,10 +41,21 @@ public class Spectrum implements ISpectrum { * Constructs a new {@link com.gzoltar.core.spectrum.Spectrum}. */ public Spectrum() { + this.maxScore = new LinkedHashMap(); this.probeGroups = new LinkedHashMap(); this.transactions = new ArrayList(); } + @Override + public void maxScore(String formula, double score) { + this.maxScore.put(formula, Math.max(this.maxScore.getOrDefault(formula, score), score)); + } + + @Override + public double getMaxScore(String formula) { + return this.maxScore.get(formula); + } + // === ProbeGroups === /** diff --git a/com.gzoltar.fl/src/main/java/com/gzoltar/fl/FaultLocalization.java b/com.gzoltar.fl/src/main/java/com/gzoltar/fl/FaultLocalization.java index a47dd63a..f954f0cc 100644 --- a/com.gzoltar.fl/src/main/java/com/gzoltar/fl/FaultLocalization.java +++ b/com.gzoltar.fl/src/main/java/com/gzoltar/fl/FaultLocalization.java @@ -63,7 +63,7 @@ public ISpectrum diagnose(final String buildLocation, final AgentConfigs agentCo FilteredSpectrum filter = new FilteredSpectrum(agentConfigs); ISpectrum filteredSpectrum = filter.filter(spectrum); - this.fl.diagnose(filteredSpectrum); + this.fl.diagnose(filteredSpectrum, agentConfigs.getWeightedElements()); return filteredSpectrum; } diff --git a/com.gzoltar.fl/src/main/java/com/gzoltar/fl/IFaultLocalization.java b/com.gzoltar.fl/src/main/java/com/gzoltar/fl/IFaultLocalization.java index 5dd5217c..b03cd908 100644 --- a/com.gzoltar.fl/src/main/java/com/gzoltar/fl/IFaultLocalization.java +++ b/com.gzoltar.fl/src/main/java/com/gzoltar/fl/IFaultLocalization.java @@ -24,5 +24,5 @@ public interface IFaultLocalization { * * @param spectrum a {@link com.gzoltar.core.spectrum.ISpectrum} object */ - public void diagnose(final ISpectrum spectrum); + public void diagnose(final ISpectrum spectrum, Boolean weightedElements); } diff --git a/com.gzoltar.fl/src/main/java/com/gzoltar/sfl/SFL.java b/com.gzoltar.fl/src/main/java/com/gzoltar/sfl/SFL.java index fbc2bc97..39d13759 100644 --- a/com.gzoltar.fl/src/main/java/com/gzoltar/sfl/SFL.java +++ b/com.gzoltar.fl/src/main/java/com/gzoltar/sfl/SFL.java @@ -18,6 +18,9 @@ import java.util.ArrayList; import java.util.List; + +import com.gzoltar.core.runtime.Probe; +import com.gzoltar.core.runtime.ProbeGroup; import com.gzoltar.core.spectrum.ISpectrum; import com.gzoltar.fl.IFaultLocalization; import com.gzoltar.fl.IFormula; @@ -44,9 +47,22 @@ public SFL(final List sflFormulas) { /** * {@inheritDoc} */ - public void diagnose(final ISpectrum spectrum) { + public void diagnose(final ISpectrum spectrum, Boolean weightedElements) { for (F formula : this.formulas) { formula.diagnose(spectrum); + if (weightedElements) { + weightedElements(spectrum, formula.getName()); + } + } + } + + public void weightedElements(final ISpectrum spectrum, String formula) { + for (ProbeGroup probeGroup : spectrum.getProbeGroups()) { + for (Probe probe : probeGroup.getProbes()) { + if (probe.getNode().getContainsMathOperator() && probe.getNode().getHaveFailedTest()) { + probe.getNode().addSuspiciousnessValue(formula, probe.getNode().getSuspiciousnessValue(formula) + spectrum.getMaxScore(formula)); + } + } } } } diff --git a/com.gzoltar.fl/src/main/java/com/gzoltar/sfl/formulas/AbstractSFLFormula.java b/com.gzoltar.fl/src/main/java/com/gzoltar/sfl/formulas/AbstractSFLFormula.java index c3b7d922..eb9718b3 100644 --- a/com.gzoltar.fl/src/main/java/com/gzoltar/sfl/formulas/AbstractSFLFormula.java +++ b/com.gzoltar.fl/src/main/java/com/gzoltar/sfl/formulas/AbstractSFLFormula.java @@ -54,7 +54,14 @@ public void diagnose(final ISpectrum spectrum) { } } - probe.getNode().addSuspiciousnessValue(this.getName(), this.compute(n00, n01, n10, n11)); + double suspScore = this.compute(n00, n01, n10, n11); + + spectrum.maxScore(this.getName(), suspScore); + + probe.getNode().addSuspiciousnessValue(this.getName(), suspScore); + if (n11 > 0) { + probe.getNode().setHaveFailedTest(true); + } } } } diff --git a/com.gzoltar.maven/src/main/java/com/gzoltar/maven/AbstractReportMojo.java b/com.gzoltar.maven/src/main/java/com/gzoltar/maven/AbstractReportMojo.java index f1ebd0a2..838370e2 100644 --- a/com.gzoltar.maven/src/main/java/com/gzoltar/maven/AbstractReportMojo.java +++ b/com.gzoltar.maven/src/main/java/com/gzoltar/maven/AbstractReportMojo.java @@ -99,6 +99,9 @@ public abstract class AbstractReportMojo extends AbstractMavenReport { @Parameter(property = "gzoltar.inclDeprecatedMethods", defaultValue = "true") private Boolean inclDeprecatedMethods; + @Parameter(property = "gzoltar.weightedElements", defaultValue = "false") + private Boolean weightedElements; + @Override public String getName(Locale locale) { return "GZoltar"; @@ -168,6 +171,10 @@ protected AgentConfigs createAgentConfigurations() { agentConfigs.setInclDeprecatedMethods(this.inclDeprecatedMethods); } + if (this.weightedElements != null) { + agentConfigs.setWeightedElements(this.weightedElements); + } + return agentConfigs; } }