<module
analysis_module="org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.module.VirtualMachineCpuAnalysis"
automatic="false"
- id="lttng2.analysis.vm.core.VirtualMachineAnalysisModule"
+ id="org.eclipse.tracecompass.lttng2.analysis.vm.core.VirtualMachineAnalysisModule"
name="Virtual Machine Analysis">
<tracetype
applies="true"
*/
public class VirtualMachineCpuAnalysis extends TmfStateSystemAnalysisModule {
+ /** The ID of this analysis module */
+ public static final String ID = "org.eclipse.tracecompass.lttng2.analysis.vm.core.VirtualMachineAnalysisModule"; //$NON-NLS-1$
+
// TODO: Update with event layout when requirements are back */
static final Set<String> REQUIRED_EVENTS = NonNullUtils.checkNotNull(ImmutableSet.of(
// LttngStrings.SCHED_SWITCH
org.eclipse.tracecompass.common.core,
org.eclipse.tracecompass.tmf.core,
org.eclipse.tracecompass.tmf.ui,
+ org.eclipse.tracecompass.analysis.os.linux.core,
org.eclipse.tracecompass.analysis.os.linux.ui,
org.eclipse.tracecompass.tmf.ctf.core,
org.eclipse.tracecompass.lttng2.control.core,
Import-Package: com.google.common.collect
Export-Package: org.eclipse.tracecompass.internal.lttng2.kernel.ui;x-internal:=true,
org.eclipse.tracecompass.internal.lttng2.kernel.ui.criticalpath;x-internal:=true,
- org.eclipse.tracecompass.internal.lttng2.kernel.ui.views;x-friends:="org.eclipse.tracecompass.lttng2.kernel.ui.swtbot.tests"
+ org.eclipse.tracecompass.internal.lttng2.kernel.ui.views;x-friends:="org.eclipse.tracecompass.lttng2.kernel.ui.swtbot.tests",
+ org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview;x-internal:=true
tracetype.type.kernel = LTTng Kernel Trace
analysis.lttngkernel = LTTng Kernel Analysis
+
+vcpuview.name = Virtual CPU View
\ No newline at end of file
name="%kernel.perspective.name">
</perspective>
</extension>
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ allowMultiple="false"
+ category="org.eclipse.linuxtools.tmf.ui.views.category"
+ class="org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview.VirtualMachineView"
+ id="org.eclipse.tracecompass.lttng2.analysis.vm.ui.vmview"
+ name="%vcpuview.name"
+ restorable="true">
+ </view>
+ </extension>
<extension
point="org.eclipse.ui.navigator.navigatorContent">
<commonWizard
</enablement>
</commonWizard>
</extension>
+ <extension
+ point="org.eclipse.linuxtools.tmf.core.analysis">
+ <output
+ class="org.eclipse.tracecompass.tmf.ui.analysis.TmfAnalysisViewOutput"
+ id="org.eclipse.tracecompass.lttng2.analysis.vm.ui.vmview">
+ <analysisId
+ id="org.eclipse.tracecompass.lttng2.analysis.vm.core.VirtualMachineAnalysisModule">
+ </analysisId>
+ </output>
+ </extension>
<extension
point="org.eclipse.linuxtools.tmf.ui.tracetypeui">
<type
--- /dev/null
+/*******************************************************************************
+ * 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 v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview.messages"; //$NON-NLS-1$
+
+ public static @Nullable String VmView_threads;
+ public static @Nullable String VmView_stateTypeName;
+ public static @Nullable String VmView_multipleStates;
+ public static @Nullable String VmView_nextResourceActionNameText;
+ public static @Nullable String VmView_nextResourceActionToolTipText;
+ public static @Nullable String VmView_previousResourceActionNameText;
+ public static @Nullable String VmView_previousResourceActionToolTipText;
+ public static @Nullable String VmView_VCpu;
+ public static @Nullable String VmView_virtualMachine;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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 v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview;
+
+/**
+ * Provides some common elements for all virtual machine views
+ *
+ * @author Mohamad Gebai
+ */
+public final class VirtualMachineCommon {
+
+ /** Type of resources/entries */
+ public static enum Type {
+ /** Entries for VMs */
+ VM,
+ /** Entries for VCPUs */
+ VCPU,
+ /** Entries for Threads */
+ THREAD,
+ /** Null resources (filler rows, etc.) */
+ NULL
+ }
+}
--- /dev/null
+/*******************************************************************************
+ * 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 v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.StateValues;
+import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.VcpuStateValues;
+import org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview.VirtualMachineCommon.Type;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.StateItem;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.TimeGraphPresentationProvider;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
+
+/**
+ * Presentation provider for the Virtual Machine view, based on the generic TMF
+ * presentation provider.
+ *
+ * @author Mohamad Gebai
+ */
+public class VirtualMachinePresentationProvider extends TimeGraphPresentationProvider {
+
+ /*
+ * TODO: Some of it is copy-pasted from the control flow presentation
+ * provider because it actually is the same data as from the control flow
+ * view. Ideally, we should reuse what is there instead of rewriting it here
+ */
+ private enum State {
+ UNKNOWN(new RGB(100, 100, 100)),
+ IDLE(new RGB(200, 200, 200)),
+ USERMODE(new RGB(0, 200, 0)),
+ WAIT_VMM(new RGB(200, 0, 0)),
+ VCPU_PREEMPTED(new RGB(120, 40, 90)),
+ THREAD_UNKNOWN(new RGB(100, 100, 100)),
+ THREAD_WAIT_BLOCKED(new RGB(200, 200, 0)),
+ THREAD_WAIT_FOR_CPU(new RGB(200, 100, 0)),
+ THREAD_USERMODE(new RGB(0, 200, 0)),
+ THREAD_SYSCALL(new RGB(0, 0, 200)),
+ THREAD_INTERRUPTED(new RGB(200, 0, 100));
+
+ public final RGB rgb;
+
+ private State(RGB rgb) {
+ this.rgb = rgb;
+ }
+ }
+
+ /**
+ * Default constructor
+ */
+ public VirtualMachinePresentationProvider() {
+ super();
+ }
+
+ private static State[] getStateValues() {
+ return State.values();
+ }
+
+ private static State getStateForVcpu(int value) {
+ if ((value & VcpuStateValues.VCPU_PREEMPT) > 0) {
+ return State.VCPU_PREEMPTED;
+ } else if ((value & VcpuStateValues.VCPU_VMM) > 0) {
+ return State.WAIT_VMM;
+ } else if (value == 2) {
+ return State.USERMODE;
+ } else if (value == 1) {
+ return State.IDLE;
+ } else {
+ return State.UNKNOWN;
+ }
+ }
+
+ private static @Nullable State getStateForThread(int value) {
+ if (value == VcpuStateValues.VCPU_PREEMPT) {
+ return null;
+ }
+ switch (value) {
+ case StateValues.PROCESS_STATUS_RUN_USERMODE:
+ return State.THREAD_USERMODE;
+ case StateValues.PROCESS_STATUS_RUN_SYSCALL:
+ return State.THREAD_SYSCALL;
+ case StateValues.PROCESS_STATUS_WAIT_FOR_CPU:
+ return State.THREAD_WAIT_FOR_CPU;
+ case StateValues.PROCESS_STATUS_WAIT_BLOCKED:
+ return State.THREAD_WAIT_BLOCKED;
+ case StateValues.PROCESS_STATUS_INTERRUPTED:
+ return State.THREAD_INTERRUPTED;
+ case StateValues.PROCESS_STATUS_UNKNOWN:
+ case StateValues.PROCESS_STATUS_WAIT_UNKNOWN:
+ return State.THREAD_UNKNOWN;
+ default:
+ return null;
+ }
+ }
+
+ private static @Nullable State getEventState(TimeEvent event) {
+ if (event.hasValue()) {
+ VirtualMachineViewEntry entry = (VirtualMachineViewEntry) event.getEntry();
+ int value = event.getValue();
+
+ if (entry.getType() == Type.VCPU) {
+ return getStateForVcpu(value);
+ } else if (entry.getType() == Type.THREAD) {
+ return getStateForThread(value);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public int getStateTableIndex(@Nullable ITimeEvent event) {
+ if (event == null) {
+ return TRANSPARENT;
+ }
+ State state = getEventState((TimeEvent) event);
+ if (state != null) {
+ return state.ordinal();
+ }
+ if (event instanceof NullTimeEvent) {
+ return INVISIBLE;
+ }
+ return TRANSPARENT;
+ }
+
+ @Override
+ public StateItem[] getStateTable() {
+ State[] states = getStateValues();
+ StateItem[] stateTable = new StateItem[states.length];
+ for (int i = 0; i < stateTable.length; i++) {
+ State state = states[i];
+ stateTable[i] = new StateItem(state.rgb, state.toString());
+ }
+ return stateTable;
+ }
+
+ @Override
+ public @Nullable String getEventName(@Nullable ITimeEvent event) {
+ if (event == null) {
+ return null;
+ }
+ State state = getEventState((TimeEvent) event);
+ if (state != null) {
+ return state.toString();
+ }
+ if (event instanceof NullTimeEvent) {
+ return null;
+ }
+ return Messages.VmView_multipleStates;
+ }
+
+ @Override
+ public void postDrawEvent(@Nullable ITimeEvent event, @Nullable Rectangle bounds, @Nullable GC gc) {
+ if (bounds == null || gc == null) {
+ return;
+ }
+ boolean visible = bounds.width == 0 ? false : true;
+ if (!visible) {
+ return;
+ }
+ if (!(event instanceof TimeEvent)) {
+ return;
+ }
+ TimeEvent ev = (TimeEvent) event;
+ /*
+ * FIXME: There seems to be a bug when multiple events should be drawn
+ * under a alpha event. See FIXME comment in
+ * VirtualMachineView#getEventList
+ */
+ if (ev.hasValue()) {
+ VirtualMachineViewEntry entry = (VirtualMachineViewEntry) event.getEntry();
+
+ if (entry.getType() == Type.THREAD) {
+ int value = ev.getValue();
+ if ((value & VcpuStateValues.VCPU_PREEMPT) != 0) {
+ /*
+ * If the status was preempted at this time, draw an alpha
+ * over this state
+ */
+ Color alphaColor = Display.getDefault().getSystemColor(SWT.COLOR_RED);
+
+ int alpha = gc.getAlpha();
+ Color background = gc.getBackground();
+ // fill all rect area
+ gc.setBackground(alphaColor);
+ gc.setAlpha(70);
+ gc.fillRectangle(bounds);
+
+ gc.setBackground(background);
+ gc.setAlpha(alpha);
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * 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 v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.KernelAnalysisModule;
+import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.KernelThreadInformationProvider;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.VmAttributes;
+import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.module.VirtualMachineCpuAnalysis;
+import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.trace.VirtualMachineExperiment;
+import org.eclipse.tracecompass.internal.lttng2.kernel.ui.Activator;
+import org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview.VirtualMachineCommon.Type;
+import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
+import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
+import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
+import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperimentUtils;
+import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractTimeGraphView;
+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;
+
+import com.google.common.collect.Multimap;
+
+/**
+ * Main implementation for the Virtual Machine view
+ *
+ * @author Mohamad Gebai
+ */
+public class VirtualMachineView extends AbstractTimeGraphView {
+
+ /** View ID. */
+ public static final String ID = "org.eclipse.tracecompass.lttng2.analysis.vm.ui.vmview"; //$NON-NLS-1$
+
+ private static final String[] COLUMN_NAMES = new String[] {
+ Messages.VmView_stateTypeName
+ };
+
+ private static final String[] FILTER_COLUMN_NAMES = new String[] {
+ Messages.VmView_stateTypeName
+ };
+
+ // Timeout between updates in the build thread in ms
+ private static final long BUILD_UPDATE_TIMEOUT = 500;
+ private static final int[] DEFAULT_WEIGHT = { 1, 3 };
+
+ private Comparator<ITimeGraphEntry> fComparator = new Comparator<ITimeGraphEntry>() {
+ @Override
+ public int compare(@Nullable ITimeGraphEntry o1, @Nullable ITimeGraphEntry o2) {
+ if (!((o1 instanceof VirtualMachineViewEntry) && (o2 instanceof VirtualMachineViewEntry))) {
+ return 0;
+ }
+ VirtualMachineViewEntry entry1 = (VirtualMachineViewEntry) o1;
+ VirtualMachineViewEntry entry2 = (VirtualMachineViewEntry) o2;
+ int cmp = entry1.getType().compareTo(entry2.getType());
+ /* FIXME: Threads should be ordered by their thread IDs instead */
+ if (cmp == 0) {
+ cmp = entry1.getName().compareTo(entry2.getName());
+ }
+ return cmp;
+ }
+ };
+
+ // ------------------------------------------------------------------------
+ // Constructors
+ // ------------------------------------------------------------------------
+
+ /**
+ * Default constructor
+ */
+ public VirtualMachineView() {
+ super(ID, new VirtualMachinePresentationProvider());
+ setFilterColumns(FILTER_COLUMN_NAMES);
+ setTreeColumns(COLUMN_NAMES);
+ setTreeLabelProvider(new VmViewTreeLabelProvider());
+ setWeight(DEFAULT_WEIGHT);
+ setAutoExpandLevel(2);
+ }
+
+ @Override
+ protected @Nullable String getNextText() {
+ return Messages.VmView_nextResourceActionNameText;
+ }
+
+ @Override
+ protected @Nullable String getNextTooltip() {
+ return Messages.VmView_nextResourceActionToolTipText;
+ }
+
+ @Override
+ protected @Nullable String getPrevText() {
+ return Messages.VmView_previousResourceActionNameText;
+ }
+
+ @Override
+ protected @Nullable String getPrevTooltip() {
+ return Messages.VmView_previousResourceActionToolTipText;
+ }
+
+ private static class VmViewTreeLabelProvider extends TreeLabelProvider {
+
+ @Override
+ public String getColumnText(@Nullable Object element, int columnIndex) {
+ if (!(element instanceof VirtualMachineViewEntry)) {
+ return ""; //$NON-NLS-1$
+ }
+ VirtualMachineViewEntry entry = (VirtualMachineViewEntry) element;
+
+ if (COLUMN_NAMES[columnIndex].equals(Messages.VmView_stateTypeName)) {
+ String name = entry.getName();
+ return (name == null) ? "" : name; //$NON-NLS-1$
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ }
+
+ // ------------------------------------------------------------------------
+ // Internal
+ // ------------------------------------------------------------------------
+
+ @Override
+ protected void buildEventList(ITmfTrace trace, ITmfTrace parentTrace, IProgressMonitor monitor) {
+ setStartTime(Long.MAX_VALUE);
+ setEndTime(Long.MIN_VALUE);
+
+ if (monitor.isCanceled()) {
+ return;
+ }
+ if (!(parentTrace instanceof VirtualMachineExperiment)) {
+ return;
+ }
+ ITmfStateSystem ssq = TmfStateSystemAnalysisModule.getStateSystem(parentTrace, VirtualMachineCpuAnalysis.ID);
+ if (ssq == null) {
+ return;
+ }
+ VirtualMachineExperiment vmExperiment = (VirtualMachineExperiment) parentTrace;
+ long startTime = ssq.getStartTime();
+
+ ArrayList<VirtualMachineViewEntry> entryList = new ArrayList<>();
+ Map<String, VirtualMachineViewEntry> entryMap = new HashMap<>();
+
+ boolean complete = false;
+ VirtualMachineViewEntry groupEntry = null;
+
+ while (!complete) {
+ if (monitor.isCanceled()) {
+ return;
+ }
+ complete = ssq.waitUntilBuilt(BUILD_UPDATE_TIMEOUT);
+ if (ssq.isCancelled()) {
+ return;
+ }
+
+ long endTime = ssq.getCurrentEndTime() + 1;
+ if (groupEntry == null) {
+ groupEntry = new VirtualMachineViewEntry.VmEntryBuilder(vmExperiment.getName(), startTime, endTime, vmExperiment).build();
+ /*
+ * There is only one root entry for this view, so we can put it
+ * right away
+ */
+ entryList.add(groupEntry);
+ putEntryList(parentTrace, new ArrayList<TimeGraphEntry>(entryList));
+ } else {
+ groupEntry.updateEndTime(endTime);
+ }
+
+ setStartTime(Math.min(getStartTime(), startTime));
+ setEndTime(Math.max(getEndTime(), endTime));
+
+ /*
+ * Create the entries for the VMs in this experiment and their
+ * respective threads
+ */
+ buildEntries(ssq, startTime, endTime, groupEntry, entryMap, vmExperiment);
+
+ if (parentTrace.equals(getTrace())) {
+ refresh();
+ }
+
+ for (VirtualMachineViewEntry entry : entryList) {
+ if (monitor.isCanceled()) {
+ return;
+ }
+ buildEntryEventList(entry, ssq, startTime, endTime, monitor);
+ }
+ }
+ }
+
+ private void buildEntries(ITmfStateSystem ssq, long startTime, long endTime, VirtualMachineViewEntry groupEntry,
+ Map<@NonNull String, @NonNull VirtualMachineViewEntry> entryMap, VirtualMachineExperiment vmExperiment) {
+ try {
+ List<Integer> vmQuarks = ssq.getQuarks(VmAttributes.VIRTUAL_MACHINES, "*"); //$NON-NLS-1$
+ /* For each virtual machine in the analysis */
+ for (Integer vmQuark : vmQuarks) {
+
+ /* Display an entry for the virtual machine */
+ String vmHostId = NonNullUtils.checkNotNull(ssq.getAttributeName(vmQuark));
+ ITmfStateInterval vmNameInterval = StateSystemUtils.queryUntilNonNullValue(ssq, vmQuark, startTime, endTime);
+ String vmName = vmHostId;
+ if (vmNameInterval != null) {
+ vmName = vmNameInterval.getStateValue().unboxStr();
+ }
+
+ VirtualMachineViewEntry vmEntry = entryMap.get(vmHostId);
+ if (vmEntry == null) {
+ vmEntry = new VirtualMachineViewEntry.VmEntryBuilder(vmName, startTime, endTime, vmExperiment).setId(vmHostId).setVmName(vmName).setNumericId(vmQuark).setType(Type.VM).build();
+ vmEntry.sortChildren(fComparator);
+
+ groupEntry.addChild(vmEntry);
+ entryMap.put(vmHostId, vmEntry);
+ } else {
+ vmEntry.updateEndTime(endTime);
+ }
+
+ /* Display an entry for each of its CPUs */
+ for (Integer vCpuQuark : ssq.getSubAttributes(vmQuark, false)) {
+ String vcpuId = ssq.getAttributeName(vCpuQuark);
+ VirtualMachineViewEntry vcpuEntry = entryMap.get(vmHostId + vcpuId);
+ if (vcpuEntry == null) {
+ vcpuEntry = new VirtualMachineViewEntry.VmEntryBuilder(vcpuId, startTime, endTime, vmExperiment).setId(vcpuId).setVmName(vmName).setNumericId(vCpuQuark).setType(Type.VCPU).build();
+
+ vmEntry.addChild(vcpuEntry);
+ entryMap.put(vmHostId + vcpuId, vcpuEntry);
+ } else {
+ vcpuEntry.updateEndTime(endTime);
+ }
+
+ }
+
+ /*
+ * Get the LTTng Kernel analysis module from the corresponding
+ * trace
+ */
+ KernelAnalysisModule kernelModule = TmfExperimentUtils.getAnalysisModuleOfClassForHost(vmExperiment, vmHostId, KernelAnalysisModule.class);
+ if (kernelModule == null) {
+ continue;
+ }
+
+ VirtualMachineViewEntry threadEntry = entryMap.get(vmHostId + NonNullUtils.nullToEmptyString(Messages.VmView_threads));
+ if (threadEntry == null) {
+ threadEntry = new VirtualMachineViewEntry.VmEntryBuilder(NonNullUtils.nullToEmptyString(Messages.VmView_threads), startTime, endTime, vmExperiment).build();
+ entryMap.put(vmHostId + NonNullUtils.nullToEmptyString(Messages.VmView_threads), threadEntry);
+ vmEntry.addChild(threadEntry);
+ } else {
+ threadEntry.updateEndTime(endTime);
+ }
+
+ /*
+ * Display an entry for each thread.
+ *
+ * For each interval that is in a running status, intersect with
+ * the status of the virtual CPU it is currently running on
+ */
+ Collection<Integer> threadIds = KernelThreadInformationProvider.getThreadIds(kernelModule);
+ for (Integer threadId : threadIds) {
+ if (threadId == -1) {
+ continue;
+ }
+ VirtualMachineViewEntry oneThreadEntry = entryMap.get(vmHostId + ':' + threadId);
+ if (oneThreadEntry != null) {
+ oneThreadEntry.updateEndTime(endTime);
+ continue;
+ }
+ /*
+ * FIXME: Only add threads that are active during the trace
+ */
+ String threadName = KernelThreadInformationProvider.getExecutableName(kernelModule, threadId);
+ String tidString = NonNullUtils.checkNotNull(threadId.toString());
+ threadName = (threadName != null) ? tidString + ':' + ' ' + threadName : tidString;
+ oneThreadEntry = new VirtualMachineViewEntry.VmEntryBuilder(threadName, startTime, endTime, vmExperiment).setId(threadName).setVmName(vmName).setNumericId(threadId).setType(Type.THREAD).build();
+
+ threadEntry.addChild(oneThreadEntry);
+ entryMap.put(vmHostId + ':' + threadId, oneThreadEntry);
+ }
+
+ }
+ } catch (AttributeNotFoundException e) {
+ /*
+ * The attribute may not exist yet if the state system is being
+ * built
+ */
+ } catch (TimeRangeException | StateValueTypeException e) {
+ Activator.getDefault().logError("VirtualMachineView: error building event list", e); //$NON-NLS-1$
+ }
+ }
+
+ private void buildEntryEventList(TimeGraphEntry entry, ITmfStateSystem ssq, long start, long end, IProgressMonitor monitor) {
+ if (start < entry.getEndTime() && end > entry.getStartTime()) {
+
+ long startTime = Math.max(start, entry.getStartTime());
+ long endTime = Math.min(end + 1, entry.getEndTime());
+ long resolution = Math.max(1, (end - ssq.getStartTime()) / getDisplayWidth());
+ List<ITimeEvent> eventList = getEventList(entry, startTime, endTime, resolution, monitor);
+ entry.setEventList(eventList);
+ redraw();
+ for (ITimeGraphEntry child : entry.getChildren()) {
+ if (!(child instanceof TimeGraphEntry)) {
+ continue;
+ }
+ if (monitor.isCanceled()) {
+ return;
+ }
+ buildEntryEventList((TimeGraphEntry) child, ssq, start, end, monitor);
+ }
+ }
+ }
+
+ @Override
+ protected @Nullable List<ITimeEvent> getEventList(TimeGraphEntry entry,
+ long startTime, long endTime, long resolution,
+ IProgressMonitor monitor) {
+ if (!(entry instanceof VirtualMachineViewEntry)) {
+ return null;
+ }
+ if (monitor.isCanceled()) {
+ return null;
+ }
+
+ VirtualMachineViewEntry vmEntry = (VirtualMachineViewEntry) entry;
+
+ switch (vmEntry.getType()) {
+ case THREAD: {
+ return getThreadEventList(entry, vmEntry, monitor);
+ }
+ case VCPU: {
+ return getVcpuEventList(entry, vmEntry, startTime, endTime, resolution, monitor);
+ }
+ case VM: {
+ VirtualMachineExperiment experiment = vmEntry.getExperiment();
+ VirtualMachineCpuAnalysis vmAnalysis = null;
+ for (VirtualMachineCpuAnalysis module : TmfTraceUtils.getAnalysisModulesOfClass(experiment, VirtualMachineCpuAnalysis.class)) {
+ vmAnalysis = module;
+ break;
+ }
+ if (vmAnalysis == null) {
+ break;
+ }
+ Multimap<Integer, ITmfStateInterval> updatedThreadIntervals = vmAnalysis.getUpdatedThreadIntervals(vmEntry.getNumericId(), startTime, endTime, resolution, monitor);
+ vmEntry.setThreadIntervals(updatedThreadIntervals);
+ }
+ break;
+ case NULL:
+ /* These entry types are not used in this view */
+ break;
+ default:
+ break;
+ }
+
+ return null;
+ }
+
+ private static @Nullable List<@NonNull ITimeEvent> getVcpuEventList(TimeGraphEntry entry, VirtualMachineViewEntry vmEntry, long startTime, long endTime, long resolution, IProgressMonitor monitor) {
+ List<ITimeEvent> eventList = null;
+ try {
+ int quark = vmEntry.getNumericId();
+
+ ITmfStateSystem ssq = TmfStateSystemAnalysisModule.getStateSystem(vmEntry.getExperiment(), VirtualMachineCpuAnalysis.ID);
+ if (ssq == null) {
+ return Collections.EMPTY_LIST;
+ }
+ final long realStart = Math.max(startTime, ssq.getStartTime());
+ final long realEnd = Math.min(endTime, ssq.getCurrentEndTime() + 1);
+ if (realEnd <= realStart) {
+ return Collections.EMPTY_LIST;
+ }
+ quark = ssq.getQuarkRelative(quark, VmAttributes.STATUS);
+ List<ITmfStateInterval> statusIntervals = StateSystemUtils.queryHistoryRange(ssq, quark, realStart, realEnd - 1, resolution, monitor);
+ eventList = new ArrayList<>(statusIntervals.size());
+ long lastEndTime = -1;
+ for (ITmfStateInterval statusInterval : statusIntervals) {
+ if (monitor.isCanceled()) {
+ return null;
+ }
+
+ long time = statusInterval.getStartTime();
+ long duration = statusInterval.getEndTime() - time + 1;
+ if (!statusInterval.getStateValue().isNull()) {
+ int status = statusInterval.getStateValue().unboxInt();
+ if (lastEndTime != time && lastEndTime != -1) {
+ eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime));
+ }
+ eventList.add(new TimeEvent(entry, time, duration, status));
+ } else if (lastEndTime == -1 || time + duration >= endTime) {
+ // add null event if it intersects the start or end time
+ eventList.add(new NullTimeEvent(entry, time, duration));
+ }
+ lastEndTime = time + duration;
+ }
+ } catch (AttributeNotFoundException | TimeRangeException | StateValueTypeException e) {
+ Activator.getDefault().logError("Error getting event list", e); //$NON-NLS-1$
+ } catch (StateSystemDisposedException e) {
+ /* Ignored */
+ }
+ return eventList;
+ }
+
+ private static @Nullable List<@NonNull ITimeEvent> getThreadEventList(TimeGraphEntry entry, VirtualMachineViewEntry vmEntry, IProgressMonitor monitor) {
+ List<ITimeEvent> eventList = null;
+ /*
+ * The parent VM entry will contain the thread intervals for all thread.
+ * Just take the list from there
+ */
+ Collection<ITmfStateInterval> threadIntervals = null;
+ ITimeGraphEntry parent = vmEntry.getParent();
+ while (threadIntervals == null && parent != null) {
+ if (parent instanceof VirtualMachineViewEntry) {
+ threadIntervals = ((VirtualMachineViewEntry) parent).getThreadIntervals(vmEntry.getNumericId());
+ }
+ if (parent instanceof TimeGraphEntry) {
+ parent = ((TimeGraphEntry) parent).getParent();
+ }
+ }
+ if (threadIntervals != null) {
+ eventList = new ArrayList<>(threadIntervals.size());
+ long lastEndTime = -1;
+ for (ITmfStateInterval interval : threadIntervals) {
+ if (monitor.isCanceled()) {
+ return null;
+ }
+ long time = interval.getStartTime();
+ long duration = interval.getEndTime() - time + 1;
+ /*
+ * FIXME: I think the key for the alpha bug when alpha overlaps
+ * multiple events is around here
+ *
+ * Hint by Patrick: "The problem is that the thread intervals
+ * are sorted by start time, and drawn in that order.
+ *
+ * Given the intervals: Blue [0,10] Alpha [5,15] Red [10,20]
+ *
+ * Blue is drawn, then Alpha makes DarkBlue from [5,10] and
+ * DarkBackground from [10,15], then Red is drawn over [10,20],
+ * overwriting the DarkBackground. There is no DarkRed.
+ *
+ * For this to work you would have to draw all real states
+ * first, then all alpha states second.
+ *
+ * I think this would also have the side-effect that the find
+ * item used for tool tips would always find the real event and
+ * never the alpha event. This might be what we want. Right now
+ * the tool tip has State: (multiple).
+ *
+ * But using the Next Event button, we would skip to the next
+ * real event and not at the preemption event. Maybe not what we
+ * want.
+ *
+ * Maybe what we need is separate thread interval events:
+ *
+ * Blue [0,5] Preempted Blue [5,10] Preempted Red [10,15] Red
+ * [15,20]...
+ *
+ * The preempted events would have the real state value, but
+ * with a flag for alpha to be used in the postDrawEvent."
+ */
+ if (!interval.getStateValue().isNull()) {
+ int status = interval.getStateValue().unboxInt();
+ if (lastEndTime < time && lastEndTime != -1) {
+ /*
+ * Add a time event to fill the blanks between intervals
+ */
+ eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime));
+ }
+ eventList.add(new TimeEvent(entry, time, duration, status));
+ } else if (lastEndTime == -1) {
+ eventList.add(new NullTimeEvent(entry, time, duration));
+ }
+ lastEndTime = time + duration;
+ }
+ }
+ return eventList;
+ }
+
+ @Override
+ protected Iterable<ITmfTrace> getTracesToBuild(@Nullable ITmfTrace trace) {
+ if (trace == null) {
+ return NonNullUtils.checkNotNull(Collections.EMPTY_SET);
+ }
+ return NonNullUtils.checkNotNull(Collections.singleton(trace));
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * 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 v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview;
+
+import java.util.Collection;
+import java.util.Comparator;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tracecompass.common.core.NonNullUtils;
+import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.trace.VirtualMachineExperiment;
+import org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview.VirtualMachineCommon.Type;
+import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
+import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
+
+import com.google.common.collect.Multimap;
+
+/**
+ * An entry, or row, in the Virtual CPU view
+ *
+ * @author Mohamad Gebai
+ */
+public class VirtualMachineViewEntry extends TimeGraphEntry {
+
+ private static final Comparator<ITimeGraphEntry> COMPARATOR = new Comparator<ITimeGraphEntry>() {
+
+ @Override
+ public int compare(@Nullable ITimeGraphEntry o1, @Nullable ITimeGraphEntry o2) {
+
+ int result = 0;
+
+ if ((o1 instanceof VirtualMachineViewEntry) && (o2 instanceof VirtualMachineViewEntry)) {
+ VirtualMachineViewEntry entry1 = (VirtualMachineViewEntry) o1;
+ VirtualMachineViewEntry entry2 = (VirtualMachineViewEntry) o2;
+ result = entry1.getType().compareTo(entry2.getType());
+ if (result == 0) {
+ result = entry1.getId().compareTo(entry2.getId());
+ }
+ }
+ return result;
+ }
+ };
+
+ private final String fId;
+ private final @Nullable String fVmName;
+ private final ITmfTrace fTrace;
+ private final VirtualMachineExperiment fExperiment;
+ private final Type fType;
+ private final Integer fNid;
+ private @Nullable Multimap<Integer, ITmfStateInterval> fThreadIntervals = null;
+
+ /**
+ * Private constructor using a builder to build an entry
+ *
+ * @param builder
+ * The builder from which to build this entry
+ */
+ private VirtualMachineViewEntry(VmEntryBuilder builder) {
+ super(builder.fbEntryName, builder.fbStartTime, builder.fbEndTime);
+ fId = builder.fbId;
+ fExperiment = builder.fbExperiment;
+ /* If trace is not set, initialize to experiment */
+ ITmfTrace trace = builder.fbTrace;
+ if (trace == null) {
+ trace = fExperiment;
+ }
+ fTrace = trace;
+ fType = builder.fbType;
+ Integer nid = builder.fbNid;
+ if (nid == null) {
+ nid = -1;
+ }
+ fNid = nid;
+ fVmName = builder.fbVmName;
+ this.sortChildren(COMPARATOR);
+ }
+
+ /**
+ * Builder class that allows to build an entry by setting the parameters
+ * independently, instead of using directly the constructors with many
+ * parameters.
+ *
+ * @author Geneviève Bastien
+ */
+ public static class VmEntryBuilder {
+
+ private final long fbStartTime;
+ private final long fbEndTime;
+ private final VirtualMachineExperiment fbExperiment;
+
+ private String fbEntryName;
+ private String fbId;
+ private Type fbType;
+ private @Nullable String fbVmName;
+ private @Nullable ITmfTrace fbTrace;
+ private @Nullable Integer fbNid;
+
+ /**
+ * Virtual Machine Entry builder constructor.
+ *
+ * @param name
+ * The name of this entry. It is also the default ID of this
+ * entry. So the ID does not need to be set if it is the same
+ * as the name.
+ * @param startTime
+ * The start time of the entry
+ * @param endTime
+ * The end time of the entry
+ * @param experiment
+ * The experiment this entry applies to
+ */
+ public VmEntryBuilder(String name, long startTime, long endTime, VirtualMachineExperiment experiment) {
+ fbEntryName = name;
+ fbStartTime = startTime;
+ fbEndTime = endTime;
+ fbExperiment = experiment;
+ fbId = name;
+ fbType = Type.NULL;
+ }
+
+ /**
+ * Sets the ID of this entry
+ *
+ * @param id
+ * The ID of the virtual machine entry
+ * @return The builder with updated fields
+ */
+ public VmEntryBuilder setId(String id) {
+ fbId = id;
+ return this;
+ }
+
+ /**
+ * Sets the virtual machine name of this entry
+ *
+ * @param vmName
+ * The virtual machine name of the virtual machine entry
+ * @return The builder with updated fields
+ */
+ public VmEntryBuilder setVmName(String vmName) {
+ fbVmName = vmName;
+ return this;
+ }
+
+ /**
+ * Sets the trace this entry applies to
+ *
+ * @param trace
+ * The trace this entry is for
+ * @return The builder with updated fields
+ */
+ public VmEntryBuilder setTrace(ITmfTrace trace) {
+ fbTrace = trace;
+ return this;
+ }
+
+ /**
+ * Sets the type of this entry
+ *
+ * @param type
+ * The type of the virtual machine entry
+ * @return The builder with updated fields
+ */
+ public VmEntryBuilder setType(Type type) {
+ fbType = type;
+ return this;
+ }
+
+ /**
+ * Sets the numeric ID of this entry. For VM or VCPU types, it is the
+ * quark of the object represented by this entry, for THREAD types, it
+ * is the thread ID of the corresponding thread.
+ *
+ * @param nid
+ * The numeric ID of the virtual machine entry
+ * @return The builder with updated fields
+ */
+ public VmEntryBuilder setNumericId(Integer nid) {
+ fbNid = nid;
+ return this;
+ }
+
+ /**
+ * Creates a new instance of {@link VirtualMachineViewEntry} with the
+ * fields corresponding to those set in the builder.
+ *
+ * @return A new {@link VirtualMachineViewEntry} object
+ */
+ public VirtualMachineViewEntry build() {
+ switch(fbType) {
+ case VCPU:
+ fbEntryName = Messages.VmView_VCpu + ' ' + fbEntryName;
+ break;
+ case VM:
+ fbEntryName = NonNullUtils.nullToEmptyString(NLS.bind(Messages.VmView_virtualMachine, fbEntryName));
+ break;
+ case NULL:
+ case THREAD:
+ default:
+ break;
+
+ }
+ return new VirtualMachineViewEntry(this);
+ }
+
+ }
+
+ /**
+ * Get the entry's id
+ *
+ * @return the entry's id
+ */
+ public String getId() {
+ return fId;
+ }
+
+ /**
+ * Get the name of the virtual machine this entry belongs to
+ *
+ * @return The name of the virtual machine
+ */
+ public @Nullable String getVmName() {
+ return fVmName;
+ }
+
+ /**
+ * Get the entry's kernel trace
+ *
+ * @return the entry's kernel trace
+ */
+ public ITmfTrace getTrace() {
+ return fTrace;
+ }
+
+ /**
+ * Get the entry's kernel trace
+ *
+ * @return the entry's kernel trace
+ */
+ public VirtualMachineExperiment getExperiment() {
+ return fExperiment;
+ }
+
+ /**
+ * Get the entry Type of this entry. Uses the virtual machine enum
+ * {@link Type}
+ *
+ * @return The entry type
+ */
+ public Type getType() {
+ return fType;
+ }
+
+ /**
+ * Retrieve the numeric ID that represents this entry.
+ *
+ * @return The numeric ID matching the entry
+ */
+ public Integer getNumericId() {
+ return fNid;
+ }
+
+ @Override
+ public boolean hasTimeEvents() {
+ if (fType == Type.NULL) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get the state intervals for a given thread ID
+ *
+ * @param threadId
+ * The thread ID for which to get the intervals
+ * @return A collection of intervals for this thread, or {@code null} if no
+ * intervals are available for this thread
+ */
+ public @Nullable Collection<ITmfStateInterval> getThreadIntervals(Integer threadId) {
+ final Multimap<Integer, ITmfStateInterval> threadIntervals = fThreadIntervals;
+ if (threadIntervals == null) {
+ return null;
+ }
+ return threadIntervals.get(threadId);
+
+ }
+
+ /**
+ * Set the intervals for the threads of the corresponding virtual machine.
+ * This should be called only if the type of this entry is {@link Type#VM}.
+ *
+ * @param threadIntervals
+ * The map of intervals for each thread ID
+ */
+ public void setThreadIntervals(Multimap<Integer, ITmfStateInterval> threadIntervals) {
+ fThreadIntervals = threadIntervals;
+ }
+
+}
--- /dev/null
+###############################################################################
+# 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 v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+VmView_stateTypeName=Resource
+VmView_threads=Threads
+VmView_multipleStates=(multiple)
+VmView_nextResourceActionNameText=Next Resource
+VmView_nextResourceActionToolTipText=Select Next Resource
+VmView_previousResourceActionNameText=Previous Resource
+VmView_previousResourceActionToolTipText=Select Previous Resource
+VmView_VCpu=VCPU
+VmView_virtualMachine=Guest: {0}
--- /dev/null
+/*******************************************************************************
+ * 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 v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview;
\ No newline at end of file