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
.kernel
.KernelAnalysisModule
;
24 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernel
.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
= VirtualMachineViewEntry
.getComparator();
75 // ------------------------------------------------------------------------
77 // ------------------------------------------------------------------------
82 public VirtualMachineView() {
83 super(ID
, new VirtualMachinePresentationProvider());
84 setFilterColumns(FILTER_COLUMN_NAMES
);
85 setTreeColumns(COLUMN_NAMES
);
86 setTreeLabelProvider(new VmViewTreeLabelProvider());
87 setWeight(DEFAULT_WEIGHT
);
88 setAutoExpandLevel(2);
92 protected @Nullable String
getNextText() {
93 return Messages
.VmView_nextResourceActionNameText
;
97 protected @Nullable String
getNextTooltip() {
98 return Messages
.VmView_nextResourceActionToolTipText
;
102 protected @Nullable String
getPrevText() {
103 return Messages
.VmView_previousResourceActionNameText
;
107 protected @Nullable String
getPrevTooltip() {
108 return Messages
.VmView_previousResourceActionToolTipText
;
111 private static class VmViewTreeLabelProvider
extends TreeLabelProvider
{
114 public String
getColumnText(@Nullable Object element
, int columnIndex
) {
115 if (!(element
instanceof VirtualMachineViewEntry
)) {
116 return ""; //$NON-NLS-1$
118 VirtualMachineViewEntry entry
= (VirtualMachineViewEntry
) element
;
120 if (COLUMN_NAMES
[columnIndex
].equals(Messages
.VmView_stateTypeName
)) {
121 String name
= entry
.getName();
122 return (name
== null) ?
"" : name
; //$NON-NLS-1$
124 return ""; //$NON-NLS-1$
129 // ------------------------------------------------------------------------
131 // ------------------------------------------------------------------------
134 protected void buildEventList(ITmfTrace trace
, ITmfTrace parentTrace
, IProgressMonitor monitor
) {
135 setStartTime(Long
.MAX_VALUE
);
136 setEndTime(Long
.MIN_VALUE
);
138 if (!(parentTrace
instanceof VirtualMachineExperiment
)) {
141 ITmfStateSystem ssq
= TmfStateSystemAnalysisModule
.getStateSystem(parentTrace
, VirtualMachineCpuAnalysis
.ID
);
145 VirtualMachineExperiment vmExperiment
= (VirtualMachineExperiment
) parentTrace
;
146 long startTime
= ssq
.getStartTime();
148 ArrayList
<VirtualMachineViewEntry
> entryList
= new ArrayList
<>();
149 Map
<String
, VirtualMachineViewEntry
> entryMap
= new HashMap
<>();
151 boolean complete
= false;
152 VirtualMachineViewEntry groupEntry
= new VirtualMachineViewEntry
.VmEntryBuilder(vmExperiment
.getName(), startTime
, startTime
, vmExperiment
).build();
153 entryList
.add(groupEntry
);
154 putEntryList(parentTrace
, new ArrayList
<TimeGraphEntry
>(entryList
));
157 if (monitor
.isCanceled()) {
160 complete
= ssq
.waitUntilBuilt(BUILD_UPDATE_TIMEOUT
);
161 if (ssq
.isCancelled()) {
165 long endTime
= ssq
.getCurrentEndTime() + 1;
166 groupEntry
.updateEndTime(endTime
);
168 setStartTime(Math
.min(getStartTime(), startTime
));
169 setEndTime(Math
.max(getEndTime(), endTime
));
172 * Create the entries for the VMs in this experiment and their
175 buildEntries(ssq
, startTime
, endTime
, groupEntry
, entryMap
, vmExperiment
);
177 if (parentTrace
.equals(getTrace())) {
181 /* Build event lists for each entry */
182 buildEntryEventLists(entryList
, ssq
, startTime
, endTime
, monitor
);
186 private void buildEntries(ITmfStateSystem ssq
, long startTime
, long endTime
, VirtualMachineViewEntry groupEntry
,
187 Map
<@NonNull String
, @NonNull VirtualMachineViewEntry
> entryMap
, VirtualMachineExperiment vmExperiment
) {
189 List
<Integer
> vmQuarks
= ssq
.getQuarks(VmAttributes
.VIRTUAL_MACHINES
, "*"); //$NON-NLS-1$
190 /* For each virtual machine in the analysis */
191 for (Integer vmQuark
: vmQuarks
) {
193 /* Display an entry for the virtual machine */
194 String vmHostId
= ssq
.getAttributeName(vmQuark
);
195 ITmfStateInterval vmNameInterval
= StateSystemUtils
.queryUntilNonNullValue(ssq
, vmQuark
, startTime
, endTime
);
196 String vmName
= vmHostId
;
197 if (vmNameInterval
!= null) {
198 vmName
= vmNameInterval
.getStateValue().unboxStr();
201 VirtualMachineViewEntry vmEntry
= entryMap
.get(vmHostId
);
202 if (vmEntry
== null) {
203 vmEntry
= new VirtualMachineViewEntry
.VmEntryBuilder(vmName
, startTime
, endTime
, vmExperiment
).setId(vmHostId
).setVmName(vmName
).setNumericId(vmQuark
).setType(Type
.VM
).build();
204 vmEntry
.sortChildren(fComparator
);
206 groupEntry
.addChild(vmEntry
);
207 entryMap
.put(vmHostId
, vmEntry
);
209 vmEntry
.updateEndTime(endTime
);
212 /* Display an entry for each of its CPUs */
213 for (Integer vCpuQuark
: ssq
.getSubAttributes(vmQuark
, false)) {
214 String vcpuId
= ssq
.getAttributeName(vCpuQuark
);
215 VirtualMachineViewEntry vcpuEntry
= entryMap
.get(vmHostId
+ vcpuId
);
216 if (vcpuEntry
== null) {
217 vcpuEntry
= new VirtualMachineViewEntry
.VmEntryBuilder(vcpuId
, startTime
, endTime
, vmExperiment
).setId(vcpuId
).setVmName(vmName
).setNumericId(vCpuQuark
).setType(Type
.VCPU
).build();
219 vmEntry
.addChild(vcpuEntry
);
220 entryMap
.put(vmHostId
+ vcpuId
, vcpuEntry
);
222 vcpuEntry
.updateEndTime(endTime
);
227 /* Add the entries for the threads */
228 buildThreadEntries(vmEntry
, entryMap
, startTime
, endTime
);
230 } catch (AttributeNotFoundException e
) {
232 * The attribute may not exist yet if the state system is being
235 } catch (TimeRangeException
| StateValueTypeException e
) {
236 Activator
.getDefault().logError("VirtualMachineView: error building event list", e
); //$NON-NLS-1$
240 private static void buildThreadEntries(VirtualMachineViewEntry vmEntry
, Map
<String
, VirtualMachineViewEntry
> entryMap
, long startTime
, long endTime
) {
241 String vmHostId
= vmEntry
.getId();
242 VirtualMachineExperiment vmExperiment
= vmEntry
.getExperiment();
245 * Get the LTTng Kernel analysis module from the corresponding trace
247 KernelAnalysisModule kernelModule
= TmfExperimentUtils
.getAnalysisModuleOfClassForHost(vmExperiment
, vmHostId
, KernelAnalysisModule
.class);
248 if (kernelModule
== null) {
252 VirtualMachineViewEntry threadEntry
= entryMap
.get(vmHostId
+ NonNullUtils
.nullToEmptyString(Messages
.VmView_threads
));
253 if (threadEntry
== null) {
254 threadEntry
= new VirtualMachineViewEntry
.VmEntryBuilder(NonNullUtils
.nullToEmptyString(Messages
.VmView_threads
), startTime
, endTime
, vmExperiment
).build();
255 entryMap
.put(vmHostId
+ NonNullUtils
.nullToEmptyString(Messages
.VmView_threads
), threadEntry
);
256 vmEntry
.addChild(threadEntry
);
258 threadEntry
.updateEndTime(endTime
);
261 String vmName
= vmEntry
.getVmName();
262 if (vmName
== null) {
267 * Display an entry for each thread.
269 * For each interval that is in a running status, intersect with the
270 * status of the virtual CPU it is currently running on
272 Collection
<Integer
> threadIds
= KernelThreadInformationProvider
.getThreadIds(kernelModule
);
273 for (Integer threadId
: threadIds
) {
274 if (threadId
== -1) {
277 VirtualMachineViewEntry oneThreadEntry
= entryMap
.get(vmHostId
+ ':' + threadId
);
278 if (oneThreadEntry
!= null) {
279 oneThreadEntry
.updateEndTime(endTime
);
283 * FIXME: Only add threads that are active during the trace
285 String threadName
= KernelThreadInformationProvider
.getExecutableName(kernelModule
, threadId
);
286 String tidString
= threadId
.toString();
287 threadName
= (threadName
!= null) ? tidString
+ ':' + ' ' + threadName
: tidString
;
288 oneThreadEntry
= new VirtualMachineViewEntry
.VmEntryBuilder(threadName
, startTime
, endTime
, vmExperiment
).setId(threadName
).setVmName(vmName
).setNumericId(threadId
).setType(Type
.THREAD
).build();
290 threadEntry
.addChild(oneThreadEntry
);
291 entryMap
.put(vmHostId
+ ':' + threadId
, oneThreadEntry
);
296 private void buildEntryEventLists(List
<@NonNull VirtualMachineViewEntry
> entryList
, ITmfStateSystem ssq
, long startTime
, long endTime
, IProgressMonitor monitor
) {
297 for (VirtualMachineViewEntry entry
: entryList
) {
298 if (monitor
.isCanceled()) {
301 buildEntryEventList(entry
, ssq
, startTime
, endTime
, monitor
);
305 private void buildEntryEventList(TimeGraphEntry entry
, ITmfStateSystem ssq
, long start
, long end
, IProgressMonitor monitor
) {
306 if (start
< entry
.getEndTime() && end
> entry
.getStartTime()) {
308 long startTime
= Math
.max(start
, entry
.getStartTime());
309 long endTime
= Math
.min(end
+ 1, entry
.getEndTime());
310 long resolution
= Math
.max(1, (end
- ssq
.getStartTime()) / getDisplayWidth());
311 List
<ITimeEvent
> eventList
= getEventList(entry
, startTime
, endTime
, resolution
, monitor
);
312 entry
.setEventList(eventList
);
314 for (ITimeGraphEntry child
: entry
.getChildren()) {
315 if (!(child
instanceof TimeGraphEntry
)) {
318 if (monitor
.isCanceled()) {
321 buildEntryEventList((TimeGraphEntry
) child
, ssq
, start
, end
, monitor
);
327 protected @Nullable List
<ITimeEvent
> getEventList(TimeGraphEntry entry
,
328 long startTime
, long endTime
, long resolution
,
329 IProgressMonitor monitor
) {
330 if (!(entry
instanceof VirtualMachineViewEntry
)) {
333 if (monitor
.isCanceled()) {
337 VirtualMachineViewEntry vmEntry
= (VirtualMachineViewEntry
) entry
;
339 switch (vmEntry
.getType()) {
341 return getThreadEventList(vmEntry
, endTime
, monitor
);
344 return getVcpuEventList(vmEntry
, startTime
, endTime
, resolution
, monitor
);
347 VirtualMachineExperiment experiment
= vmEntry
.getExperiment();
348 VirtualMachineCpuAnalysis vmAnalysis
= TmfTraceUtils
.getAnalysisModuleOfClass(experiment
, VirtualMachineCpuAnalysis
.class, VirtualMachineCpuAnalysis
.ID
);
349 if (vmAnalysis
== null) {
352 Multimap
<Integer
, ITmfStateInterval
> updatedThreadIntervals
= vmAnalysis
.getUpdatedThreadIntervals(vmEntry
.getNumericId(), startTime
, endTime
, resolution
, monitor
);
353 vmEntry
.setThreadIntervals(updatedThreadIntervals
);
357 /* These entry types are not used in this view */
366 private static @Nullable List
<@NonNull ITimeEvent
> getVcpuEventList(VirtualMachineViewEntry vmEntry
, long startTime
, long endTime
, long resolution
, IProgressMonitor monitor
) {
367 List
<ITimeEvent
> eventList
= null;
369 int quark
= vmEntry
.getNumericId();
371 ITmfStateSystem ssq
= TmfStateSystemAnalysisModule
.getStateSystem(vmEntry
.getExperiment(), VirtualMachineCpuAnalysis
.ID
);
373 return Collections
.EMPTY_LIST
;
375 final long realStart
= Math
.max(startTime
, ssq
.getStartTime());
376 final long realEnd
= Math
.min(endTime
, ssq
.getCurrentEndTime() + 1);
377 if (realEnd
<= realStart
) {
378 return Collections
.EMPTY_LIST
;
380 quark
= ssq
.getQuarkRelative(quark
, VmAttributes
.STATUS
);
381 List
<ITmfStateInterval
> statusIntervals
= StateSystemUtils
.queryHistoryRange(ssq
, quark
, realStart
, realEnd
- 1, resolution
, monitor
);
383 eventList
= parseIntervalsForEvents(vmEntry
, statusIntervals
, endTime
, monitor
);
384 } catch (AttributeNotFoundException
| TimeRangeException
| StateValueTypeException e
) {
385 Activator
.getDefault().logError("Error getting event list", e
); //$NON-NLS-1$
386 } catch (StateSystemDisposedException e
) {
392 private static @Nullable List
<@NonNull ITimeEvent
> getThreadEventList(VirtualMachineViewEntry vmEntry
, long endTime
, IProgressMonitor monitor
) {
393 List
<ITimeEvent
> eventList
= null;
394 Collection
<ITmfStateInterval
> threadIntervals
= getThreadIntervalsForEntry(vmEntry
);
396 if (threadIntervals
!= null) {
398 * FIXME: I think the key for the alpha bug when alpha overlaps
399 * multiple events is around here
401 * Hint by Patrick: "The problem is that the thread intervals
402 * are sorted by start time, and drawn in that order.
404 * Given the intervals: Blue [0,10] Alpha [5,15] Red [10,20]
406 * Blue is drawn, then Alpha makes DarkBlue from [5,10] and
407 * DarkBackground from [10,15], then Red is drawn over [10,20],
408 * overwriting the DarkBackground. There is no DarkRed.
410 * For this to work you would have to draw all real states
411 * first, then all alpha states second.
413 * I think this would also have the side-effect that the find
414 * item used for tool tips would always find the real event and
415 * never the alpha event. This might be what we want. Right now
416 * the tool tip has State: (multiple).
418 * But using the Next Event button, we would skip to the next
419 * real event and not at the preemption event. Maybe not what we
422 * Maybe what we need is separate thread interval events:
424 * Blue [0,5] Preempted Blue [5,10] Preempted Red [10,15] Red
427 * The preempted events would have the real state value, but
428 * with a flag for alpha to be used in the postDrawEvent."
430 eventList
= parseIntervalsForEvents(vmEntry
, threadIntervals
, endTime
, monitor
);
435 private static @Nullable List
<@NonNull ITimeEvent
> parseIntervalsForEvents(VirtualMachineViewEntry vmEntry
, Collection
<@NonNull ITmfStateInterval
> intervals
, long endTime
, IProgressMonitor monitor
) {
436 List
<ITimeEvent
> eventList
= new ArrayList
<>(intervals
.size());
437 long lastEndTime
= -1;
438 for (ITmfStateInterval interval
: intervals
) {
439 if (monitor
.isCanceled()) {
443 long time
= interval
.getStartTime();
444 long duration
= interval
.getEndTime() - time
+ 1;
445 if (!interval
.getStateValue().isNull()) {
446 int status
= interval
.getStateValue().unboxInt();
447 if (lastEndTime
!= time
&& lastEndTime
!= -1) {
448 eventList
.add(new TimeEvent(vmEntry
, lastEndTime
, time
- lastEndTime
));
450 eventList
.add(new TimeEvent(vmEntry
, time
, duration
, status
));
451 } else if (lastEndTime
== -1 || time
+ duration
>= endTime
) {
452 /* add null event if it intersects the start or end time */
453 eventList
.add(new NullTimeEvent(vmEntry
, time
, duration
));
455 lastEndTime
= time
+ duration
;
461 private static @Nullable Collection
<@NonNull ITmfStateInterval
> getThreadIntervalsForEntry(VirtualMachineViewEntry vmEntry
) {
462 Collection
<ITmfStateInterval
> threadIntervals
= null;
465 * The parent VM entry will contain the thread intervals for all
466 * threads. Just take the list from there
468 ITimeGraphEntry parent
= vmEntry
.getParent();
469 while (threadIntervals
== null && parent
!= null) {
470 if (parent
instanceof VirtualMachineViewEntry
) {
471 threadIntervals
= ((VirtualMachineViewEntry
) parent
).getThreadIntervals(vmEntry
.getNumericId());
473 if (parent
instanceof TimeGraphEntry
) {
474 parent
= ((TimeGraphEntry
) parent
).getParent();
477 return threadIntervals
;
481 protected Iterable
<ITmfTrace
> getTracesToBuild(@Nullable ITmfTrace trace
) {
483 return Collections
.EMPTY_SET
;
485 return Collections
.singleton(trace
);