1 /*******************************************************************************
2 * Copyright (c) 2014, 2017 École Polytechnique de Montréal
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
10 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.internal
.analysis
.os
.linux
.ui
.views
.cpuusage
;
15 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
17 import java
.util
.ArrayList
;
18 import java
.util
.Collections
;
19 import java
.util
.HashMap
;
20 import java
.util
.List
;
22 import java
.util
.Map
.Entry
;
24 import java
.util
.TreeSet
;
26 import org
.eclipse
.jdt
.annotation
.NonNull
;
27 import org
.eclipse
.jface
.viewers
.Viewer
;
28 import org
.eclipse
.jface
.viewers
.ViewerComparator
;
29 import org
.eclipse
.osgi
.util
.NLS
;
30 import org
.eclipse
.swt
.widgets
.Composite
;
31 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.cpuusage
.KernelCpuUsageAnalysis
;
32 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernel
.KernelAnalysisModule
;
33 import org
.eclipse
.tracecompass
.internal
.analysis
.os
.linux
.core
.kernel
.Attributes
;
34 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.StateSystemUtils
;
36 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
37 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
38 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
39 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
40 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceOpenedSignal
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceSelectedSignal
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.TmfStateSystemAnalysisModule
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceContext
;
47 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceManager
;
48 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
49 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.AbstractTmfTreeViewer
;
50 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.ITmfTreeColumnDataProvider
;
51 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.ITmfTreeViewerEntry
;
52 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.TmfTreeColumnData
;
53 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.TmfTreeColumnData
.ITmfColumnPercentageProvider
;
54 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.tree
.TmfTreeViewerEntry
;
57 * Tree viewer to display CPU usage information in a specified time range. It
58 * shows the process's TID, its name, the time spent on the CPU during that
59 * range, in % and absolute value.
61 * @author Geneviève Bastien
63 public class CpuUsageComposite
extends AbstractTmfTreeViewer
{
65 // Timeout between to wait for in the updateElements method
66 private static final long BUILD_UPDATE_TIMEOUT
= 500;
68 private KernelCpuUsageAnalysis fModule
= null;
69 private String fSelectedThread
= null;
71 private static final String
[] COLUMN_NAMES
= new String
[] {
72 Messages
.CpuUsageComposite_ColumnTID
,
73 Messages
.CpuUsageComposite_ColumnProcess
,
74 Messages
.CpuUsageComposite_ColumnPercent
,
75 Messages
.CpuUsageComposite_ColumnTime
78 /* A map that saves the mapping of a thread ID to its executable name */
79 private final Map
<String
, String
> fProcessNameMap
= new HashMap
<>();
81 private final @NonNull Set
<@NonNull Integer
> fCpus
= new TreeSet
<>();
83 /** Provides label for the CPU usage tree viewer cells */
84 protected static class CpuLabelProvider
extends TreeLabelProvider
{
87 public String
getColumnText(Object element
, int columnIndex
) {
88 CpuUsageEntry obj
= (CpuUsageEntry
) element
;
89 if (columnIndex
== 0) {
91 } else if (columnIndex
== 1) {
92 return obj
.getProcessName();
93 } else if (columnIndex
== 2) {
94 return String
.format(Messages
.CpuUsageComposite_TextPercent
, obj
.getPercent());
95 } else if (columnIndex
== 3) {
96 return NLS
.bind(Messages
.CpuUsageComposite_TextTime
, obj
.getTime());
99 return element
.toString();
108 * The parent composite that holds this viewer
110 public CpuUsageComposite(Composite parent
) {
111 super(parent
, false);
112 setLabelProvider(new CpuLabelProvider());
116 protected ITmfTreeColumnDataProvider
getColumnDataProvider() {
117 return new ITmfTreeColumnDataProvider() {
120 public List
<TmfTreeColumnData
> getColumnData() {
121 /* All columns are sortable */
122 List
<TmfTreeColumnData
> columns
= new ArrayList
<>();
123 TmfTreeColumnData column
= new TmfTreeColumnData(COLUMN_NAMES
[0]);
124 column
.setComparator(new ViewerComparator() {
126 public int compare(Viewer viewer
, Object e1
, Object e2
) {
127 CpuUsageEntry n1
= (CpuUsageEntry
) e1
;
128 CpuUsageEntry n2
= (CpuUsageEntry
) e2
;
130 return n1
.getTid().compareTo(n2
.getTid());
135 column
= new TmfTreeColumnData(COLUMN_NAMES
[1]);
136 column
.setComparator(new ViewerComparator() {
138 public int compare(Viewer viewer
, Object e1
, Object e2
) {
139 CpuUsageEntry n1
= (CpuUsageEntry
) e1
;
140 CpuUsageEntry n2
= (CpuUsageEntry
) e2
;
142 return n1
.getProcessName().compareTo(n2
.getProcessName());
147 column
= new TmfTreeColumnData(COLUMN_NAMES
[2]);
148 column
.setComparator(new ViewerComparator() {
150 public int compare(Viewer viewer
, Object e1
, Object e2
) {
151 CpuUsageEntry n1
= (CpuUsageEntry
) e1
;
152 CpuUsageEntry n2
= (CpuUsageEntry
) e2
;
154 return n1
.getPercent().compareTo(n2
.getPercent());
158 column
.setPercentageProvider(new ITmfColumnPercentageProvider() {
161 public double getPercentage(Object data
) {
162 CpuUsageEntry parent
= (CpuUsageEntry
) data
;
163 return parent
.getPercent() / 100;
167 column
= new TmfTreeColumnData(COLUMN_NAMES
[3]);
168 column
.setComparator(new ViewerComparator() {
170 public int compare(Viewer viewer
, Object e1
, Object e2
) {
171 CpuUsageEntry n1
= (CpuUsageEntry
) e1
;
172 CpuUsageEntry n2
= (CpuUsageEntry
) e2
;
174 return n1
.getTime().compareTo(n2
.getTime());
187 protected ITmfTrace
getTrace() {
188 return super.getTrace();
191 // ------------------------------------------------------------------------
193 // ------------------------------------------------------------------------
196 protected void contentChanged(ITmfTreeViewerEntry rootEntry
) {
197 String selectedThread
= fSelectedThread
;
198 if (selectedThread
!= null) {
199 /* Find the selected thread among the inputs */
200 for (ITmfTreeViewerEntry entry
: rootEntry
.getChildren()) {
201 if (entry
instanceof CpuUsageEntry
) {
202 if (selectedThread
.equals(((CpuUsageEntry
) entry
).getTid())) {
203 List
<ITmfTreeViewerEntry
> list
= Collections
.singletonList(entry
);
204 super.setSelection(list
);
213 public void initializeDataSource() {
214 /* Should not be called while trace is still null */
215 ITmfTrace trace
= checkNotNull(getTrace());
217 fModule
= TmfTraceUtils
.getAnalysisModuleOfClass(trace
, KernelCpuUsageAnalysis
.class, KernelCpuUsageAnalysis
.ID
);
218 if (fModule
== null) {
222 fModule
.waitForInitialization();
223 fProcessNameMap
.clear();
227 protected ITmfTreeViewerEntry
updateElements(long start
, long end
, boolean isSelection
) {
228 if (isSelection
|| (start
== end
)) {
231 if (getTrace() == null || fModule
== null) {
234 fModule
.waitForInitialization();
235 ITmfStateSystem ss
= fModule
.getStateSystem();
240 boolean complete
= false;
241 long currentEnd
= Math
.max(start
, ss
.getStartTime());
243 while (!complete
&& currentEnd
< end
) {
244 complete
= ss
.waitUntilBuilt(BUILD_UPDATE_TIMEOUT
);
245 currentEnd
= ss
.getCurrentEndTime();
248 /* Initialize the data */
249 Map
<String
, Long
> cpuUsageMap
= fModule
.getCpuUsageInRange(fCpus
, Math
.max(start
, getStartTime()), Math
.min(end
, getEndTime()));
251 TmfTreeViewerEntry root
= new TmfTreeViewerEntry(""); //$NON-NLS-1$
252 List
<ITmfTreeViewerEntry
> entryList
= root
.getChildren();
254 for (Entry
<String
, Long
> entry
: cpuUsageMap
.entrySet()) {
256 * Process only entries representing the total of all CPUs and that
259 if (entry
.getValue() == 0) {
262 if (!entry
.getKey().startsWith(KernelCpuUsageAnalysis
.TOTAL
)) {
265 String
[] strings
= entry
.getKey().split(KernelCpuUsageAnalysis
.SPLIT_STRING
, 2);
267 if ((strings
.length
> 1) && !(strings
[1].equals(KernelCpuUsageAnalysis
.TID_ZERO
))) {
268 CpuUsageEntry obj
= new CpuUsageEntry(strings
[1], getProcessName(strings
[1]), (double) entry
.getValue() / (double) (end
- start
) * 100, entry
.getValue());
277 * Get the process name from its TID by using the LTTng kernel analysis
280 private String
getProcessName(String tid
) {
281 String execName
= fProcessNameMap
.get(tid
);
282 if (execName
!= null) {
285 ITmfTrace trace
= getTrace();
289 ITmfStateSystem kernelSs
= TmfStateSystemAnalysisModule
.getStateSystem(trace
, KernelAnalysisModule
.ID
);
290 if (kernelSs
== null) {
295 int cpusNode
= kernelSs
.getQuarkAbsolute(Attributes
.THREADS
);
297 /* Get the quarks for each cpu */
298 List
<Integer
> cpuNodes
= kernelSs
.getSubAttributes(cpusNode
, false);
300 for (Integer tidQuark
: cpuNodes
) {
301 if (kernelSs
.getAttributeName(tidQuark
).equals(tid
)) {
303 List
<ITmfStateInterval
> execNameIntervals
;
305 execNameQuark
= kernelSs
.getQuarkRelative(tidQuark
, Attributes
.EXEC_NAME
);
306 execNameIntervals
= StateSystemUtils
.queryHistoryRange(kernelSs
, execNameQuark
, getStartTime(), getEndTime());
307 } catch (TimeRangeException
| AttributeNotFoundException e
) {
309 * No information on this thread (yet?), skip it for now
312 } catch (StateSystemDisposedException e
) {
313 /* State system is closing down, no point continuing */
317 for (ITmfStateInterval execNameInterval
: execNameIntervals
) {
318 if (!execNameInterval
.getStateValue().isNull() &&
319 execNameInterval
.getStateValue().getType() == ITmfStateValue
.Type
.STRING
) {
320 execName
= execNameInterval
.getStateValue().unboxStr();
321 fProcessNameMap
.put(tid
, execName
);
328 } catch (AttributeNotFoundException e
) {
329 /* can't find the process name, just return the tid instead */
335 * Set the currently selected thread ID
338 * The selected thread ID
340 public void setSelectedThread(String tid
) {
341 fSelectedThread
= tid
;
351 public void addCpu(int core
) {
353 updateContent(getWindowStartTime(), getWindowEndTime(), false);
363 public void removeCpu(int core
) {
365 updateContent(getWindowStartTime(), getWindowEndTime(), false);
374 public void clearCpu() {
376 updateContent(getWindowStartTime(), getWindowEndTime(), false);
381 public void traceSelected(TmfTraceSelectedSignal signal
) {
384 super.traceSelected(signal
);
389 public void traceOpened(TmfTraceOpenedSignal signal
) {
392 super.traceOpened(signal
);
395 private void initSelection() {
396 TmfTraceContext ctx
= TmfTraceManager
.getInstance().getCurrentTraceContext();
397 String thread
= (String
) ctx
.getData(CpuUsageView
.CPU_USAGE_SELECTED_THREAD
);
398 setSelectedThread(thread
);
401 private void initCPU() {
403 TmfTraceContext ctx
= TmfTraceManager
.getInstance().getCurrentTraceContext();
404 Object data
= ctx
.getData(CpuUsageView
.CPU_USAGE_FOLLOW_CPU
);
405 if (data
instanceof Set
<?
>) {
406 Set
<?
> set
= (Set
<?
>) data
;
407 for (Object coreObject
: set
) {
408 if (coreObject
instanceof Integer
) {
409 Integer core
= (Integer
) coreObject
;