blob: 913cd51bc01484c8fbb956580db08dc775f4345f [file] [log] [blame]
/*******************************************************************************
* 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.core.statemachine.variable;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout;
import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelTrace;
import org.eclipse.tracecompass.common.core.NonNullUtils;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.backend.Attributes;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.backend.BackendStateValue;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.constraint.StateMachineConstraint;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.constraint.ValueType;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.exception.StateMachineNoInvalidCaseException;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.exception.StateMachineNoValidCaseException;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.exception.StateMachineStateSystemNotFoundException;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.exception.StateMachineStateSystemTimeRangeException;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.exception.StateMachineTIDNotFoundException;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.exception.StateMachineUnexpectedEventException;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.exception.StateMachineVariableException;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.variable.StateMachineVariableAnalysis.DefaultInterruptionEventRunner;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.variable.StateMachineVariableAnalysis.InterruptionEventGetter;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.variable.utils.InterruptionEvent;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.variable.utils.InterruptionEventList;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.variable.utils.InterruptionReason;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.variable.utils.ResponsibilityMap;
import org.eclipse.tracecompass.incubator.internal.xaf.core.statemachine.variable.utils.State;
import org.eclipse.tracecompass.incubator.internal.xaf.ui.statemachine.StateMachineInstance;
import org.eclipse.tracecompass.incubator.internal.xaf.ui.statemachine.StateMachineReport;
import org.eclipse.tracecompass.incubator.internal.xaf.ui.statemachine.StateMachineUtils;
import org.eclipse.tracecompass.incubator.internal.xaf.ui.statemachine.StateMachineInstance.InstanceStepInformation;
import org.eclipse.tracecompass.incubator.internal.xaf.ui.statemachine.StateMachineUtils.TimestampInterval;
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
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.timestamp.TmfTimestamp;
/**
* Variable representing a deadline
*
* @author Raphaël Beamonte
*/
public class StateMachineVariableDeadline extends StateMachineVariable {
/*
* static { StateMachineVariable.VARIABLE_TYPES.put("deadline",
* StateMachineVariableDeadline.class); //$NON-NLS-1$
*
* System.out.println("DEADLINE Map contains keys:"); for (String k :
* StateMachineVariable.VARIABLE_TYPES.keySet()) { System.out.println(k); }
* }
*/
/**
* @param name
* the name
* @param value
* the value
*/
@SuppressWarnings("rawtypes")
public StateMachineVariableDeadline(String name, Comparable value) {
super(name, value);
}
@Override
public Comparable getInitValue(ITmfEvent event) {
return event.getTimestamp();
}
@Override
public Comparable getValue(ITmfEvent event, StateMachineInstance stateMachineInstance) {
return event.getTimestamp().getDelta((ITmfTimestamp) getValue());
}
@Override
public Integer compareTo(Comparable toCompare, ITmfEvent event, StateMachineInstance stateMachineInstance) throws StateMachineVariableException {
ITmfTimestamp delta = (ITmfTimestamp) getValue(event, stateMachineInstance);
ITmfTimestamp t = StateMachineUtils.strToTimestamp((String) toCompare);
Integer comparison = delta.compareTo(t);
return comparison;
}
@Override
public Double getNumericalValue(Comparable format, ITmfEvent event, StateMachineInstance stateMachineInstance) {
return ((Long) ((ITmfTimestamp) getValue(event, stateMachineInstance)).getValue()).doubleValue();
}
@Override
public String getFormattedValue(Comparable format, ITmfEvent event, StateMachineInstance stateMachineInstance) {
ITmfTimestamp delta = (ITmfTimestamp) getValue(event, stateMachineInstance);
ITmfTimestamp t = StateMachineUtils.strToTimestamp((String) format);
// delta = delta.normalize(0, t.getScale());
Double value = delta.getValue() * Math.pow(10, delta.getScale() - t.getScale());
return getFormattedValue(format, value);
}
@Override
public String getFormattedValue(Comparable format, Double value) {
String symbol = ""; //$NON-NLS-1$
ITmfTimestamp t = StateMachineUtils.strToTimestamp((String) format);
switch (t.getScale()) {
case ITmfTimestamp.SECOND_SCALE:
symbol = "s"; //$NON-NLS-1$
break;
case ITmfTimestamp.MILLISECOND_SCALE:
symbol = "ms"; //$NON-NLS-1$
break;
case ITmfTimestamp.MICROSECOND_SCALE:
symbol = "us"; //$NON-NLS-1$
break;
case ITmfTimestamp.NANOSECOND_SCALE:
symbol = "ns"; //$NON-NLS-1$
break;
default:
break;
}
return String.format("%.4f%s", //$NON-NLS-1$
value,
symbol);
}
@Override
public String toString() {
StringBuilder path = new StringBuilder();
path.append("(Deadline)"); //$NON-NLS-1$
path.append(super.toString());
return path.toString();
}
/**
* @return a copy of the current variable
*/
@Override
public StateMachineVariableDeadline getCopy() {
return new StateMachineVariableDeadline(getName(), getValue());
}
@Override
public StateMachineVariableAnalysis analyze(StateMachineConstraint constraint, List<InstanceStepInformation> invalidIsiList, List<InstanceStepInformation> validIsiList) {
// Treat only constants for now
// TODO: treat comparison between variables
if (constraint.getValueType() != ValueType.CONSTANT) {
System.out.println("TODO"); //$NON-NLS-1$
return null;
}
if (validIsiList == null || validIsiList.isEmpty()) {
System.out.print("No valid instance"); //$NON-NLS-1$
return null;
}
StateMachineVariableAnalysis smva = new StateMachineVariableAnalysis(
this,
constraint,
invalidIsiList,
validIsiList);
StateMachineReport.R.println();
smva.runInstancesDurationAnalysis();
smva.runCpuFrequencyScalingAnalysis();
StateMachineReport.R.println_subsubsubsection("State analysis:"); //$NON-NLS-1$
StateMachineReport.R.inc();
try {
DefaultInterruptionEventRunner dier = new DefaultInterruptionEventRunner(this, NonNullUtils.checkNotNull(Attributes.STATE), new StateInterruptionEventGetter());
smva.doAnalysis(dier);
} catch (StateMachineNoValidCaseException | StateMachineNoInvalidCaseException e) {
e.printStackTrace();
return null;
}
StateMachineReport.R.printDropped(smva);
StateMachineReport.R.println(smva.getDifferentValidCases() +
" different valid situation" + (smva.getDifferentValidCases() > 1 ? "s" : "") + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
" for " + validIsiList.size() + //$NON-NLS-1$
" different valid case" + (validIsiList.size() > 1 ? "s" : "") + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
"." //$NON-NLS-1$
);
StateMachineReport.R.println(smva.getDifferentInvalidCases() +
" different invalid situation" + (smva.getDifferentInvalidCases() > 1 ? "s" : "") + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
" for " + invalidIsiList.size() + //$NON-NLS-1$
" different invalid case" + (invalidIsiList.size() > 1 ? "s" : "") + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
"." //$NON-NLS-1$
);
StateMachineReport.R.println();
ResponsibilityMap<InterruptionReason> analysisResponsibilityMap = smva.getAnalysisResponsibilityMap();
StateMachineReport.R.println("General computed difference between invalid and valid instances:"); //$NON-NLS-1$
String[] header = { "State", "Responsibility for added time" }; //$NON-NLS-1$ //$NON-NLS-2$
String[][] strs = new String[analysisResponsibilityMap.getCollection().size()][2];
int idx = 0;
int progressSize = 20;
for (Entry<InterruptionReason, Double> entry : analysisResponsibilityMap.getCollection()) {
double ratioValue = entry.getValue() * 100;
String symbol = StateMachineReport.progressBar(ratioValue, progressSize);
strs[idx][0] = entry.getKey().getID();
strs[idx][1] = String.format("%.2f%% %s", ratioValue, symbol); //$NON-NLS-1$
idx++;
}
StateMachineReport.R.println_table(header, strs, Arrays.asList(new Integer[] { 1 }));
StateMachineReport.R.println(String.format(
"Minimum responsibility for a case to be considered: %.2f%%", //$NON-NLS-1$
analysisResponsibilityMap.getMinProba() * 100));
StateMachineReport.R.println();
StateMachineReport.R.println_subsubsubsubsection("Analysis of the states to keep for consideration:"); //$NON-NLS-1$
for (Entry<InterruptionReason, Double> entry : analysisResponsibilityMap.getTopStdDevCollection()) {
StateMachineReport.R.println(String.format(
"%s (responsible for %.2f%% of the time added in invalid cases)", //$NON-NLS-1$
entry.getKey().getID(),
(entry.getValue() * 100)));
StateMachineReport.R.inc();
if (entry.getKey() instanceof State) {
BackendStateValue val = NonNullUtils.checkNotNull(((State) entry.getKey()).getValue());
switch (val) {
case UNKNOWN:
StateMachineReport.R.println("Now we can't do a thorough analysis, can we?"); //$NON-NLS-1$
break;
case SYSCALL:
case BLOCKED:
smva.runPriorityInheritance();
smva.runCriticalPath();
break;
case HRTIMER:
StateMachineReport.R.println("Further analysis for HRTIMER ?"); //$NON-NLS-1$
break;
case IRQ:
StateMachineReport.R.println("Further analysis for IRQ ?"); //$NON-NLS-1$
break;
case PREEMPTED:
StateMachineReport.R.println("Further analysis for PREEMPTED ?"); //$NON-NLS-1$
break;
case RUNNING:
smva.runStateMachineStateAnalysis();
break;
case SOFTIRQ_BLOCK:
case SOFTIRQ_BLOCK_IOPOLL:
case SOFTIRQ_HI:
case SOFTIRQ_HRTIMER:
case SOFTIRQ_NET_RX:
case SOFTIRQ_NET_TX:
case SOFTIRQ_RCU:
case SOFTIRQ_SCHED:
case SOFTIRQ_TASKLET:
case SOFTIRQ_TIMER:
StateMachineReport.R.println("Further analysis for SOFTIRQ ?"); //$NON-NLS-1$
break;
case WAKING:
smva.runCriticalPath();
break;
default:
StateMachineReport.R.println("We don't know why we're here"); //$NON-NLS-1$
break;
}
}
StateMachineReport.R.dec();
}
StateMachineReport.R.dec();
return smva;
}
private static class StateInterruptionEventGetter extends InterruptionEventGetter {
private String lastSyscall = null;
@Override
public InterruptionEventList execute(TimestampInterval ti, ITmfStateInterval si, ITmfEvent e)
throws StateMachineStateSystemNotFoundException, StateMachineTIDNotFoundException, StateMachineStateSystemTimeRangeException, StateMachineUnexpectedEventException {
InterruptionEventList taskList = new InterruptionEventList();
// Get the start and end of the state, given the start and end of
// the interval
long startTime = Math.max(si.getStartTime(), ti.getStartTime().getValue());
long endTime = Math.min(si.getEndTime(), ti.getEndTime().getValue());
Long stateValue = si.getStateValue().unboxLong();
BackendStateValue s = BackendStateValue.getValue(stateValue);
if (s == BackendStateValue.UNKNOWN) {
// Could be considered later, but we'll consider we can't use
// this for the analysis for now
throw new StateMachineStateSystemTimeRangeException();
}
String data = null;
if (s == BackendStateValue.SYSCALL) {
IKernelAnalysisEventLayout layout = ((IKernelTrace) e.getTrace()).getKernelEventLayout();
if (!(e.getName().startsWith(layout.eventSyscallEntryPrefix())
|| e.getName().equals(layout.eventSchedSwitch())
|| e.getName().equals(layout.eventSoftIrqExit()) || e.getName().equals("irq_softirq_exit") //$NON-NLS-1$
|| e.getName().equals(layout.eventHRTimerExpireExit()) || e.getName().equals("timer_hrtimer_expire_exit") //$NON-NLS-1$
|| e.getName().equals(layout.eventIrqHandlerExit()))) {
throw new StateMachineUnexpectedEventException(
String.format("The event should be a '%s', a '%s', a '%s' or a '%s', but is a '%s' instead", //$NON-NLS-1$
layout.eventSyscallEntryPrefix(),
layout.eventSchedSwitch(),
layout.eventIrqHandlerExit(),
layout.eventSoftIrqExit(),
e.getName()));
}
if (e.getType().getName().startsWith(layout.eventSyscallEntryPrefix())) {
lastSyscall = e.getType().getName().substring(layout.eventSyscallEntryPrefix().length());
}
data = lastSyscall;
} else if (s == BackendStateValue.BLOCKED) {
data = lastSyscall;
} else if (s == BackendStateValue.PREEMPTED) {
IKernelAnalysisEventLayout layout = ((IKernelTrace) e.getTrace()).getKernelEventLayout();
if (!e.getName().equals(layout.eventSchedSwitch())) {
throw new StateMachineUnexpectedEventException(
String.format("The event should be a '%s', but is a '%s' instead", //$NON-NLS-1$
layout.eventSchedSwitch(),
e.getName()));
}
ITmfEventField content = e.getContent();
data = String.format("%s (%s)", //$NON-NLS-1$
NonNullUtils.checkNotNull(content.getField(layout.fieldNextComm())).getFormattedValue(),
NonNullUtils.checkNotNull(content.getField(layout.fieldNextTid())).getFormattedValue());
} else if (s == BackendStateValue.IRQ) {
IKernelAnalysisEventLayout layout = ((IKernelTrace) e.getTrace()).getKernelEventLayout();
if (!e.getName().equals(layout.eventIrqHandlerEntry())) {
throw new StateMachineUnexpectedEventException(
String.format("The event should be a '%s', but is a '%s' instead", //$NON-NLS-1$
layout.eventIrqHandlerEntry(),
e.getName()));
}
ITmfEventField content = e.getContent();
data = String.format("(%s)", //$NON-NLS-1$
content.getField(layout.fieldName()).getFormattedValue());
}
State state = new State(stateValue, data);
// Compute the actual duration (to print, so not useful in the end)
// double xx = ((double)endTime - (double)startTime) / 1e6;
/*
* System.out.print("STATE " + state.toString());
* System.out.print(" (" + si.getStateValue().unboxLong() + ")");
* System.out.print(" FOR " + xx + "ms");
* System.out.printf(" (at time %d / %s)\n", startTime,
* e.getTimestamp().toString());
*/
// System.out.println(e);
taskList.events.add(new InterruptionEvent(e,
TmfTimestamp.create(startTime, ITmfTimestamp.NANOSECOND_SCALE),
TmfTimestamp.create(endTime, ITmfTimestamp.NANOSECOND_SCALE),
state));
return taskList;
}
}
}