Skip to content
Open

Tse26 #217

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,4 @@ One can run the framework tests by running the check task:

## Building

`.\gradlew build -x test`
`./gradlew build -x test`
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package services.outputProcessors.soot
import util.ProcessRunner

import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference

/**
* Runs a soot algorithm with:
Expand All @@ -19,23 +20,29 @@ class ConflictDetectionAlgorithm {
private SootAnalysisWrapper sootWrapper;
private boolean interprocedural;
private long depthLimit;


ConflictDetectionAlgorithm(String name, String mode, SootAnalysisWrapper sootWrapper, long timeout) {
this(name, mode, sootWrapper, timeout, false, 5);
}

ConflictDetectionAlgorithm(String name, String mode, SootAnalysisWrapper sootWrapper, long timeout, boolean interprocedural) {
this(name, mode, sootWrapper, timeout, interprocedural, 5);
}

ConflictDetectionAlgorithm(String name, String mode, SootAnalysisWrapper sootWrapper, long timeout, boolean interprocedural, long depthLimit) {
this.name = name;
this.mode = mode;
this.timeout = timeout;
this.sootWrapper = sootWrapper;
this.interprocedural = interprocedural;
this.depthLimit = depthLimit;
private String callgraph;
private boolean partialResultsOnTimeout;

// Grace period to let the output thread finish reading buffered output after process is destroyed
private static final long GRACE_PERIOD_MILLIS = 5000L;


ConflictDetectionAlgorithm(String name,
String mode,
SootAnalysisWrapper sootWrapper,
long timeout,
boolean interprocedural = false,
long depthLimit = 5,
String callgraph = "SPARK",
boolean partialResultsOnTimeout = false) {
this.name = name
this.mode = mode
this.sootWrapper = sootWrapper
this.timeout = timeout
this.interprocedural = interprocedural
this.depthLimit = depthLimit
this.callgraph = callgraph
this.partialResultsOnTimeout = partialResultsOnTimeout
}

String getMode() {
Expand All @@ -54,6 +61,10 @@ class ConflictDetectionAlgorithm {
return interprocedural
}

void setPartialResultsOnTimeout(boolean partialResultsOnTimeout) {
this.partialResultsOnTimeout = partialResultsOnTimeout
}

@Override
String toString() {
return "ConflictDetectionAlgorithm{" +
Expand All @@ -69,6 +80,10 @@ class ConflictDetectionAlgorithm {
return depthLimit
}

String getCallgraph() {
return callgraph
}

String run(Scenario scenario) {
try {
println "Running ${toString()}"
Expand All @@ -83,6 +98,7 @@ class ConflictDetectionAlgorithm {

sootConfig.addOption("-entrypoints", scenario.getEntrypoints());
sootConfig.addOption("-depthLimit", this.getDepthLimit());
sootConfig.addOption("-cg", this.getCallgraph());

return runAndReportResult(sootConfig);
} catch (ClassNotFoundInJarException e) {
Expand All @@ -92,7 +108,10 @@ class ConflictDetectionAlgorithm {


protected String runAndReportResult(SootConfig sootConfig) throws InterruptedException, IOException {
String result;
// AtomicReference allows the output thread to safely publish its result to the main thread
// Default is "false": if timeout fires before any [CONFLICT_FOUND] is seen, we record false
AtomicReference<String> atomicResult = new AtomicReference<>("false")

println "Using jar at " + sootConfig.getClassPath()

File inputFile = new File(sootConfig.getInputFilePath());
Expand All @@ -103,51 +122,57 @@ class ConflictDetectionAlgorithm {

Process sootProcess = sootWrapper.executeSoot(sootConfig);

// this is needed because if th waitFor command is called without reading the output
// in some executions the output buffer might get full and block the process
// so we execute both the output reading and the process waiting in parallel
// Reading output and waiting for process must run in parallel to avoid blocking
// when the output buffer fills up before the process finishes
Thread processOutputThread = new Thread(new Runnable() {
@Override
void run() {
result = hasSootFlow(sootProcess);
atomicResult.set(hasSootFlow(sootProcess));
}
})
processOutputThread.start(); // start processing the output
processOutputThread.start();

boolean executionCompleted = true;
if (timeout > 0) {
// wait for the execution to end setting a timeout
executionCompleted = sootProcess.waitFor(timeout, TimeUnit.SECONDS)
}

// if the timeout has been reached
if (!executionCompleted) {
processOutputThread.interrupt(); // cancel the output reading thread
print ("Execution exceeded the timeout of " + timeout + " seconds")
result = "timeout";
} else {
processOutputThread.join();
println "Execution exceeded the timeout of ${timeout} seconds"
// Destroy the process first so its output stream closes, allowing the reader thread to exit
sootProcess.destroy();

if (partialResultsOnTimeout) {
// Wait for the reader thread to finish consuming any buffered output that was
// already in the pipe before the process was destroyed
processOutputThread.join(GRACE_PERIOD_MILLIS)
String partial = atomicResult.get()
println "Result at timeout: ${partial}"
return partial + "-timeout"
} else {
processOutputThread.interrupt();
}
return "timeout";
Comment on lines +152 to +155
}


// force destroy process
// if we don't use this command some processes will keep running and consuming a lot of memory
// even after the analysis execution ends
processOutputThread.join();
// Force destroy to prevent zombie processes that keep consuming memory
sootProcess.destroy();

return result;
return atomicResult.get();
}

private String hasSootFlow(Process sootProcess) {
String result = "error"

sootProcess.getInputStream().eachLine {
println it;
if (it.stripIndent().startsWith("Number of conflicts:")) {
result = "true"
} else if (it.stripIndent() == "No conflicts detected") {
result = "false"
String result = "false"
try {
sootProcess.getInputStream().eachLine {
println it;
if (it.stripIndent() == "[CONFLICT_FOUND]") {
result = "true"
}
}
} catch (IOException ignored) {
// Stream closed because the process was destroyed (timeout case) — return whatever was found so far
}
return result
}
Expand Down
16 changes: 10 additions & 6 deletions src/main/services/outputProcessors/soot/Main.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class Main {
sootRunner.setDetectionAlgorithms(configureDetectionAlgorithms(appArguments, sootWrapper))
}

if (appArguments.isPartialResultsOnTimeout()) {
println "Setting partial results on timeout"
sootRunner.configurePartialResultsOnTimeout(true)
}

sootRunner.executeAnalyses(outputPath)

if (appArguments.isReport()) {
Expand Down Expand Up @@ -76,7 +81,7 @@ class Main {
private static ArrayList<ConflictDetectionAlgorithm> configureDetectionAlgorithms(Arguments appArguments, SootAnalysisWrapper sootWrapper) {
List<ConflictDetectionAlgorithm> detectionAlgorithms = new ArrayList<ConflictDetectionAlgorithm>();


String callgraph = appArguments.getCallgraph();
long timeout = appArguments.getTimeout()
long depthLimit = appArguments.getDepthLimit();
if (appArguments.getDfIntra()) {
Expand All @@ -89,26 +94,25 @@ class Main {
detectionAlgorithms.add(new ConflictDetectionAlgorithm("Confluence Intra", "dfp-confluence-intraprocedural", sootWrapper, timeout, false,depthLimit))
}
if (appArguments.getCfInter()) {
detectionAlgorithms.add(new ConflictDetectionAlgorithm("Confluence Inter", "dfp-confluence-interprocedural", sootWrapper, timeout, true,depthLimit))
detectionAlgorithms.add(new ConflictDetectionAlgorithm("Confluence Inter", "dfp-confluence-interprocedural", sootWrapper, timeout, true,depthLimit, callgraph))
}
if (appArguments.getOaIntra()) {
detectionAlgorithms.add(new ConflictDetectionAlgorithm("OA Intra", "oa", sootWrapper, timeout, false,depthLimit))
}
if (appArguments.getOaInter()) {
detectionAlgorithms.add(new ConflictDetectionAlgorithm("OA Inter", "ioa", sootWrapper, timeout, true,depthLimit))
detectionAlgorithms.add(new ConflictDetectionAlgorithm("OA Inter", "ioa", sootWrapper, timeout, true,depthLimit, callgraph))
}
if (appArguments.getOaIntraWithoutPA()) {
detectionAlgorithms.add(new ConflictDetectionAlgorithm("OA Intra Without Pointer Analysis", "oa-without-pa", sootWrapper, timeout, false,depthLimit))
}
if (appArguments.getOaInterWithoutPA()) {
detectionAlgorithms.add(new ConflictDetectionAlgorithm("OA Inter Without Pointer Analysis", "ioa-without-pa", sootWrapper, timeout, true,depthLimit))
detectionAlgorithms.add(new ConflictDetectionAlgorithm("OA Inter Without Pointer Analysis", "ioa-without-pa", sootWrapper, timeout, true,depthLimit, callgraph))
}

if (appArguments.getDfpIntra()) {
detectionAlgorithms.add(new NonCommutativeConflictDetectionAlgorithm("DFP-Intra", "dfp-intra", sootWrapper, timeout, false,depthLimit))
}
if (appArguments.getDfpInter()) {
detectionAlgorithms.add(new NonCommutativeConflictDetectionAlgorithm("DFP-Inter", "dfp-inter", sootWrapper, timeout, true,depthLimit))
detectionAlgorithms.add(new NonCommutativeConflictDetectionAlgorithm("DFP-Inter", "dfp-inter", sootWrapper, timeout, true,depthLimit, callgraph))
}
if (appArguments.getCd()) {
detectionAlgorithms.add(new NonCommutativeConflictDetectionAlgorithm("CD", "cd", sootWrapper, timeout, false,depthLimit))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
package services.outputProcessors.soot


/**
* Runs a soot algorithm twice, once with:
* left -> source
* right -> sink
* and once with:
* left -> sink
* right -> source
* This is used for algorithms that are non commutative, that means different conflicts can be found running from
* left to right and right to left
* This is used for algorithms that are non-commutative, meaning that
* running left→right may produce different conflicts than right→left.
*/
class NonCommutativeConflictDetectionAlgorithm extends ConflictDetectionAlgorithm {

NonCommutativeConflictDetectionAlgorithm(String name, String mode, SootAnalysisWrapper sootWrapper, long timeout) {
super(name, mode, sootWrapper, timeout)
}

NonCommutativeConflictDetectionAlgorithm(String name, String mode, SootAnalysisWrapper sootWrapper, long timeout, boolean interprocedural) {
super(name, mode, sootWrapper, timeout, interprocedural)
}

NonCommutativeConflictDetectionAlgorithm(String name, String mode, SootAnalysisWrapper sootWrapper, long timeout, boolean interprocedural, long depthLimit) {
super(name, mode, sootWrapper, timeout, interprocedural, depthLimit);
NonCommutativeConflictDetectionAlgorithm(String name,
String mode,
SootAnalysisWrapper sootWrapper,
long timeout,
boolean interprocedural = false,
long depthLimit = 5,
String callgraph = "SPARK") {
super(name, mode, sootWrapper, timeout, interprocedural, depthLimit, callgraph)
}

@Override
Expand All @@ -33,25 +30,35 @@ class NonCommutativeConflictDetectionAlgorithm extends ConflictDetectionAlgorith
@Override
String run(Scenario scenario) {
try {
SootConfig configLeftRight = buildSootConfig(scenario.getLinesFilePath(), scenario);
SootConfig configRightLeft = buildSootConfig(scenario.getLinesReverseFilePath(), scenario);
println "Running ${toString()}"

println "Running left right " + toString();
String leftRightResult = super.runAndReportResult(configLeftRight);
// LEFT → RIGHT
String filePathLeftRight = scenario.getLinesFilePath()
SootConfig configLeftRight = buildSootConfig(filePathLeftRight, scenario)

println "Running right left " + toString();
String rightLeftResult = super.runAndReportResult(configRightLeft);
println "Running left → right ${toString()}"
String leftRightResult = runAndReportResult(configLeftRight)

return "${leftRightResult};${rightLeftResult}";
// RIGHT → LEFT
String filePathRightLeft = scenario.getLinesReverseFilePath()
SootConfig configRightLeft = buildSootConfig(filePathRightLeft, scenario)

println "Running right → left ${toString()}"
String rightLeftResult = runAndReportResult(configRightLeft)

return "${leftRightResult};${rightLeftResult}"
} catch (ClassNotFoundInJarException e) {
return "not-found;not-found";
}
}

private SootConfig buildSootConfig(String filePath, Scenario scenario) {
SootConfig config = new SootConfig(filePath, scenario.getClassPath(), super.getMode());

config.addOption("-entrypoints", scenario.getEntrypoints());
config.addOption("-depthLimit", this.getDepthLimit());
config.addOption("-cg", getCallgraph())

return config;
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/services/outputProcessors/soot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Options:
-t,--timeout <timeout> Run -t time: time limit for each analysis (default: 240)
-depthLimit Sets the depth limit on accessing methods when performing Overriding Assignment Interprocedural, Direct Flow Interprocedural and Confluence Interprocedural analyses. Default = 5
-printDepthSVFA Print depth in SVFA analysis
-cg,--callgraph <algorithm> Select call graph algorithm [CHA, RTA, VTA, SPARK]
-prt,--partial-results-on-timeout When a soot analysis times out, capture and record the partial results
```

For example:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ class RunSootAnalysisOutputProcessor implements OutputProcessor {
}
}

void configurePartialResultsOnTimeout(boolean partialResultsOnTimeout) {
for (ConflictDetectionAlgorithm algorithm : detectionAlgorithms) {
algorithm.setPartialResultsOnTimeout(partialResultsOnTimeout);
}
}

void processOutput() {
// check if file generated by FetchBuildsOutputProcessor exists
println "Executing RunSootAnalysisOutputProcessor"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class ArgsParser {
this.cli.pd(longOpt: 'pessimistic-dataflow', "Run pessimistic-dataflow")
this.cli.report(longOpt: 'report', "Run report results for experiment using -icf -ioa -idfp -pdg")
this.cli.r(longOpt: 'reachability', "Run reachability")
this.cli.cg(longOpt: 'callgraph', args: 1, argName: 'algorithm', "Call graph algorithm [CHA, RTA, VTA, SPARK]")
this.cli.prt(longOpt: 'partial-results-on-timeout', "When a soot analysis times out, capture and record the partial results found up to that point instead of discarding them")
}

Arguments parse(args) {
Expand Down Expand Up @@ -117,5 +119,15 @@ class ArgsParser {
if (this.options.r) {
args.setReachability(true)
}
if (this.options.cg) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@barbosamaatheus se o usuário não usar a opção cg, qual o algoritmo padrão usado? deveria ser o mesmo usado antes, para mantermos compatibilidade com experimentos anteriores; não termos que alterar experimentos anteriores para adicionar essa opção

String algorithm = options.cg?.toString()?.trim()?.toUpperCase()
if (!algorithm || !["CHA", "RTA", "VTA", "SPARK"].contains(algorithm)) {
throw new IllegalArgumentException("Invalid callgraph algorithm: ${options.cg}")
}
args.setCallgraph(algorithm)
}
if (this.options.prt) {
args.setPartialResultsOnTimeout(true)
}
}
}
Loading