Skip to content

Commit ec5d62e

Browse files
committed
Add progress monitors.
1 parent 40b77ea commit ec5d62e

File tree

4 files changed

+121
-43
lines changed

4 files changed

+121
-43
lines changed

edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/StreamAnalyzer.java

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
import org.eclipse.core.runtime.CoreException;
2323
import org.eclipse.core.runtime.IPath;
24+
import org.eclipse.core.runtime.IProgressMonitor;
25+
import org.eclipse.core.runtime.NullProgressMonitor;
26+
import org.eclipse.core.runtime.SubMonitor;
2427
import org.eclipse.jdt.core.IJavaProject;
2528
import org.eclipse.jdt.core.dom.ASTVisitor;
2629
import org.eclipse.jdt.core.dom.IMethodBinding;
@@ -44,6 +47,7 @@
4447
import com.ibm.wala.util.scope.JUnitEntryPoints;
4548

4649
import edu.cuny.hunter.streamrefactoring.core.analysis.StreamStateMachine.Statistics;
50+
import edu.cuny.hunter.streamrefactoring.core.messages.Messages;
4751
import edu.cuny.hunter.streamrefactoring.core.utils.LoggerNames;
4852
import edu.cuny.hunter.streamrefactoring.core.utils.TimeCollector;
4953
import edu.cuny.hunter.streamrefactoring.core.wala.EclipseProjectAnalysisEngine;
@@ -177,12 +181,15 @@ public StreamAnalyzer(boolean visitDocTags, int nForStreams, boolean findImplici
177181

178182
/**
179183
* Analyzes this {@link StreamAnalyzer}'s streams.
184+
*
185+
* @param subMonitor
186+
* @param optional
180187
*
181188
* @return A {@link Map} of project's analyzed along with the entry points used.
182189
* @see #analyze(Optional).
183190
*/
184191
public Map<IJavaProject, Collection<Entrypoint>> analyze() throws CoreException {
185-
return this.analyze(Optional.empty());
192+
return this.analyze(Optional.empty(), new NullProgressMonitor());
186193
}
187194

188195
/**
@@ -193,7 +200,10 @@ public Map<IJavaProject, Collection<Entrypoint>> analyze() throws CoreException
193200
* @return A {@link Map} of project's analyzed along with the entry points used.
194201
* @see #analyze().
195202
*/
196-
public Map<IJavaProject, Collection<Entrypoint>> analyze(Optional<TimeCollector> collector) throws CoreException {
203+
public Map<IJavaProject, Collection<Entrypoint>> analyze(Optional<TimeCollector> collector,
204+
IProgressMonitor monitor) throws CoreException {
205+
SubMonitor subMonitor = SubMonitor.convert(monitor, "Analyzing...", IProgressMonitor.UNKNOWN);
206+
197207
LOGGER.fine(() -> "Using N = " + this.getNForStreams() + ".");
198208

199209
Map<IJavaProject, Collection<Entrypoint>> ret = new HashMap<>();
@@ -203,6 +213,8 @@ public Map<IJavaProject, Collection<Entrypoint>> analyze(Optional<TimeCollector>
203213
.collect(Collectors.groupingBy(Stream::getCreationJavaProject, Collectors.toSet()));
204214

205215
// process each project.
216+
subMonitor.beginTask("Processing projects ...", projectToStreams.keySet().size());
217+
206218
for (IJavaProject project : projectToStreams.keySet()) {
207219
// create the analysis engine for the project.
208220
// exclude from the analysis because the IR will be built here.
@@ -221,7 +233,8 @@ public Map<IJavaProject, Collection<Entrypoint>> analyze(Optional<TimeCollector>
221233
// build the call graph for the project.
222234
Collection<Entrypoint> entryPoints = null;
223235
try {
224-
entryPoints = this.buildCallGraph(engine, collector);
236+
entryPoints = this.buildCallGraph(engine, collector,
237+
subMonitor.split(IProgressMonitor.UNKNOWN, SubMonitor.SUPPRESS_NONE));
225238
} catch (IOException | CoreException | CancelException e) {
226239
LOGGER.log(Level.SEVERE,
227240
"Exception encountered while building call graph for: " + project.getElementName() + ".", e);
@@ -231,17 +244,21 @@ public Map<IJavaProject, Collection<Entrypoint>> analyze(Optional<TimeCollector>
231244
// save the entry points.
232245
ret.put(project, entryPoints);
233246

247+
Set<Stream> streamSet = projectToStreams.get(project);
248+
234249
if (entryPoints.isEmpty()) {
235250
// add a status entry for each stream in the project
236-
for (Stream stream : projectToStreams.get(project))
251+
for (Stream stream : streamSet)
237252
stream.addStatusEntry(PreconditionFailure.NO_ENTRY_POINT,
238253
"Project: " + engine.getProject().getElementName() + " has no entry points.");
239254
return ret;
240255
}
241256

242257
OrderingInference orderingInference = new OrderingInference(engine.getClassHierarchy());
243258

244-
for (Iterator<Stream> iterator = projectToStreams.get(project).iterator(); iterator.hasNext();) {
259+
subMonitor.beginTask("Inferring initial stream attributes...", streamSet.size());
260+
261+
for (Iterator<Stream> iterator = streamSet.iterator(); iterator.hasNext();) {
245262
Stream stream = iterator.next();
246263
try {
247264
stream.inferInitialAttributes(engine, orderingInference);
@@ -261,14 +278,16 @@ public Map<IJavaProject, Collection<Entrypoint>> analyze(Optional<TimeCollector>
261278
iterator.remove();
262279
this.getStreamSet().remove(stream);
263280
}
281+
subMonitor.worked(1);
264282
}
265283

266284
// start the state machine for each valid stream in the project.
267285
StreamStateMachine stateMachine = new StreamStateMachine();
268286
try {
269-
Map<TypestateRule, StreamStateMachine.Statistics> ruleToStats = stateMachine.start(projectToStreams
270-
.get(project).parallelStream().filter(s -> s.getStatus().isOK()).collect(Collectors.toSet()),
271-
engine, orderingInference);
287+
Map<TypestateRule, StreamStateMachine.Statistics> ruleToStats = stateMachine.start(
288+
streamSet.parallelStream().filter(s -> s.getStatus().isOK()).collect(Collectors.toSet()),
289+
engine, orderingInference,
290+
subMonitor.split(IProgressMonitor.UNKNOWN, SubMonitor.SUPPRESS_NONE));
272291

273292
// use just one the rules.
274293
assert !ruleToStats.isEmpty() : "Should have stats available.";
@@ -283,9 +302,16 @@ public Map<IJavaProject, Collection<Entrypoint>> analyze(Optional<TimeCollector>
283302
}
284303

285304
// check preconditions.
286-
for (Stream stream : projectToStreams.get(project).parallelStream().filter(s -> s.getStatus().isOK())
287-
.collect(Collectors.toSet()))
305+
SubMonitor checkMonitor = subMonitor.split(IProgressMonitor.UNKNOWN, SubMonitor.SUPPRESS_NONE);
306+
checkMonitor.beginTask(Messages.CheckingPreconditions, streamSet.size());
307+
308+
for (Stream stream : streamSet.parallelStream().filter(s -> s.getStatus().isOK())
309+
.collect(Collectors.toSet())) {
288310
stream.check();
311+
checkMonitor.worked(1);
312+
}
313+
314+
subMonitor.worked(1);
289315
} // end for each stream.
290316
return ret;
291317
}
@@ -302,7 +328,7 @@ public Map<IJavaProject, Collection<Entrypoint>> analyze(Optional<TimeCollector>
302328
* @return The {@link Entrypoint}s used in building the {@link CallGraph}.
303329
*/
304330
protected Collection<Entrypoint> buildCallGraph(EclipseProjectAnalysisEngine<InstanceKey> engine,
305-
Optional<TimeCollector> collector)
331+
Optional<TimeCollector> collector, IProgressMonitor monitor)
306332
throws IOException, CoreException, CallGraphBuilderCancelException, CancelException {
307333
// if we haven't built the call graph yet.
308334
if (!this.enginesWithBuiltCallGraphsToEntrypointsUsed.keySet().contains(engine)) {
@@ -373,7 +399,7 @@ protected Collection<Entrypoint> buildCallGraph(EclipseProjectAnalysisEngine<Ins
373399
options.getSSAOptions().setPiNodePolicy(SSAOptions.getAllBuiltInPiNodes());
374400

375401
try {
376-
engine.buildSafeCallGraph(options);
402+
engine.buildSafeCallGraph(options, SubMonitor.convert(monitor, "Building call graph", 1));
377403
} catch (IllegalStateException e) {
378404
LOGGER.log(Level.SEVERE, e, () -> "Exception encountered while building call graph for project: "
379405
+ engine.getProject().getElementName());

edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/StreamStateMachine.java

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
import java.util.stream.Collectors;
2323

2424
import org.eclipse.core.runtime.CoreException;
25-
import org.eclipse.core.runtime.NullProgressMonitor;
25+
import org.eclipse.core.runtime.IProgressMonitor;
26+
import org.eclipse.core.runtime.SubMonitor;
2627
import org.eclipse.jdt.core.JavaModelException;
2728

2829
import com.google.common.collect.HashBasedTable;
@@ -567,8 +568,11 @@ private boolean deriveRomForNonScalarMethod(Collection<TypeAbstraction> possible
567568
}
568569

569570
private void discoverIfReduceOrderingPossiblyMatters(EclipseProjectAnalysisEngine<InstanceKey> engine,
570-
OrderingInference orderingInference) throws UTFDataFormatException, JavaModelException,
571-
NoniterableException, NoninstantiableException, CannotExtractSpliteratorException {
571+
OrderingInference orderingInference, IProgressMonitor monitor) throws UTFDataFormatException,
572+
JavaModelException, NoniterableException, NoninstantiableException, CannotExtractSpliteratorException {
573+
monitor.beginTask("Discovering if reduce order matters...",
574+
this.terminalBlockToPossibleReceivers.keySet().size());
575+
572576
// for each terminal operation call, I think?
573577
for (BasicBlockInContext<IExplodedBasicBlock> block : this.terminalBlockToPossibleReceivers.keySet()) {
574578
int processedInstructions = 0;
@@ -648,6 +652,7 @@ private void discoverIfReduceOrderingPossiblyMatters(EclipseProjectAnalysisEngin
648652
++processedInstructions;
649653
}
650654
assert processedInstructions == 1 : "Expecting to process one and only one instruction here.";
655+
monitor.worked(1);
651656
}
652657
}
653658

@@ -721,8 +726,10 @@ private void discoverLambdaSideEffects(EclipseProjectAnalysisEngine<InstanceKey>
721726
LOGGER.warning("Def was an instance of a: " + def.getClass());
722727
}
723728

724-
private void discoverPossibleSideEffects(EclipseProjectAnalysisEngine<InstanceKey> engine)
729+
private void discoverPossibleSideEffects(EclipseProjectAnalysisEngine<InstanceKey> engine, IProgressMonitor monitor)
725730
throws IOException, CoreException {
731+
SubMonitor subMonitor = SubMonitor.convert(monitor, "Discovering side-effects...", 100);
732+
726733
// create the ModRef analysis.
727734
ModRef<InstanceKey> modRef = ModRef.make();
728735

@@ -732,6 +739,9 @@ private void discoverPossibleSideEffects(EclipseProjectAnalysisEngine<InstanceKe
732739
Map<CGNode, OrdinalSet<PointerKey>> mod = modRef.computeMod(engine.getCallGraph(), engine.getPointerAnalysis());
733740

734741
// for each terminal operation call, I think?
742+
SubMonitor loopMonitor = subMonitor.split(50, SubMonitor.SUPPRESS_NONE)
743+
.setWorkRemaining(this.terminalBlockToPossibleReceivers.keySet().size());
744+
735745
for (BasicBlockInContext<IExplodedBasicBlock> block : this.terminalBlockToPossibleReceivers.keySet()) {
736746
int processedInstructions = 0;
737747
for (SSAInstruction instruction : block) {
@@ -760,10 +770,13 @@ private void discoverPossibleSideEffects(EclipseProjectAnalysisEngine<InstanceKe
760770
}
761771

762772
assert processedInstructions == 1 : "Expecting to process one and only one instruction here.";
773+
loopMonitor.worked(1);
763774
}
764775

765776
// for each instance in the analysis result (these should be the
766777
// "intermediate" streams).
778+
loopMonitor = subMonitor.split(50, SubMonitor.SUPPRESS_NONE).setWorkRemaining(this.trackedInstances.size());
779+
767780
for (InstanceKey instance : this.trackedInstances) {
768781
// make sure that the stream is the result of an intermediate
769782
// operation.
@@ -792,14 +805,18 @@ private void discoverPossibleSideEffects(EclipseProjectAnalysisEngine<InstanceKe
792805
this.discoverLambdaSideEffects(engine, mod, Collections.singleton(instance),
793806
callString.getMethods()[0].getReference(), ir, use);
794807
}
808+
809+
loopMonitor.worked(1);
795810
}
796811
}
797812

798-
private void discoverPossibleStatefulIntermediateOperations(IClassHierarchy hierarchy, CallGraph callGraph)
799-
throws IOException, CoreException {
813+
private void discoverPossibleStatefulIntermediateOperations(IClassHierarchy hierarchy, CallGraph callGraph,
814+
IProgressMonitor monitor) throws IOException, CoreException {
815+
monitor.beginTask("Discovering stateful intermediate operations...", this.trackedInstances.size());
816+
800817
// for each instance in the analysis result (these should be the
801818
// "intermediate" streams).
802-
for (InstanceKey instance : this.trackedInstances)
819+
for (InstanceKey instance : this.trackedInstances) {
803820
if (!this.instanceToStatefulIntermediateOperationContainment.containsKey(instance)) {
804821
// make sure that the stream is the result of an intermediate
805822
// operation.
@@ -816,21 +833,26 @@ private void discoverPossibleStatefulIntermediateOperations(IClassHierarchy hier
816833
}
817834
this.instanceToStatefulIntermediateOperationContainment.put(instance, found);
818835
}
836+
monitor.worked(1);
837+
}
819838
}
820839

821-
private void discoverTerminalOperations() {
840+
private void discoverTerminalOperations(IProgressMonitor monitor) {
822841
Collection<OrdinalSet<InstanceKey>> receiverSetsThatHaveTerminalOperations = this.terminalBlockToPossibleReceivers
823842
.values();
824843

825844
// This will be the OK set.
826845
Collection<InstanceKey> validStreams = new HashSet<>();
827846

828847
// Now, we need to flatten the receiver sets.
829-
for (OrdinalSet<InstanceKey> receiverSet : receiverSetsThatHaveTerminalOperations)
848+
monitor.beginTask("Flattening...", receiverSetsThatHaveTerminalOperations.size());
849+
for (OrdinalSet<InstanceKey> receiverSet : receiverSetsThatHaveTerminalOperations) {
830850
// for each receiver set
831851
for (InstanceKey instance : receiverSet)
832852
// add it to the OK set.
833853
validStreams.add(instance);
854+
monitor.worked(1);
855+
}
834856

835857
// Now, we have the OK set. Let's propagate it.
836858
this.propagateStreamInstanceProperty(validStreams);
@@ -864,8 +886,9 @@ private void fillInstanceToPredecessorMap(EclipseProjectAnalysisEngine<InstanceK
864886
}
865887
}
866888

867-
private void fillInstanceToStreamMap(Set<Stream> streamSet, EclipseProjectAnalysisEngine<InstanceKey> engine)
868-
throws InvalidClassFileException, IOException, CoreException {
889+
private void fillInstanceToStreamMap(Set<Stream> streamSet, EclipseProjectAnalysisEngine<InstanceKey> engine,
890+
IProgressMonitor monitor) throws InvalidClassFileException, IOException, CoreException {
891+
monitor.beginTask("Propagating...", streamSet.size());
869892
int skippedStreams = 0;
870893
for (Stream stream : streamSet) {
871894
InstanceKey instanceKey = null;
@@ -904,6 +927,7 @@ private void fillInstanceToStreamMap(Set<Stream> streamSet, EclipseProjectAnalys
904927
LOGGER.warning("Reassociating stream: " + stream.getCreation() + " with: " + instanceKey
905928
+ ". Old stream was: " + oldValue.getCreation() + ".");
906929

930+
monitor.worked(1);
907931
} // end each stream.
908932

909933
// sanity check since it's a bijection.
@@ -952,9 +976,10 @@ private void propagateStreamInstanceProperty(Collection<InstanceKey> streamInsta
952976
}
953977

954978
public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjectAnalysisEngine<InstanceKey> engine,
955-
OrderingInference orderingInference)
979+
OrderingInference orderingInference, IProgressMonitor monitor)
956980
throws PropertiesException, CancelException, IOException, CoreException, NoniterableException,
957981
NoninstantiableException, CannotExtractSpliteratorException, InvalidClassFileException {
982+
SubMonitor subMonitor = SubMonitor.convert(monitor, "Performing typestate analysis (may take a while)", 100);
958983
Map<TypestateRule, Statistics> ret = new HashMap<>();
959984

960985
BenignOracle ora = new ModifiedBenignOracle(engine.getCallGraph(), engine.getPointerAnalysis());
@@ -973,6 +998,8 @@ public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjec
973998
StreamAttributeTypestateRule[] ruleArray = createStreamAttributeTypestateRules(streamClass);
974999

9751000
// for each rule.
1001+
SubMonitor ruleMonitor = subMonitor.split(70, SubMonitor.SUPPRESS_NONE).setWorkRemaining(ruleArray.length);
1002+
9761003
for (StreamAttributeTypestateRule rule : ruleArray) {
9771004
// create a DFA based on the rule.
9781005
TypeStateProperty dfa = new TypeStateProperty(rule, engine.getClassHierarchy());
@@ -984,7 +1011,7 @@ public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjec
9841011

9851012
AggregateSolverResult result;
9861013
try {
987-
result = (AggregateSolverResult) solver.perform(new NullProgressMonitor());
1014+
result = (AggregateSolverResult) solver.perform(ruleMonitor.split(50, SubMonitor.SUPPRESS_NONE));
9881015
} catch (SolverTimeoutException | MaxFindingsException | SetUpException | WalaException e) {
9891016
throw new RuntimeException("Exception caught during typestate analysis.", e);
9901017
}
@@ -997,6 +1024,9 @@ public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjec
9971024
assert lastStatistics == null : "Reassociating statistics.";
9981025

9991026
// for each instance in the typestate analysis result.
1027+
SubMonitor instanceMonitor = ruleMonitor.split(20, SubMonitor.SUPPRESS_NONE)
1028+
.setWorkRemaining(result.totalInstancesNum());
1029+
10001030
for (Iterator<InstanceKey> iterator = result.iterateInstances(); iterator.hasNext();) {
10011031
// get the instance's key.
10021032
InstanceKey instanceKey = iterator.next();
@@ -1140,6 +1170,7 @@ public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjec
11401170
}
11411171
}
11421172
}
1173+
instanceMonitor.worked(1);
11431174
} // end for each instance in the typestate analysis result.
11441175

11451176
// fill the instance to predecessors map if it's empty.
@@ -1183,23 +1214,26 @@ public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjec
11831214
});
11841215
}
11851216
}
1217+
ruleMonitor.worked(1);
11861218
} // end for each rule.
11871219

11881220
// create a mapping between stream instances (from the analysis) and stream
11891221
// objects (from the refactoring).
1190-
this.fillInstanceToStreamMap(streamSet, engine);
1222+
this.fillInstanceToStreamMap(streamSet, engine, subMonitor.split(5, SubMonitor.SUPPRESS_NONE));
11911223

1192-
this.discoverTerminalOperations();
1224+
this.discoverTerminalOperations(subMonitor.split(5, SubMonitor.SUPPRESS_NONE));
11931225

11941226
// fill the instance side-effect set.
1195-
this.discoverPossibleSideEffects(engine);
1227+
this.discoverPossibleSideEffects(engine, subMonitor.split(5, SubMonitor.SUPPRESS_NONE));
11961228

11971229
// discover whether any stateful intermediate operations are
11981230
// present.
1199-
this.discoverPossibleStatefulIntermediateOperations(engine.getClassHierarchy(), engine.getCallGraph());
1231+
this.discoverPossibleStatefulIntermediateOperations(engine.getClassHierarchy(), engine.getCallGraph(),
1232+
subMonitor.split(5, SubMonitor.SUPPRESS_NONE));
12001233

12011234
// does reduction order matter?
1202-
this.discoverIfReduceOrderingPossiblyMatters(engine, orderingInference);
1235+
this.discoverIfReduceOrderingPossiblyMatters(engine, orderingInference,
1236+
subMonitor.split(5, SubMonitor.SUPPRESS_NONE));
12031237

12041238
// propagate the instances with side-effects.
12051239
this.propagateStreamInstanceProperty(this.instancesWithSideEffects);

0 commit comments

Comments
 (0)