Skip to content

Commit 551ce99

Browse files
adding partial results
1 parent 4bbd763 commit 551ce99

7 files changed

Lines changed: 89 additions & 27 deletions

File tree

src/main/arguments/ArgsParser.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class ArgsParser {
3939
this.cli.e(longOpt: 'extension', args: 1, argName: 'file extenson', 'Specify the file extension that should be used in the analysis (e.g. .rb, .ts, .java, .cpp. Default: .java)')
4040
this.cli.l(longOpt: 'language-separators', args: 1, argName: 'language syntactic separators', 'Specify the language separators that should be used in the analysis. Required for (and only considered when) running studies with the CSDiff tool. Default: \"{ } ( ) ; ,\"')
4141
this.cli.log(longOpt: 'log-level', args: 1, argName: 'log level', 'Specify the minimum log level: (OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL). Default: \"INFO\"')
42+
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')
4243
}
4344

4445
Arguments parse(args) {
@@ -142,6 +143,10 @@ class ArgsParser {
142143
if(this.options.log) {
143144
args.setLogLevel(Level.toLevel(this.options.log))
144145
}
146+
147+
if (this.options.prt) {
148+
args.setPartialResultsOnTimeout(true)
149+
}
145150
}
146151

147152
private boolean repositoryExists(String repositoryURL) {

src/main/arguments/Arguments.groovy

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class Arguments {
2121
private String syntacticSeparators
2222
private String fileExtension
2323
private Level logLevel
24+
private boolean partialResultsOnTimeout
2425

2526
Arguments() { // set the default values for all parameters
2627
randomSeed = 1
@@ -37,6 +38,7 @@ class Arguments {
3738
syntacticSeparators = '{ } ( ) ; ,'
3839
fileExtension = '.java'
3940
logLevel = Level.INFO
41+
partialResultsOnTimeout = false
4042
}
4143

4244
void setRandomSeed(int randomSeed) {
@@ -167,4 +169,12 @@ class Arguments {
167169
this.logLevel = logLevel
168170
Configurator.setRootLevel(logLevel)
169171
}
172+
173+
boolean isPartialResultsOnTimeout() {
174+
return partialResultsOnTimeout
175+
}
176+
177+
void setPartialResultsOnTimeout(boolean partialResultsOnTimeout) {
178+
this.partialResultsOnTimeout = partialResultsOnTimeout
179+
}
170180
}

src/main/services/outputProcessors/soot/ConflictDetectionAlgorithm.groovy

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package services.outputProcessors.soot
33
import util.ProcessRunner
44

55
import java.util.concurrent.TimeUnit
6+
import java.util.concurrent.atomic.AtomicReference
67

78
/**
89
* Runs a soot algorithm with:
@@ -20,6 +21,10 @@ class ConflictDetectionAlgorithm {
2021
private boolean interprocedural;
2122
private long depthLimit;
2223
private String callgraph;
24+
private boolean partialResultsOnTimeout;
25+
26+
// Grace period to let the output thread finish reading buffered output after process is destroyed
27+
private static final long GRACE_PERIOD_MILLIS = 5000L;
2328

2429

2530
ConflictDetectionAlgorithm(String name,
@@ -28,14 +33,16 @@ class ConflictDetectionAlgorithm {
2833
long timeout,
2934
boolean interprocedural = false,
3035
long depthLimit = 5,
31-
String callgraph = "SPARK") {
36+
String callgraph = "SPARK",
37+
boolean partialResultsOnTimeout = false) {
3238
this.name = name
3339
this.mode = mode
3440
this.sootWrapper = sootWrapper
3541
this.timeout = timeout
3642
this.interprocedural = interprocedural
3743
this.depthLimit = depthLimit
3844
this.callgraph = callgraph
45+
this.partialResultsOnTimeout = partialResultsOnTimeout
3946
}
4047

4148
String getMode() {
@@ -54,6 +61,10 @@ class ConflictDetectionAlgorithm {
5461
return interprocedural
5562
}
5663

64+
void setPartialResultsOnTimeout(boolean partialResultsOnTimeout) {
65+
this.partialResultsOnTimeout = partialResultsOnTimeout
66+
}
67+
5768
@Override
5869
String toString() {
5970
return "ConflictDetectionAlgorithm{" +
@@ -97,7 +108,10 @@ class ConflictDetectionAlgorithm {
97108

98109

99110
protected String runAndReportResult(SootConfig sootConfig) throws InterruptedException, IOException {
100-
String result;
111+
// AtomicReference allows the output thread to safely publish its result to the main thread
112+
// Default is "false": if timeout fires before any [CONFLICT_FOUND] is seen, we record false
113+
AtomicReference<String> atomicResult = new AtomicReference<>("false")
114+
101115
println "Using jar at " + sootConfig.getClassPath()
102116

103117
File inputFile = new File(sootConfig.getInputFilePath());
@@ -108,51 +122,57 @@ class ConflictDetectionAlgorithm {
108122

109123
Process sootProcess = sootWrapper.executeSoot(sootConfig);
110124

111-
// this is needed because if th waitFor command is called without reading the output
112-
// in some executions the output buffer might get full and block the process
113-
// so we execute both the output reading and the process waiting in parallel
125+
// Reading output and waiting for process must run in parallel to avoid blocking
126+
// when the output buffer fills up before the process finishes
114127
Thread processOutputThread = new Thread(new Runnable() {
115128
@Override
116129
void run() {
117-
result = hasSootFlow(sootProcess);
130+
atomicResult.set(hasSootFlow(sootProcess));
118131
}
119132
})
120-
processOutputThread.start(); // start processing the output
133+
processOutputThread.start();
121134

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

128-
// if the timeout has been reached
129140
if (!executionCompleted) {
130-
processOutputThread.interrupt(); // cancel the output reading thread
131-
print ("Execution exceeded the timeout of " + timeout + " seconds")
132-
result = "timeout";
133-
} else {
134-
processOutputThread.join();
141+
println "Execution exceeded the timeout of ${timeout} seconds"
142+
// Destroy the process first so its output stream closes, allowing the reader thread to exit
143+
sootProcess.destroy();
144+
145+
if (partialResultsOnTimeout) {
146+
// Wait for the reader thread to finish consuming any buffered output that was
147+
// already in the pipe before the process was destroyed
148+
processOutputThread.join(GRACE_PERIOD_MILLIS)
149+
String partial = atomicResult.get()
150+
println "Result at timeout: ${partial}"
151+
return partial
152+
} else {
153+
processOutputThread.interrupt();
154+
}
155+
return "timeout";
135156
}
136157

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

143-
return result;
162+
return atomicResult.get();
144163
}
145164

146165
private String hasSootFlow(Process sootProcess) {
147-
String result = "error"
148-
149-
sootProcess.getInputStream().eachLine {
150-
println it;
151-
if (it.stripIndent().startsWith("Number of conflicts:")) {
152-
result = "true"
153-
} else if (it.stripIndent() == "No conflicts detected") {
154-
result = "false"
166+
String result = "false"
167+
try {
168+
sootProcess.getInputStream().eachLine {
169+
println it;
170+
if (it.stripIndent() == "[CONFLICT_FOUND]") {
171+
result = "true"
172+
}
155173
}
174+
} catch (IOException ignored) {
175+
// Stream closed because the process was destroyed (timeout case) — return whatever was found so far
156176
}
157177
return result
158178
}

src/main/services/outputProcessors/soot/Main.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ class Main {
4040
sootRunner.setDetectionAlgorithms(configureDetectionAlgorithms(appArguments, sootWrapper))
4141
}
4242

43+
if (appArguments.getPartialResultsOnTimeout()) {
44+
sootRunner.configurePartialResultsOnTimeout(true)
45+
}
46+
4347
sootRunner.executeAnalyses(outputPath)
4448

4549
if (appArguments.isReport()) {

src/main/services/outputProcessors/soot/RunSootAnalysisOutputProcessor.groovy

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,18 @@ class RunSootAnalysisOutputProcessor implements OutputProcessor {
6666
}
6767
}
6868

69+
void configurePartialResultsOnTimeout(boolean partialResultsOnTimeout) {
70+
for (ConflictDetectionAlgorithm algorithm : detectionAlgorithms) {
71+
algorithm.setPartialResultsOnTimeout(partialResultsOnTimeout);
72+
}
73+
}
74+
6975
void processOutput() {
7076
// check if file generated by FetchBuildsOutputProcessor exists
7177
println "Executing RunSootAnalysisOutputProcessor"
78+
if (arguments.isPartialResultsOnTimeout()) {
79+
configurePartialResultsOnTimeout(true)
80+
}
7281
executeAnalyses(arguments.getOutputPath())
7382
}
7483

src/main/services/outputProcessors/soot/arguments/ArgsParser.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class ArgsParser {
3838
this.cli.report(longOpt: 'report', "Run report results for experiment using -icf -ioa -idfp -pdg")
3939
this.cli.r(longOpt: 'reachability', "Run reachability")
4040
this.cli.cg(longOpt: 'callgraph', args: 1, argName: 'algorithm', "Call graph algorithm [CHA, RTA, VTA, SPARK]")
41+
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")
4142
}
4243

4344
Arguments parse(args) {
@@ -125,5 +126,8 @@ class ArgsParser {
125126
}
126127
args.setCallgraph(algorithm)
127128
}
129+
if (this.options.prt) {
130+
args.setPartialResultsOnTimeout(true)
131+
}
128132
}
129133
}

src/main/services/outputProcessors/soot/arguments/Arguments.groovy

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class Arguments {
2424
private long depthLimit
2525
private boolean printDepthSVFA
2626
private String callgraph
27+
private boolean partialResultsOnTimeout
2728

2829
Arguments() { // set the default values for all parameters
2930
isHelp = false
@@ -49,6 +50,7 @@ class Arguments {
4950
printDepthSVFA = false
5051
depthLimit = 5
5152
callgraph = "SPARK"
53+
partialResultsOnTimeout = false
5254
}
5355

5456
boolean getOaIntraWithoutPA() {
@@ -234,4 +236,12 @@ class Arguments {
234236
void setCallgraph(String callgraph) {
235237
this.callgraph = callgraph
236238
}
239+
240+
boolean getPartialResultsOnTimeout() {
241+
return partialResultsOnTimeout
242+
}
243+
244+
void setPartialResultsOnTimeout(boolean partialResultsOnTimeout) {
245+
this.partialResultsOnTimeout = partialResultsOnTimeout
246+
}
237247
}

0 commit comments

Comments
 (0)