tmf : Improve toString() in TmfXmlStateValue and TmfXmlCondition
[deliverable/tracecompass.git] / lttng / org.eclipse.tracecompass.lttng2.kernel.ui / src / org / eclipse / tracecompass / internal / lttng2 / kernel / ui / views / vm / vcpuview / VirtualMachineView.java
CommitLineData
b1a7aebb
MG
1/*******************************************************************************
2 * Copyright (c) 2016 École Polytechnique de Montréal
3 *
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 *******************************************************************************/
9
10package org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview;
11
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.Comparator;
16import java.util.HashMap;
17import java.util.List;
18import java.util.Map;
19
20import org.eclipse.core.runtime.IProgressMonitor;
21import org.eclipse.jdt.annotation.NonNull;
22import org.eclipse.jdt.annotation.Nullable;
23import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.KernelAnalysisModule;
24import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.KernelThreadInformationProvider;
25import org.eclipse.tracecompass.common.core.NonNullUtils;
26import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.VmAttributes;
27import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.module.VirtualMachineCpuAnalysis;
28import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.trace.VirtualMachineExperiment;
29import org.eclipse.tracecompass.internal.lttng2.kernel.ui.Activator;
30import org.eclipse.tracecompass.internal.lttng2.kernel.ui.views.vm.vcpuview.VirtualMachineCommon.Type;
31import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
32import org.eclipse.tracecompass.statesystem.core.StateSystemUtils;
33import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
34import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
35import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
36import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
37import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
38import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
39import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
40import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
41import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperimentUtils;
42import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractTimeGraphView;
43import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
44import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
45import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
46import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
47import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
48
49import com.google.common.collect.Multimap;
50
51/**
52 * Main implementation for the Virtual Machine view
53 *
54 * @author Mohamad Gebai
55 */
56public class VirtualMachineView extends AbstractTimeGraphView {
57
58 /** View ID. */
59 public static final String ID = "org.eclipse.tracecompass.lttng2.analysis.vm.ui.vmview"; //$NON-NLS-1$
60
61 private static final String[] COLUMN_NAMES = new String[] {
62 Messages.VmView_stateTypeName
63 };
64
65 private static final String[] FILTER_COLUMN_NAMES = new String[] {
66 Messages.VmView_stateTypeName
67 };
68
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 };
72
73 private Comparator<ITimeGraphEntry> fComparator = new Comparator<ITimeGraphEntry>() {
74 @Override
75 public int compare(@Nullable ITimeGraphEntry o1, @Nullable ITimeGraphEntry o2) {
76 if (!((o1 instanceof VirtualMachineViewEntry) && (o2 instanceof VirtualMachineViewEntry))) {
77 return 0;
78 }
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 */
83 if (cmp == 0) {
84 cmp = entry1.getName().compareTo(entry2.getName());
85 }
86 return cmp;
87 }
88 };
89
90 // ------------------------------------------------------------------------
91 // Constructors
92 // ------------------------------------------------------------------------
93
94 /**
95 * Default constructor
96 */
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);
104 }
105
106 @Override
107 protected @Nullable String getNextText() {
108 return Messages.VmView_nextResourceActionNameText;
109 }
110
111 @Override
112 protected @Nullable String getNextTooltip() {
113 return Messages.VmView_nextResourceActionToolTipText;
114 }
115
116 @Override
117 protected @Nullable String getPrevText() {
118 return Messages.VmView_previousResourceActionNameText;
119 }
120
121 @Override
122 protected @Nullable String getPrevTooltip() {
123 return Messages.VmView_previousResourceActionToolTipText;
124 }
125
126 private static class VmViewTreeLabelProvider extends TreeLabelProvider {
127
128 @Override
129 public String getColumnText(@Nullable Object element, int columnIndex) {
130 if (!(element instanceof VirtualMachineViewEntry)) {
131 return ""; //$NON-NLS-1$
132 }
133 VirtualMachineViewEntry entry = (VirtualMachineViewEntry) element;
134
135 if (COLUMN_NAMES[columnIndex].equals(Messages.VmView_stateTypeName)) {
136 String name = entry.getName();
137 return (name == null) ? "" : name; //$NON-NLS-1$
138 }
139 return ""; //$NON-NLS-1$
140 }
141
142 }
143
144 // ------------------------------------------------------------------------
145 // Internal
146 // ------------------------------------------------------------------------
147
148 @Override
149 protected void buildEventList(ITmfTrace trace, ITmfTrace parentTrace, IProgressMonitor monitor) {
150 setStartTime(Long.MAX_VALUE);
151 setEndTime(Long.MIN_VALUE);
152
153 if (monitor.isCanceled()) {
154 return;
155 }
156 if (!(parentTrace instanceof VirtualMachineExperiment)) {
157 return;
158 }
159 ITmfStateSystem ssq = TmfStateSystemAnalysisModule.getStateSystem(parentTrace, VirtualMachineCpuAnalysis.ID);
160 if (ssq == null) {
161 return;
162 }
163 VirtualMachineExperiment vmExperiment = (VirtualMachineExperiment) parentTrace;
164 long startTime = ssq.getStartTime();
165
166 ArrayList<VirtualMachineViewEntry> entryList = new ArrayList<>();
167 Map<String, VirtualMachineViewEntry> entryMap = new HashMap<>();
168
169 boolean complete = false;
170 VirtualMachineViewEntry groupEntry = null;
171
172 while (!complete) {
173 if (monitor.isCanceled()) {
174 return;
175 }
176 complete = ssq.waitUntilBuilt(BUILD_UPDATE_TIMEOUT);
177 if (ssq.isCancelled()) {
178 return;
179 }
180
181 long endTime = ssq.getCurrentEndTime() + 1;
182 if (groupEntry == null) {
183 groupEntry = new VirtualMachineViewEntry.VmEntryBuilder(vmExperiment.getName(), startTime, endTime, vmExperiment).build();
184 /*
185 * There is only one root entry for this view, so we can put it
186 * right away
187 */
188 entryList.add(groupEntry);
189 putEntryList(parentTrace, new ArrayList<TimeGraphEntry>(entryList));
190 } else {
191 groupEntry.updateEndTime(endTime);
192 }
193
194 setStartTime(Math.min(getStartTime(), startTime));
195 setEndTime(Math.max(getEndTime(), endTime));
196
197 /*
198 * Create the entries for the VMs in this experiment and their
199 * respective threads
200 */
201 buildEntries(ssq, startTime, endTime, groupEntry, entryMap, vmExperiment);
202
203 if (parentTrace.equals(getTrace())) {
204 refresh();
205 }
206
207 for (VirtualMachineViewEntry entry : entryList) {
208 if (monitor.isCanceled()) {
209 return;
210 }
211 buildEntryEventList(entry, ssq, startTime, endTime, monitor);
212 }
213 }
214 }
215
216 private void buildEntries(ITmfStateSystem ssq, long startTime, long endTime, VirtualMachineViewEntry groupEntry,
217 Map<@NonNull String, @NonNull VirtualMachineViewEntry> entryMap, VirtualMachineExperiment vmExperiment) {
218 try {
219 List<Integer> vmQuarks = ssq.getQuarks(VmAttributes.VIRTUAL_MACHINES, "*"); //$NON-NLS-1$
220 /* For each virtual machine in the analysis */
221 for (Integer vmQuark : vmQuarks) {
222
223 /* Display an entry for the virtual machine */
224 String vmHostId = NonNullUtils.checkNotNull(ssq.getAttributeName(vmQuark));
225 ITmfStateInterval vmNameInterval = StateSystemUtils.queryUntilNonNullValue(ssq, vmQuark, startTime, endTime);
226 String vmName = vmHostId;
227 if (vmNameInterval != null) {
228 vmName = vmNameInterval.getStateValue().unboxStr();
229 }
230
231 VirtualMachineViewEntry vmEntry = entryMap.get(vmHostId);
232 if (vmEntry == null) {
233 vmEntry = new VirtualMachineViewEntry.VmEntryBuilder(vmName, startTime, endTime, vmExperiment).setId(vmHostId).setVmName(vmName).setNumericId(vmQuark).setType(Type.VM).build();
234 vmEntry.sortChildren(fComparator);
235
236 groupEntry.addChild(vmEntry);
237 entryMap.put(vmHostId, vmEntry);
238 } else {
239 vmEntry.updateEndTime(endTime);
240 }
241
242 /* Display an entry for each of its CPUs */
243 for (Integer vCpuQuark : ssq.getSubAttributes(vmQuark, false)) {
244 String vcpuId = ssq.getAttributeName(vCpuQuark);
245 VirtualMachineViewEntry vcpuEntry = entryMap.get(vmHostId + vcpuId);
246 if (vcpuEntry == null) {
247 vcpuEntry = new VirtualMachineViewEntry.VmEntryBuilder(vcpuId, startTime, endTime, vmExperiment).setId(vcpuId).setVmName(vmName).setNumericId(vCpuQuark).setType(Type.VCPU).build();
248
249 vmEntry.addChild(vcpuEntry);
250 entryMap.put(vmHostId + vcpuId, vcpuEntry);
251 } else {
252 vcpuEntry.updateEndTime(endTime);
253 }
254
255 }
256
257 /*
258 * Get the LTTng Kernel analysis module from the corresponding
259 * trace
260 */
261 KernelAnalysisModule kernelModule = TmfExperimentUtils.getAnalysisModuleOfClassForHost(vmExperiment, vmHostId, KernelAnalysisModule.class);
262 if (kernelModule == null) {
263 continue;
264 }
265
266 VirtualMachineViewEntry threadEntry = entryMap.get(vmHostId + NonNullUtils.nullToEmptyString(Messages.VmView_threads));
267 if (threadEntry == null) {
268 threadEntry = new VirtualMachineViewEntry.VmEntryBuilder(NonNullUtils.nullToEmptyString(Messages.VmView_threads), startTime, endTime, vmExperiment).build();
269 entryMap.put(vmHostId + NonNullUtils.nullToEmptyString(Messages.VmView_threads), threadEntry);
270 vmEntry.addChild(threadEntry);
271 } else {
272 threadEntry.updateEndTime(endTime);
273 }
274
275 /*
276 * Display an entry for each thread.
277 *
278 * For each interval that is in a running status, intersect with
279 * the status of the virtual CPU it is currently running on
280 */
281 Collection<Integer> threadIds = KernelThreadInformationProvider.getThreadIds(kernelModule);
282 for (Integer threadId : threadIds) {
283 if (threadId == -1) {
284 continue;
285 }
286 VirtualMachineViewEntry oneThreadEntry = entryMap.get(vmHostId + ':' + threadId);
287 if (oneThreadEntry != null) {
288 oneThreadEntry.updateEndTime(endTime);
289 continue;
290 }
291 /*
292 * FIXME: Only add threads that are active during the trace
293 */
294 String threadName = KernelThreadInformationProvider.getExecutableName(kernelModule, threadId);
295 String tidString = NonNullUtils.checkNotNull(threadId.toString());
296 threadName = (threadName != null) ? tidString + ':' + ' ' + threadName : tidString;
297 oneThreadEntry = new VirtualMachineViewEntry.VmEntryBuilder(threadName, startTime, endTime, vmExperiment).setId(threadName).setVmName(vmName).setNumericId(threadId).setType(Type.THREAD).build();
298
299 threadEntry.addChild(oneThreadEntry);
300 entryMap.put(vmHostId + ':' + threadId, oneThreadEntry);
301 }
302
303 }
304 } catch (AttributeNotFoundException e) {
305 /*
306 * The attribute may not exist yet if the state system is being
307 * built
308 */
309 } catch (TimeRangeException | StateValueTypeException e) {
310 Activator.getDefault().logError("VirtualMachineView: error building event list", e); //$NON-NLS-1$
311 }
312 }
313
314 private void buildEntryEventList(TimeGraphEntry entry, ITmfStateSystem ssq, long start, long end, IProgressMonitor monitor) {
315 if (start < entry.getEndTime() && end > entry.getStartTime()) {
316
317 long startTime = Math.max(start, entry.getStartTime());
318 long endTime = Math.min(end + 1, entry.getEndTime());
319 long resolution = Math.max(1, (end - ssq.getStartTime()) / getDisplayWidth());
320 List<ITimeEvent> eventList = getEventList(entry, startTime, endTime, resolution, monitor);
321 entry.setEventList(eventList);
322 redraw();
323 for (ITimeGraphEntry child : entry.getChildren()) {
324 if (!(child instanceof TimeGraphEntry)) {
325 continue;
326 }
327 if (monitor.isCanceled()) {
328 return;
329 }
330 buildEntryEventList((TimeGraphEntry) child, ssq, start, end, monitor);
331 }
332 }
333 }
334
335 @Override
336 protected @Nullable List<ITimeEvent> getEventList(TimeGraphEntry entry,
337 long startTime, long endTime, long resolution,
338 IProgressMonitor monitor) {
339 if (!(entry instanceof VirtualMachineViewEntry)) {
340 return null;
341 }
342 if (monitor.isCanceled()) {
343 return null;
344 }
345
346 VirtualMachineViewEntry vmEntry = (VirtualMachineViewEntry) entry;
347
348 switch (vmEntry.getType()) {
349 case THREAD: {
350 return getThreadEventList(entry, vmEntry, monitor);
351 }
352 case VCPU: {
353 return getVcpuEventList(entry, vmEntry, startTime, endTime, resolution, monitor);
354 }
355 case VM: {
356 VirtualMachineExperiment experiment = vmEntry.getExperiment();
357 VirtualMachineCpuAnalysis vmAnalysis = null;
358 for (VirtualMachineCpuAnalysis module : TmfTraceUtils.getAnalysisModulesOfClass(experiment, VirtualMachineCpuAnalysis.class)) {
359 vmAnalysis = module;
360 break;
361 }
362 if (vmAnalysis == null) {
363 break;
364 }
365 Multimap<Integer, ITmfStateInterval> updatedThreadIntervals = vmAnalysis.getUpdatedThreadIntervals(vmEntry.getNumericId(), startTime, endTime, resolution, monitor);
366 vmEntry.setThreadIntervals(updatedThreadIntervals);
367 }
368 break;
369 case NULL:
370 /* These entry types are not used in this view */
371 break;
372 default:
373 break;
374 }
375
376 return null;
377 }
378
379 private static @Nullable List<@NonNull ITimeEvent> getVcpuEventList(TimeGraphEntry entry, VirtualMachineViewEntry vmEntry, long startTime, long endTime, long resolution, IProgressMonitor monitor) {
380 List<ITimeEvent> eventList = null;
381 try {
382 int quark = vmEntry.getNumericId();
383
384 ITmfStateSystem ssq = TmfStateSystemAnalysisModule.getStateSystem(vmEntry.getExperiment(), VirtualMachineCpuAnalysis.ID);
385 if (ssq == null) {
386 return Collections.EMPTY_LIST;
387 }
388 final long realStart = Math.max(startTime, ssq.getStartTime());
389 final long realEnd = Math.min(endTime, ssq.getCurrentEndTime() + 1);
390 if (realEnd <= realStart) {
391 return Collections.EMPTY_LIST;
392 }
393 quark = ssq.getQuarkRelative(quark, VmAttributes.STATUS);
394 List<ITmfStateInterval> statusIntervals = StateSystemUtils.queryHistoryRange(ssq, quark, realStart, realEnd - 1, resolution, monitor);
395 eventList = new ArrayList<>(statusIntervals.size());
396 long lastEndTime = -1;
397 for (ITmfStateInterval statusInterval : statusIntervals) {
398 if (monitor.isCanceled()) {
399 return null;
400 }
401
402 long time = statusInterval.getStartTime();
403 long duration = statusInterval.getEndTime() - time + 1;
404 if (!statusInterval.getStateValue().isNull()) {
405 int status = statusInterval.getStateValue().unboxInt();
406 if (lastEndTime != time && lastEndTime != -1) {
407 eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime));
408 }
409 eventList.add(new TimeEvent(entry, time, duration, status));
410 } else if (lastEndTime == -1 || time + duration >= endTime) {
411 // add null event if it intersects the start or end time
412 eventList.add(new NullTimeEvent(entry, time, duration));
413 }
414 lastEndTime = time + duration;
415 }
416 } catch (AttributeNotFoundException | TimeRangeException | StateValueTypeException e) {
417 Activator.getDefault().logError("Error getting event list", e); //$NON-NLS-1$
418 } catch (StateSystemDisposedException e) {
419 /* Ignored */
420 }
421 return eventList;
422 }
423
424 private static @Nullable List<@NonNull ITimeEvent> getThreadEventList(TimeGraphEntry entry, VirtualMachineViewEntry vmEntry, IProgressMonitor monitor) {
425 List<ITimeEvent> eventList = null;
426 /*
427 * The parent VM entry will contain the thread intervals for all thread.
428 * Just take the list from there
429 */
430 Collection<ITmfStateInterval> threadIntervals = null;
431 ITimeGraphEntry parent = vmEntry.getParent();
432 while (threadIntervals == null && parent != null) {
433 if (parent instanceof VirtualMachineViewEntry) {
434 threadIntervals = ((VirtualMachineViewEntry) parent).getThreadIntervals(vmEntry.getNumericId());
435 }
436 if (parent instanceof TimeGraphEntry) {
437 parent = ((TimeGraphEntry) parent).getParent();
438 }
439 }
440 if (threadIntervals != null) {
441 eventList = new ArrayList<>(threadIntervals.size());
442 long lastEndTime = -1;
443 for (ITmfStateInterval interval : threadIntervals) {
444 if (monitor.isCanceled()) {
445 return null;
446 }
447 long time = interval.getStartTime();
448 long duration = interval.getEndTime() - time + 1;
449 /*
450 * FIXME: I think the key for the alpha bug when alpha overlaps
451 * multiple events is around here
452 *
453 * Hint by Patrick: "The problem is that the thread intervals
454 * are sorted by start time, and drawn in that order.
455 *
456 * Given the intervals: Blue [0,10] Alpha [5,15] Red [10,20]
457 *
458 * Blue is drawn, then Alpha makes DarkBlue from [5,10] and
459 * DarkBackground from [10,15], then Red is drawn over [10,20],
460 * overwriting the DarkBackground. There is no DarkRed.
461 *
462 * For this to work you would have to draw all real states
463 * first, then all alpha states second.
464 *
465 * I think this would also have the side-effect that the find
466 * item used for tool tips would always find the real event and
467 * never the alpha event. This might be what we want. Right now
468 * the tool tip has State: (multiple).
469 *
470 * But using the Next Event button, we would skip to the next
471 * real event and not at the preemption event. Maybe not what we
472 * want.
473 *
474 * Maybe what we need is separate thread interval events:
475 *
476 * Blue [0,5] Preempted Blue [5,10] Preempted Red [10,15] Red
477 * [15,20]...
478 *
479 * The preempted events would have the real state value, but
480 * with a flag for alpha to be used in the postDrawEvent."
481 */
482 if (!interval.getStateValue().isNull()) {
483 int status = interval.getStateValue().unboxInt();
484 if (lastEndTime < time && lastEndTime != -1) {
485 /*
486 * Add a time event to fill the blanks between intervals
487 */
488 eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime));
489 }
490 eventList.add(new TimeEvent(entry, time, duration, status));
491 } else if (lastEndTime == -1) {
492 eventList.add(new NullTimeEvent(entry, time, duration));
493 }
494 lastEndTime = time + duration;
495 }
496 }
497 return eventList;
498 }
499
500 @Override
501 protected Iterable<ITmfTrace> getTracesToBuild(@Nullable ITmfTrace trace) {
502 if (trace == null) {
503 return NonNullUtils.checkNotNull(Collections.EMPTY_SET);
504 }
505 return NonNullUtils.checkNotNull(Collections.singleton(trace));
506 }
507
508}
This page took 0.043781 seconds and 5 git commands to generate.