1 /*******************************************************************************
2 * Copyright (c) 2016 École Polytechnique de Montréal
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.ui
.views
.vm
.vcpuview
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Collection
;
14 import java
.util
.Collections
;
15 import java
.util
.Comparator
;
16 import java
.util
.HashMap
;
17 import java
.util
.List
;
20 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
21 import org
.eclipse
.jdt
.annotation
.NonNull
;
22 import org
.eclipse
.jdt
.annotation
.Nullable
;
23 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernelanalysis
.KernelAnalysisModule
;
24 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernelanalysis
.KernelThreadInformationProvider
;
25 import org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
;
26 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.VmAttributes
;
27 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.module
.VirtualMachineCpuAnalysis
;
28 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.trace
.VirtualMachineExperiment
;
29 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.ui
.Activator
;
30 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.ui
.views
.vm
.vcpuview
.VirtualMachineCommon
.Type
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.StateSystemUtils
;
33 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
34 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
36 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
37 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
38 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.TmfStateSystemAnalysisModule
;
39 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
40 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperimentUtils
;
42 import org
.eclipse
.tracecompass
.tmf
.ui
.views
.timegraph
.AbstractTimeGraphView
;
43 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeEvent
;
44 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.ITimeGraphEntry
;
45 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.NullTimeEvent
;
46 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.TimeEvent
;
47 import org
.eclipse
.tracecompass
.tmf
.ui
.widgets
.timegraph
.model
.TimeGraphEntry
;
49 import com
.google
.common
.collect
.Multimap
;
52 * Main implementation for the Virtual Machine view
54 * @author Mohamad Gebai
56 public class VirtualMachineView
extends AbstractTimeGraphView
{
59 public static final String ID
= "org.eclipse.tracecompass.lttng2.analysis.vm.ui.vmview"; //$NON-NLS-1$
61 private static final String
[] COLUMN_NAMES
= new String
[] {
62 Messages
.VmView_stateTypeName
65 private static final String
[] FILTER_COLUMN_NAMES
= new String
[] {
66 Messages
.VmView_stateTypeName
69 // Timeout between updates in the build thread in ms
70 private static final long BUILD_UPDATE_TIMEOUT
= 500;
71 private static final int[] DEFAULT_WEIGHT
= { 1, 3 };
73 private Comparator
<ITimeGraphEntry
> fComparator
= new Comparator
<ITimeGraphEntry
>() {
75 public int compare(@Nullable ITimeGraphEntry o1
, @Nullable ITimeGraphEntry o2
) {
76 if (!((o1
instanceof VirtualMachineViewEntry
) && (o2
instanceof VirtualMachineViewEntry
))) {
79 VirtualMachineViewEntry entry1
= (VirtualMachineViewEntry
) o1
;
80 VirtualMachineViewEntry entry2
= (VirtualMachineViewEntry
) o2
;
81 int cmp
= entry1
.getType().compareTo(entry2
.getType());
82 /* FIXME: Threads should be ordered by their thread IDs instead */
84 cmp
= entry1
.getName().compareTo(entry2
.getName());
90 // ------------------------------------------------------------------------
92 // ------------------------------------------------------------------------
97 public VirtualMachineView() {
98 super(ID
, new VirtualMachinePresentationProvider());
99 setFilterColumns(FILTER_COLUMN_NAMES
);
100 setTreeColumns(COLUMN_NAMES
);
101 setTreeLabelProvider(new VmViewTreeLabelProvider());
102 setWeight(DEFAULT_WEIGHT
);
103 setAutoExpandLevel(2);
107 protected @Nullable String
getNextText() {
108 return Messages
.VmView_nextResourceActionNameText
;
112 protected @Nullable String
getNextTooltip() {
113 return Messages
.VmView_nextResourceActionToolTipText
;
117 protected @Nullable String
getPrevText() {
118 return Messages
.VmView_previousResourceActionNameText
;
122 protected @Nullable String
getPrevTooltip() {
123 return Messages
.VmView_previousResourceActionToolTipText
;
126 private static class VmViewTreeLabelProvider
extends TreeLabelProvider
{
129 public String
getColumnText(@Nullable Object element
, int columnIndex
) {
130 if (!(element
instanceof VirtualMachineViewEntry
)) {
131 return ""; //$NON-NLS-1$
133 VirtualMachineViewEntry entry
= (VirtualMachineViewEntry
) element
;
135 if (COLUMN_NAMES
[columnIndex
].equals(Messages
.VmView_stateTypeName
)) {
136 String name
= entry
.getName();
137 return (name
== null) ?
"" : name
; //$NON-NLS-1$
139 return ""; //$NON-NLS-1$
144 // ------------------------------------------------------------------------
146 // ------------------------------------------------------------------------
149 protected void buildEventList(ITmfTrace trace
, ITmfTrace parentTrace
, IProgressMonitor monitor
) {
150 setStartTime(Long
.MAX_VALUE
);
151 setEndTime(Long
.MIN_VALUE
);
153 if (!(parentTrace
instanceof VirtualMachineExperiment
)) {
156 ITmfStateSystem ssq
= TmfStateSystemAnalysisModule
.getStateSystem(parentTrace
, VirtualMachineCpuAnalysis
.ID
);
160 VirtualMachineExperiment vmExperiment
= (VirtualMachineExperiment
) parentTrace
;
161 long startTime
= ssq
.getStartTime();
163 ArrayList
<VirtualMachineViewEntry
> entryList
= new ArrayList
<>();
164 Map
<String
, VirtualMachineViewEntry
> entryMap
= new HashMap
<>();
166 boolean complete
= false;
167 VirtualMachineViewEntry groupEntry
= new VirtualMachineViewEntry
.VmEntryBuilder(vmExperiment
.getName(), startTime
, startTime
, vmExperiment
).build();
168 entryList
.add(groupEntry
);
169 putEntryList(parentTrace
, new ArrayList
<TimeGraphEntry
>(entryList
));
172 if (monitor
.isCanceled()) {
175 complete
= ssq
.waitUntilBuilt(BUILD_UPDATE_TIMEOUT
);
176 if (ssq
.isCancelled()) {
180 long endTime
= ssq
.getCurrentEndTime() + 1;
181 groupEntry
.updateEndTime(endTime
);
183 setStartTime(Math
.min(getStartTime(), startTime
));
184 setEndTime(Math
.max(getEndTime(), endTime
));
187 * Create the entries for the VMs in this experiment and their
190 buildEntries(ssq
, startTime
, endTime
, groupEntry
, entryMap
, vmExperiment
);
192 if (parentTrace
.equals(getTrace())) {
196 /* Build event lists for each entry */
197 buildEntryEventLists(entryList
, ssq
, startTime
, endTime
, monitor
);
201 private void buildEntries(ITmfStateSystem ssq
, long startTime
, long endTime
, VirtualMachineViewEntry groupEntry
,
202 Map
<@NonNull String
, @NonNull VirtualMachineViewEntry
> entryMap
, VirtualMachineExperiment vmExperiment
) {
204 List
<Integer
> vmQuarks
= ssq
.getQuarks(VmAttributes
.VIRTUAL_MACHINES
, "*"); //$NON-NLS-1$
205 /* For each virtual machine in the analysis */
206 for (Integer vmQuark
: vmQuarks
) {
208 /* Display an entry for the virtual machine */
209 String vmHostId
= NonNullUtils
.checkNotNull(ssq
.getAttributeName(vmQuark
));
210 ITmfStateInterval vmNameInterval
= StateSystemUtils
.queryUntilNonNullValue(ssq
, vmQuark
, startTime
, endTime
);
211 String vmName
= vmHostId
;
212 if (vmNameInterval
!= null) {
213 vmName
= vmNameInterval
.getStateValue().unboxStr();
216 VirtualMachineViewEntry vmEntry
= entryMap
.get(vmHostId
);
217 if (vmEntry
== null) {
218 vmEntry
= new VirtualMachineViewEntry
.VmEntryBuilder(vmName
, startTime
, endTime
, vmExperiment
).setId(vmHostId
).setVmName(vmName
).setNumericId(vmQuark
).setType(Type
.VM
).build();
219 vmEntry
.sortChildren(fComparator
);
221 groupEntry
.addChild(vmEntry
);
222 entryMap
.put(vmHostId
, vmEntry
);
224 vmEntry
.updateEndTime(endTime
);
227 /* Display an entry for each of its CPUs */
228 for (Integer vCpuQuark
: ssq
.getSubAttributes(vmQuark
, false)) {
229 String vcpuId
= ssq
.getAttributeName(vCpuQuark
);
230 VirtualMachineViewEntry vcpuEntry
= entryMap
.get(vmHostId
+ vcpuId
);
231 if (vcpuEntry
== null) {
232 vcpuEntry
= new VirtualMachineViewEntry
.VmEntryBuilder(vcpuId
, startTime
, endTime
, vmExperiment
).setId(vcpuId
).setVmName(vmName
).setNumericId(vCpuQuark
).setType(Type
.VCPU
).build();
234 vmEntry
.addChild(vcpuEntry
);
235 entryMap
.put(vmHostId
+ vcpuId
, vcpuEntry
);
237 vcpuEntry
.updateEndTime(endTime
);
242 /* Add the entries for the threads */
243 buildThreadEntries(vmEntry
, entryMap
, startTime
, endTime
);
245 } catch (AttributeNotFoundException e
) {
247 * The attribute may not exist yet if the state system is being
250 } catch (TimeRangeException
| StateValueTypeException e
) {
251 Activator
.getDefault().logError("VirtualMachineView: error building event list", e
); //$NON-NLS-1$
255 private static void buildThreadEntries(VirtualMachineViewEntry vmEntry
, Map
<String
, VirtualMachineViewEntry
> entryMap
, long startTime
, long endTime
) {
256 String vmHostId
= vmEntry
.getId();
257 VirtualMachineExperiment vmExperiment
= vmEntry
.getExperiment();
260 * Get the LTTng Kernel analysis module from the corresponding trace
262 KernelAnalysisModule kernelModule
= TmfExperimentUtils
.getAnalysisModuleOfClassForHost(vmExperiment
, vmHostId
, KernelAnalysisModule
.class);
263 if (kernelModule
== null) {
267 VirtualMachineViewEntry threadEntry
= entryMap
.get(vmHostId
+ NonNullUtils
.nullToEmptyString(Messages
.VmView_threads
));
268 if (threadEntry
== null) {
269 threadEntry
= new VirtualMachineViewEntry
.VmEntryBuilder(NonNullUtils
.nullToEmptyString(Messages
.VmView_threads
), startTime
, endTime
, vmExperiment
).build();
270 entryMap
.put(vmHostId
+ NonNullUtils
.nullToEmptyString(Messages
.VmView_threads
), threadEntry
);
271 vmEntry
.addChild(threadEntry
);
273 threadEntry
.updateEndTime(endTime
);
276 String vmName
= vmEntry
.getVmName();
277 if (vmName
== null) {
282 * Display an entry for each thread.
284 * For each interval that is in a running status, intersect with the
285 * status of the virtual CPU it is currently running on
287 Collection
<Integer
> threadIds
= KernelThreadInformationProvider
.getThreadIds(kernelModule
);
288 for (Integer threadId
: threadIds
) {
289 if (threadId
== -1) {
292 VirtualMachineViewEntry oneThreadEntry
= entryMap
.get(vmHostId
+ ':' + threadId
);
293 if (oneThreadEntry
!= null) {
294 oneThreadEntry
.updateEndTime(endTime
);
298 * FIXME: Only add threads that are active during the trace
300 String threadName
= KernelThreadInformationProvider
.getExecutableName(kernelModule
, threadId
);
301 String tidString
= NonNullUtils
.checkNotNull(threadId
.toString());
302 threadName
= (threadName
!= null) ? tidString
+ ':' + ' ' + threadName
: tidString
;
303 oneThreadEntry
= new VirtualMachineViewEntry
.VmEntryBuilder(threadName
, startTime
, endTime
, vmExperiment
).setId(threadName
).setVmName(vmName
).setNumericId(threadId
).setType(Type
.THREAD
).build();
305 threadEntry
.addChild(oneThreadEntry
);
306 entryMap
.put(vmHostId
+ ':' + threadId
, oneThreadEntry
);
311 private void buildEntryEventLists(ArrayList
<@NonNull VirtualMachineViewEntry
> entryList
, ITmfStateSystem ssq
, long startTime
, long endTime
, IProgressMonitor monitor
) {
312 for (VirtualMachineViewEntry entry
: entryList
) {
313 if (monitor
.isCanceled()) {
316 buildEntryEventList(entry
, ssq
, startTime
, endTime
, monitor
);
320 private void buildEntryEventList(TimeGraphEntry entry
, ITmfStateSystem ssq
, long start
, long end
, IProgressMonitor monitor
) {
321 if (start
< entry
.getEndTime() && end
> entry
.getStartTime()) {
323 long startTime
= Math
.max(start
, entry
.getStartTime());
324 long endTime
= Math
.min(end
+ 1, entry
.getEndTime());
325 long resolution
= Math
.max(1, (end
- ssq
.getStartTime()) / getDisplayWidth());
326 List
<ITimeEvent
> eventList
= getEventList(entry
, startTime
, endTime
, resolution
, monitor
);
327 entry
.setEventList(eventList
);
329 for (ITimeGraphEntry child
: entry
.getChildren()) {
330 if (!(child
instanceof TimeGraphEntry
)) {
333 if (monitor
.isCanceled()) {
336 buildEntryEventList((TimeGraphEntry
) child
, ssq
, start
, end
, monitor
);
342 protected @Nullable List
<ITimeEvent
> getEventList(TimeGraphEntry entry
,
343 long startTime
, long endTime
, long resolution
,
344 IProgressMonitor monitor
) {
345 if (!(entry
instanceof VirtualMachineViewEntry
)) {
348 if (monitor
.isCanceled()) {
352 VirtualMachineViewEntry vmEntry
= (VirtualMachineViewEntry
) entry
;
354 switch (vmEntry
.getType()) {
356 return getThreadEventList(vmEntry
, endTime
, monitor
);
359 return getVcpuEventList(vmEntry
, startTime
, endTime
, resolution
, monitor
);
362 VirtualMachineExperiment experiment
= vmEntry
.getExperiment();
363 VirtualMachineCpuAnalysis vmAnalysis
= TmfTraceUtils
.getAnalysisModuleOfClass(experiment
, VirtualMachineCpuAnalysis
.class, VirtualMachineCpuAnalysis
.ID
);
364 if (vmAnalysis
== null) {
367 Multimap
<Integer
, ITmfStateInterval
> updatedThreadIntervals
= vmAnalysis
.getUpdatedThreadIntervals(vmEntry
.getNumericId(), startTime
, endTime
, resolution
, monitor
);
368 vmEntry
.setThreadIntervals(updatedThreadIntervals
);
372 /* These entry types are not used in this view */
381 private static @Nullable List
<@NonNull ITimeEvent
> getVcpuEventList(VirtualMachineViewEntry vmEntry
, long startTime
, long endTime
, long resolution
, IProgressMonitor monitor
) {
382 List
<ITimeEvent
> eventList
= null;
384 int quark
= vmEntry
.getNumericId();
386 ITmfStateSystem ssq
= TmfStateSystemAnalysisModule
.getStateSystem(vmEntry
.getExperiment(), VirtualMachineCpuAnalysis
.ID
);
388 return Collections
.EMPTY_LIST
;
390 final long realStart
= Math
.max(startTime
, ssq
.getStartTime());
391 final long realEnd
= Math
.min(endTime
, ssq
.getCurrentEndTime() + 1);
392 if (realEnd
<= realStart
) {
393 return Collections
.EMPTY_LIST
;
395 quark
= ssq
.getQuarkRelative(quark
, VmAttributes
.STATUS
);
396 List
<ITmfStateInterval
> statusIntervals
= StateSystemUtils
.queryHistoryRange(ssq
, quark
, realStart
, realEnd
- 1, resolution
, monitor
);
398 eventList
= parseIntervalsForEvents(vmEntry
, statusIntervals
, endTime
, monitor
);
399 } catch (AttributeNotFoundException
| TimeRangeException
| StateValueTypeException e
) {
400 Activator
.getDefault().logError("Error getting event list", e
); //$NON-NLS-1$
401 } catch (StateSystemDisposedException e
) {
407 private static @Nullable List
<@NonNull ITimeEvent
> getThreadEventList(VirtualMachineViewEntry vmEntry
, long endTime
, IProgressMonitor monitor
) {
408 List
<ITimeEvent
> eventList
= null;
409 Collection
<ITmfStateInterval
> threadIntervals
= getThreadIntervalsForEntry(vmEntry
);
411 if (threadIntervals
!= null) {
413 * FIXME: I think the key for the alpha bug when alpha overlaps
414 * multiple events is around here
416 * Hint by Patrick: "The problem is that the thread intervals
417 * are sorted by start time, and drawn in that order.
419 * Given the intervals: Blue [0,10] Alpha [5,15] Red [10,20]
421 * Blue is drawn, then Alpha makes DarkBlue from [5,10] and
422 * DarkBackground from [10,15], then Red is drawn over [10,20],
423 * overwriting the DarkBackground. There is no DarkRed.
425 * For this to work you would have to draw all real states
426 * first, then all alpha states second.
428 * I think this would also have the side-effect that the find
429 * item used for tool tips would always find the real event and
430 * never the alpha event. This might be what we want. Right now
431 * the tool tip has State: (multiple).
433 * But using the Next Event button, we would skip to the next
434 * real event and not at the preemption event. Maybe not what we
437 * Maybe what we need is separate thread interval events:
439 * Blue [0,5] Preempted Blue [5,10] Preempted Red [10,15] Red
442 * The preempted events would have the real state value, but
443 * with a flag for alpha to be used in the postDrawEvent."
445 eventList
= parseIntervalsForEvents(vmEntry
, threadIntervals
, endTime
, monitor
);
450 private static @Nullable List
<@NonNull ITimeEvent
> parseIntervalsForEvents(VirtualMachineViewEntry vmEntry
, Collection
<@NonNull ITmfStateInterval
> intervals
, long endTime
, IProgressMonitor monitor
) {
451 List
<ITimeEvent
> eventList
= new ArrayList
<>(intervals
.size());
452 long lastEndTime
= -1;
453 for (ITmfStateInterval interval
: intervals
) {
454 if (monitor
.isCanceled()) {
458 long time
= interval
.getStartTime();
459 long duration
= interval
.getEndTime() - time
+ 1;
460 if (!interval
.getStateValue().isNull()) {
461 int status
= interval
.getStateValue().unboxInt();
462 if (lastEndTime
!= time
&& lastEndTime
!= -1) {
463 eventList
.add(new TimeEvent(vmEntry
, lastEndTime
, time
- lastEndTime
));
465 eventList
.add(new TimeEvent(vmEntry
, time
, duration
, status
));
466 } else if (lastEndTime
== -1 || time
+ duration
>= endTime
) {
467 /* add null event if it intersects the start or end time */
468 eventList
.add(new NullTimeEvent(vmEntry
, time
, duration
));
470 lastEndTime
= time
+ duration
;
476 private static @Nullable Collection
<@NonNull ITmfStateInterval
> getThreadIntervalsForEntry(VirtualMachineViewEntry vmEntry
) {
477 Collection
<ITmfStateInterval
> threadIntervals
= null;
480 * The parent VM entry will contain the thread intervals for all
481 * threads. Just take the list from there
483 ITimeGraphEntry parent
= vmEntry
.getParent();
484 while (threadIntervals
== null && parent
!= null) {
485 if (parent
instanceof VirtualMachineViewEntry
) {
486 threadIntervals
= ((VirtualMachineViewEntry
) parent
).getThreadIntervals(vmEntry
.getNumericId());
488 if (parent
instanceof TimeGraphEntry
) {
489 parent
= ((TimeGraphEntry
) parent
).getParent();
492 return threadIntervals
;
496 protected Iterable
<ITmfTrace
> getTracesToBuild(@Nullable ITmfTrace trace
) {
498 return NonNullUtils
.checkNotNull(Collections
.EMPTY_SET
);
500 return NonNullUtils
.checkNotNull(Collections
.singleton(trace
));