critical path: bug 495020 fix statistics when reopening a trace
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.graph.ui / src / org / eclipse / tracecompass / internal / analysis / graph / ui / criticalpath / view / CriticalPathView.java
1 /*******************************************************************************
2 * Copyright (c) 2015, 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
10 package org.eclipse.tracecompass.internal.analysis.graph.ui.criticalpath.view;
11
12 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
13
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.Comparator;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.concurrent.locks.Lock;
21 import java.util.concurrent.locks.ReentrantLock;
22
23 import org.eclipse.core.runtime.IProgressMonitor;
24 import org.eclipse.core.runtime.NullProgressMonitor;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.eclipse.jface.viewers.Viewer;
28 import org.eclipse.tracecompass.analysis.graph.core.base.IGraphWorker;
29 import org.eclipse.tracecompass.analysis.graph.core.base.TmfEdge;
30 import org.eclipse.tracecompass.analysis.graph.core.base.TmfEdge.EdgeType;
31 import org.eclipse.tracecompass.analysis.graph.core.base.TmfGraph;
32 import org.eclipse.tracecompass.analysis.graph.core.base.TmfVertex;
33 import org.eclipse.tracecompass.analysis.graph.core.building.TmfGraphBuilderModule;
34 import org.eclipse.tracecompass.analysis.graph.core.criticalpath.CriticalPathModule;
35 import org.eclipse.tracecompass.common.core.NonNullUtils;
36 import org.eclipse.tracecompass.internal.analysis.graph.core.base.TmfGraphStatistics;
37 import org.eclipse.tracecompass.internal.analysis.graph.core.base.TmfGraphVisitor;
38 import org.eclipse.tracecompass.internal.analysis.graph.ui.criticalpath.view.CriticalPathPresentationProvider.State;
39 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
40 import org.eclipse.tracecompass.tmf.core.signal.TmfStartAnalysisSignal;
41 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
42 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
43 import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractTimeGraphView;
44 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.ITimeGraphContentProvider;
45 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
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.TimeEvent;
49 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
50 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeLinkEvent;
51
52 import com.google.common.collect.HashBasedTable;
53 import com.google.common.collect.Iterables;
54 import com.google.common.collect.Table;
55
56 /**
57 * The Critical Path view
58 *
59 * @author Geneviève Bastien
60 * @author Francis Giraldeau
61 */
62 public class CriticalPathView extends AbstractTimeGraphView {
63
64 // ------------------------------------------------------------------------
65 // Constants
66 // ------------------------------------------------------------------------
67
68 /** View ID */
69 public static final String ID = "org.eclipse.linuxtools.tmf.analysis.graph.ui.criticalpath.view.criticalpathview"; //$NON-NLS-1$
70
71 private static final double NANOINV = 0.000000001;
72
73 private static final String COLUMN_PROCESS = Messages.getMessage(Messages.CriticalFlowView_columnProcess);
74 private static final String COLUMN_ELAPSED = Messages.getMessage(Messages.CriticalFlowView_columnElapsed);
75 private static final String COLUMN_PERCENT = Messages.getMessage(Messages.CriticalFlowView_columnPercent);
76
77 private static final String[] COLUMN_NAMES = new String[] {
78 COLUMN_PROCESS,
79 COLUMN_ELAPSED,
80 COLUMN_PERCENT
81 };
82
83 private static final String[] FILTER_COLUMN_NAMES = new String[] {
84 COLUMN_PROCESS
85 };
86
87 private final Table<ITmfTrace, Object, List<ILinkEvent>> fLinks = HashBasedTable.create();
88 /** The trace to entry list hash map */
89 private final Table<ITmfTrace, Object, TmfGraphStatistics> fObjectStatistics = HashBasedTable.create();
90
91 private final CriticalPathContentProvider fContentProvider = new CriticalPathContentProvider();
92
93 private TmfGraphStatistics fStats = new TmfGraphStatistics();
94
95 private static final IGraphWorker DEFAULT_WORKER = new IGraphWorker() {
96 @Override
97 public String getHostId() {
98 return "default"; //$NON-NLS-1$
99 }
100 };
101
102 private class CriticalPathContentProvider implements ITimeGraphContentProvider {
103
104 private final class HorizontalLinksVisitor extends TmfGraphVisitor {
105 private final CriticalPathEntry fDefaultParent;
106 private final Map<String, CriticalPathEntry> fHostEntries;
107 private final TmfGraph fGraph;
108 private final ITmfTrace fTrace;
109 private final HashMap<Object, CriticalPathEntry> fRootList;
110
111 private HorizontalLinksVisitor(CriticalPathEntry defaultParent, Map<String, CriticalPathEntry> hostEntries, TmfGraph graph, ITmfTrace trace, HashMap<Object, CriticalPathEntry> rootList) {
112 fDefaultParent = defaultParent;
113 fHostEntries = hostEntries;
114 fGraph = graph;
115 fTrace = trace;
116 fRootList = rootList;
117 }
118
119 @Override
120 public void visitHead(TmfVertex node) {
121 /* TODO possible null pointer ? */
122 IGraphWorker owner = fGraph.getParentOf(node);
123 if (owner == null) {
124 return;
125 }
126 if (fRootList.containsKey(owner)) {
127 return;
128 }
129 TmfVertex first = fGraph.getHead(owner);
130 TmfVertex last = fGraph.getTail(owner);
131 if (first == null || last == null) {
132 return;
133 }
134 setStartTime(Math.min(getStartTime(), first.getTs()));
135 setEndTime(Math.max(getEndTime(), last.getTs()));
136 // create host entry
137 CriticalPathEntry parent = fDefaultParent;
138 String host = owner.getHostId();
139 if (!fHostEntries.containsKey(host)) {
140 fHostEntries.put(host, new CriticalPathEntry(host, fTrace, getStartTime(), getEndTime(), owner));
141 }
142 parent = checkNotNull(fHostEntries.get(host));
143 CriticalPathEntry entry = new CriticalPathEntry(NonNullUtils.nullToEmptyString(owner), fTrace, getStartTime(), getEndTime(), owner);
144 parent.addChild(entry);
145
146 fRootList.put(owner, entry);
147 }
148
149 @Override
150 public void visit(TmfEdge link, boolean horizontal) {
151 if (horizontal) {
152 Object parent = fGraph.getParentOf(link.getVertexFrom());
153 CriticalPathEntry entry = fRootList.get(parent);
154 TimeEvent ev = new TimeEvent(entry, link.getVertexFrom().getTs(), link.getDuration(),
155 getMatchingState(link.getType()).ordinal());
156 entry.addEvent(ev);
157 }
158 }
159 }
160
161 private final class VerticalLinksVisitor extends TmfGraphVisitor {
162 private final TmfGraph fGraph;
163 private final List<ILinkEvent> fGraphLinks;
164 private final Map<Object, CriticalPathEntry> fEntryMap;
165
166 private VerticalLinksVisitor(TmfGraph graph, List<ILinkEvent> graphLinks, Map<Object, CriticalPathEntry> entryMap) {
167 fGraph = graph;
168 fGraphLinks = graphLinks;
169 fEntryMap = entryMap;
170 }
171
172 @Override
173 public void visitHead(TmfVertex node) {
174
175 }
176
177 @Override
178 public void visit(TmfVertex node) {
179
180 }
181
182 @Override
183 public void visit(TmfEdge link, boolean horizontal) {
184 if (!horizontal) {
185 Object parentFrom = fGraph.getParentOf(link.getVertexFrom());
186 Object parentTo = fGraph.getParentOf(link.getVertexTo());
187 CriticalPathEntry entryFrom = fEntryMap.get(parentFrom);
188 CriticalPathEntry entryTo = fEntryMap.get(parentTo);
189 TimeLinkEvent lk = new TimeLinkEvent(entryFrom, entryTo, link.getVertexFrom().getTs(),
190 link.getVertexTo().getTs() - link.getVertexFrom().getTs(), getMatchingState(link.getType()).ordinal());
191 fGraphLinks.add(lk);
192 }
193 }
194 }
195
196 private class BuildThread extends Thread {
197 private final ITmfTrace fBuildTrace;
198 private final IProgressMonitor fMonitor;
199
200 public BuildThread(final ITmfTrace trace) {
201 super("Critical path view build"); //$NON-NLS-1$
202 fBuildTrace = trace;
203 fMonitor = new NullProgressMonitor();
204 }
205
206 @Override
207 public void run() {
208 try {
209 CriticalPathModule module = Iterables.<@Nullable CriticalPathModule> getFirst(
210 TmfTraceUtils.getAnalysisModulesOfClass(fBuildTrace, CriticalPathModule.class),
211 null);
212 if (module == null) {
213 return;
214 }
215 module.schedule();
216 if (module.waitForCompletion(fMonitor)) {
217 // Module is completed, set the start and end time of
218 // this view
219 setStartEndTime(module);
220 refresh();
221 }
222
223 } finally {
224 fSyncLock.lock();
225 fBuildThread = null;
226 fSyncLock.unlock();
227 }
228 }
229
230 public void cancel() {
231 fMonitor.setCanceled(true);
232 }
233 }
234
235 private final Lock fSyncLock = new ReentrantLock();
236 private final Map<Object, Map<Object, CriticalPathEntry>> workerMaps = new HashMap<>();
237 private final Map<Object, List<TimeGraphEntry>> workerEntries = new HashMap<>();
238 private final Map<Object, List<ILinkEvent>> linkMap = new HashMap<>();
239 private @Nullable Object fCurrentObject;
240 private @Nullable BuildThread fBuildThread = null;
241
242 @Override
243 public ITimeGraphEntry[] getElements(@Nullable Object inputElement) {
244 ITimeGraphEntry[] ret = new ITimeGraphEntry[0];
245 if (inputElement instanceof List) {
246 List<?> list = (List<?>) inputElement;
247 if (!list.isEmpty()) {
248 Object first = list.get(0);
249 if (first instanceof CriticalPathBaseEntry) {
250 IGraphWorker worker = ((CriticalPathBaseEntry) first).getWorker();
251 ret = getWorkerEntries(worker);
252 }
253 }
254 }
255 return ret;
256 }
257
258 private ITimeGraphEntry[] getWorkerEntries(IGraphWorker worker) {
259 fCurrentObject = worker;
260 List<TimeGraphEntry> entries = workerEntries.get(worker);
261 ITmfTrace trace = getTrace();
262 if (entries == null) {
263 buildEntryList(worker);
264 entries = workerEntries.get(worker);
265 } else if (trace != null) {
266 // Get the statistics object for this worker
267 TmfGraphStatistics stats = fObjectStatistics.get(trace, worker);
268 if (stats == null) {
269 stats = new TmfGraphStatistics();
270 final TmfGraph graph = getGraph(trace);
271 if (graph != null) {
272 stats.computeGraphStatistics(graph, worker);
273 }
274 }
275 fStats = stats;
276 }
277
278 return (entries == null) ?
279 new ITimeGraphEntry[0] :
280 entries.toArray(new @NonNull ITimeGraphEntry[entries.size()]);
281 }
282
283 private void buildEntryList(IGraphWorker worker) {
284 final ITmfTrace trace = getTrace();
285 if (trace == null) {
286 return;
287 }
288 final TmfGraph graph = getGraph(trace);
289 if (graph == null) {
290 return;
291 }
292
293 final HashMap<Object, CriticalPathEntry> rootList = new HashMap<>();
294 fLinks.remove(trace, worker);
295
296 TmfVertex vertex = graph.getHead();
297
298 /* Calculate statistics */
299 fStats = new TmfGraphStatistics();
300 fStats.computeGraphStatistics(graph, worker);
301 fObjectStatistics.put(trace, worker, fStats);
302
303 // Hosts entries are parent of each worker entries
304 final Map<String, CriticalPathEntry> hostEntries = new HashMap<>();
305
306 /* create all interval entries and horizontal links */
307
308 final CriticalPathEntry defaultParent = new CriticalPathEntry("default", trace, getStartTime(), getEndTime(), DEFAULT_WORKER); //$NON-NLS-1$
309 graph.scanLineTraverse(vertex, new HorizontalLinksVisitor(defaultParent, hostEntries, graph, trace, rootList));
310
311 workerMaps.put(worker, rootList);
312
313 List<TimeGraphEntry> list = new ArrayList<>();
314 list.addAll(hostEntries.values());
315 if (defaultParent.hasChildren()) {
316 list.add(defaultParent);
317 }
318
319 workerEntries.put(worker, list);
320 }
321
322 private @Nullable TmfGraph getGraph(final ITmfTrace trace) {
323 CriticalPathModule module = Iterables.<@Nullable CriticalPathModule> getFirst(
324 TmfTraceUtils.getAnalysisModulesOfClass(trace, CriticalPathModule.class),
325 null);
326 if (module == null) {
327 throw new IllegalStateException("View requires an analysis module"); //$NON-NLS-1$
328 }
329
330 final TmfGraph graph = module.getCriticalPath();
331 return graph;
332 }
333
334 public @Nullable List<ILinkEvent> getLinkList(long startTime, long endTime) {
335 Object current = fCurrentObject;
336 if (current == null) {
337 return null;
338 }
339 /*
340 * Critical path typically has relatively few links, so we calculate
341 * and save them all, but just return those in range
342 */
343 List<ILinkEvent> links = linkMap.get(current);
344 if (links != null) {
345 return links;
346 }
347 final ITmfTrace trace = getTrace();
348 if (trace == null) {
349 return null;
350 }
351 CriticalPathModule module = Iterables.<@Nullable CriticalPathModule> getFirst(
352 TmfTraceUtils.getAnalysisModulesOfClass(trace, CriticalPathModule.class), null);
353 if (module == null) {
354 throw new IllegalStateException("View requires an analysis module"); //$NON-NLS-1$
355 }
356
357 final TmfGraph graph = module.getCriticalPath();
358 if (graph == null) {
359 return null;
360 }
361 final Map<Object, CriticalPathEntry> entryMap = workerMaps.get(current);
362 if (entryMap == null) {
363 return null;
364 }
365
366 TmfVertex vertex = graph.getHead();
367
368 final List<ILinkEvent> graphLinks = new ArrayList<>();
369
370 /* find vertical links */
371 graph.scanLineTraverse(vertex, new VerticalLinksVisitor(graph, graphLinks, entryMap));
372 fLinks.put(trace, checkNotNull(fCurrentObject), graphLinks);
373 links = graphLinks;
374
375 List<ILinkEvent> linksInRange = new ArrayList<>();
376 for (ILinkEvent link : links) {
377 if (((link.getTime() >= startTime) && (link.getTime() <= endTime)) ||
378 ((link.getTime() + link.getDuration() >= startTime) && (link.getTime() + link.getDuration() <= endTime))) {
379 linksInRange.add(link);
380 }
381 }
382 return linksInRange;
383 }
384
385 @Override
386 public void dispose() {
387 fSyncLock.lock();
388 try {
389 BuildThread buildThread = fBuildThread;
390 if (buildThread != null) {
391 buildThread.cancel();
392 }
393 } finally {
394 fSyncLock.unlock();
395 }
396 }
397
398 @Override
399 public void inputChanged(@Nullable Viewer viewer, @Nullable Object oldInput, @Nullable Object newInput) {
400 // The input has changed, the critical path will be re-computed,
401 // wait for the analysis to be finished, then call the refresh
402 // method of the view
403 if (!(newInput instanceof List)) {
404 return;
405 }
406 List<?> list = (List<?>) newInput;
407 if (list.isEmpty()) {
408 return;
409 }
410 final ITmfTrace trace = getTrace();
411 if (trace == null) {
412 return;
413 }
414
415 fSyncLock.lock();
416 try {
417 BuildThread buildThread = fBuildThread;
418 if (buildThread != null) {
419 buildThread.cancel();
420 }
421 buildThread = new BuildThread(trace);
422 buildThread.start();
423 fBuildThread = buildThread;
424 } finally {
425 fSyncLock.unlock();
426 }
427 }
428
429 @Override
430 public ITimeGraphEntry @Nullable [] getChildren(@Nullable Object parentElement) {
431 if (parentElement instanceof CriticalPathEntry) {
432 List<? extends ITimeGraphEntry> children = ((CriticalPathEntry) parentElement).getChildren();
433 return children.toArray(new TimeGraphEntry[children.size()]);
434 }
435 return null;
436 }
437
438 @Override
439 public @Nullable ITimeGraphEntry getParent(@Nullable Object element) {
440 if (element instanceof CriticalPathEntry) {
441 return ((CriticalPathEntry) element).getParent();
442 }
443 return null;
444 }
445
446 @Override
447 public boolean hasChildren(@Nullable Object element) {
448 if (element instanceof CriticalPathEntry) {
449 return ((CriticalPathEntry) element).hasChildren();
450 }
451 return false;
452 }
453
454 }
455
456 private class CriticalPathTreeLabelProvider extends TreeLabelProvider {
457
458 @Override
459 public String getColumnText(@Nullable Object element, int columnIndex) {
460 if (element == null) {
461 return ""; //$NON-NLS-1$
462 }
463 CriticalPathEntry entry = (CriticalPathEntry) element;
464 if (columnIndex == 0) {
465 return NonNullUtils.nullToEmptyString(entry.getName());
466 } else if (columnIndex == 1) {
467 Long sum = fStats.getSum(entry.getWorker());
468 String value = String.format("%.9f", sum * NANOINV); //$NON-NLS-1$
469 return NonNullUtils.nullToEmptyString(value);
470 } else if (columnIndex == 2) {
471 Double percent = fStats.getPercent(entry.getWorker());
472 String value = String.format("%.2f", percent * 100); //$NON-NLS-1$
473 return NonNullUtils.nullToEmptyString(value);
474 }
475 return ""; //$NON-NLS-1$
476 }
477
478 }
479
480 private class CriticalPathEntryComparator implements Comparator<ITimeGraphEntry> {
481
482 @Override
483 public int compare(@Nullable ITimeGraphEntry o1, @Nullable ITimeGraphEntry o2) {
484
485 int result = 0;
486
487 if ((o1 instanceof CriticalPathEntry) && (o2 instanceof CriticalPathEntry)) {
488 CriticalPathEntry entry1 = (CriticalPathEntry) o1;
489 CriticalPathEntry entry2 = (CriticalPathEntry) o2;
490 result = -1 * fStats.getSum(entry1.getWorker()).compareTo(fStats.getSum(entry2.getWorker()));
491 }
492 return result;
493 }
494 }
495
496 /**
497 * Constructor
498 */
499 public CriticalPathView() {
500 super(ID, new CriticalPathPresentationProvider());
501 setTreeColumns(COLUMN_NAMES);
502 setFilterColumns(FILTER_COLUMN_NAMES);
503 setTreeLabelProvider(new CriticalPathTreeLabelProvider());
504 setTimeGraphContentProvider(fContentProvider);
505 setEntryComparator(new CriticalPathEntryComparator());
506 }
507
508 // ------------------------------------------------------------------------
509 // Internal
510 // ------------------------------------------------------------------------
511
512 private static State getMatchingState(EdgeType type) {
513 State state = State.UNKNOWN;
514 switch (type) {
515 case RUNNING:
516 state = State.RUNNING;
517 break;
518 case PREEMPTED:
519 state = State.PREEMPTED;
520 break;
521 case TIMER:
522 state = State.TIMER;
523 break;
524 case BLOCK_DEVICE:
525 state = State.BLOCK_DEVICE;
526 break;
527 case INTERRUPTED:
528 state = State.INTERRUPTED;
529 break;
530 case NETWORK:
531 state = State.NETWORK;
532 break;
533 case USER_INPUT:
534 state = State.USER_INPUT;
535 break;
536 case IPI:
537 state = State.IPI;
538 break;
539 case EPS:
540 case UNKNOWN:
541 case DEFAULT:
542 case BLOCKED:
543 break;
544 default:
545 break;
546 }
547 return state;
548 }
549
550 @Override
551 protected void buildEntryList(@NonNull ITmfTrace trace, @NonNull ITmfTrace parentTrace, @NonNull IProgressMonitor monitor) {
552 /* This class uses a content provider instead */
553 }
554
555 @Override
556 protected @Nullable List<ITimeEvent> getEventList(TimeGraphEntry entry,
557 long startTime, long endTime, long resolution,
558 IProgressMonitor monitor) {
559 /*
560 * The event list is built in the HorizontalLinksVisitor. This is called
561 * only from the zoom thread and only for the CriticalPathBaseEntry.
562 */
563 return null;
564 }
565
566 @Override
567 protected @Nullable List<ILinkEvent> getLinkList(long startTime, long endTime, long resolution, IProgressMonitor monitor) {
568 return fContentProvider.getLinkList(startTime, endTime);
569 }
570
571 /**
572 * Signal handler for analysis started
573 *
574 * @param signal
575 * The signal
576 */
577 @TmfSignalHandler
578 public void analysisStarted(TmfStartAnalysisSignal signal) {
579 if (!(signal.getAnalysisModule() instanceof CriticalPathModule)) {
580 return;
581 }
582 CriticalPathModule module = (CriticalPathModule) signal.getAnalysisModule();
583 Object obj = module.getParameter(CriticalPathModule.PARAM_WORKER);
584 if (obj == null) {
585 return;
586 }
587 if (!(obj instanceof IGraphWorker)) {
588 throw new IllegalStateException("Wrong type for critical path module parameter " + //$NON-NLS-1$
589 CriticalPathModule.PARAM_WORKER +
590 " expected IGraphWorker got " + //$NON-NLS-1$
591 obj.getClass().getSimpleName());
592 }
593 ITmfTrace trace = getTrace();
594 if (trace == null) {
595 throw new IllegalStateException("Trace is null"); //$NON-NLS-1$
596 }
597 IGraphWorker worker = (IGraphWorker) obj;
598
599 TimeGraphEntry tge = new CriticalPathBaseEntry(worker);
600 List<TimeGraphEntry> list = Collections.singletonList(tge);
601 putEntryList(trace, list);
602 refresh();
603 }
604
605 private void setStartEndTime(CriticalPathModule module) {
606 // Initialize the start/end time of the view to trace's times
607 ITmfTrace trace = getTrace();
608 if (trace == null) {
609 throw new IllegalStateException("The trace should not be null when we have a critical path to display"); //$NON-NLS-1$
610 }
611 long start = trace.getStartTime().toNanos();
612 long end = trace.getEndTime().toNanos();
613
614 // Set the start/end time of the view
615 Object paramGraph = module.getParameter(CriticalPathModule.PARAM_GRAPH);
616 if (paramGraph instanceof TmfGraphBuilderModule) {
617 TmfGraphBuilderModule graphModule = (TmfGraphBuilderModule) paramGraph;
618 TmfGraph graph = graphModule.getGraph();
619 if (graph == null) {
620 return;
621 }
622 TmfVertex head = graph.getHead();
623 if (head != null) {
624 start = Math.min(start, head.getTs());
625 for (IGraphWorker w : graph.getWorkers()) {
626 TmfVertex tail = graph.getTail(w);
627 if (tail != null) {
628 end = Math.max(end, tail.getTs());
629 }
630 }
631 }
632 }
633 setStartTime(start);
634 setEndTime(end);
635 }
636
637 }
This page took 0.087984 seconds and 5 git commands to generate.