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