tmf: Add support for time range selection
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / callstack / CallStackView.java
1 /*******************************************************************************
2 * Copyright (c) 2013 Ericsson
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 * Bernd Hufmann - Updated signal handling
12 *******************************************************************************/
13
14 package org.eclipse.linuxtools.tmf.ui.views.callstack;
15
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21
22 import org.eclipse.core.runtime.IProgressMonitor;
23 import org.eclipse.core.runtime.NullProgressMonitor;
24 import org.eclipse.jface.action.Action;
25 import org.eclipse.jface.action.IAction;
26 import org.eclipse.jface.action.IStatusLineManager;
27 import org.eclipse.jface.action.IToolBarManager;
28 import org.eclipse.jface.action.Separator;
29 import org.eclipse.jface.util.IPropertyChangeListener;
30 import org.eclipse.jface.util.PropertyChangeEvent;
31 import org.eclipse.jface.viewers.DoubleClickEvent;
32 import org.eclipse.jface.viewers.IDoubleClickListener;
33 import org.eclipse.jface.viewers.ILabelProviderListener;
34 import org.eclipse.jface.viewers.ISelection;
35 import org.eclipse.jface.viewers.IStructuredSelection;
36 import org.eclipse.jface.viewers.ITableLabelProvider;
37 import org.eclipse.jface.viewers.ITreeContentProvider;
38 import org.eclipse.jface.viewers.Viewer;
39 import org.eclipse.linuxtools.internal.tmf.ui.Activator;
40 import org.eclipse.linuxtools.internal.tmf.ui.ITmfImageConstants;
41 import org.eclipse.linuxtools.internal.tmf.ui.Messages;
42 import org.eclipse.linuxtools.tmf.core.callstack.CallStackStateProvider;
43 import org.eclipse.linuxtools.tmf.core.ctfadaptor.CtfTmfTimestamp;
44 import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
45 import org.eclipse.linuxtools.tmf.core.exceptions.StateSystemDisposedException;
46 import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException;
47 import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
48 import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval;
49 import org.eclipse.linuxtools.tmf.core.signal.TmfRangeSynchSignal;
50 import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
51 import org.eclipse.linuxtools.tmf.core.signal.TmfTimeSynchSignal;
52 import org.eclipse.linuxtools.tmf.core.signal.TmfTraceClosedSignal;
53 import org.eclipse.linuxtools.tmf.core.signal.TmfTraceOpenedSignal;
54 import org.eclipse.linuxtools.tmf.core.signal.TmfTraceSelectedSignal;
55 import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem;
56 import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue;
57 import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue.Type;
58 import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
59 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
60 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
61 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestampDelta;
62 import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;
63 import org.eclipse.linuxtools.tmf.core.trace.TmfExperiment;
64 import org.eclipse.linuxtools.tmf.ui.editors.ITmfTraceEditor;
65 import org.eclipse.linuxtools.tmf.ui.views.TmfView;
66 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphRangeListener;
67 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.ITimeGraphTimeListener;
68 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.TimeGraphCombo;
69 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.TimeGraphRangeUpdateEvent;
70 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.TimeGraphTimeEvent;
71 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.TimeGraphViewer;
72 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeEvent;
73 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.ITimeGraphEntry;
74 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.NullTimeEvent;
75 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.model.TimeEvent;
76 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphControl;
77 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.TimeGraphSelection;
78 import org.eclipse.linuxtools.tmf.ui.widgets.timegraph.widgets.Utils.TimeFormat;
79 import org.eclipse.swt.SWT;
80 import org.eclipse.swt.events.ControlAdapter;
81 import org.eclipse.swt.events.ControlEvent;
82 import org.eclipse.swt.events.MouseAdapter;
83 import org.eclipse.swt.events.MouseEvent;
84 import org.eclipse.swt.graphics.Image;
85 import org.eclipse.swt.widgets.Composite;
86 import org.eclipse.swt.widgets.Display;
87 import org.eclipse.ui.IActionBars;
88 import org.eclipse.ui.IEditorPart;
89
90 /**
91 * Main implementation for the Call Stack view
92 *
93 * @author Patrick Tasse
94 * @since 2.0
95 */
96 public class CallStackView extends TmfView {
97
98 // ------------------------------------------------------------------------
99 // Constants
100 // ------------------------------------------------------------------------
101
102 /** View ID. */
103 public static final String ID = "org.eclipse.linuxtools.tmf.ui.views.callstack"; //$NON-NLS-1$
104
105 /**
106 * Redraw state enum
107 */
108 private enum State { IDLE, BUSY, PENDING }
109
110 private static final String[] COLUMN_NAMES = new String[] {
111 Messages.CallStackView_FunctionColumn,
112 Messages.CallStackView_DepthColumn,
113 Messages.CallStackView_EntryTimeColumn,
114 Messages.CallStackView_ExitTimeColumn,
115 Messages.CallStackView_DurationColumn
116 };
117
118 private static final int[] COLUMN_WIDTHS = new int[] {
119 200,
120 50,
121 120,
122 120,
123 120
124 };
125
126 // Fraction of a function duration to be added as spacing
127 private static final double SPACING_RATIO = 0.01;
128
129 private static final Image THREAD_IMAGE = Activator.getDefault().getImageFromPath("icons/obj16/thread_obj.gif"); //$NON-NLS-1$
130 private static final Image STACKFRAME_IMAGE = Activator.getDefault().getImageFromPath("icons/obj16/stckframe_obj.gif"); //$NON-NLS-1$
131
132 // ------------------------------------------------------------------------
133 // Fields
134 // ------------------------------------------------------------------------
135
136 // The time graph combo
137 private TimeGraphCombo fTimeGraphCombo;
138
139 // The selected trace
140 private ITmfTrace fTrace;
141
142 // The selected thread map
143 private final Map<ITmfTrace, String> fSelectedThreadMap = new HashMap<ITmfTrace, String>();
144
145 // The time graph entry list
146 private List<ThreadEntry> fEntryList;
147
148 // The trace to entry list hash map
149 private final Map<ITmfTrace, ArrayList<ThreadEntry>> fEntryListMap = new HashMap<ITmfTrace, ArrayList<ThreadEntry>>();
150
151 // The trace to build thread hash map
152 private final Map<ITmfTrace, BuildThread> fBuildThreadMap = new HashMap<ITmfTrace, BuildThread>();
153
154 // The start time
155 private long fStartTime;
156
157 // The end time
158 private long fEndTime;
159
160 // The display width
161 private int fDisplayWidth;
162
163 // The next event action
164 private Action fNextEventAction;
165
166 // The previous event action
167 private Action fPrevEventAction;
168
169 // The next item action
170 private Action fNextItemAction;
171
172 // The previous item action
173 private Action fPreviousItemAction;
174
175 // The zoom thread
176 private ZoomThread fZoomThread;
177
178 // The redraw state used to prevent unnecessary queuing of display runnables
179 private State fRedrawState = State.IDLE;
180
181 // The redraw synchronization object
182 private final Object fSyncObj = new Object();
183
184 // The saved time sync. signal used when switching off the pinning of a view
185 private TmfTimeSynchSignal fSavedTimeSyncSignal;
186
187 // The saved time range sync. signal used when switching off the pinning of a view
188 private TmfRangeSynchSignal fSavedRangeSyncSignal;
189
190 // ------------------------------------------------------------------------
191 // Classes
192 // ------------------------------------------------------------------------
193
194 private class ThreadEntry implements ITimeGraphEntry {
195 // The Trace
196 private final ITmfTrace fThreadTrace;
197 // The start time
198 private final long fTraceStartTime;
199 // The end time
200 private final long fTraceEndTime;
201 // The children of the entry
202 private ArrayList<CallStackEntry> fChildren;
203 // The name of entry
204 private final String fName;
205 // The thread attribute quark
206 private final int fThreadQuark;
207
208 public ThreadEntry(ITmfTrace trace, String name, int threadQuark, long startTime, long endTime) {
209 fThreadTrace = trace;
210 fChildren = new ArrayList<CallStackEntry>();
211 fName = name;
212 fTraceStartTime = startTime;
213 fTraceEndTime = endTime;
214 fThreadQuark = threadQuark;
215 }
216
217 @Override
218 public ITimeGraphEntry getParent() {
219 return null;
220 }
221
222 @Override
223 public boolean hasChildren() {
224 if (fChildren == null) {
225 ITmfStateSystem ss = fThreadTrace.getStateSystems().get(CallStackStateProvider.ID);
226 try {
227 int eventStackQuark = ss.getQuarkRelative(fThreadQuark, CallStackStateProvider.CALL_STACK);
228 ITmfStateInterval eventStackInterval = ss.querySingleState(ss.getStartTime(), eventStackQuark);
229 return ! eventStackInterval.getStateValue().isNull() || eventStackInterval.getEndTime() != ss.getCurrentEndTime();
230 } catch (AttributeNotFoundException e) {
231 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
232 } catch (TimeRangeException e) {
233 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
234 } catch (StateSystemDisposedException e) {
235 /* Ignored */
236 }
237 }
238 return fChildren != null && fChildren.size() > 0;
239 }
240
241 @Override
242 public List<CallStackEntry> getChildren() {
243 return fChildren;
244 }
245
246 @Override
247 public String getName() {
248 return fName;
249 }
250
251 @Override
252 public long getStartTime() {
253 return fTraceStartTime;
254 }
255
256 @Override
257 public long getEndTime() {
258 return fTraceEndTime;
259 }
260
261 @Override
262 public boolean hasTimeEvents() {
263 return false;
264 }
265
266 @Override
267 public Iterator<ITimeEvent> getTimeEventsIterator() {
268 return null;
269 }
270
271 @Override
272 public <T extends ITimeEvent> Iterator<T> getTimeEventsIterator(long startTime, long stopTime, long visibleDuration) {
273 return null;
274 }
275
276 public int getThreadQuark() {
277 return fThreadQuark;
278 }
279
280 public ITmfTrace getTrace() {
281 return fThreadTrace;
282 }
283
284 public void addChild(CallStackEntry entry) {
285 entry.setParent(this);
286 fChildren.add(entry);
287 }
288 }
289
290 private class TreeContentProvider implements ITreeContentProvider {
291
292 @Override
293 public void dispose() {
294 }
295
296 @Override
297 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
298 }
299
300 @Override
301 public Object[] getElements(Object inputElement) {
302 return (ITimeGraphEntry[]) inputElement;
303 }
304
305 @Override
306 public Object[] getChildren(Object parentElement) {
307 ITimeGraphEntry entry = (ITimeGraphEntry) parentElement;
308 return entry.getChildren().toArray();
309 }
310
311 @Override
312 public Object getParent(Object element) {
313 ITimeGraphEntry entry = (ITimeGraphEntry) element;
314 return entry.getParent();
315 }
316
317 @Override
318 public boolean hasChildren(Object element) {
319 ITimeGraphEntry entry = (ITimeGraphEntry) element;
320 return entry.hasChildren();
321 }
322
323 }
324
325 private class TreeLabelProvider implements ITableLabelProvider {
326
327 @Override
328 public void addListener(ILabelProviderListener listener) {
329 }
330
331 @Override
332 public void dispose() {
333 }
334
335 @Override
336 public boolean isLabelProperty(Object element, String property) {
337 return false;
338 }
339
340 @Override
341 public void removeListener(ILabelProviderListener listener) {
342 }
343
344 @Override
345 public Image getColumnImage(Object element, int columnIndex) {
346 if (columnIndex == 0) {
347 if (element instanceof ThreadEntry) {
348 return THREAD_IMAGE;
349 } else if (element instanceof CallStackEntry) {
350 CallStackEntry entry = (CallStackEntry) element;
351 if (entry.getFunctionName().length() > 0) {
352 return STACKFRAME_IMAGE;
353 }
354 }
355 }
356 return null;
357 }
358
359 @Override
360 public String getColumnText(Object element, int columnIndex) {
361 if (element instanceof ThreadEntry) {
362 if (columnIndex == 0) {
363 return ((ThreadEntry) element).getName();
364 }
365 } else if (element instanceof CallStackEntry) {
366 CallStackEntry entry = (CallStackEntry) element;
367 if (columnIndex == 0) {
368 return entry.getFunctionName();
369 } else if (columnIndex == 1 && entry.getFunctionName().length() > 0) {
370 int depth = entry.getStackLevel();
371 return Integer.toString(depth);
372 } else if (columnIndex == 2 && entry.getFunctionName().length() > 0) {
373 ITmfTimestamp ts = new TmfTimestamp(entry.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE);
374 return ts.toString();
375 } else if (columnIndex == 3 && entry.getFunctionName().length() > 0) {
376 ITmfTimestamp ts = new TmfTimestamp(entry.getEndTime(), ITmfTimestamp.NANOSECOND_SCALE);
377 return ts.toString();
378 } else if (columnIndex == 4 && entry.getFunctionName().length() > 0) {
379 ITmfTimestamp ts = new TmfTimestampDelta(entry.getEndTime() - entry.getStartTime(), ITmfTimestamp.NANOSECOND_SCALE);
380 return ts.toString();
381 }
382 }
383 return ""; //$NON-NLS-1$
384 }
385
386 }
387
388 private class BuildThread extends Thread {
389 private final ITmfTrace fBuildTrace;
390 private final IProgressMonitor fMonitor;
391
392 public BuildThread(ITmfTrace trace) {
393 super("CallStackView build"); //$NON-NLS-1$
394 fBuildTrace = trace;
395 fMonitor = new NullProgressMonitor();
396 }
397
398 @Override
399 public void run() {
400 buildThreadList(fBuildTrace, fMonitor);
401 synchronized (fBuildThreadMap) {
402 fBuildThreadMap.remove(this);
403 }
404 }
405
406 public void cancel() {
407 fMonitor.setCanceled(true);
408 }
409 }
410
411 private class ZoomThread extends Thread {
412 private final List<ThreadEntry> fZoomEntryList;
413 private final long fZoomStartTime;
414 private final long fZoomEndTime;
415 private final IProgressMonitor fMonitor;
416
417 public ZoomThread(List<ThreadEntry> entryList, long startTime, long endTime) {
418 super("ResourcesView zoom"); //$NON-NLS-1$
419 fZoomEntryList = entryList;
420 fZoomStartTime = startTime;
421 fZoomEndTime = endTime;
422 fMonitor = new NullProgressMonitor();
423 }
424
425 @Override
426 public void run() {
427 if (fZoomEntryList == null) {
428 return;
429 }
430 long resolution = Math.max(1, (fZoomEndTime - fZoomStartTime) / fDisplayWidth);
431 for (ThreadEntry threadEntry : fZoomEntryList) {
432 ITmfStateSystem ss = threadEntry.fThreadTrace.getStateSystems().get(CallStackStateProvider.ID);
433 if (ss == null || !ss.waitUntilBuilt()) {
434 continue;
435 }
436 for (ITimeGraphEntry child : threadEntry.getChildren()) {
437 if (fMonitor.isCanceled()) {
438 break;
439 }
440 CallStackEntry entry = (CallStackEntry) child;
441 if (fZoomStartTime <= fStartTime && fZoomEndTime >= fEndTime) {
442 entry.setZoomedEventList(null);
443 } else {
444 List<ITimeEvent> zoomedEventList = getEventList(entry, fZoomStartTime, fZoomEndTime, resolution, fMonitor);
445 if (zoomedEventList != null) {
446 entry.setZoomedEventList(zoomedEventList);
447 }
448 }
449 redraw();
450 }
451 }
452 }
453
454 public void cancel() {
455 fMonitor.setCanceled(true);
456 }
457 }
458
459 // ------------------------------------------------------------------------
460 // Constructors
461 // ------------------------------------------------------------------------
462
463 /**
464 * Default constructor
465 */
466 public CallStackView() {
467 super(ID);
468 fDisplayWidth = Display.getDefault().getBounds().width;
469 }
470
471 // ------------------------------------------------------------------------
472 // ViewPart
473 // ------------------------------------------------------------------------
474
475 @Override
476 public void createPartControl(Composite parent) {
477 fTimeGraphCombo = new TimeGraphCombo(parent, SWT.NONE);
478
479 fTimeGraphCombo.setTreeContentProvider(new TreeContentProvider());
480
481 fTimeGraphCombo.setTreeLabelProvider(new TreeLabelProvider());
482
483 fTimeGraphCombo.setTreeColumns(COLUMN_NAMES);
484
485 fTimeGraphCombo.getTreeViewer().getTree().getColumn(0).setWidth(COLUMN_WIDTHS[0]);
486 fTimeGraphCombo.getTreeViewer().getTree().getColumn(1).setWidth(COLUMN_WIDTHS[1]);
487 fTimeGraphCombo.getTreeViewer().getTree().getColumn(2).setWidth(COLUMN_WIDTHS[2]);
488 fTimeGraphCombo.getTreeViewer().getTree().getColumn(3).setWidth(COLUMN_WIDTHS[3]);
489 fTimeGraphCombo.getTreeViewer().getTree().getColumn(4).setWidth(COLUMN_WIDTHS[4]);
490
491 fTimeGraphCombo.setTimeGraphProvider(new CallStackPresentationProvider());
492 fTimeGraphCombo.getTimeGraphViewer().setTimeFormat(TimeFormat.CALENDAR);
493
494 fTimeGraphCombo.getTimeGraphViewer().addRangeListener(new ITimeGraphRangeListener() {
495 @Override
496 public void timeRangeUpdated(TimeGraphRangeUpdateEvent event) {
497 long startTime = event.getStartTime();
498 long endTime = event.getEndTime();
499 TmfTimeRange range = new TmfTimeRange(new CtfTmfTimestamp(startTime), new CtfTmfTimestamp(endTime));
500 broadcast(new TmfRangeSynchSignal(CallStackView.this, range));
501 startZoomThread(startTime, endTime);
502 }
503 });
504
505 fTimeGraphCombo.getTimeGraphViewer().addTimeListener(new ITimeGraphTimeListener() {
506 @Override
507 public void timeSelected(TimeGraphTimeEvent event) {
508 long beginTime = event.getBeginTime();
509 long endTime = event.getEndTime();
510 selectTime(beginTime);
511 broadcast(new TmfTimeSynchSignal(CallStackView.this, new CtfTmfTimestamp(beginTime), new CtfTmfTimestamp(endTime)));
512 }
513 });
514
515 fTimeGraphCombo.getTimeGraphViewer().getControl().addControlListener(new ControlAdapter() {
516 @Override
517 public void controlResized(ControlEvent e) {
518 fDisplayWidth = fTimeGraphCombo.getTimeGraphViewer().getControl().getSize().x;
519 if (fEntryList != null) {
520 startZoomThread(fTimeGraphCombo.getTimeGraphViewer().getTime0(), fTimeGraphCombo.getTimeGraphViewer().getTime1());
521 }
522 }
523 });
524
525 fTimeGraphCombo.getTreeViewer().addDoubleClickListener(new IDoubleClickListener() {
526 @Override
527 public void doubleClick(DoubleClickEvent event) {
528 Object selection = ((IStructuredSelection) event.getSelection()).getFirstElement();
529 if (selection instanceof CallStackEntry) {
530 CallStackEntry entry = (CallStackEntry) selection;
531 if (entry.getFunctionName().length() > 0) {
532 long startTime = entry.getStartTime();
533 long endTime = entry.getEndTime();
534 long spacingTime = (long) ((endTime - startTime) * SPACING_RATIO);
535 startTime -= spacingTime;
536 endTime += spacingTime;
537 TmfTimeRange range = new TmfTimeRange(new CtfTmfTimestamp(startTime), new CtfTmfTimestamp(endTime));
538 broadcast(new TmfRangeSynchSignal(CallStackView.this, range));
539 fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(startTime, endTime);
540 startZoomThread(startTime, endTime);
541 }
542 }
543 }
544 });
545
546 fTimeGraphCombo.getTimeGraphViewer().getTimeGraphControl().addMouseListener(new MouseAdapter() {
547 @Override
548 public void mouseDoubleClick(MouseEvent e) {
549 TimeGraphControl timeGraphControl = fTimeGraphCombo.getTimeGraphViewer().getTimeGraphControl();
550 ISelection selection = timeGraphControl.getSelection();
551 if (selection instanceof TimeGraphSelection) {
552 Object o = ((TimeGraphSelection) selection).getFirstElement();
553 if (o instanceof CallStackEvent) {
554 CallStackEvent event = (CallStackEvent) o;
555 long startTime = event.getTime();
556 long endTime = startTime + event.getDuration();
557 long spacingTime = (long) ((endTime - startTime) * SPACING_RATIO);
558 startTime -= spacingTime;
559 endTime += spacingTime;
560 TmfTimeRange range = new TmfTimeRange(new CtfTmfTimestamp(startTime), new CtfTmfTimestamp(endTime));
561 broadcast(new TmfRangeSynchSignal(CallStackView.this, range));
562 fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(startTime, endTime);
563 startZoomThread(startTime, endTime);
564 }
565 }
566 }
567 });
568
569 IStatusLineManager statusLineManager = getViewSite().getActionBars().getStatusLineManager();
570 fTimeGraphCombo.getTimeGraphViewer().getTimeGraphControl().setStatusLineManager(statusLineManager);
571
572 // View Action Handling
573 makeActions();
574 contributeToActionBars();
575
576 IEditorPart editor = getSite().getPage().getActiveEditor();
577 if (editor instanceof ITmfTraceEditor) {
578 ITmfTrace trace = ((ITmfTraceEditor) editor).getTrace();
579 if (trace != null) {
580 traceSelected(new TmfTraceSelectedSignal(this, trace));
581 }
582 }
583 }
584
585 @Override
586 public void setFocus() {
587 fTimeGraphCombo.setFocus();
588 }
589
590 // ------------------------------------------------------------------------
591 // Signal handlers
592 // ------------------------------------------------------------------------
593 /**
594 * Handler for the trace opened signal.
595 * @param signal
596 * The incoming signal
597 * @since 2.0
598 */
599 @TmfSignalHandler
600 public void traceOpened(TmfTraceOpenedSignal signal) {
601 fTrace = signal.getTrace();
602 loadTrace();
603 }
604
605 /**
606 * Handler for the trace selected signal
607 *
608 * @param signal
609 * The incoming signal
610 */
611 @TmfSignalHandler
612 public void traceSelected(final TmfTraceSelectedSignal signal) {
613 if (signal.getTrace() == fTrace) {
614 return;
615 }
616 fTrace = signal.getTrace();
617 loadTrace();
618 }
619
620 /**
621 * Trace is closed: clear the data structures and the view
622 *
623 * @param signal the signal received
624 */
625 @TmfSignalHandler
626 public void traceClosed(final TmfTraceClosedSignal signal) {
627 synchronized (fBuildThreadMap) {
628 BuildThread buildThread = fBuildThreadMap.remove(signal.getTrace());
629 if (buildThread != null) {
630 buildThread.cancel();
631 }
632 }
633 synchronized (fEntryListMap) {
634 fEntryListMap.remove(signal.getTrace());
635 }
636 fSelectedThreadMap.remove(signal.getTrace());
637 if (signal.getTrace() == fTrace) {
638 fTrace = null;
639 fStartTime = 0;
640 fEndTime = 0;
641 refresh();
642 }
643 }
644
645 /**
646 * Handler for the TimeSynch signal
647 *
648 * @param signal
649 * The incoming signal
650 */
651 @TmfSignalHandler
652 public void synchToTime(final TmfTimeSynchSignal signal) {
653
654 fSavedTimeSyncSignal = isPinned() ? new TmfTimeSynchSignal(signal.getSource(), signal.getBeginTime(), signal.getEndTime()) : null;
655
656 if (signal.getSource() == this || fTrace == null || isPinned()) {
657 return;
658 }
659 final long beginTime = signal.getBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
660 final long endTime = signal.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
661 Display.getDefault().asyncExec(new Runnable() {
662 @Override
663 public void run() {
664 if (fTimeGraphCombo.isDisposed()) {
665 return;
666 }
667 if (beginTime == endTime) {
668 fTimeGraphCombo.getTimeGraphViewer().setSelectedTime(beginTime, true);
669 } else {
670 fTimeGraphCombo.getTimeGraphViewer().setSelectionRange(beginTime, endTime);
671 }
672 selectTime(beginTime);
673 startZoomThread(fTimeGraphCombo.getTimeGraphViewer().getTime0(), fTimeGraphCombo.getTimeGraphViewer().getTime1());
674 if (fEntryList == null) {
675 return;
676 }
677 TimeGraphViewer viewer = fTimeGraphCombo.getTimeGraphViewer();
678 for (ThreadEntry threadEntry : fEntryList) {
679 ITmfStateSystem ss = threadEntry.getTrace().getStateSystems().get(CallStackStateProvider.ID);
680 if (ss == null || beginTime < ss.getStartTime() || beginTime > ss.getCurrentEndTime()) {
681 continue;
682 }
683 try {
684 int quark = ss.getQuarkRelative(threadEntry.getThreadQuark(), CallStackStateProvider.CALL_STACK);
685 ITmfStateInterval stackInterval = ss.querySingleState(beginTime, quark);
686 if (beginTime == stackInterval.getStartTime()) {
687 int stackLevel = stackInterval.getStateValue().unboxInt();
688 CallStackEntry selectedEntry = threadEntry.getChildren().get(Math.max(0, stackLevel - 1));
689 fTimeGraphCombo.setSelection(selectedEntry);
690 viewer.getTimeGraphControl().fireSelectionChanged();
691 break;
692 }
693 } catch (AttributeNotFoundException e) {
694 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
695 } catch (TimeRangeException e) {
696 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
697 } catch (StateSystemDisposedException e) {
698 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
699 } catch (StateValueTypeException e) {
700 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
701 }
702 }
703 }
704 });
705 }
706
707 /**
708 * Handler for the RangeSynch signal
709 *
710 * @param signal
711 * The incoming signal
712 */
713 @TmfSignalHandler
714 public void synchToRange(final TmfRangeSynchSignal signal) {
715
716 if (isPinned()) {
717 fSavedRangeSyncSignal =
718 new TmfRangeSynchSignal(signal.getSource(), new TmfTimeRange(signal.getCurrentRange().getStartTime(), signal.getCurrentRange().getEndTime()));
719
720 fSavedTimeSyncSignal = null;
721 }
722
723 if (signal.getSource() == this || fTrace == null || isPinned()) {
724 return;
725 }
726 if (signal.getCurrentRange().getIntersection(fTrace.getTimeRange()) == null) {
727 return;
728 }
729 final long startTime = signal.getCurrentRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
730 final long endTime = signal.getCurrentRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
731 Display.getDefault().asyncExec(new Runnable() {
732 @Override
733 public void run() {
734 if (fTimeGraphCombo.isDisposed()) {
735 return;
736 }
737 fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(startTime, endTime);
738 startZoomThread(startTime, endTime);
739 }
740 });
741 }
742
743 // ------------------------------------------------------------------------
744 // Internal
745 // ------------------------------------------------------------------------
746 private void loadTrace() {
747 synchronized (fEntryListMap) {
748 fEntryList = fEntryListMap.get(fTrace);
749 if (fEntryList == null) {
750 synchronized (fBuildThreadMap) {
751 BuildThread buildThread = new BuildThread(fTrace);
752 fBuildThreadMap.put(fTrace, buildThread);
753 buildThread.start();
754 }
755 } else {
756 fStartTime = fTrace.getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
757 fEndTime = fTrace.getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
758 refresh();
759 }
760 }
761 }
762
763 private void buildThreadList(final ITmfTrace trace, IProgressMonitor monitor) {
764 fStartTime = Long.MAX_VALUE;
765 fEndTime = Long.MIN_VALUE;
766 ITmfTrace[] traces;
767 if (trace instanceof TmfExperiment) {
768 TmfExperiment experiment = (TmfExperiment) trace;
769 traces = experiment.getTraces();
770 } else {
771 traces = new ITmfTrace[] { trace };
772 }
773 ArrayList<ThreadEntry> entryList = new ArrayList<ThreadEntry>();
774 for (ITmfTrace aTrace : traces) {
775 if (monitor.isCanceled()) {
776 return;
777 }
778 ITmfStateSystem ss = aTrace.getStateSystems().get(CallStackStateProvider.ID);
779 if (ss == null || !ss.waitUntilBuilt()) {
780 String threadName = Messages.CallStackView_StackInfoNotAvailable + ' ' + '(' + aTrace.getName() + ')';
781 ThreadEntry threadEntry = new ThreadEntry(aTrace, threadName, -1, 0, 0);
782 entryList.add(threadEntry);
783 continue;
784 }
785 long startTime = ss.getStartTime();
786 long endTime = ss.getCurrentEndTime() + 1;
787 fStartTime = Math.min(fStartTime, startTime);
788 fEndTime = Math.max(fEndTime, endTime);
789 List<Integer> threadQuarks = ss.getQuarks(CallStackStateProvider.THREADS, "*"); //$NON-NLS-1$
790 for (int i = 0; i < threadQuarks.size(); i++) {
791 if (monitor.isCanceled()) {
792 return;
793 }
794 int threadQuark = threadQuarks.get(i);
795 String thread = ss.getAttributeName(threadQuark);
796 String threadEntryName = thread + ' ' + '(' + aTrace.getName() + ')';
797 ThreadEntry threadEntry = new ThreadEntry(aTrace, threadEntryName, threadQuark, startTime, endTime);
798 entryList.add(threadEntry);
799 int eventStackQuark;
800 try {
801 eventStackQuark = ss.getQuarkRelative(threadQuark, CallStackStateProvider.CALL_STACK);
802 int level = 1;
803 for (int stackLevelQuark : ss.getSubAttributes(eventStackQuark, false)) {
804 CallStackEntry callStackEntry = new CallStackEntry(stackLevelQuark, level++, aTrace);
805 threadEntry.addChild(callStackEntry);
806 }
807 } catch (AttributeNotFoundException e) {
808 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
809 }
810 }
811 }
812 synchronized (fEntryListMap) {
813 fEntryListMap.put(trace, (ArrayList<ThreadEntry>) entryList.clone());
814 }
815 if (trace == fTrace) {
816 refresh();
817 }
818 for (ThreadEntry threadEntry : entryList) {
819 for (CallStackEntry callStackEntry : threadEntry.getChildren()) {
820 if (monitor.isCanceled()) {
821 return;
822 }
823 buildStatusEvents(trace, callStackEntry, monitor);
824 }
825 }
826 }
827
828 private void buildStatusEvents(ITmfTrace trace, CallStackEntry entry, IProgressMonitor monitor) {
829 ITmfStateSystem ss = entry.getTrace().getStateSystems().get(CallStackStateProvider.ID);
830 long start = ss.getStartTime();
831 long end = ss.getCurrentEndTime() + 1;
832 long resolution = Math.max(1, (end - start) / fDisplayWidth);
833 List<ITimeEvent> eventList = getEventList(entry, start, end, resolution, monitor);
834 if (monitor.isCanceled()) {
835 return;
836 }
837 entry.setEventList(eventList);
838 if (trace == fTrace) {
839 redraw();
840 }
841 }
842
843 private static List<ITimeEvent> getEventList(CallStackEntry entry,
844 long startTime, long endTime, long resolution,
845 IProgressMonitor monitor) {
846 ITmfStateSystem ss = entry.getTrace().getStateSystems().get(CallStackStateProvider.ID);
847 long start = Math.max(startTime, ss.getStartTime());
848 long end = Math.min(endTime, ss.getCurrentEndTime() + 1);
849 if (end <= start) {
850 return null;
851 }
852 List<ITimeEvent> eventList = null;
853 try {
854 List<ITmfStateInterval> stackIntervals = ss.queryHistoryRange(entry.getQuark(), start, end - 1, resolution, monitor);
855 eventList = new ArrayList<ITimeEvent>(stackIntervals.size());
856 long lastEndTime = -1;
857 boolean lastIsNull = true;
858 for (ITmfStateInterval statusInterval : stackIntervals) {
859 if (monitor.isCanceled()) {
860 return null;
861 }
862 long time = statusInterval.getStartTime();
863 long duration = statusInterval.getEndTime() - time + 1;
864 if (!statusInterval.getStateValue().isNull()) {
865 final int modulo = CallStackPresentationProvider.NUM_COLORS / 2;
866 int value = statusInterval.getStateValue().toString().hashCode() % modulo + modulo;
867 eventList.add(new CallStackEvent(entry, time, duration, value));
868 lastIsNull = false;
869 } else {
870 if (lastEndTime != time && lastEndTime != -1 && lastIsNull) {
871 eventList.add(new TimeEvent(entry, lastEndTime, time - lastEndTime));
872 }
873 eventList.add(new NullTimeEvent(entry, time, duration));
874 lastIsNull = true;
875 }
876 lastEndTime = time + duration;
877 }
878 } catch (AttributeNotFoundException e) {
879 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
880 } catch (TimeRangeException e) {
881 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
882 } catch (StateSystemDisposedException e) {
883 /* Ignored */
884 }
885 return eventList;
886 }
887
888 private void selectTime(long time) {
889 if (fEntryList == null) {
890 return;
891 }
892 for (ThreadEntry threadEntry : fEntryList) {
893 ITmfStateSystem ss = threadEntry.fThreadTrace.getStateSystems().get(CallStackStateProvider.ID);
894 if (ss == null || !ss.waitUntilBuilt()) {
895 continue;
896 }
897 long queryTime = Math.max(ss.getStartTime(), Math.min(ss.getCurrentEndTime(), time));
898 for (CallStackEntry callStackEntry : threadEntry.getChildren()) {
899 try {
900 ITmfStateInterval stackLevelInterval = ss.querySingleState(queryTime, callStackEntry.getQuark());
901 ITmfStateValue nameValue = stackLevelInterval.getStateValue();
902 String name = ""; //$NON-NLS-1$
903 try {
904 if (nameValue.getType() == Type.STRING) {
905 name = nameValue.unboxStr();
906 } else if (nameValue.getType() == Type.INTEGER) {
907 name = "0x" + Integer.toHexString(nameValue.unboxInt()); //$NON-NLS-1$
908 } else if (nameValue.getType() == Type.LONG) {
909 name = "0x" + Long.toHexString(nameValue.unboxLong()); //$NON-NLS-1$
910 }
911 } catch (StateValueTypeException e) {
912 }
913 callStackEntry.setFunctionName(name);
914 if (name.length() > 0) {
915 callStackEntry.setStartTime(stackLevelInterval.getStartTime());
916 callStackEntry.setEndTime(stackLevelInterval.getEndTime() + 1);
917 }
918 } catch (AttributeNotFoundException e) {
919 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
920 } catch (TimeRangeException e) {
921 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
922 } catch (StateSystemDisposedException e) {
923 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
924 }
925 }
926 }
927 fTimeGraphCombo.refresh();
928 }
929
930 private void refresh() {
931 Display.getDefault().asyncExec(new Runnable() {
932 @Override
933 public void run() {
934 if (fTimeGraphCombo.isDisposed()) {
935 return;
936 }
937 ITimeGraphEntry[] entries = null;
938 synchronized (fEntryListMap) {
939 fEntryList = fEntryListMap.get(fTrace);
940 if (fEntryList == null) {
941 fEntryList = new ArrayList<ThreadEntry>();
942 }
943 entries = fEntryList.toArray(new ITimeGraphEntry[0]);
944 }
945 fTimeGraphCombo.setInput(entries);
946 fTimeGraphCombo.getTimeGraphViewer().setTimeBounds(fStartTime, fEndTime);
947
948 long selectionBeginTime = fTrace == null ? 0 : fTraceManager.getSelectionBeginTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
949 long selectionEndTime = fTrace == null ? 0 : fTraceManager.getSelectionEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
950 long startTime = fTrace == null ? 0 : fTraceManager.getCurrentRange().getStartTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
951 long endTime = fTrace == null ? 0 : fTraceManager.getCurrentRange().getEndTime().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
952 startTime = Math.max(startTime, fStartTime);
953 endTime = Math.min(endTime, fEndTime);
954 fTimeGraphCombo.getTimeGraphViewer().setSelectionRange(selectionBeginTime, selectionEndTime);
955 selectTime(selectionBeginTime);
956 fTimeGraphCombo.getTimeGraphViewer().setStartFinishTime(startTime, endTime);
957 startZoomThread(startTime, endTime);
958 }
959 });
960 }
961
962 private void redraw() {
963 synchronized (fSyncObj) {
964 if (fRedrawState == State.IDLE) {
965 fRedrawState = State.BUSY;
966 } else {
967 fRedrawState = State.PENDING;
968 return;
969 }
970 }
971 Display.getDefault().asyncExec(new Runnable() {
972 @Override
973 public void run() {
974 if (fTimeGraphCombo.isDisposed()) {
975 return;
976 }
977 fTimeGraphCombo.redraw();
978 fTimeGraphCombo.update();
979 synchronized (fSyncObj) {
980 if (fRedrawState == State.PENDING) {
981 fRedrawState = State.IDLE;
982 redraw();
983 } else {
984 fRedrawState = State.IDLE;
985 }
986 }
987 }
988 });
989 }
990
991 private void startZoomThread(long startTime, long endTime) {
992 if (fZoomThread != null) {
993 fZoomThread.cancel();
994 }
995 fZoomThread = new ZoomThread(fEntryList, startTime, endTime);
996 fZoomThread.start();
997 }
998
999 private void makeActions() {
1000 fPreviousItemAction = fTimeGraphCombo.getTimeGraphViewer().getPreviousItemAction();
1001 fPreviousItemAction.setText(Messages.TmfTimeGraphViewer_PreviousItemActionNameText);
1002 fPreviousItemAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousItemActionToolTipText);
1003 fNextItemAction = fTimeGraphCombo.getTimeGraphViewer().getNextItemAction();
1004 fNextItemAction.setText(Messages.TmfTimeGraphViewer_NextItemActionNameText);
1005 fNextItemAction.setToolTipText(Messages.TmfTimeGraphViewer_NextItemActionToolTipText);
1006 }
1007
1008 private void contributeToActionBars() {
1009 IActionBars bars = getViewSite().getActionBars();
1010 fillLocalToolBar(bars.getToolBarManager());
1011
1012 // Create pin action
1013 contributePinActionToToolBar();
1014 fPinAction.addPropertyChangeListener(new IPropertyChangeListener(){
1015 @Override
1016 public void propertyChange(PropertyChangeEvent event) {
1017 if (IAction.CHECKED.equals(event.getProperty()) && !isPinned()) {
1018 if (fSavedRangeSyncSignal != null) {
1019 synchToRange(fSavedRangeSyncSignal);
1020 fSavedRangeSyncSignal = null;
1021 }
1022
1023 if (fSavedTimeSyncSignal != null) {
1024 synchToTime(fSavedTimeSyncSignal);
1025 fSavedTimeSyncSignal = null;
1026 }
1027 }
1028 }
1029 });
1030 }
1031
1032 private void fillLocalToolBar(IToolBarManager manager) {
1033 manager.add(fTimeGraphCombo.getTimeGraphViewer().getResetScaleAction());
1034 manager.add(getPreviousEventAction());
1035 manager.add(getNextEventAction());
1036 manager.add(fPreviousItemAction);
1037 manager.add(fNextItemAction);
1038 manager.add(fTimeGraphCombo.getTimeGraphViewer().getZoomInAction());
1039 manager.add(fTimeGraphCombo.getTimeGraphViewer().getZoomOutAction());
1040 manager.add(new Separator());
1041 }
1042
1043 /**
1044 * Get the the next event action.
1045 *
1046 * @return The action object
1047 */
1048 private Action getNextEventAction() {
1049 if (fNextEventAction == null) {
1050 fNextEventAction = new Action() {
1051 @Override
1052 public void run() {
1053 TimeGraphViewer viewer = fTimeGraphCombo.getTimeGraphViewer();
1054 ITimeGraphEntry entry = viewer.getSelection();
1055 if (entry instanceof CallStackEntry) {
1056 try {
1057 CallStackEntry callStackEntry = (CallStackEntry) entry;
1058 ITmfTrace trace = callStackEntry.getTrace();
1059 ITmfStateSystem ss = trace.getStateSystems().get(CallStackStateProvider.ID);
1060 long time = Math.max(ss.getStartTime(), Math.min(ss.getCurrentEndTime(), viewer.getSelectionBegin()));
1061 ThreadEntry threadEntry = (ThreadEntry) callStackEntry.getParent();
1062 int quark = ss.getQuarkRelative(threadEntry.getThreadQuark(), CallStackStateProvider.CALL_STACK);
1063 ITmfStateInterval stackInterval = ss.querySingleState(time, quark);
1064 long newTime = stackInterval.getEndTime() + 1;
1065 viewer.setSelectedTimeNotify(newTime, true);
1066 stackInterval = ss.querySingleState(Math.min(ss.getCurrentEndTime(), newTime), quark);
1067 int stackLevel = stackInterval.getStateValue().unboxInt();
1068 CallStackEntry selectedEntry = threadEntry.getChildren().get(Math.max(0, stackLevel - 1));
1069 fTimeGraphCombo.setSelection(selectedEntry);
1070 viewer.getTimeGraphControl().fireSelectionChanged();
1071 startZoomThread(viewer.getTime0(), viewer.getTime1());
1072 } catch (AttributeNotFoundException e) {
1073 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1074 } catch (TimeRangeException e) {
1075 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1076 } catch (StateSystemDisposedException e) {
1077 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1078 } catch (StateValueTypeException e) {
1079 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1080 }
1081 }
1082 }
1083 };
1084
1085 fNextEventAction.setText(Messages.TmfTimeGraphViewer_NextEventActionNameText);
1086 fNextEventAction.setToolTipText(Messages.TmfTimeGraphViewer_NextEventActionToolTipText);
1087 fNextEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_NEXT_EVENT));
1088 }
1089
1090 return fNextEventAction;
1091 }
1092
1093 /**
1094 * Get the previous event action.
1095 *
1096 * @return The Action object
1097 */
1098 private Action getPreviousEventAction() {
1099 if (fPrevEventAction == null) {
1100 fPrevEventAction = new Action() {
1101 @Override
1102 public void run() {
1103 TimeGraphViewer viewer = fTimeGraphCombo.getTimeGraphViewer();
1104 ITimeGraphEntry entry = viewer.getSelection();
1105 if (entry instanceof CallStackEntry) {
1106 try {
1107 CallStackEntry callStackEntry = (CallStackEntry) entry;
1108 ITmfTrace trace = callStackEntry.getTrace();
1109 ITmfStateSystem ss = trace.getStateSystems().get(CallStackStateProvider.ID);
1110 long time = Math.max(ss.getStartTime(), Math.min(ss.getCurrentEndTime(), viewer.getSelectionBegin()));
1111 ThreadEntry threadEntry = (ThreadEntry) callStackEntry.getParent();
1112 int quark = ss.getQuarkRelative(threadEntry.getThreadQuark(), CallStackStateProvider.CALL_STACK);
1113 ITmfStateInterval stackInterval = ss.querySingleState(time, quark);
1114 if (stackInterval.getStartTime() == time && time > ss.getStartTime()) {
1115 stackInterval = ss.querySingleState(time - 1, quark);
1116 }
1117 viewer.setSelectedTimeNotify(stackInterval.getStartTime(), true);
1118 int stackLevel = stackInterval.getStateValue().unboxInt();
1119 CallStackEntry selectedEntry = threadEntry.getChildren().get(Math.max(0, stackLevel - 1));
1120 fTimeGraphCombo.setSelection(selectedEntry);
1121 viewer.getTimeGraphControl().fireSelectionChanged();
1122 startZoomThread(viewer.getTime0(), viewer.getTime1());
1123 } catch (AttributeNotFoundException e) {
1124 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1125 } catch (TimeRangeException e) {
1126 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1127 } catch (StateSystemDisposedException e) {
1128 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1129 } catch (StateValueTypeException e) {
1130 Activator.getDefault().logError("Error querying state system", e); //$NON-NLS-1$
1131 }
1132 }
1133 }
1134 };
1135
1136 fPrevEventAction.setText(Messages.TmfTimeGraphViewer_PreviousEventActionNameText);
1137 fPrevEventAction.setToolTipText(Messages.TmfTimeGraphViewer_PreviousEventActionToolTipText);
1138 fPrevEventAction.setImageDescriptor(Activator.getDefault().getImageDescripterFromPath(ITmfImageConstants.IMG_UI_PREV_EVENT));
1139 }
1140
1141 return fPrevEventAction;
1142 }
1143
1144 }
This page took 0.057521 seconds and 5 git commands to generate.