tmf: Bug 509691: Changes to mutable trace context can be lost
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.os.linux.ui / src / org / eclipse / tracecompass / internal / analysis / os / linux / ui / views / resources / ResourcesView.java
1 /*******************************************************************************
2 * Copyright (c) 2012, 2017 Ericsson, É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 * Contributors:
10 * Patrick Tasse - Initial API and implementation
11 * Geneviève Bastien - Move code to provide base classes for time graph views
12 *******************************************************************************/
13
14 package org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.resources;
15
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 import org.eclipse.core.runtime.IProgressMonitor;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.eclipse.jface.action.IMenuManager;
29 import org.eclipse.jface.viewers.ISelection;
30 import org.eclipse.jface.viewers.IStructuredSelection;
31 import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule;
32 import org.eclipse.tracecompass.analysis.os.linux.core.signals.TmfCpuSelectedSignal;
33 import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attributes;
34 import org.eclipse.tracecompass.internal.analysis.os.linux.ui.Messages;
35 import org.eclipse.tracecompass.internal.analysis.os.linux.ui.actions.FollowCpuAction;
36 import org.eclipse.tracecompass.internal.analysis.os.linux.ui.actions.UnfollowCpuAction;
37 import org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.resources.ResourcesEntry.Type;
38 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
39 import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
40 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
41 import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
42 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
43 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceContext;
44 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
45 import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractStateSystemTimeGraphView;
46 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
47 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
48 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
49 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
50 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
51
52 /**
53 * Main implementation for the LTTng 2.0 kernel Resource view
54 *
55 * @author Patrick Tasse
56 */
57 public class ResourcesView extends AbstractStateSystemTimeGraphView {
58
59 /** View ID. */
60 public static final @NonNull String ID = "org.eclipse.tracecompass.analysis.os.linux.views.resources"; //$NON-NLS-1$
61
62 /** ID of the followed CPU in the map data in {@link TmfTraceContext} */
63 public static final @NonNull String RESOURCES_FOLLOW_CPU = ID + ".FOLLOW_CPU"; //$NON-NLS-1$
64
65 private static final String[] FILTER_COLUMN_NAMES = new String[] {
66 Messages.ResourcesView_stateTypeName
67 };
68
69 // Timeout between updates in the build thread in ms
70 private static final long BUILD_UPDATE_TIMEOUT = 500;
71
72 // ------------------------------------------------------------------------
73 // Constructors
74 // ------------------------------------------------------------------------
75
76 /**
77 * Default constructor
78 */
79 public ResourcesView() {
80 super(ID, new ResourcesPresentationProvider());
81 setFilterColumns(FILTER_COLUMN_NAMES);
82 setFilterLabelProvider(new ResourcesFilterLabelProvider());
83 setEntryComparator(new ResourcesEntryComparator());
84 setAutoExpandLevel(1);
85 }
86
87 private static class ResourcesEntryComparator implements Comparator<ITimeGraphEntry> {
88 @Override
89 public int compare(ITimeGraphEntry o1, ITimeGraphEntry o2) {
90 ResourcesEntry entry1 = (ResourcesEntry) o1;
91 ResourcesEntry entry2 = (ResourcesEntry) o2;
92 if (entry1.getType() == Type.NULL && entry2.getType() == Type.NULL) {
93 /* sort trace entries alphabetically */
94 return entry1.getName().compareTo(entry2.getName());
95 }
96 /* sort resource entries by their defined order */
97 return entry1.compareTo(entry2);
98 }
99 }
100
101
102 /**
103 * @since 2.0
104 */
105 @Override
106 protected void fillTimeGraphEntryContextMenu(@NonNull IMenuManager menuManager) {
107 ISelection selection = getSite().getSelectionProvider().getSelection();
108 if (selection instanceof IStructuredSelection) {
109 IStructuredSelection sSel = (IStructuredSelection) selection;
110 if (sSel.getFirstElement() instanceof ResourcesEntry) {
111 ResourcesEntry resourcesEntry = (ResourcesEntry) sSel.getFirstElement();
112 if (resourcesEntry.getType().equals(ResourcesEntry.Type.CPU)) {
113 TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
114 Integer data = (Integer) ctx.getData(RESOURCES_FOLLOW_CPU);
115 int cpu = data != null ? data.intValue() : -1;
116 if (cpu >= 0) {
117 menuManager.add(new UnfollowCpuAction(ResourcesView.this, resourcesEntry.getId(), resourcesEntry.getTrace()));
118 } else {
119 menuManager.add(new FollowCpuAction(ResourcesView.this, resourcesEntry.getId(), resourcesEntry.getTrace()));
120 }
121 }
122 }
123 }
124 }
125
126 private static class ResourcesFilterLabelProvider extends TreeLabelProvider {
127 @Override
128 public String getColumnText(Object element, int columnIndex) {
129 ResourcesEntry entry = (ResourcesEntry) element;
130 if (columnIndex == 0) {
131 return entry.getName();
132 }
133 return ""; //$NON-NLS-1$
134 }
135
136 }
137
138 // ------------------------------------------------------------------------
139 // Internal
140 // ------------------------------------------------------------------------
141
142 @Override
143 protected String getNextText() {
144 return Messages.ResourcesView_nextResourceActionNameText;
145 }
146
147 @Override
148 protected String getNextTooltip() {
149 return Messages.ResourcesView_nextResourceActionToolTipText;
150 }
151
152 @Override
153 protected String getPrevText() {
154 return Messages.ResourcesView_previousResourceActionNameText;
155 }
156
157 @Override
158 protected String getPrevTooltip() {
159 return Messages.ResourcesView_previousResourceActionToolTipText;
160 }
161
162 @Override
163 protected void buildEntryList(ITmfTrace trace, ITmfTrace parentTrace, final IProgressMonitor monitor) {
164 final ITmfStateSystem ssq = TmfStateSystemAnalysisModule.getStateSystem(trace, KernelAnalysisModule.ID);
165 if (ssq == null) {
166 return;
167 }
168
169 Map<Integer, ResourcesEntry> entryMap = new HashMap<>();
170 TimeGraphEntry traceEntry = null;
171
172 long startTime = ssq.getStartTime();
173 long start = startTime;
174 setStartTime(Math.min(getStartTime(), startTime));
175 boolean complete = false;
176 while (!complete) {
177 if (monitor.isCanceled()) {
178 return;
179 }
180 complete = ssq.waitUntilBuilt(BUILD_UPDATE_TIMEOUT);
181 if (ssq.isCancelled()) {
182 return;
183 }
184 long end = ssq.getCurrentEndTime();
185 if (start == end && !complete) {
186 // when complete execute one last time regardless of end time
187 continue;
188 }
189 long endTime = end + 1;
190 setEndTime(Math.max(getEndTime(), endTime));
191
192 if (traceEntry == null) {
193 traceEntry = new ResourcesEntry(trace, trace.getName(), startTime, endTime, 0);
194 List<TimeGraphEntry> entryList = Collections.singletonList(traceEntry);
195 addToEntryList(parentTrace, ssq, entryList);
196 } else {
197 traceEntry.updateEndTime(endTime);
198 }
199 List<Integer> cpuQuarks = ssq.getQuarks(Attributes.CPUS, "*"); //$NON-NLS-1$
200 createCpuEntriesWithQuark(trace, ssq, entryMap, traceEntry, startTime, endTime, cpuQuarks);
201 if (parentTrace.equals(getTrace())) {
202 refresh();
203 }
204 final List<@NonNull TimeGraphEntry> traceEntryChildren = traceEntry.getChildren();
205 final long resolution = Math.max(1, (endTime - ssq.getStartTime()) / getDisplayWidth());
206 queryFullStates(ssq, ssq.getStartTime(), end, resolution, monitor, new IQueryHandler() {
207 @Override
208 public void handle(List<List<ITmfStateInterval>> fullStates, List<ITmfStateInterval> prevFullState) {
209 for (TimeGraphEntry child : traceEntryChildren) {
210 if (!populateEventsRecursively(fullStates, prevFullState, child).isOK()) {
211 return;
212 }
213 }
214 }
215
216 private IStatus populateEventsRecursively(@NonNull List<List<ITmfStateInterval>> fullStates, @Nullable List<ITmfStateInterval> prevFullState, @NonNull TimeGraphEntry entry) {
217 if (monitor.isCanceled()) {
218 return Status.CANCEL_STATUS;
219 }
220 List<ITimeEvent> eventList = getEventList(entry, ssq, fullStates, prevFullState, monitor);
221 if (eventList != null) {
222 /* Start a new event list on first iteration, then append to it */
223 if (prevFullState == null) {
224 entry.setEventList(eventList);
225 } else {
226 for (ITimeEvent event : eventList) {
227 entry.addEvent(event);
228 }
229 }
230 }
231 for (TimeGraphEntry child : entry.getChildren()) {
232 IStatus status = populateEventsRecursively(fullStates, prevFullState, child);
233 if (!status.isOK()) {
234 return status;
235 }
236 }
237 return Status.OK_STATUS;
238 }
239 });
240
241 start = end;
242 }
243
244 }
245
246 private static void createCpuEntriesWithQuark(@NonNull ITmfTrace trace, final ITmfStateSystem ssq, Map<Integer, ResourcesEntry> entryMap, TimeGraphEntry traceEntry, long startTime, long endTime, List<Integer> cpuQuarks) {
247 for (Integer cpuQuark : cpuQuarks) {
248 final @NonNull String cpuName = ssq.getAttributeName(cpuQuark);
249 int cpu = Integer.parseInt(cpuName);
250 ResourcesEntry cpuEntry = entryMap.get(cpuQuark);
251 if (cpuEntry == null) {
252 cpuEntry = new ResourcesEntry(cpuQuark, trace, startTime, endTime, Type.CPU, cpu);
253 entryMap.put(cpuQuark, cpuEntry);
254 traceEntry.addChild(cpuEntry);
255 } else {
256 cpuEntry.updateEndTime(endTime);
257 }
258 List<Integer> irqQuarks = ssq.getQuarks(Attributes.CPUS, cpuName, Attributes.IRQS, "*"); //$NON-NLS-1$
259 createCpuInterruptEntryWithQuark(trace, ssq, entryMap, startTime, endTime, traceEntry, cpuEntry, irqQuarks, Type.IRQ);
260 List<Integer> softIrqQuarks = ssq.getQuarks(Attributes.CPUS, cpuName, Attributes.SOFT_IRQS, "*"); //$NON-NLS-1$
261 createCpuInterruptEntryWithQuark(trace, ssq, entryMap, startTime, endTime, traceEntry, cpuEntry, softIrqQuarks, Type.SOFT_IRQ);
262 }
263 }
264
265 /**
266 * Create and add execution contexts to a cpu entry. Also creates an
267 * aggregate entry in the root trace entry. The execution context is
268 * basically what the cpu is doing in its execution stack. It can be in an
269 * IRQ, Soft IRQ. MCEs, NMIs, Userland and Kernel execution is not yet
270 * supported.
271 *
272 * @param trace
273 * the trace
274 * @param ssq
275 * the state system
276 * @param entryMap
277 * the entry map
278 * @param startTime
279 * the start time in nanoseconds
280 * @param endTime
281 * the end time in nanoseconds
282 * @param traceEntry
283 * the trace timegraph entry
284 * @param cpuEntry
285 * the cpu timegraph entry (the entry under the trace entry
286 * @param childrenQuarks
287 * the quarks to add to cpu entry
288 * @param type
289 * the type of entry being added
290 */
291 private static void createCpuInterruptEntryWithQuark(@NonNull ITmfTrace trace,
292 final ITmfStateSystem ssq, Map<Integer, ResourcesEntry> entryMap,
293 long startTime, long endTime,
294 TimeGraphEntry traceEntry, ResourcesEntry cpuEntry,
295 List<Integer> childrenQuarks, Type type) {
296 for (Integer quark : childrenQuarks) {
297 final @NonNull String resourceName = ssq.getAttributeName(quark);
298 int resourceId = Integer.parseInt(resourceName);
299 ResourcesEntry interruptEntry = entryMap.get(quark);
300 if (interruptEntry == null) {
301 interruptEntry = new ResourcesEntry(quark, trace, startTime, endTime, type, resourceId);
302 entryMap.put(quark, interruptEntry);
303 cpuEntry.addChild(interruptEntry);
304 boolean found = false;
305 for (ITimeGraphEntry rootElem : traceEntry.getChildren()) {
306 if (rootElem instanceof AggregateResourcesEntry) {
307 AggregateResourcesEntry aggregateInterruptEntry = (AggregateResourcesEntry) rootElem;
308 if (aggregateInterruptEntry.getId() == resourceId && aggregateInterruptEntry.getType().equals(type)) {
309 found = true;
310 aggregateInterruptEntry.addContributor(interruptEntry);
311 final AggregateResourcesEntry irqCpuEntry = new AggregateResourcesEntry(trace, cpuEntry.getName(), startTime, endTime, type, cpuEntry.getId());
312 irqCpuEntry.addContributor(interruptEntry);
313 aggregateInterruptEntry.addChild(irqCpuEntry);
314 break;
315 }
316 }
317 }
318 if (!found) {
319 AggregateResourcesEntry aggregateInterruptEntry = new AggregateResourcesEntry(trace, startTime, endTime, type, resourceId);
320 aggregateInterruptEntry.addContributor(interruptEntry);
321 final AggregateResourcesEntry irqCpuEntry = new AggregateResourcesEntry(trace, cpuEntry.getName(), startTime, endTime, type, cpuEntry.getId());
322 irqCpuEntry.addContributor(interruptEntry);
323 aggregateInterruptEntry.addChild(irqCpuEntry);
324 traceEntry.addChild(aggregateInterruptEntry);
325 }
326 } else {
327 interruptEntry.updateEndTime(endTime);
328 }
329 }
330 }
331
332 @Override
333 protected @Nullable List<ITimeEvent> getEventList(@NonNull TimeGraphEntry entry, ITmfStateSystem ssq,
334 @NonNull List<List<ITmfStateInterval>> fullStates, @Nullable List<ITmfStateInterval> prevFullState, @NonNull IProgressMonitor monitor) {
335 ResourcesEntry resourcesEntry = (ResourcesEntry) entry;
336 int quark = resourcesEntry.getQuark();
337
338 if (resourcesEntry.getType().equals(Type.CPU)) {
339 return createCpuEventsList(entry, fullStates, prevFullState, monitor, quark);
340 } else if ((resourcesEntry.getType().equals(Type.IRQ) || resourcesEntry.getType().equals(Type.SOFT_IRQ)) && (quark >= 0)) {
341 return createIrqEventsList(entry, fullStates, prevFullState, monitor, quark);
342 }
343
344 return null;
345 }
346
347 private static List<ITimeEvent> createCpuEventsList(ITimeGraphEntry entry, List<List<ITmfStateInterval>> fullStates, List<ITmfStateInterval> prevFullState, IProgressMonitor monitor, int quark) {
348 List<ITimeEvent> eventList;
349 boolean isZoomThread = Thread.currentThread() instanceof ZoomThread;
350 eventList = new ArrayList<>(fullStates.size());
351 ITmfStateInterval lastInterval = prevFullState == null || quark >= prevFullState.size() ? null : prevFullState.get(quark);
352 long lastStartTime = lastInterval == null ? -1 : lastInterval.getStartTime();
353 long lastEndTime = lastInterval == null ? -1 : lastInterval.getEndTime() + 1;
354 for (List<ITmfStateInterval> fullState : fullStates) {
355 if (monitor.isCanceled()) {
356 return null;
357 }
358 if (quark >= fullState.size()) {
359 /* No information on this CPU (yet?), skip it for now */
360 continue;
361 }
362 ITmfStateInterval statusInterval = fullState.get(quark);
363 int status = statusInterval.getStateValue().unboxInt();
364 long time = statusInterval.getStartTime();
365 long duration = statusInterval.getEndTime() - time + 1;
366 if (time == lastStartTime) {
367 continue;
368 }
369 if (!statusInterval.getStateValue().isNull()) {
370 if (lastEndTime != time && lastEndTime != -1) {
371 eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime));
372 }
373 eventList.add(new TimeEvent(entry, time, duration, status));
374 } else if (isZoomThread) {
375 eventList.add(new NullTimeEvent(entry, time, duration));
376 }
377 lastStartTime = time;
378 lastEndTime = time + duration;
379 }
380 return eventList;
381 }
382
383 private static List<ITimeEvent> createIrqEventsList(ITimeGraphEntry entry, List<List<ITmfStateInterval>> fullStates, List<ITmfStateInterval> prevFullState, IProgressMonitor monitor, int quark) {
384 List<ITimeEvent> eventList;
385 boolean isZoomThread = Thread.currentThread() instanceof ZoomThread;
386 eventList = new ArrayList<>(fullStates.size());
387 ITmfStateInterval lastInterval = prevFullState == null || quark >= prevFullState.size() ? null : prevFullState.get(quark);
388 long lastStartTime = lastInterval == null ? -1 : lastInterval.getStartTime();
389 long lastEndTime = lastInterval == null ? -1 : lastInterval.getEndTime() + 1;
390 boolean lastIsNull = lastInterval == null ? false : lastInterval.getStateValue().isNull();
391 for (List<ITmfStateInterval> fullState : fullStates) {
392 if (monitor.isCanceled()) {
393 return null;
394 }
395 if (quark >= fullState.size()) {
396 /* No information on this IRQ (yet?), skip it for now */
397 continue;
398 }
399 ITmfStateInterval irqInterval = fullState.get(quark);
400 long time = irqInterval.getStartTime();
401 long duration = irqInterval.getEndTime() - time + 1;
402 if (time == lastStartTime) {
403 continue;
404 }
405 if (!irqInterval.getStateValue().isNull()) {
406 int cpu = irqInterval.getStateValue().unboxInt();
407 eventList.add(new TimeEvent(entry, time, duration, cpu));
408 lastIsNull = false;
409 } else {
410 if (lastEndTime != time && lastIsNull) {
411 /*
412 * This is a special case where we want to show IRQ_ACTIVE
413 * state but we don't know the CPU (it is between two null
414 * samples)
415 */
416 eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime, -1));
417 }
418 if (isZoomThread) {
419 eventList.add(new NullTimeEvent(entry, time, duration));
420 }
421 lastIsNull = true;
422 }
423 lastStartTime = time;
424 lastEndTime = time + duration;
425 }
426 return eventList;
427 }
428
429 /**
430 * Signal handler for a cpu selected signal.
431 *
432 * @param signal
433 * the cpu selected signal
434 * @since 2.0
435 */
436 @TmfSignalHandler
437 public void listenToCpu(TmfCpuSelectedSignal signal) {
438 int data = signal.getCore() >= 0 ? signal.getCore() : -1;
439 ITmfTrace trace = getTrace();
440 if (trace == null) {
441 return;
442 }
443 TmfTraceManager.getInstance().updateTraceContext(trace,
444 builder -> builder.setData(RESOURCES_FOLLOW_CPU, data));
445 }
446
447 }
This page took 0.041417 seconds and 5 git commands to generate.