| /******************************************************************************* |
| * Copyright (c) 2016 École Polytechnique de Montréal |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.incubator.internal.xaf.ui.statemachine; |
| |
| import java.util.ArrayList; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.stream.Collectors; |
| |
| import org.eclipse.tracecompass.analysis.os.linux.core.event.aspect.LinuxTidAspect; |
| import org.eclipse.tracecompass.common.core.NonNullUtils; |
| import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.backend.StateMachineBackendAnalysis; |
| import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.constraint.StateMachineConstraint; |
| import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.constraint.Status; |
| import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.constraint.StatusValue; |
| import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.variable.StateMachineVariable; |
| import org.eclipse.tracecompass.incubator.internal.xaf.ui.statemachine.StateMachineUtils.TimestampInterval; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; |
| import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; |
| import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils; |
| |
| import com.google.common.base.Joiner; |
| |
| /** |
| * @author Raphaël Beamonte |
| */ |
| public class StateMachineInstance implements Cloneable { |
| |
| private StateMachineNode currentNode; |
| |
| /** |
| * Represents the information of a step of an instance. A step of an |
| * instance is the status of the instance at a given node in the model. |
| * @author Raphaël Beamonte |
| */ |
| public class InstanceStepInformation { |
| /** The state machine instance in which this step is */ |
| public StateMachineInstance instance = StateMachineInstance.this; |
| /** The state machine node for which this step is */ |
| public StateMachineNode node = null; |
| /** The state machine event at which this step enters the node */ |
| public ITmfEvent event = null; |
| /** The status of the instance, according to its constraints, at this step */ |
| public StatusValue stepStatus = null; |
| /** The list of the constraint status for each constraints at this step */ |
| public List<Status> stepConstraintsStatus = null; |
| /** The map of last initialization step for each variables of this instance */ |
| public Map<String, Integer> variablesLastInit = new HashMap<>(); |
| |
| @Override |
| public InstanceStepInformation clone() { |
| instance = StateMachineInstance.this; |
| InstanceStepInformation isi = new InstanceStepInformation(); |
| isi.node = node; |
| isi.event = event; |
| isi.stepStatus = stepStatus; |
| isi.stepConstraintsStatus.addAll(isi.stepConstraintsStatus); |
| isi.variablesLastInit.putAll(isi.variablesLastInit); |
| |
| return isi; |
| } |
| |
| /** |
| * Clean up the adaptive constraints that couldn't be deduced |
| */ |
| public void cleanUpAdaptive() { |
| Iterator<Status> it = stepConstraintsStatus.iterator(); |
| while (it.hasNext()) { |
| Status cs = it.next(); |
| if (!cs.getConstraint().canBeUsed()) { |
| it.remove(); |
| } |
| } |
| } |
| |
| /** |
| * @param variableName The name of the variable for which to return the interval |
| * @return The interval of time for which we computed the given variable until the current step |
| */ |
| public TimestampInterval getVariableInterval(String variableName) { |
| InstanceStepInformation initIsi = instance.getInstanceStepInformation(NonNullUtils.checkNotNull(variablesLastInit.get(variableName))); |
| |
| // Timestamp of the start of the interval |
| ITmfTimestamp intervalStart = initIsi.event.getTimestamp(); |
| |
| // Timestamp of the end of the interval |
| ITmfTimestamp intervalEnd = event.getTimestamp(); |
| |
| return new TimestampInterval(intervalStart, intervalEnd); |
| } |
| } |
| |
| private List<InstanceStepInformation> isiList = new ArrayList<>(); |
| private StatusValue statusValue = StatusValue.VALID; |
| private Map<String,StateMachineVariable> currentVariables = new HashMap<>(); |
| private Map<String,Integer> stepInitVariables = new HashMap<>(); |
| |
| private Integer vtid = null; |
| private StateMachineInstanceGroup stateMachineInstanceGroup; |
| |
| /** |
| * @param initialNode the initial node |
| * @param initialEvent the initial event |
| * @param stateMachineInstanceGroup the state machine instance group |
| */ |
| public StateMachineInstance(StateMachineNode initialNode, ITmfEvent initialEvent, StateMachineInstanceGroup stateMachineInstanceGroup) { |
| this.stateMachineInstanceGroup = stateMachineInstanceGroup; |
| setCurrentNode(initialNode, initialEvent); |
| vtid = getEventTid(initialEvent, stateMachineInstanceGroup.getStateMachineBackendAnalysisModules()); |
| } |
| |
| /** |
| * @param instance the original instance to copy |
| */ |
| public StateMachineInstance(StateMachineInstance instance) { |
| vtid = instance.vtid; |
| currentNode = instance.currentNode; |
| for (InstanceStepInformation isi : instance.isiList) { |
| isiList.add(isi.clone()); |
| } |
| stateMachineInstanceGroup = instance.stateMachineInstanceGroup; |
| statusValue = instance.statusValue; |
| currentVariables.putAll(instance.currentVariables); |
| stepInitVariables.putAll(instance.stepInitVariables); |
| } |
| |
| /** |
| * @param ti The timestamp interval |
| * @return All the steps that where met during the given time interval |
| */ |
| public List<InstanceStepInformation> getStepsInInterval(TimestampInterval ti) { |
| List<InstanceStepInformation> isiIntvl = new ArrayList<>(); |
| |
| for (InstanceStepInformation isi : isiList) { |
| if (isi.event.getTimestamp().compareTo(ti.getEndTime()) > 0) { |
| break; |
| } else if (isi.event.getTimestamp().compareTo(ti.getStartTime()) < 0) { |
| continue; |
| } |
| isiIntvl.add(isi); |
| } |
| |
| return isiIntvl; |
| } |
| |
| /** |
| * @param event The event for which to get the TID |
| * @return The TID |
| */ |
| public static Integer getEventTid(ITmfEvent event) { |
| return getEventTid(event, null); |
| } |
| |
| /** |
| * @param event The event for which to get the TID |
| * @param smssaList The list of StateMAchineBackendAnalysisModule that we could use to get the TID |
| * @return The TID |
| */ |
| public static Integer getEventTid(ITmfEvent event, List<StateMachineBackendAnalysis> smssaList) { |
| return (Integer)TmfTraceUtils.resolveEventAspectOfClassForEvent(event.getTrace(), LinuxTidAspect.class, event); |
| } |
| |
| /** |
| * @return the vtid |
| */ |
| public Integer getVTid() { |
| return vtid; |
| } |
| |
| /** |
| * @return the state machine instance group |
| */ |
| public StateMachineInstanceGroup getStateMachineInstanceGroup() { |
| return stateMachineInstanceGroup; |
| } |
| |
| /** |
| * @return the currentNode |
| */ |
| public StateMachineNode getCurrentNode() { |
| return currentNode; |
| } |
| |
| /** |
| * @param nextNode the next node |
| * @param event the event |
| */ |
| private void setCurrentNode(StateMachineNode nextNode, ITmfEvent event) { |
| currentNode = nextNode; |
| |
| InstanceStepInformation isi = new InstanceStepInformation(); |
| isiList.add(isi); |
| |
| isi.node = nextNode; |
| isi.event = event; |
| |
| // Constraints verification |
| List<Status> listIsValid = new ArrayList<>(); |
| StatusValue isValid = StatusValue.VALID; |
| if (isiList.size() > 1) { |
| List<StateMachineConstraint> constraints = isiList.get(isiList.size()-2).node.getTransition(event).getConstraints(); |
| for (StateMachineConstraint c : constraints) { |
| Status cs = c.verify(currentVariables, event, this); |
| listIsValid.add(cs); |
| isValid = StateMachineConstraint.worstStatus(isValid, cs.getStatus()); |
| |
| //TODO: Stop when invalid? Not for a thorough report... add an option? |
| /*if (isValid == StateMachineConstraint.Status.INVALID) { |
| break; |
| }*/ |
| } |
| } |
| statusValue = StateMachineConstraint.worstStatus(statusValue, isValid); |
| isi.stepConstraintsStatus = listIsValid; |
| isi.stepStatus = isValid; |
| isi.variablesLastInit.putAll(stepInitVariables); |
| |
| // Updating local variables |
| for (StateMachineVariable nodeVar : nextNode.getVariables()) { |
| StateMachineVariable v = currentVariables.get(nodeVar.getName()); |
| |
| Comparable<?> value = nodeVar.getInitValue(event); |
| |
| if (v == null) { |
| v = nodeVar.getCopy(); |
| v.setValue(value); |
| currentVariables.put(v.getName(), v); |
| } else { |
| v.setValue(value); |
| } |
| |
| // Update the step we're at |
| stepInitVariables.put(v.getName(), (isiList.size() - 1)); |
| } |
| } |
| |
| /** |
| * @return Whether or not the current instance has a next node |
| */ |
| public boolean hasNextNode() { |
| return !currentNode.getTransitions().isEmpty(); |
| } |
| |
| /** |
| * @param event the received event |
| * @return Whether this instance has used the event or not |
| */ |
| |
| public boolean receivedEvent(ITmfEvent event) { |
| StateMachineTransition transition = currentNode.getTransition(event); |
| if (transition != null) { |
| //Integer tid = getTid(event); |
| if (vtid != null && !getEventTid(event).equals(vtid)) { |
| return false; |
| } |
| |
| setCurrentNode(transition.getNextNode(), event); |
| |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder path = new StringBuilder(); |
| if (vtid != null) { |
| path.append("Instance TID: "); //$NON-NLS-1$ |
| path.append(vtid); |
| path.append("\n"); //$NON-NLS-1$ |
| } |
| for (InstanceStepInformation isi : isiList) { |
| path.append("Received "); //$NON-NLS-1$ |
| path.append(isi.event.getType().getName()); |
| ITmfEventField content = isi.event.getContent(); |
| if (content != null) { |
| path.append("["); //$NON-NLS-1$ |
| path.append(Joiner.on(", ").join( //$NON-NLS-1$ |
| content.getFields().stream() |
| .sorted(new Comparator<ITmfEventField>() { |
| @Override |
| public int compare(ITmfEventField field0, ITmfEventField field1) { |
| if (field0.getName().startsWith("_") && !field1.getName().startsWith("_")) { //$NON-NLS-1$ //$NON-NLS-2$ |
| return 1; |
| } else if (!field0.getName().startsWith("_") && field1.getName().startsWith("_")) { //$NON-NLS-1$ //$NON-NLS-2$ |
| return -1; |
| } |
| return field0.getName().compareTo(field1.getName()); |
| } |
| }) |
| .filter(f -> f != null) |
| .filter(f -> !NonNullUtils.checkNotNull(f).getName().startsWith("context.")) //$NON-NLS-1$ |
| .map(f -> String.format("%s=%s", //$NON-NLS-1$ |
| NonNullUtils.checkNotNull(f).getName(), |
| NonNullUtils.checkNotNull(f).getValue().toString() |
| )) |
| .collect(Collectors.toList()))); |
| path.append("]"); //$NON-NLS-1$ |
| } |
| path.append(" at "); //$NON-NLS-1$ |
| path.append(isi.event.getTimestamp()); |
| /*if (i > 0) { |
| path.append("("); //$NON-NLS-1$ |
| path.append(usedPathEvents.get(i).getTimestamp().getDelta(usedPathEvents.get(i-1).getTimestamp())); |
| path.append(" since previous)"); //$NON-NLS-1$ |
| }*/ |
| path.append("\n\tEntering state: "); //$NON-NLS-1$ |
| path.append(isi.node.getName()); |
| if (!isi.node.getVariables().isEmpty()) { |
| path.append("\n\tVariables:"); //$NON-NLS-1$ |
| for (StateMachineVariable v : isi.node.getVariables()) { |
| path.append("\n\t\t - "); //$NON-NLS-1$ |
| path.append(v.getName()); |
| path.append(" = "); //$NON-NLS-1$ |
| path.append(v.getValue()); |
| } |
| } |
| if (!isi.stepConstraintsStatus.isEmpty()) { |
| path.append("\n\tConstraints:"); //$NON-NLS-1$ |
| for (Status cs : isi.stepConstraintsStatus) { |
| path.append("\n\t\t - "); //$NON-NLS-1$ |
| |
| path.append(cs.getConstraintLeftOperand()); |
| path.append(" "); //$NON-NLS-1$ |
| path.append(cs.getConstraintOperator()); |
| path.append(" "); //$NON-NLS-1$ |
| path.append(cs.getConstraintRightOperand()); |
| |
| path.append(" "); //$NON-NLS-1$ |
| |
| path.append("["); //$NON-NLS-1$ |
| path.append(cs.getStatus().toString()); |
| path.append("]"); //$NON-NLS-1$ |
| if (cs.getStatus() == StatusValue.INVALID) { |
| path.append(" value: "); //$NON-NLS-1$ |
| path.append(cs.getActualLeftValue().toString()); |
| } |
| } |
| } |
| path.append("\n"); //$NON-NLS-1$ |
| } |
| return path.toString(); |
| } |
| |
| /** |
| * @return the step status list |
| */ |
| public List<StatusValue> getStepStatus() { |
| List<StatusValue> stepStatusList = new ArrayList<>(); |
| |
| for (InstanceStepInformation isi : isiList) { |
| stepStatusList.add(isi.stepStatus); |
| } |
| |
| return stepStatusList; |
| } |
| |
| /** |
| * @param nodeIndex the index of the node |
| * @return the step status of that node |
| */ |
| public StatusValue getStepStatus(int nodeIndex) { |
| return isiList.get(nodeIndex).stepStatus; |
| } |
| |
| /** |
| * @return the constraintsValidations |
| */ |
| public List<List<Status>> getStepConstraintsStatus() { |
| List<List<Status>> stepConstraintsStatusList = new ArrayList<>(); |
| |
| for (InstanceStepInformation isi : isiList) { |
| stepConstraintsStatusList.add(isi.stepConstraintsStatus); |
| } |
| |
| return stepConstraintsStatusList; |
| } |
| |
| /** |
| * @param nodeIndex the index of the node |
| * @return the constraintsValidations of that node |
| */ |
| public List<Status> getStepConstraintsStatus(int nodeIndex) { |
| return isiList.get(nodeIndex).stepConstraintsStatus; |
| } |
| |
| /** |
| * @param nodeIndex the index of the node |
| * @param constraintIndex the index of the constraint |
| * @return the constraintsValidations of that node |
| */ |
| public Status getStepConstraintsStatus(int nodeIndex, int constraintIndex) { |
| return isiList.get(nodeIndex).stepConstraintsStatus.get(constraintIndex); |
| } |
| |
| /** |
| * @return the status |
| */ |
| public StatusValue getStatus() { |
| return statusValue; |
| } |
| |
| /** |
| * @return the number of nodes |
| */ |
| public int getNumNodes() { |
| return isiList.size(); |
| } |
| |
| /** |
| * @param index the index of the node |
| * @return the node |
| */ |
| public StateMachineNode getNode(int index) { |
| return isiList.get(index).node; |
| } |
| |
| /** |
| * @param index the index of the event |
| * @return the event |
| */ |
| public ITmfEvent getEvent(int index) { |
| return isiList.get(index).event; |
| } |
| |
| /** |
| * @param index The index of the instance step information to get |
| * @return The instance step information |
| */ |
| public InstanceStepInformation getInstanceStepInformation(int index) { |
| return isiList.get(index); |
| } |
| |
| /** |
| * @return The list of instance step information |
| */ |
| public List<InstanceStepInformation> getInstanceStepInformation() { |
| return isiList; |
| } |
| |
| /** |
| * @param name The name of the variable |
| * @return The variable |
| */ |
| public StateMachineVariable getVariable(String name) { |
| return currentVariables.get(name); |
| } |
| |
| /** |
| * Allows to verify that the instance given as parameter and the |
| * current instance followed the same chain of nodes, provoked by |
| * the same chain of events |
| * @param smi the instance |
| * @return true if it is comparable |
| */ |
| public boolean isComparable(StateMachineInstance smi) { |
| if (isiList.size() != smi.isiList.size()) { |
| return false; |
| } |
| for (int i = 0; i < isiList.size(); i++) { |
| if (isiList.get(i).node != smi.isiList.get(i).node |
| || isiList.get(i).event.getType().getName() != smi.isiList.get(i).event.getType().getName()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Clean up the adaptive constraints that couldn't be deduced |
| */ |
| public void cleanUpAdaptive() { |
| for (InstanceStepInformation isi : isiList) { |
| isi.cleanUpAdaptive(); |
| } |
| } |
| |
| } |