MeasurementEventParser.java
package de.uka.ipd.sdq.beagle.core.measurement;
import de.uka.ipd.sdq.beagle.core.CodeSection;
import de.uka.ipd.sdq.beagle.core.ExternalCallParameter;
import de.uka.ipd.sdq.beagle.core.MeasurableSeffElement;
import de.uka.ipd.sdq.beagle.core.ResourceDemandingInternalAction;
import de.uka.ipd.sdq.beagle.core.SeffBranch;
import de.uka.ipd.sdq.beagle.core.SeffLoop;
import de.uka.ipd.sdq.beagle.core.measurement.order.CodeSectionEnteredEvent;
import de.uka.ipd.sdq.beagle.core.measurement.order.CodeSectionLeftEvent;
import de.uka.ipd.sdq.beagle.core.measurement.order.MeasurementEvent;
import de.uka.ipd.sdq.beagle.core.measurement.order.MeasurementEventVisitor;
import de.uka.ipd.sdq.beagle.core.measurement.order.ResourceDemandCapturedEvent;
import org.apache.commons.collections4.IteratorUtils;
import org.apache.commons.lang3.Validate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* Parses {@linkplain MeasurementEvent MeasurementEvents} in order to generate
* {@linkplain ParameterisationDependentMeasurementResult
* ParameterisationDependentMeasurementResults} out of them. It is created for a sequence
* of events that occurred while measuring software (the “input events”). The parsed
* results can be obtained by querying with
*
* {@linkplain MeasurableSeffElement MeasurableSeffElements} which will return all
* measurement results found for that element.
*
* @author Joshua Gleitze
* @author Roman Langrehr
*/
public class MeasurementEventParser {
/**
* All measurement events in chronological order.
*/
private final List<MeasurementEvent> measurementEvents;
/**
* Maps for each code sections to all occurrences of measurement events for this code
* section in {@link #measurementEvents}.
*/
private final Map<CodeSection, Set<Integer>> codeSectionMapping = new HashMap<>();
/**
* Creates a parser to parse {@code events}. The events must be provided in
* chronological order, starting with the event that occurred first.
*
* @param events The events to be parsed. Must not be {@code null} and all contained
* events must not be {@code null}.
*/
public MeasurementEventParser(final MeasurementEvent... events) {
Validate.noNullElements(events);
this.measurementEvents = Arrays.asList(events);
this.createCodeSectionMapping();
}
/**
* Creates a parser to parse {@code events}. The events must be provided in
* chronological order, starting with the event that occurred first.
*
* @param events The events to be parsed. Must not be {@code null} and all contained
* events must not be {@code null}.
*/
public MeasurementEventParser(final Iterable<MeasurementEvent> events) {
Validate.noNullElements(events);
this.measurementEvents = IteratorUtils.toList(events.iterator());
this.createCodeSectionMapping();
}
/**
* Generates a new {@link #codeSectionMapping} for the {@link #measurementEvents}.
*/
private void createCodeSectionMapping() {
for (int i = 0; i < this.measurementEvents.size(); i++) {
final CodeSection currentCodeSection = this.measurementEvents.get(i).getCodeSection();
if (!this.codeSectionMapping.containsKey(currentCodeSection)) {
this.codeSectionMapping.put(currentCodeSection, new HashSet<>());
}
this.codeSectionMapping.get(currentCodeSection).add(i);
}
}
/**
* Gets all results that could be parsed from the input events for {@code rdia}.
*
* @param resourceDemandingInternalAction A resource demanding internal action to get
* the measurement results of. Must not be {@code null} .
* @return All measurement results parsed for {@code rdia}. Is never {@code null}.
* Contains never {@code null} elements.
*/
public Set<ResourceDemandMeasurementResult> getMeasurementResultsFor(
final ResourceDemandingInternalAction resourceDemandingInternalAction) {
Validate.notNull(resourceDemandingInternalAction);
final Set<ResourceDemandMeasurementResult> resourceDemandMeasurementResults = new HashSet<>();
final ResourceDemandingInternalActionMeasurementEventVisitor resourceDemandingInternalActionMeasurementEventVisitor =
new ResourceDemandingInternalActionMeasurementEventVisitor(resourceDemandingInternalAction,
resourceDemandMeasurementResults);
final Set<Integer> indices = this.codeSectionMapping.get(resourceDemandingInternalAction.getAction());
if (indices != null) {
for (final Integer index : indices) {
final MeasurementEvent measurementEvent = this.measurementEvents.get(index);
measurementEvent.receive(resourceDemandingInternalActionMeasurementEventVisitor);
}
}
return resourceDemandMeasurementResults;
}
/**
* Gets all results that could be parsed from the input events for {@code branch}.
*
* @param branch A SEFF Branch to get the measurement results of. Must not be
* {@code null} .
* @return All measurement results parsed for {@code branch}. Is never {@code null}.
* Contains never {@code null} elements.
*/
public Set<BranchDecisionMeasurementResult> getMeasurementResultsFor(final SeffBranch branch) {
final Set<BranchDecisionMeasurementResult> branchDecisionMeasurementResults = new HashSet<>();
// Assemble all measurement events, that could fit for this branch according to
// their code section.
final Set<MeasurementEvent> codeSectionEventsForThisBranch = new HashSet<>();
for (final CodeSection possibilyPickedBranch : branch.getBranches()) {
final Set<Integer> indices = this.codeSectionMapping.get(possibilyPickedBranch);
if (indices != null) {
for (final Integer index : indices) {
codeSectionEventsForThisBranch.add(this.measurementEvents.get(index));
}
}
}
final SeffBranchMeasurementEventVisitor seffBranchMeasurementEventVisitor =
new SeffBranchMeasurementEventVisitor(branch, branchDecisionMeasurementResults);
for (final MeasurementEvent measurementEvent : codeSectionEventsForThisBranch) {
measurementEvent.receive(seffBranchMeasurementEventVisitor);
}
return branchDecisionMeasurementResults;
}
/**
* Gets all results that could be parsed from the input events for {@code loop}.
*
* @param loop A SEFF Loop to get the measurement results of. Must not be {@code null}
* .
* @return All measurement results parsed for {@code loop}. Is never {@code null}.
* Contains never {@code null} elements.
*/
public Set<LoopRepetitionCountMeasurementResult> getMeasurementResultsFor(final SeffLoop loop) {
final Set<LoopRepetitionCountMeasurementResult> loopRepetitionCountMeasurementResults = new HashSet<>();
final SeffLoopMeasurementEventVisitor seffLoopMeasurementEventVisitor =
new SeffLoopMeasurementEventVisitor(loopRepetitionCountMeasurementResults);
final Set<Integer> indices = this.codeSectionMapping.get(loop.getLoopBody());
if (indices != null) {
for (final Integer index : indices) {
this.measurementEvents.get(index).receive(seffLoopMeasurementEventVisitor);
}
}
seffLoopMeasurementEventVisitor.finalise();
return loopRepetitionCountMeasurementResults;
}
/**
* Note: this method is out of our projects scope and not yet implemented. Gets all
* results that could be parsed from the input events for
* {@code externalCallParameter}.
*
* @param externalCallParameter An external parameter to get the measurement results
* of. Must not be {@code null}.
* @return All measurement results parsed for {@code externalCallParameter}. Is never
* {@code null}. Contains never {@code null} elements.
*/
public Set<ParameterChangeMeasurementResult> getMeasurementResultsFor(
final ExternalCallParameter externalCallParameter) {
return new HashSet<>();
}
/**
* A {@link MeasurementEventVisitor} for a specific
* {@link ResourceDemandingInternalAction}.
*
* <p>Only {@linkplain MeasurementEvent MeasurementEvents} with the correct
* {@link CodeSection} may be visited with this visitor.
*
* @author Roman Langrehr
*/
private class ResourceDemandingInternalActionMeasurementEventVisitor extends AbstractMeasurementEventVisitor {
/**
* The {@link ResourceDemandingInternalAction} where we want to know the new
* measurement results.
*/
private final ResourceDemandingInternalAction resourceDemandingInternalAction;
/**
* The set, where the {@link ResourceDemandMeasurementResult} should be added.
*/
private final Set<ResourceDemandMeasurementResult> resourceDemandMeasurementResults;
/**
* Creates a visitor for a specific {@link ResourceDemandingInternalAction}.
*
* @param resourceDemandingInternalAction The
* {@link ResourceDemandingInternalAction} where we want to know the
* new measurement results.
* @param resourceDemandMeasurementResults The set, where the
* {@link ResourceDemandMeasurementResult} should be added.
*/
ResourceDemandingInternalActionMeasurementEventVisitor(
final ResourceDemandingInternalAction resourceDemandingInternalAction,
final Set<ResourceDemandMeasurementResult> resourceDemandMeasurementResults) {
this.resourceDemandingInternalAction = resourceDemandingInternalAction;
this.resourceDemandMeasurementResults = resourceDemandMeasurementResults;
}
@Override
public void visit(final ResourceDemandCapturedEvent resourceDemandCapturedEvent) {
// Check if this measurement event is for the correct resource type.
if (resourceDemandCapturedEvent.getType() == this.resourceDemandingInternalAction.getResourceType()) {
this.resourceDemandMeasurementResults
.add(new ResourceDemandMeasurementResult(resourceDemandCapturedEvent.getValue()));
}
}
}
/**
* A {@link MeasurementEventVisitor} for a specific {@link SeffBranch}.
*
* @author Roman Langrehr
*/
private class SeffBranchMeasurementEventVisitor extends AbstractMeasurementEventVisitor {
/**
* The {@link SeffBranch} where we want to know the new measurement results.
*/
private final SeffBranch branch;
/**
* The set, where the {@linkplain ResourceDemandMeasurementResult
* ResourceDemandMeasurementResults} should be added.
*/
private final Set<BranchDecisionMeasurementResult> branchDecisionMeasurementResults;
/**
* Creates a visitor for a specific {@link SeffBranch}.
*
* @param branch The {@link SeffBranch} where we want to know the new measurement
* results.
* @param branchDecisionMeasurementResults The set, where the
* {@linkplain BranchDecisionMeasurementResult
* BranchDecisionMeasurementResults} should be added.
*/
SeffBranchMeasurementEventVisitor(final SeffBranch branch,
final Set<BranchDecisionMeasurementResult> branchDecisionMeasurementResults) {
this.branch = branch;
this.branchDecisionMeasurementResults = branchDecisionMeasurementResults;
}
@Override
public void visit(final CodeSectionEnteredEvent codeSectionExecutedEvent) {
final int branchIndex = this.branch.getBranches().indexOf(codeSectionExecutedEvent.getCodeSection());
this.branchDecisionMeasurementResults.add(new BranchDecisionMeasurementResult(branchIndex));
}
// We don't care about CodeSectionLeftEvents, because we defined a SeffBranch to
// be executed, exactly when it was entered.
}
/**
* A {@link MeasurementEventVisitor} for a specific {@link SeffLoop}.
*
* <p>You must call {@linkplain MeasurementEvent#receive(MeasurementEventVisitor)
* receive} on the {@linkplain MeasurementEvent MeasurementEvents} in the correct
* order!
*
* @author Roman Langrehr
*/
private class SeffLoopMeasurementEventVisitor extends AbstractMeasurementEventVisitor {
/**
* The set, where the {@linkplain ResourceDemandMeasurementResult
* ResourceDemandMeasurementResults} should be added.
*/
private final Set<LoopRepetitionCountMeasurementResult> loopRepetitionCountMeasurementResults;
/**
* The number of loops measured in the current sequence of measurement events for
* one loop body. The stack contains all
*/
private final Stack<LoopExecutionCounter> currentLoopCounts;
/**
* Creates a visitor for a specific {@link SeffLoop}.
*
* @param loopRepetitionCountMeasurementResults The set, where the
* {@linkplain BranchDecisionMeasurementResult
* BranchDecisionMeasurementResults} should be added.
*/
SeffLoopMeasurementEventVisitor(
final Set<LoopRepetitionCountMeasurementResult> loopRepetitionCountMeasurementResults) {
this.loopRepetitionCountMeasurementResults = loopRepetitionCountMeasurementResults;
this.currentLoopCounts = new Stack<>();
}
@Override
public void visit(final CodeSectionEnteredEvent codeSectionEnteredEvent) {
if (this.currentLoopCounts.isEmpty() || this.currentLoopCounts.peek().isOpen) {
// The current branch was not finished before a this, so we have a
// recursive call, or there is no current execution of this loop.
this.currentLoopCounts.push(new LoopExecutionCounter());
}
assert !this.currentLoopCounts.peek().isOpen;
if (!(this.currentLoopCounts.peek().lastCodeSectionLeftEventIndex == -1
|| this.currentLoopCounts.peek().lastCodeSectionLeftEventIndex
+ 1 == MeasurementEventParser.this.measurementEvents.indexOf(codeSectionEnteredEvent))) {
// We have not continuous loop body executions, so we create a new
// execution event.
this.loopFinished();
this.currentLoopCounts.push(new LoopExecutionCounter());
}
this.currentLoopCounts.peek().numberOfExecutions++;
this.currentLoopCounts.peek().isOpen = true;
}
@Override
public void visit(final CodeSectionLeftEvent codeSectionLeftEvent) {
if (!this.currentLoopCounts.isEmpty()) {
if (this.currentLoopCounts.peek().isOpen) {
// The current execution is finished.
this.currentLoopCounts.peek().isOpen = false;
this.currentLoopCounts.peek().lastCodeSectionLeftEventIndex =
MeasurementEventParser.this.measurementEvents.indexOf(codeSectionLeftEvent);
} else {
// The current execution is already finished, so we need to close
// the one "below" that.
// The actual closing
this.loopFinished();
if (!this.currentLoopCounts.isEmpty()) {
// The invariant for this stack
assert this.currentLoopCounts.peek().isOpen;
// allows us to just close the next layer.
this.currentLoopCounts.peek().isOpen = false;
this.currentLoopCounts.peek().lastCodeSectionLeftEventIndex =
MeasurementEventParser.this.measurementEvents.indexOf(codeSectionLeftEvent);
}
}
}
// If the currentLoopCounts stack is empty, we have a CodeSectionLeftEvent
// event without an CodeSectionEnteredEvent and ignore this.
}
/**
* This method must be called after the last measurement event was visited,
* because otherwise the resulting {@code loopRepetitionCountMeasurementResults}
* may be incomplete.
*/
public void finalise() {
// Close all open executions. There {@link CodeSectionLeftEvent} is missing.
while (!this.currentLoopCounts.isEmpty()) {
this.loopFinished();
}
}
/**
* Pops the top of {@link #currentLoopCounts} and adds the
* {@link LoopRepetitionCountMeasurementResult} for this execution.
*/
private void loopFinished() {
this.loopRepetitionCountMeasurementResults
.add(new LoopRepetitionCountMeasurementResult(this.currentLoopCounts.pop().numberOfExecutions));
}
/**
* Container for the information needed to save the state of an loop execution.
*
* @author Roman Langrehr
*/
private class LoopExecutionCounter {
/**
* How many times the loop was executed.
*/
private int numberOfExecutions;
/**
* Whether the last execution of the loop body was not finished. (An
* {@link CodeSectionEnteredEvent} was parsed, but not the corresponding
* {@link CodeSectionLeftEvent}.
*/
private boolean isOpen;
/**
* If {@link #isOpen} is {@code false}, this field is the index of the last
* CodeSectionLeftEvent of an loop body for this branch or {@code -1}, if no
* CodeSectionLeftEvent was parsed for this loop execution.
*/
private int lastCodeSectionLeftEventIndex;
/**
* Creates a new, empty LoopExecutionCounter.
*/
LoopExecutionCounter() {
this.numberOfExecutions = 0;
this.isOpen = false;
this.lastCodeSectionLeftEventIndex = -1;
}
}
}
}