2d690eb45bdc25f264ecfbab21b98963c87c3772
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.os.linux.ui / src / org / eclipse / tracecompass / internal / analysis / os / linux / ui / views / controlflow / ControlFlowView.java
1 /*******************************************************************************
2 * Copyright (c) 2012, 2016 Ericsson, École Polytechnique de Montréal and others.
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 view
12 * Christian Mansky - Add check active / uncheck inactive buttons
13 * Mahdi Zolnouri & Samuel Gagnon - Add flat / hierarchical button
14 *******************************************************************************/
15
16 package org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.controlflow;
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.Comparator;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27 import java.util.function.Function;
28 import java.util.function.Predicate;
29 import java.util.stream.Collectors;
30 import java.util.stream.Stream;
31
32 import org.eclipse.core.runtime.IProgressMonitor;
33 import org.eclipse.core.runtime.IStatus;
34 import org.eclipse.core.runtime.Status;
35 import org.eclipse.core.runtime.jobs.ISchedulingRule;
36 import org.eclipse.core.runtime.jobs.Job;
37 import org.eclipse.jdt.annotation.NonNull;
38 import org.eclipse.jdt.annotation.Nullable;
39 import org.eclipse.jface.action.Action;
40 import org.eclipse.jface.action.IAction;
41 import org.eclipse.jface.action.IMenuManager;
42 import org.eclipse.jface.action.IToolBarManager;
43 import org.eclipse.jface.action.MenuManager;
44 import org.eclipse.jface.action.Separator;
45 import org.eclipse.jface.dialogs.IDialogSettings;
46 import org.eclipse.jface.viewers.ISelection;
47 import org.eclipse.jface.viewers.StructuredSelection;
48 import org.eclipse.swt.widgets.Composite;
49 import org.eclipse.swt.widgets.Event;
50 import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule;
51 import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelTidAspect;
52 import org.eclipse.tracecompass.common.core.StreamUtils.StreamFlattener;
53 import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attributes;
54 import org.eclipse.tracecompass.internal.analysis.os.linux.ui.Activator;
55 import org.eclipse.tracecompass.internal.analysis.os.linux.ui.Messages;
56 import org.eclipse.tracecompass.internal.analysis.os.linux.ui.actions.FollowThreadAction;
57 import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
58 import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
59 import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
60 import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
61 import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
62 import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
63 import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
64 import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
65 import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
66 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
67 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
68 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
69 import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
70 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
71 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
72 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
73 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
74 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
75 import org.eclipse.tracecompass.tmf.core.util.Pair;
76 import org.eclipse.tracecompass.tmf.ui.views.timegraph.AbstractStateSystemTimeGraphView;
77 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ILinkEvent;
78 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent;
79 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
80 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.NullTimeEvent;
81 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeEvent;
82 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeGraphEntry;
83 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.TimeLinkEvent;
84 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
85 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils;
86 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.Resolution;
87 import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
88
89 import com.google.common.collect.ImmutableList;
90
91 /**
92 * The Control Flow view main object
93 *
94 */
95 public class ControlFlowView extends AbstractStateSystemTimeGraphView {
96
97 // ------------------------------------------------------------------------
98 // Constants
99 // ------------------------------------------------------------------------
100 /**
101 * View ID.
102 */
103 public static final @NonNull String ID = "org.eclipse.tracecompass.analysis.os.linux.views.controlflow"; //$NON-NLS-1$
104
105 private static final String ICONS_PATH = "icons/"; //$NON-NLS-1$
106 private static final String OPTIMIZE_ICON = ICONS_PATH + "elcl16/Optimization.png"; //$NON-NLS-1$
107
108 private static final String PROCESS_COLUMN = Messages.ControlFlowView_processColumn;
109 private static final String TID_COLUMN = Messages.ControlFlowView_tidColumn;
110 private static final String PTID_COLUMN = Messages.ControlFlowView_ptidColumn;
111 private static final String BIRTH_TIME_COLUMN = Messages.ControlFlowView_birthTimeColumn;
112 private static final String INVISIBLE_COLUMN = Messages.ControlFlowView_invisibleColumn;
113 private Action fOptimizationAction;
114
115 private static final String NEXT_EVENT_ICON_PATH = "icons/elcl16/shift_r_edit.gif"; //$NON-NLS-1$
116 private static final String PREV_EVENT_ICON_PATH = "icons/elcl16/shift_l_edit.gif"; //$NON-NLS-1$
117
118 private static final String[] COLUMN_NAMES = new String[] {
119 PROCESS_COLUMN,
120 TID_COLUMN,
121 PTID_COLUMN,
122 BIRTH_TIME_COLUMN
123 };
124
125 private static final String[] FILTER_COLUMN_NAMES = new String[] {
126 PROCESS_COLUMN,
127 TID_COLUMN
128 };
129
130 // Timeout between updates in the build thread in ms
131 private static final long BUILD_UPDATE_TIMEOUT = 500;
132
133 private static final Comparator<ITimeGraphEntry>[] COLUMN_COMPARATORS;
134
135 private final Function<Collection<ILinkEvent>, Map<Integer, Long>> UPDATE_SCHEDULING_COLUMN_ALGO = new NaiveOptimizationAlgorithm();
136
137 private static final int INITIAL_SORT_COLUMN_INDEX = 3;
138
139 static {
140 ImmutableList.Builder<Comparator<ITimeGraphEntry>> builder = ImmutableList.builder();
141 builder.add(ControlFlowColumnComparators.PROCESS_NAME_COLUMN_COMPARATOR)
142 .add(ControlFlowColumnComparators.TID_COLUMN_COMPARATOR)
143 .add(ControlFlowColumnComparators.PTID_COLUMN_COMPARATOR)
144 .add(ControlFlowColumnComparators.BIRTH_TIME_COLUMN_COMPARATOR);
145 List<Comparator<ITimeGraphEntry>> l = builder.build();
146 COLUMN_COMPARATORS = l.toArray(new Comparator[l.size()]);
147 }
148
149 /**
150 * Mutex rule for search action jobs, making sure they execute sequentially
151 */
152 private final ISchedulingRule fSearchActionMutexRule = new ISchedulingRule() {
153 @Override
154 public boolean isConflicting(ISchedulingRule rule) {
155 return (rule == this);
156 }
157
158 @Override
159 public boolean contains(ISchedulingRule rule) {
160 return (rule == this);
161 }
162 };
163
164 private final Set<ITmfTrace> fFlatTraces = new HashSet<>();
165
166 private IAction fFlatAction;
167
168 private IAction fHierarchicalAction;
169 private IAction fPreviousEventAction;
170 private IAction fNextEventAction;
171
172 // ------------------------------------------------------------------------
173 // Constructors
174 // ------------------------------------------------------------------------
175
176 /**
177 * Constructor
178 */
179 public ControlFlowView() {
180 super(ID, new ControlFlowPresentationProvider());
181 setTreeColumns(COLUMN_NAMES, COLUMN_COMPARATORS, INITIAL_SORT_COLUMN_INDEX);
182 setTreeLabelProvider(new ControlFlowTreeLabelProvider());
183 setFilterColumns(FILTER_COLUMN_NAMES);
184 setFilterLabelProvider(new ControlFlowFilterLabelProvider());
185 setEntryComparator(ControlFlowColumnComparators.BIRTH_TIME_COLUMN_COMPARATOR);
186 }
187
188 @Override
189 public void createPartControl(Composite parent) {
190 super.createPartControl(parent);
191 // add "Check active" Button to TimeGraphFilterDialog
192 super.getTimeGraphCombo().addTimeGraphFilterCheckActiveButton(
193 new ControlFlowCheckActiveProvider(Messages.ControlFlowView_checkActiveLabel, Messages.ControlFlowView_checkActiveToolTip));
194 // add "Uncheck inactive" Button to TimeGraphFilterDialog
195 super.getTimeGraphCombo().addTimeGraphFilterUncheckInactiveButton(
196 new ControlFlowCheckActiveProvider(Messages.ControlFlowView_uncheckInactiveLabel, Messages.ControlFlowView_uncheckInactiveToolTip));
197 }
198
199 /**
200 * @since 2.0
201 */
202 @Override
203 protected void fillTimeGraphEntryContextMenu(@NonNull IMenuManager menuManager) {
204 ISelection selection = getSite().getSelectionProvider().getSelection();
205 if (selection instanceof StructuredSelection) {
206 StructuredSelection sSel = (StructuredSelection) selection;
207 if (sSel.getFirstElement() instanceof ControlFlowEntry) {
208 ControlFlowEntry entry = (ControlFlowEntry) sSel.getFirstElement();
209 menuManager.add(new FollowThreadAction(ControlFlowView.this, entry.getName(), entry.getThreadId(), entry.getTrace()));
210 }
211 }
212 }
213
214 @Override
215 protected void fillLocalToolBar(IToolBarManager manager) {
216 // add "Optimization" Button to local tool bar of Controlflow
217 IAction optimizationAction = getOptimizationAction();
218 manager.add(optimizationAction);
219
220 // add a separator to local tool bar
221 manager.add(new Separator());
222
223 super.fillLocalToolBar(manager);
224 IDialogSettings settings = Activator.getDefault().getDialogSettings();
225 IDialogSettings section = settings.getSection(getClass().getName());
226 if (section == null) {
227 section = settings.addNewSection(getClass().getName());
228 }
229
230 IAction hideArrowsAction = getTimeGraphCombo().getTimeGraphViewer().getHideArrowsAction(section);
231 manager.add(hideArrowsAction);
232
233 IAction followArrowBwdAction = getTimeGraphCombo().getTimeGraphViewer().getFollowArrowBwdAction();
234 followArrowBwdAction.setText(Messages.ControlFlowView_followCPUBwdText);
235 followArrowBwdAction.setToolTipText(Messages.ControlFlowView_followCPUBwdText);
236 manager.add(followArrowBwdAction);
237
238 IAction followArrowFwdAction = getTimeGraphCombo().getTimeGraphViewer().getFollowArrowFwdAction();
239 followArrowFwdAction.setText(Messages.ControlFlowView_followCPUFwdText);
240 followArrowFwdAction.setToolTipText(Messages.ControlFlowView_followCPUFwdText);
241 manager.add(followArrowFwdAction);
242
243 fPreviousEventAction = new SearchEventAction(false, PackageMessages.ControlFlowView_PreviousEventJobName);
244 fPreviousEventAction.setText(PackageMessages.ControlFlowView_PreviousEventActionName);
245 fPreviousEventAction.setToolTipText(PackageMessages.ControlFlowView_PreviousEventActionTooltip);
246 fPreviousEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(PREV_EVENT_ICON_PATH));
247 manager.add(fPreviousEventAction);
248
249 fNextEventAction = new SearchEventAction(true, PackageMessages.ControlFlowView_NextEventJobName);
250 fNextEventAction.setText(PackageMessages.ControlFlowView_NextEventActionName);
251 fNextEventAction.setToolTipText(PackageMessages.ControlFlowView_NextEventActionTooltip);
252 fNextEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(NEXT_EVENT_ICON_PATH));
253 manager.add(fNextEventAction);
254 }
255
256 private IAction getOptimizationAction() {
257 if (fOptimizationAction == null) {
258 fOptimizationAction = new OptimizationAction();
259 fOptimizationAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(OPTIMIZE_ICON));
260 fOptimizationAction.setText(Messages.ControlFlowView_optimizeLabel);
261 fOptimizationAction.setToolTipText(Messages.ControlFlowView_optimizeToolTip);
262 }
263 return fOptimizationAction;
264 }
265
266 @Override
267 protected void fillLocalMenu(IMenuManager manager) {
268 super.fillLocalMenu(manager);
269 final MenuManager item = new MenuManager(Messages.ControlFlowView_threadPresentation);
270 fFlatAction = createFlatAction();
271 item.add(fFlatAction);
272
273 fHierarchicalAction = createHierarchicalAction();
274 item.add(fHierarchicalAction);
275 manager.add(item);
276
277 }
278
279 /**
280 * Base Action for the "Go to Next/Previous Event for thread" actions
281 */
282 private class SearchEventAction extends Action {
283
284 private final boolean ifDirection;
285 private final String ifJobName;
286
287 /**
288 * Constructor
289 *
290 * @param direction
291 * The direction of the search, "true" for forwards and
292 * "false" for backwards.
293 * @param jobName
294 * The name of the job that will be spawned
295 */
296 public SearchEventAction(boolean direction, String jobName) {
297 ifDirection = direction;
298 ifJobName = jobName;
299 }
300
301 @Override
302 public void run() {
303 Job job = new Job(ifJobName) {
304 @Override
305 protected IStatus run(IProgressMonitor monitor) {
306 TimeGraphControl ctrl = getTimeGraphViewer().getTimeGraphControl();
307 ITimeGraphEntry traceEntry = ctrl.getSelectedTrace();
308
309 long ts = getTimeGraphViewer().getSelectionBegin();
310 ITimeEvent selectedState = Utils.findEvent(traceEntry, ts, 0);
311
312 if (selectedState == null) {
313 /* No selection currently in the view, do nothing */
314 return Status.OK_STATUS;
315 }
316 ITimeGraphEntry entry = selectedState.getEntry();
317 if (!(entry instanceof ControlFlowEntry)) {
318 return Status.OK_STATUS;
319 }
320 ControlFlowEntry cfEntry = (ControlFlowEntry) entry;
321 int tid = cfEntry.getThreadId();
322
323 ITmfTrace trace = cfEntry.getTrace();
324 ITmfContext ctx = trace.seekEvent(TmfTimestamp.fromNanos(ts));
325 long rank = ctx.getRank();
326 ctx.dispose();
327
328 Predicate<@NonNull ITmfEvent> predicate = event -> {
329 /*
330 * TODO Specific to the Control Flow View and kernel
331 * traces for now. Could be eventually generalized to
332 * anything represented by the time graph row.
333 */
334 Integer eventTid = KernelTidAspect.INSTANCE.resolve(event);
335 return (eventTid != null && eventTid.intValue() == tid);
336 };
337
338 ITmfEvent event = (ifDirection ?
339 TmfTraceUtils.getNextEventMatching(cfEntry.getTrace(), rank, predicate, monitor) :
340 TmfTraceUtils.getPreviousEventMatching(cfEntry.getTrace(), rank, predicate, monitor));
341 if (event != null) {
342 TmfSignalManager.dispatchSignal(new TmfSelectionRangeUpdatedSignal(this, event.getTimestamp()));
343 }
344 return Status.OK_STATUS;
345
346 }
347 };
348 /*
349 * Make subsequent jobs not run concurrently, but wait after one
350 * another.
351 */
352 job.setRule(fSearchActionMutexRule);
353 job.schedule();
354 }
355 }
356
357 private IAction createHierarchicalAction() {
358 IAction action = new Action(Messages.ControlFlowView_hierarchicalViewLabel, IAction.AS_RADIO_BUTTON) {
359 @Override
360 public void run() {
361 ITmfTrace parentTrace = getTrace();
362 synchronized (fFlatTraces) {
363 fFlatTraces.remove(parentTrace);
364 for (ITmfTrace trace : TmfTraceManager.getTraceSet(parentTrace)) {
365 final ITmfStateSystem ss = TmfStateSystemAnalysisModule.getStateSystem(trace, KernelAnalysisModule.ID);
366 for (TimeGraphEntry traceEntry : getEntryList(ss)) {
367 List<ControlFlowEntry> currentRootList = traceEntry.getChildren().stream()
368 .filter(e -> e instanceof ControlFlowEntry)
369 .map(e -> (ControlFlowEntry) e)
370 .collect(Collectors.toList());
371 addEntriesToHierarchicalTree(currentRootList, traceEntry);
372 }
373 }
374 }
375 refresh();
376 }
377 };
378 action.setChecked(true);
379 action.setToolTipText(Messages.ControlFlowView_hierarchicalViewToolTip);
380 return action;
381 }
382
383 private IAction createFlatAction() {
384 IAction action = new Action(Messages.ControlFlowView_flatViewLabel, IAction.AS_RADIO_BUTTON) {
385 @Override
386 public void run() {
387 ITmfTrace parentTrace = getTrace();
388 synchronized (fFlatTraces) {
389 fFlatTraces.add(parentTrace);
390 for (ITmfTrace trace : TmfTraceManager.getTraceSet(parentTrace)) {
391 final ITmfStateSystem ss = TmfStateSystemAnalysisModule.getStateSystem(trace, KernelAnalysisModule.ID);
392 List<@NonNull TimeGraphEntry> entryList = getEntryList(ss);
393 if (entryList != null) {
394 for (TimeGraphEntry traceEntry : entryList) {
395 hierarchicalToFlatTree(traceEntry);
396 }
397 }
398 }
399 }
400 refresh();
401 }
402 };
403 action.setToolTipText(Messages.ControlFlowView_flatViewToolTip);
404 return action;
405 }
406
407 @Override
408 protected String getNextText() {
409 return Messages.ControlFlowView_nextProcessActionNameText;
410 }
411
412 @Override
413 protected String getNextTooltip() {
414 return Messages.ControlFlowView_nextProcessActionToolTipText;
415 }
416
417 @Override
418 protected String getPrevText() {
419 return Messages.ControlFlowView_previousProcessActionNameText;
420 }
421
422 @Override
423 protected String getPrevTooltip() {
424 return Messages.ControlFlowView_previousProcessActionToolTipText;
425 }
426
427 /**
428 * Get the optimization function for the scheduling column. In the base
429 * implementation, this optimizes by Line arrows, but can be overidden.
430 * <p>
431 * It takes a collection of link events, looking at the entries being
432 * linked, and returns a list of the proposed order. The list of indexes
433 * should be in ascending order. There can be duplicates, but the values and
434 * order should always be the same for the same input.
435 *
436 * @return the returned column order, where the integer is the tid of the
437 * entry, and the return value is the position, there can be
438 * duplicates.
439 */
440 public Function<Collection<ILinkEvent>, Map<Integer, Long>> getUpdatedSchedulingColumn() {
441 return UPDATE_SCHEDULING_COLUMN_ALGO;
442 }
443
444 /**
445 * This is an optimization action used to find cliques of entries due to
446 * links and put them closer together
447 *
448 * @author Samuel Gagnon
449 */
450 private final class OptimizationAction extends Action {
451
452 @Override
453 public void runWithEvent(Event event) {
454 ITmfTrace trace = getTrace();
455 if (trace == null) {
456 return;
457 }
458
459 createFlatAction().run();
460
461 /*
462 * This method only returns the arrows in the current time interval
463 * [a,b] of ControlFlowView. Thus, we only optimize for that time
464 * interval
465 */
466 List<ILinkEvent> arrows = getTimeGraphViewer().getTimeGraphControl().getArrows();
467 final ITmfStateSystem ss = TmfStateSystemAnalysisModule.getStateSystem(trace, KernelAnalysisModule.ID);
468 List<TimeGraphEntry> currentList = getEntryList(ss);
469
470 Map<Integer, Long> orderedTidMap = getUpdatedSchedulingColumn().apply(arrows);
471
472 /*
473 * Now that we have our list of ordered tid, it's time to assign a
474 * position for each threads in the view. For this, we assign a
475 * value to an invisible column and sort according to the values in
476 * this column.
477 */
478 for (TimeGraphEntry entry : currentList) {
479 if (entry instanceof TraceEntry) {
480 for (TimeGraphEntry child : ((TraceEntry) entry).getChildren()) {
481 if (child instanceof ControlFlowEntry) {
482 ControlFlowEntry cEntry = (ControlFlowEntry) child;
483 /*
484 * If the thread is in our list, we give it a
485 * position. Otherwise, it means there's no activity
486 * in the current interval for that thread. We set
487 * its position to Long.MAX_VALUE so it goes to the
488 * bottom.
489 */
490 cEntry.setSchedulingPosition(orderedTidMap.getOrDefault(cEntry.getThreadId(), Long.MAX_VALUE));
491 }
492 }
493 }
494 }
495
496 setEntryComparator(ControlFlowColumnComparators.SCHEDULING_COLUMN_COMPARATOR);
497 refresh();
498 }
499
500 }
501
502 /**
503 * @author gbastien
504 *
505 */
506 protected static class ControlFlowTreeLabelProvider extends TreeLabelProvider {
507
508 @Override
509 public String getColumnText(Object element, int columnIndex) {
510 if (element instanceof TraceEntry) {
511 if (columnIndex == 0) {
512 return ((TraceEntry) element).getName();
513 }
514 return ""; //$NON-NLS-1$
515 }
516 ControlFlowEntry entry = (ControlFlowEntry) element;
517
518 if (COLUMN_NAMES[columnIndex].equals(Messages.ControlFlowView_processColumn)) {
519 return entry.getName();
520 } else if (COLUMN_NAMES[columnIndex].equals(Messages.ControlFlowView_tidColumn)) {
521 return Integer.toString(entry.getThreadId());
522 } else if (COLUMN_NAMES[columnIndex].equals(Messages.ControlFlowView_ptidColumn)) {
523 if (entry.getParentThreadId() > 0) {
524 return Integer.toString(entry.getParentThreadId());
525 }
526 } else if (COLUMN_NAMES[columnIndex].equals(Messages.ControlFlowView_birthTimeColumn)) {
527 return Utils.formatTime(entry.getStartTime(), TimeFormat.CALENDAR, Resolution.NANOSEC);
528 } else if (COLUMN_NAMES[columnIndex].equals(Messages.ControlFlowView_traceColumn)) {
529 return entry.getTrace().getName();
530 } else if (COLUMN_NAMES[columnIndex].equals(INVISIBLE_COLUMN)) {
531 return Long.toString(entry.getSchedulingPosition());
532 }
533 return ""; //$NON-NLS-1$
534 }
535
536 }
537
538 private static class ControlFlowFilterLabelProvider extends TreeLabelProvider {
539
540 @Override
541 public String getColumnText(Object element, int columnIndex) {
542 if (element instanceof TraceEntry) {
543 if (columnIndex == 0) {
544 return ((TraceEntry) element).getName();
545 }
546 return ""; //$NON-NLS-1$
547 }
548 ControlFlowEntry entry = (ControlFlowEntry) element;
549
550 if (columnIndex == 0) {
551 return entry.getName();
552 } else if (columnIndex == 1) {
553 return Integer.toString(entry.getThreadId());
554 }
555 return ""; //$NON-NLS-1$
556 }
557
558 }
559
560 private static class TraceEntry extends TimeGraphEntry {
561
562 public TraceEntry(String name, long startTime, long endTime) {
563 super(name, startTime, endTime);
564 }
565
566 @Override
567 public boolean hasTimeEvents() {
568 return false;
569 }
570 }
571
572 @TmfSignalHandler
573 @Override
574 public void traceClosed(org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal signal) {
575 super.traceClosed(signal);
576 synchronized (fFlatTraces) {
577 fFlatTraces.remove(signal.getTrace());
578 }
579 }
580
581 @TmfSignalHandler
582 @Override
583 public void traceSelected(TmfTraceSelectedSignal signal) {
584 super.traceSelected(signal);
585 synchronized (fFlatTraces) {
586 if (fFlatTraces.contains(signal.getTrace())) {
587 fHierarchicalAction.setChecked(false);
588 fFlatAction.setChecked(true);
589 } else {
590 fFlatAction.setChecked(false);
591 fHierarchicalAction.setChecked(true);
592 }
593 }
594 }
595
596 // ------------------------------------------------------------------------
597 // Internal
598 // ------------------------------------------------------------------------
599
600 @Override
601 protected void buildEntryList(final ITmfTrace trace, final ITmfTrace parentTrace, final IProgressMonitor monitor) {
602 final ITmfStateSystem ssq = TmfStateSystemAnalysisModule.getStateSystem(trace, KernelAnalysisModule.ID);
603 if (ssq == null) {
604 return;
605 }
606
607 final List<ControlFlowEntry> entryList = new ArrayList<>();
608 /** Map of trace entries */
609 Map<ITmfTrace, TraceEntry> traceEntryMap = new HashMap<>();
610 /** Map of control flow entries, key is a pair [threadId, cpuId] */
611 final Map<Pair<Integer, Integer>, ControlFlowEntry> entryMap = new HashMap<>();
612
613 long start = ssq.getStartTime();
614 setStartTime(Math.min(getStartTime(), start));
615
616 boolean complete = false;
617 while (!complete) {
618 if (monitor.isCanceled()) {
619 return;
620 }
621 complete = ssq.waitUntilBuilt(BUILD_UPDATE_TIMEOUT);
622 if (ssq.isCancelled()) {
623 return;
624 }
625 long end = ssq.getCurrentEndTime();
626 if (start == end && !complete) { // when complete execute one last time regardless of end time
627 continue;
628 }
629
630 TraceEntry aTraceEntry = traceEntryMap.get(trace);
631 if (aTraceEntry == null) {
632 aTraceEntry = new TraceEntry(trace.getName(), start, end + 1);
633 traceEntryMap.put(trace, aTraceEntry);
634 addToEntryList(parentTrace, ssq, Collections.singletonList(aTraceEntry));
635 } else {
636 aTraceEntry.updateEndTime(end + 1);
637 }
638 final TraceEntry traceEntry = aTraceEntry;
639
640 final long resolution = Math.max(1, (end - ssq.getStartTime()) / getDisplayWidth());
641 setEndTime(Math.max(getEndTime(), end + 1));
642 final List<Integer> threadQuarks = ssq.getQuarks(Attributes.THREADS, "*"); //$NON-NLS-1$
643 queryFullStates(ssq, start, end, resolution, monitor, new IQueryHandler() {
644 @Override
645 public void handle(List<List<ITmfStateInterval>> fullStates, List<ITmfStateInterval> prevFullState) {
646 for (int threadQuark : threadQuarks) {
647 String threadAttributeName = ssq.getAttributeName(threadQuark);
648
649 Pair<Integer, Integer> entryKey = Attributes.parseThreadAttributeName(threadAttributeName);
650 int threadId = entryKey.getFirst();
651
652 if (threadId < 0) { // ignore the 'unknown' (-1) thread
653 continue;
654 }
655
656 int execNameQuark = ssq.optQuarkRelative(threadQuark, Attributes.EXEC_NAME);
657 int ppidQuark = ssq.optQuarkRelative(threadQuark, Attributes.PPID);
658 if (execNameQuark == ITmfStateSystem.INVALID_ATTRIBUTE) {
659 /* No information on this thread (yet?), skip it for now */
660 continue;
661 }
662 ITmfStateInterval lastExecNameInterval = prevFullState == null || execNameQuark >= prevFullState.size() ? null : prevFullState.get(execNameQuark);
663 long lastExecNameStartTime = lastExecNameInterval == null ? -1 : lastExecNameInterval.getStartTime();
664 long lastExecNameEndTime = lastExecNameInterval == null ? -1 : lastExecNameInterval.getEndTime() + 1;
665 long lastPpidStartTime = prevFullState == null || ppidQuark >= prevFullState.size() || ppidQuark == ITmfStateSystem.INVALID_ATTRIBUTE ? -1 : prevFullState.get(ppidQuark).getStartTime();
666 for (List<ITmfStateInterval> fullState : fullStates) {
667 if (monitor.isCanceled()) {
668 return;
669 }
670 if (execNameQuark >= fullState.size() || ppidQuark >= fullState.size()) {
671 /* No information on this thread (yet?), skip it for now */
672 continue;
673 }
674 ITmfStateInterval execNameInterval = fullState.get(execNameQuark);
675 ITmfStateInterval ppidInterval = ppidQuark == ITmfStateSystem.INVALID_ATTRIBUTE ? null : fullState.get(ppidQuark);
676 long startTime = execNameInterval.getStartTime();
677 long endTime = execNameInterval.getEndTime() + 1;
678 if (startTime == lastExecNameStartTime && ppidInterval != null && ppidInterval.getStartTime() == lastPpidStartTime) {
679 continue;
680 }
681 boolean isNull = execNameInterval.getStateValue().isNull();
682 if (isNull && lastExecNameEndTime < startTime && lastExecNameEndTime != -1) {
683 /*
684 * There was a non-null interval in between the
685 * full states, try to use it.
686 */
687 try {
688 execNameInterval = ssq.querySingleState(startTime - 1, execNameQuark);
689 ppidInterval = ppidQuark == ITmfStateSystem.INVALID_ATTRIBUTE ? null : ssq.querySingleState(startTime - 1, ppidQuark);
690 startTime = execNameInterval.getStartTime();
691 endTime = execNameInterval.getEndTime() + 1;
692 } catch (StateSystemDisposedException e) {
693 /* ignored */
694 }
695 }
696 if (!execNameInterval.getStateValue().isNull() &&
697 execNameInterval.getStateValue().getType() == ITmfStateValue.Type.STRING) {
698 String execName = execNameInterval.getStateValue().unboxStr();
699 int ppid = ppidInterval == null ? -1 : ppidInterval.getStateValue().unboxInt();
700 ControlFlowEntry entry = entryMap.get(entryKey);
701 if (entry == null) {
702 entry = new ControlFlowEntry(threadQuark, trace, execName, threadId, ppid, startTime, endTime);
703 entryList.add(entry);
704 entryMap.put(entryKey, entry);
705 } else {
706 /*
707 * Update the name of the entry to the
708 * latest execName and the parent thread id
709 * to the latest ppid.
710 */
711 entry.setName(execName);
712 entry.setParentThreadId(ppid);
713 entry.updateEndTime(endTime);
714 }
715 }
716 if (isNull) {
717 entryMap.remove(entryKey);
718 }
719 lastExecNameStartTime = startTime;
720 lastExecNameEndTime = endTime;
721 lastPpidStartTime = ppidInterval == null ? -1 : ppidInterval.getStartTime();
722 }
723 }
724 synchronized (fFlatTraces) {
725 if (fFlatTraces.contains(parentTrace)) {
726 addEntriesToFlatTree(entryList, traceEntry);
727 } else {
728 addEntriesToHierarchicalTree(entryList, traceEntry);
729 }
730 }
731 }
732 });
733
734 queryFullStates(ssq, ssq.getStartTime(), end, resolution, monitor, new IQueryHandler() {
735 @Override
736 public void handle(@NonNull List<List<ITmfStateInterval>> fullStates, @Nullable List<ITmfStateInterval> prevFullState) {
737 for (final TimeGraphEntry entry : traceEntry.getChildren()) {
738 if (monitor.isCanceled()) {
739 return;
740 }
741 buildStatusEvents(trace, parentTrace, ssq, fullStates, prevFullState, (ControlFlowEntry) entry, monitor, ssq.getStartTime(), end);
742 }
743 }
744 });
745
746 if (parentTrace.equals(getTrace())) {
747 refresh();
748 }
749
750 start = end;
751 }
752 }
753
754 /**
755 * Add entries to the traces's child list in a flat fashion (no hierarchy).
756 * If one entry has children, we do a depth first search to add each child
757 * to the trace's child list and update the parent and child relations.
758 */
759 private static void hierarchicalToFlatTree(TimeGraphEntry traceEntry) {
760 List<@NonNull TimeGraphEntry> rootList = traceEntry.getChildren();
761 // We visit the children of every entry to add
762 StreamFlattener<TimeGraphEntry> sf = new StreamFlattener<>(entry -> entry.getChildren().stream());
763 Stream<TimeGraphEntry> allEntries = rootList.stream().flatMap(entry -> sf.flatten(entry));
764
765 // We add every entry that is missing from the trace's entry list
766 List<@NonNull TimeGraphEntry> rootListToAdd = allEntries
767 .filter(entry -> !rootList.contains(entry))
768 .collect(Collectors.toList());
769 rootList.forEach(entry -> {
770 entry.clearChildren();
771 });
772 rootListToAdd.forEach(entry -> {
773 traceEntry.addChild(entry);
774 entry.clearChildren();
775 });
776 }
777
778 /**
779 * Add entries to the traces's child list in a flat fashion (no hierarchy).
780 */
781 private static void addEntriesToFlatTree(List<@NonNull ControlFlowEntry> entryList, TimeGraphEntry traceEntry) {
782 List<TimeGraphEntry> rootList = traceEntry.getChildren();
783 for (ControlFlowEntry entry : entryList) {
784 if (!rootList.contains(entry)) {
785 traceEntry.addChild(entry);
786 }
787 }
788 }
789
790 /**
791 * Add entries to the trace's child list in a hierarchical fashion.
792 */
793 private static void addEntriesToHierarchicalTree(List<ControlFlowEntry> entryList, TimeGraphEntry traceEntry) {
794 List<TimeGraphEntry> rootList = traceEntry.getChildren();
795
796 for (ControlFlowEntry entry : entryList) {
797 boolean root = (entry.getParent() == null || entry.getParent() == traceEntry);
798 if (root && entry.getParentThreadId() > 0) {
799 for (ControlFlowEntry parent : entryList) {
800 /*
801 * Associate the parent entry only if their time overlap. A
802 * child entry may start before its parent, for example at
803 * the beginning of the trace if a parent has not yet
804 * appeared in the state system. We just want to make sure
805 * that the entry didn't start after the parent ended or
806 * ended before the parent started.
807 */
808 if (parent.getThreadId() == entry.getParentThreadId() &&
809 !(entry.getStartTime() > parent.getEndTime() ||
810 entry.getEndTime() < parent.getStartTime())) {
811 root = false;
812 if (rootList.contains(entry)) {
813 traceEntry.removeChild(entry);
814 }
815 parent.addChild(entry);
816 break;
817 }
818 }
819 }
820 if (root && (!rootList.contains(entry))) {
821 traceEntry.addChild(entry);
822 }
823 }
824 }
825
826 private void buildStatusEvents(ITmfTrace trace, ITmfTrace parentTrace, ITmfStateSystem ss, @NonNull List<List<ITmfStateInterval>> fullStates,
827 @Nullable List<ITmfStateInterval> prevFullState, ControlFlowEntry entry, @NonNull IProgressMonitor monitor, long start, long end) {
828 if (start < entry.getEndTime() && end > entry.getStartTime()) {
829 List<ITimeEvent> eventList = getEventList(entry, ss, fullStates, prevFullState, monitor);
830 if (eventList == null) {
831 return;
832 }
833 /* Start a new event list on first iteration, then append to it */
834 if (prevFullState == null) {
835 entry.setEventList(eventList);
836 } else {
837 for (ITimeEvent event : eventList) {
838 entry.addEvent(event);
839 }
840 }
841 if (parentTrace.equals(getTrace())) {
842 redraw();
843 }
844 }
845 for (ITimeGraphEntry child : entry.getChildren()) {
846 if (monitor.isCanceled()) {
847 return;
848 }
849 buildStatusEvents(trace, parentTrace, ss, fullStates, prevFullState, (ControlFlowEntry) child, monitor, start, end);
850 }
851 }
852
853 @Override
854 protected @Nullable List<ITimeEvent> getEventList(@NonNull TimeGraphEntry tgentry, ITmfStateSystem ss,
855 @NonNull List<List<ITmfStateInterval>> fullStates, @Nullable List<ITmfStateInterval> prevFullState, @NonNull IProgressMonitor monitor) {
856 List<ITimeEvent> eventList = null;
857 if (!(tgentry instanceof ControlFlowEntry)) {
858 return eventList;
859 }
860 ControlFlowEntry entry = (ControlFlowEntry) tgentry;
861 try {
862 int statusQuark = entry.getThreadQuark();
863 eventList = new ArrayList<>(fullStates.size());
864 ITmfStateInterval lastInterval = prevFullState == null || statusQuark >= prevFullState.size() ? null : prevFullState.get(statusQuark);
865 long lastStartTime = lastInterval == null ? -1 : lastInterval.getStartTime();
866 long lastEndTime = lastInterval == null ? -1 : lastInterval.getEndTime() + 1;
867 for (List<ITmfStateInterval> fullState : fullStates) {
868 if (monitor.isCanceled()) {
869 return null;
870 }
871 if (statusQuark >= fullState.size()) {
872 /* No information on this thread (yet?), skip it for now */
873 continue;
874 }
875 ITmfStateInterval statusInterval = fullState.get(statusQuark);
876 long time = statusInterval.getStartTime();
877 if (time == lastStartTime) {
878 continue;
879 }
880 long duration = statusInterval.getEndTime() - time + 1;
881 int status = -1;
882 try {
883 status = statusInterval.getStateValue().unboxInt();
884 } catch (StateValueTypeException e) {
885 Activator.getDefault().logError(e.getMessage());
886 }
887 if (lastEndTime != time && lastEndTime != -1) {
888 eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime));
889 }
890 if (!statusInterval.getStateValue().isNull()) {
891 eventList.add(new TimeEvent(entry, time, duration, status));
892 } else {
893 eventList.add(new NullTimeEvent(entry, time, duration));
894 }
895 lastStartTime = time;
896 lastEndTime = time + duration;
897 }
898 } catch (TimeRangeException e) {
899 Activator.getDefault().logError(e.getMessage());
900 }
901 return eventList;
902 }
903
904 /**
905 * Returns a value corresponding to the selected entry.
906 *
907 * Used in conjunction with synchingToTime to change the selected entry. If
908 * one of these methods is overridden in child class, then both should be.
909 *
910 * @param time
911 * The currently selected time
912 * @return a value identifying the entry
913 */
914 private int getSelectionValue(long time) {
915 int thread = -1;
916 for (ITmfTrace trace : TmfTraceManager.getTraceSet(getTrace())) {
917 if (thread > 0) {
918 break;
919 }
920 ITmfStateSystem ssq = TmfStateSystemAnalysisModule.getStateSystem(trace, KernelAnalysisModule.ID);
921 if (ssq == null) {
922 continue;
923 }
924 if (time >= ssq.getStartTime() && time <= ssq.getCurrentEndTime()) {
925 List<Integer> currentThreadQuarks = ssq.getQuarks(Attributes.CPUS, "*", Attributes.CURRENT_THREAD); //$NON-NLS-1$
926 for (int currentThreadQuark : currentThreadQuarks) {
927 try {
928 ITmfStateInterval currentThreadInterval = ssq.querySingleState(time, currentThreadQuark);
929 int currentThread = currentThreadInterval.getStateValue().unboxInt();
930 if (currentThread > 0) {
931 int statusQuark = ssq.getQuarkAbsolute(Attributes.THREADS, Integer.toString(currentThread));
932 ITmfStateInterval statusInterval = ssq.querySingleState(time, statusQuark);
933 if (statusInterval.getStartTime() == time) {
934 thread = currentThread;
935 break;
936 }
937 }
938 } catch (AttributeNotFoundException | TimeRangeException | StateValueTypeException e) {
939 Activator.getDefault().logError(e.getMessage());
940 } catch (StateSystemDisposedException e) {
941 /* Ignored */
942 }
943 }
944 }
945 }
946 return thread;
947 }
948
949 @Override
950 protected void synchingToTime(long time) {
951 int selected = getSelectionValue(time);
952 if (selected > 0) {
953 for (Object element : getTimeGraphViewer().getExpandedElements()) {
954 if (element instanceof ControlFlowEntry) {
955 ControlFlowEntry entry = (ControlFlowEntry) element;
956 if (entry.getThreadId() == selected) {
957 getTimeGraphCombo().setSelection(entry);
958 break;
959 }
960 }
961 }
962 }
963 }
964
965 @Override
966 protected @NonNull List<ILinkEvent> getLinkList(ITmfStateSystem ss,
967 @NonNull List<List<ITmfStateInterval>> fullStates, @Nullable List<ITmfStateInterval> prevFullState, @NonNull IProgressMonitor monitor) {
968 List<ILinkEvent> list = new ArrayList<>();
969 List<TimeGraphEntry> entryList = getEntryList(ss);
970 if (entryList == null) {
971 return list;
972 }
973 for (ITmfTrace trace : TmfTraceManager.getTraceSet(getTrace())) {
974 List<Integer> currentThreadQuarks = ss.getQuarks(Attributes.CPUS, "*", Attributes.CURRENT_THREAD); //$NON-NLS-1$
975 for (int currentThreadQuark : currentThreadQuarks) {
976 if (currentThreadQuark >= fullStates.get(0).size()) {
977 /* No information on this cpu (yet?), skip it for now */
978 continue;
979 }
980 List<ITmfStateInterval> currentThreadIntervals = new ArrayList<>(fullStates.size() + 2);
981 try {
982 /*
983 * Add the previous interval if it is the first query
984 * iteration and the first interval has currentThread=0. Add
985 * the following interval if the last interval has
986 * currentThread=0. These are diagonal arrows crossing the
987 * query iteration range.
988 */
989 if (prevFullState == null) {
990 ITmfStateInterval currentThreadInterval = fullStates.get(0).get(currentThreadQuark);
991 if (currentThreadInterval.getStateValue().unboxInt() == 0) {
992 long start = Math.max(currentThreadInterval.getStartTime() - 1, ss.getStartTime());
993 currentThreadIntervals.add(ss.querySingleState(start, currentThreadQuark));
994 }
995 }
996 for (List<ITmfStateInterval> fullState : fullStates) {
997 currentThreadIntervals.add(fullState.get(currentThreadQuark));
998 }
999 ITmfStateInterval currentThreadInterval = fullStates.get(fullStates.size() - 1).get(currentThreadQuark);
1000 if (currentThreadInterval.getStateValue().unboxInt() == 0) {
1001 long end = Math.min(currentThreadInterval.getEndTime() + 1, ss.getCurrentEndTime());
1002 currentThreadIntervals.add(ss.querySingleState(end, currentThreadQuark));
1003 }
1004 } catch (StateSystemDisposedException e) {
1005 /* Ignored */
1006 return list;
1007 }
1008 int prevThread = 0;
1009 long prevEnd = 0;
1010 long lastEnd = 0;
1011 for (ITmfStateInterval currentThreadInterval : currentThreadIntervals) {
1012 if (monitor.isCanceled()) {
1013 return list;
1014 }
1015 if (currentThreadInterval.getEndTime() + 1 == lastEnd) {
1016 continue;
1017 }
1018 long time = currentThreadInterval.getStartTime();
1019 if (time != lastEnd) {
1020 // don't create links where there are gaps in intervals due to the resolution
1021 prevThread = 0;
1022 prevEnd = 0;
1023 }
1024 int thread = currentThreadInterval.getStateValue().unboxInt();
1025 if (thread > 0 && prevThread > 0) {
1026 ITimeGraphEntry prevEntry = findEntry(entryList, trace, prevThread);
1027 ITimeGraphEntry nextEntry = findEntry(entryList, trace, thread);
1028 list.add(new TimeLinkEvent(prevEntry, nextEntry, prevEnd, time - prevEnd, 0));
1029 }
1030 lastEnd = currentThreadInterval.getEndTime() + 1;
1031 if (thread != 0) {
1032 prevThread = thread;
1033 prevEnd = lastEnd;
1034 }
1035 }
1036 }
1037 }
1038 return list;
1039 }
1040
1041 private ControlFlowEntry findEntry(List<TimeGraphEntry> entryList, ITmfTrace trace, int threadId) {
1042 for (TimeGraphEntry entry : entryList) {
1043 if (entry instanceof ControlFlowEntry) {
1044 ControlFlowEntry controlFlowEntry = (ControlFlowEntry) entry;
1045 if (controlFlowEntry.getThreadId() == threadId && controlFlowEntry.getTrace() == trace) {
1046 return controlFlowEntry;
1047 }
1048 }
1049 if (entry.hasChildren()) {
1050 ControlFlowEntry controlFlowEntry = findEntry(entry.getChildren(), trace, threadId);
1051 if (controlFlowEntry != null) {
1052 return controlFlowEntry;
1053 }
1054 }
1055 }
1056 return null;
1057 }
1058
1059 @Override
1060 protected boolean canBePinned() {
1061 //TODO: actionPin is not defined
1062 return false;
1063 }
1064
1065 }
This page took 0.054073 seconds and 4 git commands to generate.