Commit | Line | Data |
---|---|---|
6e512b93 ASL |
1 | /******************************************************************************* |
2 | * Copyright (c) 2009 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: | |
dfaf8391 FC |
10 | * Yann N. Dauphin (dhaemon@gmail.com) - Implementation |
11 | * Francois Chouinard (fchouinard@gmail.com) - Initial API | |
6e512b93 ASL |
12 | *******************************************************************************/ |
13 | ||
14 | package org.eclipse.linuxtools.lttng.ui.views.statistics; | |
15 | ||
dfaf8391 FC |
16 | import java.text.DecimalFormat; |
17 | import java.util.Arrays; | |
18 | import java.util.HashSet; | |
19 | import java.util.Set; | |
20 | ||
21 | import org.eclipse.jface.viewers.ColumnLabelProvider; | |
22 | import org.eclipse.jface.viewers.ITreeContentProvider; | |
23 | import org.eclipse.jface.viewers.TreeViewer; | |
24 | import org.eclipse.jface.viewers.TreeViewerColumn; | |
25 | import org.eclipse.jface.viewers.Viewer; | |
26 | import org.eclipse.jface.viewers.ViewerComparator; | |
27 | import org.eclipse.linuxtools.lttng.state.IStateDataRequestListener; | |
28 | import org.eclipse.linuxtools.lttng.state.RequestCompletedSignal; | |
29 | import org.eclipse.linuxtools.lttng.state.RequestStartedSignal; | |
30 | import org.eclipse.linuxtools.lttng.state.evProcessor.EventProcessorProxy; | |
31 | import org.eclipse.linuxtools.lttng.state.experiment.StateExperimentManager; | |
32 | import org.eclipse.linuxtools.lttng.state.experiment.StateManagerFactory; | |
33 | import org.eclipse.linuxtools.lttng.ui.views.statistics.evProcessor.StatsEventCountHandlerFactory; | |
34 | import org.eclipse.linuxtools.lttng.ui.views.statistics.evProcessor.StatsTimeCountHandlerFactory; | |
35 | import org.eclipse.linuxtools.lttng.ui.views.statistics.model.StatisticsTreeFactory; | |
36 | import org.eclipse.linuxtools.lttng.ui.views.statistics.model.StatisticsTreeNode; | |
37 | import org.eclipse.linuxtools.tmf.signal.TmfSignalHandler; | |
38 | import org.eclipse.linuxtools.tmf.ui.views.TmfView; | |
39 | import org.eclipse.swt.SWT; | |
40 | import org.eclipse.swt.events.SelectionAdapter; | |
41 | import org.eclipse.swt.events.SelectionEvent; | |
42 | import org.eclipse.swt.graphics.Color; | |
43 | import org.eclipse.swt.graphics.Image; | |
44 | import org.eclipse.swt.layout.FillLayout; | |
6e512b93 | 45 | import org.eclipse.swt.widgets.Composite; |
dfaf8391 FC |
46 | import org.eclipse.swt.widgets.Event; |
47 | import org.eclipse.swt.widgets.Listener; | |
48 | import org.eclipse.ui.ISharedImages; | |
49 | import org.eclipse.ui.PlatformUI; | |
6e512b93 ASL |
50 | |
51 | /** | |
52 | * <b><u>StatisticsView</u></b> | |
53 | * <p> | |
dfaf8391 FC |
54 | * The Statistics View displays statistics for traces. |
55 | * | |
56 | * It is implemented according to the MVC pattern. - The model is a | |
57 | * StatisticsTreeNode built by the State Manager. - The view is built with a | |
58 | * TreeViewer. - The controller that keeps model and view synchronised is an | |
59 | * observer of the model. | |
6e512b93 | 60 | */ |
fc6ccf6f | 61 | public class StatisticsView extends TmfView implements IStateDataRequestListener { |
dfaf8391 FC |
62 | |
63 | public static final String ID = "org.eclipse.linuxtools.lttng.ui.views.statistics"; | |
64 | ||
65 | private TreeViewer treeViewer; | |
66 | ||
67 | // Table column names | |
68 | private final String LEVEL_COLUMN = "Level"; | |
69 | private final String EVENTS_COUNT_COLUMN = "Number of Events"; | |
70 | private final String CPU_TIME_COLUMN = "CPU Time"; | |
71 | private final String CUMULATIVE_CPU_TIME_COLUMN = "Cumulative CPU Time"; | |
72 | private final String ELAPSED_TIME_COLUMN = "Elapsed Time"; | |
73 | ||
74 | // Table column tooltips | |
75 | private final String LEVEL_COLUMN_TIP = "Level at which statistics apply."; | |
76 | private final String EVENTS_COUNT_COLUMN_TIP = "Total amount of events that are tied to given resource."; | |
77 | private final String CPU_TIME_COLUMN_TIP = "Total amount of time the CPU was used excluding wait times(I/O, etc.) at that level."; | |
78 | private final String CUMULATIVE_CPU_TIME_COLUMN_TIP = "Total amount of time between the first and last event excluding wait times in a level."; | |
79 | private final String ELAPSED_TIME_COLUMN_TIP = "Total amount of time the CPU was used including wait times(I/O, etc.) at that level."; | |
6e512b93 | 80 | |
dfaf8391 FC |
81 | // Level for which statistics should not be displayed. |
82 | private Set<String> folderLevels = new HashSet<String>(Arrays | |
83 | .asList(new String[] { "Event Types", "Modes", "Submodes", "CPUs", | |
84 | "Processes", "Functions" })); | |
6e512b93 | 85 | |
dfaf8391 FC |
86 | // Levels for which sub-levels should not contain time-related statistics. |
87 | private Set<String> levelsWithEmptyTime = new HashSet<String>(Arrays | |
88 | .asList(new String[] { "Event Types" })); | |
89 | ||
90 | private DecimalFormat decimalFormat = new DecimalFormat("0.#########"); | |
91 | ||
92 | // Used to draw bar charts in columns. | |
93 | private interface ColumnPercentageProvider { | |
94 | public double getPercentage(StatisticsTreeNode node); | |
95 | } | |
96 | ||
97 | /** | |
98 | * Contains all the information necessary to build a column of the table. | |
99 | */ | |
100 | private class ColumnData { | |
101 | // Name of the column. | |
102 | public final String header; | |
103 | // Width of the column. | |
104 | public final int width; | |
105 | // Alignment of the column. | |
106 | public final int alignment; | |
107 | // Tooltip of the column. | |
108 | public final String tooltip; | |
109 | // Adapts a StatisticsTreeNode into the content of it's corresponding | |
110 | // cell for that column. | |
111 | public final ColumnLabelProvider labelProvider; | |
112 | // Used to sort elements of this column. Can be null. | |
113 | public final ViewerComparator comparator; | |
114 | // Used to draw bar charts in this column. Can be null. | |
115 | public final ColumnPercentageProvider percentageProvider; | |
116 | ||
117 | public ColumnData(String h, int w, int a, String t, | |
118 | ColumnLabelProvider l, ViewerComparator c, | |
119 | ColumnPercentageProvider p) { | |
120 | header = h; | |
121 | width = w; | |
122 | alignment = a; | |
123 | tooltip = t; | |
124 | labelProvider = l; | |
125 | comparator = c; | |
126 | percentageProvider = p; | |
127 | } | |
128 | }; | |
129 | ||
130 | // List that will be used to create the table. | |
131 | private ColumnData[] columnDataList = new ColumnData[] { | |
132 | new ColumnData(LEVEL_COLUMN, 200, SWT.LEFT, LEVEL_COLUMN_TIP, | |
133 | new ColumnLabelProvider() { | |
134 | @Override | |
135 | public String getText(Object element) { | |
136 | return ((StatisticsTreeNode) element).getKey(); | |
137 | } | |
138 | ||
139 | @Override | |
140 | public Image getImage(Object element) { | |
141 | StatisticsTreeNode node = (StatisticsTreeNode) element; | |
142 | if (folderLevels.contains(node.getKey())) { | |
143 | return PlatformUI.getWorkbench() | |
144 | .getSharedImages().getImage( | |
145 | ISharedImages.IMG_OBJ_FOLDER); | |
146 | } else { | |
147 | return PlatformUI.getWorkbench() | |
148 | .getSharedImages().getImage( | |
149 | ISharedImages.IMG_OBJ_ELEMENT); | |
150 | } | |
151 | } | |
152 | }, new ViewerComparator() { | |
153 | @Override | |
154 | public int compare(Viewer viewer, Object e1, Object e2) { | |
155 | StatisticsTreeNode n1 = (StatisticsTreeNode) e1; | |
156 | StatisticsTreeNode n2 = (StatisticsTreeNode) e2; | |
157 | ||
158 | return n1.getKey().compareTo(n2.getKey()); | |
159 | } | |
160 | }, null), | |
161 | new ColumnData(EVENTS_COUNT_COLUMN, 125, SWT.LEFT, | |
162 | EVENTS_COUNT_COLUMN_TIP, new ColumnLabelProvider() { | |
163 | @Override | |
164 | public String getText(Object element) { | |
165 | StatisticsTreeNode node = (StatisticsTreeNode) element; | |
166 | if (!folderLevels.contains(node.getKey())) { | |
167 | return Long.toString(node.getValue().nbEvents); | |
168 | } else { | |
169 | return ""; | |
170 | } | |
171 | } | |
172 | }, new ViewerComparator() { | |
173 | @Override | |
174 | public int compare(Viewer viewer, Object e1, Object e2) { | |
175 | StatisticsTreeNode n1 = (StatisticsTreeNode) e1; | |
176 | StatisticsTreeNode n2 = (StatisticsTreeNode) e2; | |
177 | ||
178 | return (int) (n1.getValue().nbEvents - n2 | |
179 | .getValue().nbEvents); | |
180 | } | |
181 | }, new ColumnPercentageProvider() { | |
182 | public double getPercentage(StatisticsTreeNode node) { | |
183 | StatisticsTreeNode parent = node; | |
184 | do { | |
185 | parent = parent.getParent(); | |
186 | } while (parent != null | |
187 | && parent.getValue().nbEvents == 0); | |
188 | ||
189 | if (parent == null) { | |
190 | return 0; | |
191 | } else { | |
192 | return (double) node.getValue().nbEvents | |
193 | / parent.getValue().nbEvents; | |
194 | } | |
195 | } | |
196 | }), | |
197 | new ColumnData(CPU_TIME_COLUMN, 125, SWT.LEFT, CPU_TIME_COLUMN_TIP, | |
198 | new ColumnLabelProvider() { | |
199 | @Override | |
200 | public String getText(Object element) { | |
201 | StatisticsTreeNode node = (StatisticsTreeNode) element; | |
202 | ||
203 | if (folderLevels.contains(node.getKey())) { | |
204 | return ""; | |
205 | } else if (node.getParent() != null | |
206 | && levelsWithEmptyTime.contains(node | |
207 | .getParent().getKey())) { | |
208 | return ""; | |
209 | } else { | |
210 | return decimalFormat | |
211 | .format(node.getValue().cpuTime | |
212 | / Math.pow(10, 9)); | |
213 | } | |
214 | } | |
215 | }, null, null), | |
216 | new ColumnData(CUMULATIVE_CPU_TIME_COLUMN, 155, SWT.LEFT, | |
217 | CUMULATIVE_CPU_TIME_COLUMN_TIP, new ColumnLabelProvider() { | |
218 | @Override | |
219 | public String getText(Object element) { | |
220 | StatisticsTreeNode node = (StatisticsTreeNode) element; | |
221 | if (folderLevels.contains(node.getKey())) { | |
222 | return ""; | |
223 | } else if (node.getParent() != null | |
224 | && levelsWithEmptyTime.contains(node | |
225 | .getParent().getKey())) { | |
226 | return ""; | |
227 | } else { | |
228 | return decimalFormat | |
229 | .format(node.getValue().cumulativeCpuTime | |
230 | / Math.pow(10, 9)); | |
231 | } | |
232 | } | |
233 | }, null, null), | |
234 | new ColumnData(ELAPSED_TIME_COLUMN, 100, SWT.LEFT, | |
235 | ELAPSED_TIME_COLUMN_TIP, new ColumnLabelProvider() { | |
236 | @Override | |
237 | public String getText(Object element) { | |
238 | StatisticsTreeNode node = (StatisticsTreeNode) element; | |
239 | if (folderLevels.contains(node.getKey())) { | |
240 | return ""; | |
241 | } else if (node.getParent() != null | |
242 | && levelsWithEmptyTime.contains(node | |
243 | .getParent().getKey())) { | |
244 | return ""; | |
245 | } else { | |
246 | return decimalFormat | |
247 | .format(node.getValue().elapsedTime | |
248 | / Math.pow(10, 9)); | |
249 | } | |
250 | } | |
251 | }, null, null) }; | |
252 | ||
253 | /** | |
254 | * Adapter TreeViewers can use to interact with StatisticsTreeNode objects. | |
6e512b93 | 255 | * |
dfaf8391 | 256 | * @see org.eclipse.jface.viewers.ITreeContentProvider |
6e512b93 | 257 | */ |
dfaf8391 FC |
258 | class TreeContentProvider implements ITreeContentProvider { |
259 | /* | |
260 | * (non-Javadoc) | |
261 | * | |
262 | * @see | |
263 | * org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang | |
264 | * .Object) | |
265 | */ | |
266 | public Object[] getChildren(Object parentElement) { | |
267 | return ((StatisticsTreeNode) parentElement).getChildren().toArray(); | |
268 | } | |
269 | ||
270 | /* | |
271 | * (non-Javadoc) | |
272 | * | |
273 | * @see | |
274 | * org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang | |
275 | * .Object) | |
276 | */ | |
277 | public Object getParent(Object element) { | |
278 | return ((StatisticsTreeNode) element).getParent(); | |
279 | } | |
280 | ||
281 | /* | |
282 | * (non-Javadoc) | |
283 | * | |
284 | * @see | |
285 | * org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang | |
286 | * .Object) | |
287 | */ | |
288 | public boolean hasChildren(Object element) { | |
289 | return ((StatisticsTreeNode) element).hasChildren(); | |
290 | } | |
291 | ||
292 | /* | |
293 | * (non-Javadoc) | |
294 | * | |
295 | * @see | |
296 | * org.eclipse.jface.viewers.IStructuredContentProvider#getElements( | |
297 | * java.lang.Object) | |
298 | */ | |
299 | public Object[] getElements(Object inputElement) { | |
300 | return getChildren(inputElement); | |
301 | } | |
302 | ||
303 | /* | |
304 | * (non-Javadoc) | |
305 | * | |
306 | * @see org.eclipse.jface.viewers.IContentProvider#dispose() | |
307 | */ | |
308 | public void dispose() { | |
309 | } | |
310 | ||
311 | /* | |
312 | * (non-Javadoc) | |
313 | * | |
314 | * @see | |
315 | * org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse | |
316 | * .jface.viewers.Viewer, java.lang.Object, java.lang.Object) | |
317 | */ | |
318 | // @Override | |
319 | public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { | |
320 | } | |
6e512b93 ASL |
321 | } |
322 | ||
fc6ccf6f FC |
323 | public StatisticsView(String viewName) { |
324 | super("StatisticsView"); | |
325 | } | |
326 | ||
dfaf8391 FC |
327 | /* |
328 | * (non-Javadoc) | |
329 | * | |
330 | * @see | |
331 | * org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets | |
332 | * .Composite) | |
6e512b93 ASL |
333 | */ |
334 | @Override | |
335 | public void createPartControl(Composite parent) { | |
dfaf8391 FC |
336 | EventProcessorProxy.getInstance().addEventProcessorFactory( |
337 | StatsTimeCountHandlerFactory.getInstance()); | |
338 | EventProcessorProxy.getInstance().addEventProcessorFactory( | |
339 | StatsEventCountHandlerFactory.getInstance()); | |
340 | ||
341 | parent.setLayout(new FillLayout()); | |
342 | ||
343 | treeViewer = new TreeViewer(parent, SWT.BORDER | SWT.H_SCROLL | |
344 | | SWT.V_SCROLL); | |
345 | treeViewer.setContentProvider(new TreeContentProvider()); | |
346 | treeViewer.getTree().setHeaderVisible(true); | |
347 | treeViewer.setUseHashlookup(true); | |
348 | ||
349 | for (final ColumnData columnData : columnDataList) { | |
350 | final TreeViewerColumn treeColumn = new TreeViewerColumn( | |
351 | treeViewer, columnData.alignment); | |
352 | treeColumn.getColumn().setText(columnData.header); | |
353 | treeColumn.getColumn().setWidth(columnData.width); | |
354 | treeColumn.getColumn().setToolTipText(columnData.tooltip); | |
355 | if (columnData.comparator != null) { | |
356 | treeColumn.getColumn().addSelectionListener( | |
357 | new SelectionAdapter() { | |
358 | @Override | |
359 | public void widgetSelected(SelectionEvent e) { | |
360 | if (treeViewer.getTree().getSortDirection() == SWT.UP | |
361 | || treeViewer.getTree().getSortColumn() != treeColumn | |
362 | .getColumn()) { | |
363 | treeViewer | |
364 | .setComparator(columnData.comparator); | |
365 | treeViewer.getTree().setSortDirection( | |
366 | SWT.DOWN); | |
367 | } else { | |
368 | treeViewer | |
369 | .setComparator(new ViewerComparator() { | |
370 | @Override | |
371 | public int compare( | |
372 | Viewer viewer, | |
373 | Object e1, Object e2) { | |
374 | return -1 | |
375 | * columnData.comparator | |
376 | .compare( | |
377 | viewer, | |
378 | e1, | |
379 | e2); | |
380 | } | |
381 | }); | |
382 | treeViewer.getTree().setSortDirection( | |
383 | SWT.UP); | |
384 | } | |
385 | treeViewer.getTree().setSortColumn( | |
386 | treeColumn.getColumn()); | |
387 | } | |
388 | }); | |
389 | } | |
390 | treeColumn.setLabelProvider(columnData.labelProvider); | |
391 | } | |
392 | ||
393 | // Handler that will draw the bar charts. | |
394 | treeViewer.getTree().addListener(SWT.EraseItem, new Listener() { | |
395 | // @Override | |
396 | public void handleEvent(Event event) { | |
397 | if (columnDataList[event.index].percentageProvider != null) { | |
398 | StatisticsTreeNode node = (StatisticsTreeNode) event.item | |
399 | .getData(); | |
400 | ||
401 | double percentage = columnDataList[event.index].percentageProvider | |
402 | .getPercentage(node); | |
403 | if (percentage == 0) { | |
404 | return; | |
405 | } | |
406 | ||
407 | if ((event.detail & SWT.SELECTED) > 0) { | |
408 | Color oldForeground = event.gc.getForeground(); | |
409 | event.gc.setForeground(event.item.getDisplay() | |
410 | .getSystemColor(SWT.COLOR_LIST_SELECTION)); | |
411 | event.gc.fillRectangle(event.x, event.y, event.width, | |
412 | event.height); | |
413 | event.gc.setForeground(oldForeground); | |
414 | event.detail &= ~SWT.SELECTED; | |
415 | } | |
416 | ||
417 | int barWidth = (int) ((treeViewer.getTree().getColumn(1) | |
418 | .getWidth() - 8) * percentage); | |
419 | int oldAlpha = event.gc.getAlpha(); | |
420 | Color oldForeground = event.gc.getForeground(); | |
421 | Color oldBackground = event.gc.getBackground(); | |
422 | event.gc.setAlpha(64); | |
423 | event.gc.setForeground(event.item.getDisplay() | |
424 | .getSystemColor(SWT.COLOR_BLUE)); | |
425 | event.gc.setBackground(event.item.getDisplay() | |
426 | .getSystemColor(SWT.COLOR_LIST_BACKGROUND)); | |
427 | event.gc.fillGradientRectangle(event.x, event.y, barWidth, | |
428 | event.height, true); | |
429 | event.gc.drawRectangle(event.x, event.y, barWidth, | |
430 | event.height); | |
431 | event.gc.setForeground(oldForeground); | |
432 | event.gc.setBackground(oldBackground); | |
433 | event.gc.setAlpha(oldAlpha); | |
434 | event.detail &= ~SWT.BACKGROUND; | |
435 | } | |
436 | } | |
437 | }); | |
438 | ||
439 | treeViewer.setComparator(columnDataList[0].comparator); | |
440 | treeViewer.getTree().setSortColumn(treeViewer.getTree().getColumn(0)); | |
441 | treeViewer.getTree().setSortDirection(SWT.DOWN); | |
442 | ||
443 | treeViewer.setInput(StatisticsTreeFactory | |
444 | .getStatisticsTree("Experiment")); | |
445 | ||
446 | // Read current data if any available | |
447 | StateExperimentManager experimentManger = StateManagerFactory | |
448 | .getExperimentManager(); | |
449 | experimentManger.readExperiment("statisticsView", this); | |
450 | ||
451 | } | |
452 | @Override | |
453 | public void dispose() { | |
454 | super.dispose(); | |
455 | EventProcessorProxy.getInstance().removeEventProcessorFactory( | |
456 | StatsTimeCountHandlerFactory.getInstance()); | |
457 | EventProcessorProxy.getInstance().removeEventProcessorFactory( | |
458 | StatsEventCountHandlerFactory.getInstance()); | |
6e512b93 | 459 | |
6e512b93 ASL |
460 | } |
461 | ||
dfaf8391 FC |
462 | /* |
463 | * (non-Javadoc) | |
464 | * | |
6e512b93 ASL |
465 | * @see org.eclipse.ui.part.WorkbenchPart#setFocus() |
466 | */ | |
467 | @Override | |
468 | public void setFocus() { | |
dfaf8391 FC |
469 | treeViewer.getTree().setFocus(); |
470 | } | |
471 | ||
472 | @TmfSignalHandler | |
473 | public void processingStarted(RequestStartedSignal request) { | |
474 | // Nothing to do for the time being | |
6e512b93 | 475 | |
6e512b93 ASL |
476 | } |
477 | ||
dfaf8391 FC |
478 | @TmfSignalHandler |
479 | public void processingCompleted(RequestCompletedSignal signal) { | |
480 | treeViewer.getTree().getDisplay().asyncExec(new Runnable() { | |
481 | // @Override | |
482 | public void run() { | |
483 | treeViewer.refresh(); | |
484 | } | |
485 | }); | |
486 | } | |
6e512b93 | 487 | } |