| /******************************************************************************* |
| * Copyright (c) 2016-2017 É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.virtual.machine.analysis.ui.views.vresources; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.action.IToolBarManager; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.MouseEvent; |
| import org.eclipse.swt.events.MouseWheelListener; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.tracecompass.analysis.os.linux.core.model.HostThread; |
| import org.eclipse.tracecompass.analysis.os.linux.core.signals.TmfThreadSelectedSignal; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.fused.FusedAttributes; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.fused.FusedVMInformationProvider; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.fused.FusedVirtualMachineAnalysis; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.model.IVirtualMachineModel; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.trace.VirtualMachineExperiment; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.virtual.resources.StateValues; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.ui.Activator; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.ui.views.vresources.VirtualResourceEntry.Type; |
| import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; |
| import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; |
| import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; |
| import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; |
| import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler; |
| import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| import org.eclipse.tracecompass.tmf.ui.views.FormatTimeUtils; |
| import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractStateSystemTimeGraphView; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphPresentationProvider; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphSelectionListener; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphTimeListener; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphSelectionEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphTimeEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry; |
| |
| /** |
| * A view showing the virtual resources / CPUs for traces and their contained |
| * traces and containers |
| * |
| * @author Cedric Biancheri |
| * @author Geneviève Bastien |
| */ |
| public class VirtualResourcesView extends AbstractStateSystemTimeGraphView { |
| |
| /** View ID. */ |
| public static final @NonNull String ID = "org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.ui.views.virtualresources"; //$NON-NLS-1$ |
| |
| private static final String[] FILTER_COLUMN_NAMES = new String[] { |
| Messages.FusedVMView_stateTypeName |
| }; |
| |
| /** Button to select a machine to highlight */ |
| private final Action fHighlightMachine = new Action(Messages.FusedVMView_ButtonMachineSelected, IAction.AS_CHECK_BOX) { |
| @Override |
| public void run() { |
| // VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| // Map<String, Machine> highlightedMachines = presentationProvider.getHighlightedMachines(); |
| // Machine machine = highlightedMachines.get(presentationProvider.getSelectedMachine()); |
| // if (machine == null) { |
| // setChecked(!isChecked()); |
| // return; |
| // } |
| // machine.setHighlightedWithAllCpu(isChecked()); |
| // machine.setHighlightedWithAllContainers(isChecked()); |
| // fHighlightCPU.setChecked(isChecked()); |
| // fHighlightContainer.setChecked(isChecked()); |
| // presentationProvider.destroyTimeEventHighlight(); |
| refresh(); |
| } |
| }; |
| |
| /** Button to select a CPU to highlight */ |
| private final Action fHighlightCPU = new Action(Messages.FusedVMView_ButtonCPUSelected, IAction.AS_CHECK_BOX) { |
| @Override |
| public void run() { |
| // VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| // Map<String, Machine> highlightedMachines = presentationProvider.getHighlightedMachines(); |
| // Machine machine = highlightedMachines.get(presentationProvider.getSelectedMachine()); |
| // if (machine == null) { |
| // setChecked(!isChecked()); |
| // return; |
| // } |
| // machine.setHighlightedCpu(presentationProvider.getSelectedCpu(), isChecked()); |
| // fHighlightMachine.setChecked(machine.isOneCpuHighlighted()); |
| // presentationProvider.destroyTimeEventHighlight(); |
| refresh(); |
| } |
| }; |
| |
| /** Button to select a process to highlight */ |
| private final Action fHighlightProcess = new Action(Messages.FusedVMView_ButtonProcessSelected, IAction.AS_CHECK_BOX) { |
| @Override |
| public void run() { |
| VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| // if (isChecked()) { |
| // presentationProvider.addHighlightedThread(); |
| // } else { |
| // presentationProvider.removeHighlightedThread(); |
| // } |
| presentationProvider.resetTimeEventHighlight(); |
| refresh(); |
| } |
| }; |
| |
| /** Button to select a container to highlight */ |
| private final Action fHighlightContainer = new Action(Messages.FusedVMView_ButtonContainerSelected, IAction.AS_CHECK_BOX) { |
| @Override |
| public void run() { |
| // VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| // Map<String, Machine> highlightedMachines = presentationProvider.getHighlightedMachines(); |
| // Machine machine = highlightedMachines.get(presentationProvider.getSelectedMachine()); |
| // String container = presentationProvider.getSelectedContainer(); |
| // if (machine == null) { |
| // setChecked(!isChecked()); |
| // return; |
| // } |
| // machine.setHighlightedContainer(container, isChecked()); |
| // fHighlightMachine.setChecked(machine.isOneContainerHighlighted()); |
| // presentationProvider.destroyTimeEventHighlight(); |
| refresh(); |
| } |
| }; |
| |
| private Action fSelectMachineAction; |
| |
| /** The beginning of the selected time */ |
| private long beginSelectedTime; |
| |
| /** The end of the selected time */ |
| private long endSelectedTime; |
| |
| /** |
| * Listener that handles a change in the selected time in the FusedVM View |
| */ |
| private final ITimeGraphTimeListener fTimeListenerFusedVMView = new ITimeGraphTimeListener() { |
| |
| @Override |
| public void timeSelected(TimeGraphTimeEvent event) { |
| setBeginSelectedTime(event.getBeginTime()); |
| setEndSelectedTime(event.getEndTime()); |
| long begin = getBeginSelectedTime(); |
| long end = getEndSelectedTime(); |
| if (begin == end) { |
| VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| Object o = presentationProvider.getSelectedFusedVMViewEntry(); |
| if (o == null) { |
| return; |
| } |
| if (!(o instanceof VirtualResourceEntry)) { |
| return; |
| } |
| VirtualResourceEntry entry = (VirtualResourceEntry) o; |
| int cpuQuark = entry.getQuark(); |
| ITmfTrace trace = getTrace(); |
| if (trace == null) { |
| return; |
| } |
| final ITmfStateSystem ssq = TmfStateSystemAnalysisModule.getStateSystem(trace, FusedVirtualMachineAnalysis.ID); |
| if (ssq == null) { |
| return; |
| } |
| String machineName = null; |
| try { |
| ITmfStateInterval interval; |
| int machineNameQuark = ssq.getQuarkRelative(cpuQuark, FusedAttributes.MACHINE_NAME); |
| interval = ssq.querySingleState(begin, machineNameQuark); |
| ITmfStateValue value = interval.getStateValue(); |
| machineName = value.unboxStr(); |
| presentationProvider.setSelectedMachine(machineName); |
| |
| int threadQuark = ssq.getQuarkRelative(cpuQuark, FusedAttributes.CURRENT_THREAD); |
| interval = ssq.querySingleState(begin, threadQuark); |
| value = interval.getStateValue(); |
| int threadID = value.unboxInt(); |
| |
| presentationProvider.setSelectedThread(new HostThread(machineName, threadID)); |
| |
| int conditionQuark = ssq.getQuarkRelative(cpuQuark, FusedAttributes.CONDITION); |
| interval = ssq.querySingleState(begin, conditionQuark); |
| value = interval.getStateValue(); |
| int condition = value.unboxInt(); |
| List<Integer> list = ssq.getQuarks(cpuQuark, FusedAttributes.VIRTUAL_CPU); |
| if (condition == StateValues.CONDITION_IN_VM && !list.isEmpty()) { |
| /* |
| * Trick to get the quark and don't generate an |
| * exception if it's not there |
| */ |
| int machineVCpuQuark = list.get(0); |
| interval = ssq.querySingleState(begin, machineVCpuQuark); |
| value = interval.getStateValue(); |
| int vcpu = value.unboxInt(); |
| presentationProvider.setSelectedCpu(vcpu); |
| } else { |
| presentationProvider.setSelectedCpu(Integer.parseInt(ssq.getAttributeName(cpuQuark))); |
| } |
| |
| /* |
| * To look for the namespace number we look at process 1 if |
| * we are on process 0 |
| */ |
| if (threadID == 0) { |
| threadID++; |
| } |
| int nsInumQuark = FusedVMInformationProvider.getNodeNsInum(ssq, begin, machineName, threadID); |
| interval = ssq.querySingleState(begin, nsInumQuark); |
| String container = Long.toString(interval.getStateValue().unboxLong()); |
| presentationProvider.setSelectedContainer(container); |
| |
| } catch (AttributeNotFoundException e) { |
| Activator.getDefault().logError("Error in FusedVirtualMachineView, timestamp: " + FusedVMInformationProvider.formatTime(event.getBeginTime()), e); //$NON-NLS-1$ |
| } catch (StateSystemDisposedException e) { |
| /* Ignored */ |
| } |
| |
| updateButtonsSelection(); |
| updateToolTipTexts(); |
| |
| } else { |
| printInformations(); |
| } |
| } |
| }; |
| |
| /** Listener that handles a click on an entry in the FusedVM View */ |
| private final ITimeGraphSelectionListener fSelListenerFusedVMView = new ITimeGraphSelectionListener() { |
| |
| @Override |
| public void selectionChanged(TimeGraphSelectionEvent event) { |
| ITimeGraphEntry entry = event.getSelection(); |
| if (entry instanceof VirtualResourceEntry) { |
| VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| presentationProvider.setSelectedFusedVMViewEntry(entry); |
| } |
| |
| } |
| }; |
| |
| private final MouseWheelListener fWheelListener = new MouseWheelListener() { |
| |
| @Override |
| public void mouseScrolled(MouseEvent e) { |
| if ((e.stateMask & SWT.SHIFT) != 0) { |
| VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| presentationProvider.modifySelectedThreadAlpha(e.count); |
| presentationProvider.resetTimeEventHighlight(); |
| refresh(); |
| } |
| } |
| }; |
| |
| private final Map<ITmfTrace, Machine> fPhysicalMachines = new HashMap<>(); |
| private Map<String, Machine> fMachines; |
| |
| // ------------------------------------------------------------------------ |
| // Constructors |
| // ------------------------------------------------------------------------ |
| |
| /** |
| * Default constructor |
| */ |
| public VirtualResourcesView() { |
| super(ID, new VirtualResourcePresentationProvider()); |
| setFilterColumns(FILTER_COLUMN_NAMES); |
| setFilterLabelProvider(new FusedVMFilterLabelProvider()); |
| setEntryComparator(new FusedVMViewEntryComparator()); |
| } |
| |
| private static class FusedVMViewEntryComparator implements Comparator<ITimeGraphEntry> { |
| @Override |
| public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) { |
| VirtualResourceEntry entry1 = (VirtualResourceEntry) o1; |
| VirtualResourceEntry entry2 = (VirtualResourceEntry) o2; |
| Type typeE1 = entry1.getType(); |
| Type typeE2 = entry2.getType(); |
| if ((typeE1 == Type.VM || typeE1 == Type.CONTAINER) && (typeE2 == Type.VM || typeE2 == Type.CONTAINER)) { |
| /* sort trace entries alphabetically */ |
| return entry1.getName().compareTo(entry2.getName()); |
| } |
| if (typeE1 == Type.NULL && typeE2 == Type.NULL) { |
| if (entry1.getName() == Messages.FusedVMView_PhysicalCpusEntry) { |
| return -1; |
| } |
| if (entry2.getName() == Messages.FusedVMView_PhysicalCpusEntry) { |
| return 1; |
| } |
| if (entry1.getName() == Messages.FusedVMView_ContainersEntry) { |
| return 1; |
| } |
| if (entry2.getName() == Messages.FusedVMView_ContainersEntry) { |
| return -1; |
| } |
| } |
| /* sort resource entries by their defined order */ |
| return entry1.compareTo(entry2); |
| } |
| } |
| |
| private static class FusedVMFilterLabelProvider extends TreeLabelProvider { |
| @Override |
| public String getColumnText(Object element, int columnIndex) { |
| VirtualResourceEntry entry = (VirtualResourceEntry) element; |
| if (columnIndex == 0) { |
| return entry.getName(); |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| |
| } |
| |
| private static final Comparator<ITimeGraphEntry> ENTRY_COMPARATOR = new Comparator<ITimeGraphEntry>() { |
| @Override |
| public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) { |
| if (!((o1 instanceof VirtualResourceEntry) && (o2 instanceof VirtualResourceEntry))) { |
| return 0; |
| } |
| return ((VirtualResourceEntry) o1).compareTo(o2); |
| } |
| }; |
| |
| // ------------------------------------------------------------------------ |
| // Internal |
| // ------------------------------------------------------------------------ |
| |
| @Override |
| protected String getNextText() { |
| return Messages.FusedVMView_nextResourceActionNameText; |
| } |
| |
| @Override |
| protected String getNextTooltip() { |
| return Messages.FusedVMView_nextResourceActionToolTipText; |
| } |
| |
| @Override |
| protected String getPrevText() { |
| return Messages.FusedVMView_previousResourceActionNameText; |
| } |
| |
| @Override |
| protected String getPrevTooltip() { |
| return Messages.FusedVMView_previousResourceActionToolTipText; |
| } |
| |
| @Override |
| protected void buildEntryList(ITmfTrace trace, ITmfTrace parentTrace, final IProgressMonitor monitor) { |
| if (monitor.isCanceled()) { |
| return; |
| } |
| if (!(parentTrace instanceof VirtualMachineExperiment)) { |
| return; |
| } |
| |
| final ITmfStateSystem ssq = TmfStateSystemAnalysisModule.getStateSystem(parentTrace, FusedVirtualMachineAnalysis.ID); |
| if (ssq == null) { |
| return; |
| } |
| |
| /* if we don't wait we might don't see some machines */ |
| /* TODO: make the view built incrementally with the analysis */ |
| ssq.waitUntilBuilt(); |
| |
| /* All machines should are highlighted by default. */ |
| /* Remove highlighted machines from other analysis. */ |
| VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| presentationProvider.setSelectedElements(Collections.emptySet()); |
| |
| fMachines = new HashMap<>(); |
| // TODO: In an experiment, there can be multiple host machines |
| Machine physicalMachine = createHierarchy(ssq); |
| |
| if (physicalMachine == null) { |
| return; |
| } |
| fPhysicalMachines.put(trace, physicalMachine); |
| |
| TimeGraphEntry traceEntry = null; |
| |
| long startTime = ssq.getStartTime(); |
| long start = startTime; |
| setStartTime(Math.min(getStartTime(), startTime)); |
| |
| // FIXME: Here would start the while(!complete) loop |
| if (monitor.isCanceled()) { |
| return; |
| } |
| long end = ssq.getCurrentEndTime(); |
| long endTime = end + 1; |
| setEndTime(Math.max(getEndTime(), endTime)); |
| |
| List<Integer> machinesQuarks = ssq.getQuarks(FusedAttributes.HOSTS, "*"); //$NON-NLS-1$ |
| String hostName = null; |
| for (int quark : machinesQuarks) { |
| try { |
| if (ssq.querySingleState(trace.getStartTime().getValue(), quark).getStateValue().unboxInt() == StateValues.MACHINE_HOST) { |
| hostName = ssq.getAttributeName(quark); |
| } |
| } catch (StateSystemDisposedException e) { |
| return; |
| } |
| } |
| |
| traceEntry = new VirtualResourceEntry(trace, hostName, startTime, endTime, Type.VM, 0); |
| traceEntry.sortChildren(ENTRY_COMPARATOR); |
| List<@NonNull TimeGraphEntry> entryList = Collections.singletonList(traceEntry); |
| addToEntryList(parentTrace, ssq, entryList); |
| |
| createPhysicalCpuEntries(trace, ssq, traceEntry, startTime, endTime); |
| |
| /* Create entries for machines and containers */ |
| createMachineAndContainerEntries(trace, ssq, physicalMachine, traceEntry, startTime, endTime); |
| |
| if (parentTrace.equals(getTrace())) { |
| refresh(); |
| } |
| final List<? extends ITimeGraphEntry> traceEntryChildren = traceEntry.getChildren(); |
| final long resolution = Math.max(1, (endTime - ssq.getStartTime()) / getDisplayWidth()); |
| final long qStart = start; |
| final long qEnd = end; |
| queryFullStates(ssq, qStart, qEnd, resolution, monitor, new IQueryHandler() { |
| @Override |
| public void handle(List<List<ITmfStateInterval>> fullStates, List<ITmfStateInterval> prevFullState) { |
| for (ITimeGraphEntry child : traceEntryChildren) { |
| if (!populateEventsRecursively(fullStates, prevFullState, child).isOK()) { |
| return; |
| } |
| } |
| } |
| |
| private IStatus populateEventsRecursively(@NonNull List<List<ITmfStateInterval>> fullStates, @Nullable List<ITmfStateInterval> prevFullState, ITimeGraphEntry entry) { |
| if (monitor.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| if (entry instanceof TimeGraphEntry) { |
| TimeGraphEntry timeGraphEntry = (TimeGraphEntry) entry; |
| List<ITimeEvent> eventList = getEventList(timeGraphEntry, ssq, fullStates, prevFullState, monitor); |
| if (eventList != null) { |
| for (ITimeEvent event : eventList) { |
| timeGraphEntry.addEvent(event); |
| } |
| } |
| } |
| for (ITimeGraphEntry child : entry.getChildren()) { |
| IStatus status = populateEventsRecursively(fullStates, prevFullState, child); |
| if (!status.isOK()) { |
| return status; |
| } |
| } |
| return Status.OK_STATUS; |
| } |
| }); |
| |
| start = end; |
| } |
| |
| private void createPhysicalCpuEntries(@NonNull ITmfTrace trace, final ITmfStateSystem ssq, TimeGraphEntry traceEntry, long startTime, long endTime) { |
| List<Integer> cpuQuarks = ssq.getQuarks(FusedAttributes.CPUS, "*"); //$NON-NLS-1$ |
| int cpusQuark = ssq.optQuarkAbsolute(FusedAttributes.CPUS); |
| VirtualResourceEntry physicalCpusEntry = new VirtualResourceEntry(cpusQuark, trace, Messages.FusedVMView_PhysicalCpusEntry, startTime, endTime, Type.NULL, cpusQuark); |
| traceEntry.addChild(physicalCpusEntry); |
| |
| for (Integer cpuQuark : cpuQuarks) { |
| String cpuName = ssq.getAttributeName(cpuQuark); |
| int cpu = Integer.parseInt(cpuName); |
| VirtualResourceEntry cpuEntry = new VirtualResourceEntry(cpuQuark, trace, startTime, endTime, Type.CPU, cpu); |
| physicalCpusEntry.addChild(cpuEntry); |
| |
| List<Integer> irqQuarks = ssq.getQuarks(FusedAttributes.CPUS, cpuName, FusedAttributes.IRQS, "*"); //$NON-NLS-1$ |
| createCpuInterruptEntryWithQuark(trace, ssq, startTime, endTime, cpuEntry, irqQuarks, Type.IRQ); |
| List<Integer> softIrqQuarks = ssq.getQuarks(FusedAttributes.CPUS, cpuName, FusedAttributes.SOFT_IRQS, "*"); //$NON-NLS-1$ |
| createCpuInterruptEntryWithQuark(trace, ssq, startTime, endTime, cpuEntry, softIrqQuarks, Type.SOFT_IRQ); |
| |
| Display.getDefault().asyncExec(() -> { |
| getTimeGraphViewer().setExpandedState(cpuEntry, false); |
| }); |
| } |
| } |
| |
| private void createMachineAndContainerEntries(@NonNull ITmfTrace trace, final ITmfStateSystem ssq, Machine machine, TimeGraphEntry parentEntry, long startTime, long endTime) { |
| Machine physicalMachine = fPhysicalMachines.get(trace); |
| Collection<Machine> vms = machine.getVirtualMachines(); |
| Collection<Machine> containers = machine.getContainers(); |
| Collection<Processor> pcpus = machine.getPhysicalCpus(); |
| |
| if (!vms.isEmpty()) { |
| VirtualResourceEntry virtualMachinesEntry = new VirtualResourceEntry(0, trace, Messages.FusedVMView_VirtualMachinesEntry, startTime, endTime, Type.NULL, 3 * vms.hashCode()); |
| parentEntry.addChild(virtualMachinesEntry); |
| |
| for (Machine vm : vms) { |
| VirtualResourceEntry virtualMachineEntry = new VirtualResourceEntry(0, trace, vm.getMachineName(), startTime, endTime, Type.VM, vm.hashCode()); |
| virtualMachinesEntry.addChild(virtualMachineEntry); |
| createMachineAndContainerEntries(trace, ssq, vm, virtualMachineEntry, startTime, endTime); |
| } |
| } |
| |
| if (!containers.isEmpty()) { |
| VirtualResourceEntry containersEntry = new VirtualResourceEntry(0, trace, Messages.FusedVMView_ContainersEntry, startTime, endTime, Type.NULL, 3 * containers.hashCode()); |
| parentEntry.addChild(containersEntry); |
| |
| for (Machine container : containers) { |
| VirtualResourceEntry containerEntry = new VirtualResourceEntry(0, trace, container.getMachineName(), startTime, endTime, Type.CONTAINER, container.hashCode()); |
| containersEntry.addChild(containerEntry); |
| createMachineAndContainerEntries(trace, ssq, container, containerEntry, startTime, endTime); |
| } |
| } |
| |
| if (!pcpus.isEmpty() && (machine != physicalMachine)) { |
| VirtualResourceEntry pCpusEntry = new VirtualResourceEntry(0, trace, Messages.FusedVMView_PhysicalCpusEntry, startTime, endTime, Type.NULL, 3 * pcpus.hashCode()); |
| parentEntry.addChild(pCpusEntry); |
| |
| for (Processor p : pcpus) { |
| List<Integer> list = ssq.getQuarks(FusedAttributes.CPUS, String.valueOf(p.getNumber())); |
| if (list.isEmpty()) { |
| return; |
| } |
| int pCpuQuark = list.get(0); |
| Type type = Type.NULL; |
| Type typeMachine = ((VirtualResourceEntry) parentEntry).getType(); |
| if (typeMachine == Type.VM) { |
| type = Type.PCPU_VM; |
| } else if (typeMachine == Type.CONTAINER) { |
| type = Type.PCPU_CONTAINER; |
| } |
| VirtualResourceEntry pCpuEntry = new VirtualResourceEntry(pCpuQuark, trace, startTime, endTime, type, p.getNumber()); |
| pCpusEntry.addChild(pCpuEntry); |
| } |
| } |
| } |
| |
| /* |
| * Create and add execution contexts to a cpu entry. Also creates an |
| * aggregate entry in the root trace entry. The execution context is |
| * basically what the cpu is doing in its execution stack. It can be in an |
| * IRQ, Soft IRQ. MCEs, NMIs, Userland and Kernel execution is not yet |
| * supported. |
| */ |
| private static void createCpuInterruptEntryWithQuark(@NonNull ITmfTrace trace, |
| final ITmfStateSystem ssq, |
| long startTime, long endTime, VirtualResourceEntry cpuEntry, |
| List<Integer> childrenQuarks, Type type) { |
| for (Integer quark : childrenQuarks) { |
| final @NonNull String resourceName = ssq.getAttributeName(quark); |
| int resourceId = Integer.parseInt(resourceName); |
| VirtualResourceEntry interruptEntry = new VirtualResourceEntry(quark, trace, startTime, endTime, type, resourceId); |
| cpuEntry.addChild(interruptEntry); |
| } |
| } |
| |
| @Override |
| protected @Nullable List<ITimeEvent> getEventList(@NonNull TimeGraphEntry entry, ITmfStateSystem ssq, |
| @NonNull List<List<ITmfStateInterval>> fullStates, @Nullable List<ITmfStateInterval> prevFullState, @NonNull IProgressMonitor monitor) { |
| VirtualResourceEntry fusedVMViewEntry = (VirtualResourceEntry) entry; |
| int quark = fusedVMViewEntry.getQuark(); |
| |
| if (fusedVMViewEntry.getType().equals(Type.CPU)) { |
| return getCpuEventsList(entry, ssq, fullStates, prevFullState, monitor, quark, Type.CPU); |
| } else if ((fusedVMViewEntry.getType().equals(Type.IRQ) || fusedVMViewEntry.getType().equals(Type.SOFT_IRQ)) && (quark >= 0)) { |
| return getIrqEventsList(entry, fullStates, prevFullState, monitor, quark); |
| } else if (fusedVMViewEntry.getType().equals(Type.PCPU_VM)) { |
| return getCpuEventsList(entry, ssq, fullStates, prevFullState, monitor, quark, Type.PCPU_VM); |
| } else if (fusedVMViewEntry.getType().equals(Type.PCPU_CONTAINER)) { |
| return getCpuEventsList(entry, ssq, fullStates, prevFullState, monitor, quark, Type.PCPU_CONTAINER); |
| } |
| |
| return null; |
| } |
| |
| private List<ITimeEvent> getCpuEventsList(TimeGraphEntry entry, ITmfStateSystem ssq, List<List<ITmfStateInterval>> fullStates, List<ITmfStateInterval> prevFullState, IProgressMonitor monitor, int quark, Type type) { |
| List<ITimeEvent> eventList; |
| int statusQuark; |
| int machineQuark; |
| int currentThreadQuark; |
| String machineName = null; |
| try { |
| statusQuark = ssq.getQuarkRelative(quark, FusedAttributes.STATUS); |
| machineQuark = ssq.getQuarkRelative(quark, FusedAttributes.MACHINE_NAME); |
| currentThreadQuark = ssq.getQuarkRelative(quark, FusedAttributes.CURRENT_THREAD); |
| } catch (AttributeNotFoundException e) { |
| /* |
| * The sub-attribute "status" is not available. May happen if the |
| * trace does not have sched_switch events enabled. |
| */ |
| return null; |
| } |
| eventList = new ArrayList<>(fullStates.size()); |
| /* |
| * In order to make the filter work, a time event must be generated for |
| * each change of cpu status, current thread or current machine. |
| */ |
| ITmfStateInterval lastStatusInterval = prevFullState == null || statusQuark >= prevFullState.size() ? null : prevFullState.get(statusQuark); |
| ITmfStateInterval lastMachineInterval = prevFullState == null || machineQuark >= prevFullState.size() ? null : prevFullState.get(machineQuark); |
| ITmfStateInterval lastCurrentThreadInterval = prevFullState == null || currentThreadQuark >= prevFullState.size() ? null : prevFullState.get(currentThreadQuark); |
| long lastStatusStartTime = lastStatusInterval == null ? -1 : lastStatusInterval.getStartTime(); |
| long lastStatusEndTime = lastStatusInterval == null ? Long.MAX_VALUE : lastStatusInterval.getEndTime() + 1; |
| long lastMachineStartTime = lastMachineInterval == null ? -1 : lastMachineInterval.getStartTime(); |
| long lastMachineEndTime = lastMachineInterval == null ? Long.MAX_VALUE : lastMachineInterval.getEndTime() + 1; |
| long lastCurrentThreadStartTime = lastCurrentThreadInterval == null ? -1 : lastCurrentThreadInterval.getStartTime(); |
| long lastCurrentThreadEndTime = lastCurrentThreadInterval == null ? Long.MAX_VALUE : lastCurrentThreadInterval.getEndTime() + 1; |
| /* So we intersect the three intervals. */ |
| long lastStartTime = Math.max(lastStatusStartTime, Math.max(lastMachineStartTime, lastCurrentThreadStartTime)); |
| long lastEndTime = Math.min(lastStatusEndTime, Math.min(lastMachineEndTime, lastCurrentThreadEndTime)); |
| for (List<ITmfStateInterval> fullState : fullStates) { |
| if (monitor.isCanceled()) { |
| return null; |
| } |
| if (statusQuark >= fullState.size()) { |
| /* No information on this CPU (yet?), skip it for now */ |
| continue; |
| } |
| ITmfStateInterval statusInterval = fullState.get(statusQuark); |
| ITmfStateInterval machineInterval = fullState.get(machineQuark); |
| ITmfStateInterval currentThreadInterval = fullState.get(currentThreadQuark); |
| |
| if (type.equals(Type.CPU)) { |
| /* Just keep going */ |
| } else if (type.equals(Type.PCPU_VM)) { |
| // TODO: support vm's vms |
| machineName = entry.getParent().getParent().getName(); |
| // if |
| // (!machineInterval.getStateValue().unboxStr().equals(machineName)) |
| // { |
| if (!isInsideVM(machineInterval.getStateValue().unboxStr(), machineName)) { |
| /* Skip that interval, it's not related to the machine */ |
| continue; |
| } |
| } else if (type.equals(Type.PCPU_CONTAINER)) { |
| /* Get the entry of the machine containing the container */ |
| VirtualResourceEntry machineEntry = (VirtualResourceEntry) entry.getParent(); |
| if (machineEntry == null) { |
| continue; |
| } |
| while (machineEntry.getType() != Type.VM) { |
| machineEntry = (VirtualResourceEntry) machineEntry.getParent(); |
| } |
| machineName = machineEntry.getName(); |
| if (machineName == null || !machineInterval.getStateValue().unboxStr().equals(machineName)) { |
| /* Other machine, skip the interval */ |
| continue; |
| } |
| String containerID = entry.getParent().getParent().getName(); |
| if (containerID == null) { |
| continue; |
| } |
| int containerQuark = FusedVMInformationProvider.getContainerQuark(ssq, machineName, containerID); |
| if (containerQuark == ITmfStateSystem.INVALID_ATTRIBUTE) { |
| Activator.getDefault().logWarning("Container quark not found for " + containerID + " in machine " + machineName + ". This shouldn't happen."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| break; |
| } |
| int threadID = currentThreadInterval.getStateValue().unboxInt(); |
| List<Integer> threadsQuarks = ssq.getQuarks(containerQuark, FusedAttributes.THREADS, "*"); //$NON-NLS-1$ |
| boolean foundThread = false; |
| for (Integer threadQuark : threadsQuarks) { |
| if (Integer.parseInt(ssq.getAttributeName(threadQuark)) == threadID) { |
| foundThread = true; |
| break; |
| } |
| } |
| if (!foundThread) { |
| /* Thread not inside container, skip interval */ |
| continue; |
| } |
| } |
| |
| int status = statusInterval.getStateValue().unboxInt(); |
| long time = Math.max(statusInterval.getStartTime(), Math.max(machineInterval.getStartTime(), currentThreadInterval.getStartTime())); |
| long duration = Math.min(statusInterval.getEndTime(), Math.min(machineInterval.getEndTime(), currentThreadInterval.getEndTime())) - time + 1; |
| if (time == lastStartTime) { |
| continue; |
| } |
| if (!statusInterval.getStateValue().isNull()) { |
| if (lastEndTime != time && lastEndTime != -1) { |
| eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime)); |
| } |
| eventList.add(new TimeEvent(entry, time, duration, status)); |
| } else { |
| eventList.add(new NullTimeEvent(entry, time, duration)); |
| } |
| lastStartTime = time; |
| lastEndTime = time + duration; |
| } |
| return eventList; |
| } |
| |
| /** |
| * Return true if machine1 is a submachine of machine2 |
| * |
| * @param machine1 |
| * @param machine2 |
| * @return |
| */ |
| private boolean isInsideVM(String machine1, String machine2) { |
| Machine m2 = fMachines.get(machine2); |
| if (m2 == null) { |
| return false; |
| } |
| String machine2Host = m2.getHostId(); |
| if (machine1.equals(machine2Host)) { |
| return true; |
| } |
| |
| for (Machine child : m2.getVirtualMachines()) { |
| if (isInsideVM(machine1, child.getHostId())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static List<ITimeEvent> getIrqEventsList(TimeGraphEntry entry, List<List<ITmfStateInterval>> fullStates, List<ITmfStateInterval> prevFullState, IProgressMonitor monitor, int quark) { |
| List<ITimeEvent> eventList; |
| eventList = new ArrayList<>(fullStates.size()); |
| ITmfStateInterval lastInterval = prevFullState == null || quark >= prevFullState.size() ? null : prevFullState.get(quark); |
| long lastStartTime = lastInterval == null ? -1 : lastInterval.getStartTime(); |
| long lastEndTime = lastInterval == null ? -1 : lastInterval.getEndTime() + 1; |
| boolean lastIsNull = lastInterval == null ? false : lastInterval.getStateValue().isNull(); |
| for (List<ITmfStateInterval> fullState : fullStates) { |
| if (monitor.isCanceled()) { |
| return null; |
| } |
| if (quark >= fullState.size()) { |
| /* No information on this IRQ (yet?), skip it for now */ |
| continue; |
| } |
| ITmfStateInterval irqInterval = fullState.get(quark); |
| long time = irqInterval.getStartTime(); |
| long duration = irqInterval.getEndTime() - time + 1; |
| if (time == lastStartTime) { |
| continue; |
| } |
| if (!irqInterval.getStateValue().isNull()) { |
| int cpu = irqInterval.getStateValue().unboxInt(); |
| eventList.add(new TimeEvent(entry, time, duration, cpu)); |
| lastIsNull = false; |
| } else { |
| if (lastEndTime != time && lastIsNull) { |
| /* |
| * This is a special case where we want to show IRQ_ACTIVE |
| * state but we don't know the CPU (it is between two null |
| * samples) |
| */ |
| eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime, -1)); |
| } |
| eventList.add(new NullTimeEvent(entry, time, duration)); |
| lastIsNull = true; |
| } |
| lastStartTime = time; |
| lastEndTime = time + duration; |
| } |
| return eventList; |
| } |
| |
| @Override |
| protected void fillLocalToolBar(IToolBarManager manager) { |
| super.fillLocalToolBar(manager); |
| IAction selectMachineAction = getSelectMachineAction(); |
| selectMachineAction.setText(Messages.FusedVMView_selectMachineText); |
| selectMachineAction.setToolTipText(Messages.FusedVMView_selectMachineText); |
| manager.add(selectMachineAction); |
| // manager.add(new Separator()); |
| // manager.add(fHighlightMachine); |
| // manager.add(fHighlightCPU); |
| // manager.add(fHighlightProcess); |
| // manager.add(fHighlightContainer); |
| |
| } |
| |
| @Override |
| public void createPartControl(Composite parent) { |
| super.createPartControl(parent); |
| |
| getTimeGraphViewer().addTimeListener(fTimeListenerFusedVMView); |
| getTimeGraphViewer().addSelectionListener(fSelListenerFusedVMView); |
| |
| getTimeGraphViewer().getTimeGraphControl().addMouseWheelListener(fWheelListener); |
| } |
| |
| /** |
| * Gets the beginning of the selected time |
| * |
| * @return the beginning of the selected time |
| */ |
| public long getBeginSelectedTime() { |
| return beginSelectedTime; |
| } |
| |
| /** |
| * Sets the beginning of the selected time |
| * |
| * @param begin |
| * the beginning of the selected time |
| */ |
| public void setBeginSelectedTime(long begin) { |
| beginSelectedTime = begin; |
| } |
| |
| /** |
| * Gets the end of the selected time |
| * |
| * @return the end of the selected time |
| */ |
| public long getEndSelectedTime() { |
| return endSelectedTime; |
| } |
| |
| /** |
| * Sets the end of the selected time |
| * |
| * @param end |
| * the end of the selected time |
| */ |
| public void setEndSelectedTime(long end) { |
| endSelectedTime = end; |
| } |
| |
| /** |
| * Getter to the presentation provider |
| * |
| * @return the FusedVMViewProvider |
| */ |
| public VirtualResourcePresentationProvider getFusedVMViewPresentationProvider() { |
| ITimeGraphPresentationProvider pp = getPresentationProvider(); |
| if (!(pp instanceof VirtualResourcePresentationProvider)) { |
| return null; |
| } |
| return (VirtualResourcePresentationProvider) pp; |
| } |
| |
| private void printInformations() { |
| long begin = getBeginSelectedTime(); |
| long end = getEndSelectedTime(); |
| |
| System.out.println("Begin time: " + FormatTimeUtils.formatTime(begin, FormatTimeUtils.TimeFormat.CALENDAR, FormatTimeUtils.Resolution.NANOSEC)); //$NON-NLS-1$ |
| System.out.println("End time: " + FormatTimeUtils.formatTime(end, FormatTimeUtils.TimeFormat.CALENDAR, FormatTimeUtils.Resolution.NANOSEC)); //$NON-NLS-1$ |
| System.out.println(); |
| |
| } |
| |
| /** |
| * Updates the tooltip text of the buttons so it corresponds to the machine, |
| * cpu and process selected |
| */ |
| private void updateToolTipTexts() { |
| VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| fHighlightMachine.setToolTipText(presentationProvider.getSelectedMachine()); |
| fHighlightCPU.setToolTipText(Integer.toString((presentationProvider.getSelectedCpu()))); |
| // TODO: Add the name of the selected process |
| fHighlightProcess.setToolTipText(Messages.FusedVMView_ButtonProcessSelected); |
| fHighlightContainer.setToolTipText(presentationProvider.getSelectedContainer()); |
| } |
| |
| /** |
| * Sets the checked state of the buttons |
| */ |
| private void updateButtonsSelection() { |
| // VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| // Map<String, Machine> highlightedMachines = presentationProvider.getHighlightedMachines(); |
| // Machine machine = highlightedMachines.get(presentationProvider.getSelectedMachine()); |
| // if (machine == null) { |
| // return; |
| // } |
| // |
| // fHighlightMachine.setChecked(machine.isHighlighted()); |
| // fHighlightCPU.setChecked(machine.isCpuHighlighted(presentationProvider.getSelectedCpu())); |
| // fHighlightProcess.setChecked(presentationProvider.isThreadSelected(machine.getMachineName(), presentationProvider.getSelectedThreadID())); |
| // fHighlightContainer.setChecked(machine.isContainerHighlighted(presentationProvider.getSelectedContainer())); |
| } |
| |
| /** |
| * Get the select machine action. |
| * |
| * @return The select machine action |
| */ |
| public Action getSelectMachineAction() { |
| if (fSelectMachineAction == null) { |
| fSelectMachineAction = new Action() { |
| @Override |
| public void run() { |
| VirtualResourcePresentationProvider presentationProvider = (VirtualResourcePresentationProvider) getPresentationProvider(); |
| |
| Control dataViewer = getTimeGraphViewer().getControl(); |
| if (dataViewer == null || dataViewer.isDisposed()) { |
| return; |
| } |
| ITmfTrace trace = getTrace(); |
| if (trace == null) { |
| return; |
| } |
| Machine physicalMachine = fPhysicalMachines.get(trace); |
| if (physicalMachine == null) { |
| return; |
| } |
| SelectMachineDialog dialog = new SelectMachineDialog(dataViewer.getShell()); |
| dialog.setInput(Collections.singleton(physicalMachine)); |
| dialog.setInitialSelections(presentationProvider.getSelectedElements().toArray()); |
| dialog.open(); |
| Object[] result = dialog.getResult(); |
| if (result != null) { |
| presentationProvider.setSelectedElements(Arrays.asList(result)); |
| presentationProvider.resetTimeEventHighlight(); |
| redraw(); |
| } |
| } |
| }; |
| fSelectMachineAction.setText(Messages.FusedVMView_SelectMachineActionNameText); |
| fSelectMachineAction.setToolTipText(Messages.FusedVMView_SelectMachineActionToolTipText); |
| } |
| |
| return fSelectMachineAction; |
| } |
| |
| @Override |
| protected @NonNull Iterable<ITmfTrace> getTracesToBuild(@Nullable ITmfTrace trace) { |
| return Collections.singleton(trace); |
| } |
| |
| private Machine createHierarchy(@NonNull ITmfStateSystem ssq) { |
| /* Separate host from guests */ |
| Machine host = null; |
| List<Machine> guests = new LinkedList<>(); |
| for (String machineHost : FusedVMInformationProvider.getMachinesTraced(ssq)) { |
| int typeMachine = FusedVMInformationProvider.getTypeMachine(ssq, machineHost); |
| |
| if (typeMachine < 0) { |
| continue; |
| } |
| String machineName = FusedVMInformationProvider.getMachineName(ssq, machineHost); |
| Machine machine = null; |
| if ((typeMachine & StateValues.MACHINE_GUEST) == StateValues.MACHINE_GUEST) { |
| machine = new Machine(machineName, machineHost, typeMachine, FusedVMInformationProvider.getPhysicalCpusUsedByMachine(ssq, machineHost)); |
| fMachines.put(machine.getMachineName(), machine); |
| guests.add(machine); |
| } else if (typeMachine == StateValues.MACHINE_HOST) { |
| machine = new Machine(machineName, machineHost, typeMachine); |
| for (String cpus : FusedVMInformationProvider.getCpusUsedByMachine(ssq, machineHost)) { |
| machine.addPCpu(cpus); |
| } |
| fMachines.put(machine.getMachineName(), machine); |
| host = machine; |
| } |
| if (machine == null) { |
| continue; |
| } |
| } |
| if (host == null) { |
| return null; |
| } |
| /* Complete construction for the host */ |
| createContainersHierarchyForMachine(ssq, host); |
| createMachineHierarchy(ssq, host, guests); |
| /* Create container hierarchy for guests and add them to the host */ |
| for (Machine guest : guests) { |
| createContainersHierarchyForMachine(ssq, guest); |
| } |
| return host; |
| } |
| |
| private static void createMachineHierarchy(@NonNull ITmfStateSystem ssq, Machine host, List<Machine> guests) { |
| for (Machine m : guests) { |
| String parentHostId = FusedVMInformationProvider.getParentMachineHostId(ssq, m.getHostId()); |
| if (parentHostId.equals(host.getHostId())) { |
| m.setHost(host); |
| host.addVirtualMachine(m); |
| } |
| for (Machine m2 : guests) { |
| parentHostId = FusedVMInformationProvider.getParentMachineHostId(ssq, m2.getHostId()); |
| if (parentHostId.equals(m.getHostId())) { |
| m2.setHost(m); |
| m.addVirtualMachine(m2); |
| } |
| } |
| } |
| } |
| |
| private static void createContainersHierarchyForMachine(@NonNull ITmfStateSystem ssq, Machine m) { |
| String machineName = m.getHostId(); |
| Collection<Integer> containersQuarks = FusedVMInformationProvider.getMachineContainersQuarks(ssq, machineName); |
| /* Look for not nested containers */ |
| for (Integer quark : containersQuarks) { |
| long parentContainer = FusedVMInformationProvider.getParentContainer(ssq, quark); |
| if (parentContainer == IVirtualMachineModel.ROOT_NAMESPACE) { |
| String containerName = ssq.getAttributeName(quark); |
| List<String> pCpus = FusedVMInformationProvider.getPCpusUsedByContainer(ssq, quark); |
| Machine container = m.createContainer(containerName, m.getHostId(), pCpus); |
| /* Continue construction for these containers */ |
| createContainersHierarchyForContainer(ssq, container, containersQuarks); |
| } |
| } |
| } |
| |
| private static void createContainersHierarchyForContainer(@NonNull ITmfStateSystem ssq, Machine container, Collection<Integer> containersQuarks) { |
| Long containerName = Long.parseLong(container.getMachineName()); |
| for (int quark : containersQuarks) { |
| if (FusedVMInformationProvider.getParentContainer(ssq, quark).equals(containerName)) { |
| /* We found a child */ |
| String childName = ssq.getAttributeName(quark); |
| List<String> pCpus = FusedVMInformationProvider.getPCpusUsedByContainer(ssq, quark); |
| Machine child = container.createContainer(childName, container.getHostId(), pCpus); |
| /* Look for child's childs */ |
| createContainersHierarchyForContainer(ssq, child, containersQuarks); |
| } |
| } |
| } |
| |
| /** |
| * Update the view when a thread is selected |
| * |
| * @param signal |
| * The thread selected signal |
| */ |
| @TmfSignalHandler |
| public void threadSelected(TmfThreadSelectedSignal signal) { |
| int threadId = signal.getThreadId(); |
| VirtualResourcePresentationProvider presentationProvider = getFusedVMViewPresentationProvider(); |
| presentationProvider.setSelectedThread(new HostThread(Objects.requireNonNull(signal.getHostId()), threadId)); |
| |
| updateButtonsSelection(); |
| updateToolTipTexts(); |
| } |
| } |