1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 É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
.Arrays
;
18 import java
.util
.HashMap
;
19 import java
.util
.LinkedHashMap
;
21 import java
.util
.Map
.Entry
;
23 import java
.util
.TreeSet
;
25 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
26 import org
.eclipse
.jdt
.annotation
.NonNull
;
27 import org
.eclipse
.swt
.widgets
.Composite
;
28 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.cpuusage
.KernelCpuUsageAnalysis
;
29 import org
.eclipse
.tracecompass
.internal
.analysis
.os
.linux
.ui
.Activator
;
30 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
32 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfSignalHandler
;
33 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceOpenedSignal
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.signal
.TmfTraceSelectedSignal
;
35 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
36 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
37 import org
.eclipse
.tracecompass
.tmf
.ui
.viewers
.xycharts
.linecharts
.TmfCommonXLineChartViewer
;
39 import com
.google
.common
.base
.Joiner
;
42 * CPU usage viewer with XY line chart. It displays the total CPU usage and that
43 * of the threads selected in the CPU usage tree viewer.
45 * @author Geneviève Bastien
47 public class CpuUsageXYViewer
extends TmfCommonXLineChartViewer
{
49 private static final int NOT_SELECTED
= -1;
51 private KernelCpuUsageAnalysis fModule
= null;
53 /* Maps a thread ID to a list of y values */
54 private final Map
<String
, double[]> fYValues
= new LinkedHashMap
<>();
56 * To avoid up and downs CPU usage when process is in and out of CPU
57 * frequently, use a smaller resolution to get better averages.
59 private static final double RESOLUTION
= 0.4;
61 // Timeout between updates in the updateData thread
62 private static final long BUILD_UPDATE_TIMEOUT
= 500;
64 private long fSelectedThread
= NOT_SELECTED
;
66 private final @NonNull Set
<@NonNull Integer
> fCpus
= new TreeSet
<>();
74 public CpuUsageXYViewer(Composite parent
) {
75 super(parent
, Messages
.CpuUsageXYViewer_Title
, Messages
.CpuUsageXYViewer_TimeXAxis
, Messages
.CpuUsageXYViewer_CpuYAxis
);
76 setResolution(RESOLUTION
);
77 getSwtChart().getTitle().setVisible(true);
78 getSwtChart().getLegend().setVisible(false);
82 protected void initializeDataSource() {
83 ITmfTrace trace
= getTrace();
85 fModule
= TmfTraceUtils
.getAnalysisModuleOfClass(trace
, KernelCpuUsageAnalysis
.class, KernelCpuUsageAnalysis
.ID
);
86 if (fModule
== null) {
93 private static double[] zeroFill(int nb
) {
94 double[] arr
= new double[nb
];
95 Arrays
.fill(arr
, 0.0);
100 protected void updateData(long start
, long end
, int nb
, IProgressMonitor monitor
) {
102 if (getTrace() == null || fModule
== null) {
105 fModule
.waitForInitialization();
106 ITmfStateSystem ss
= fModule
.getStateSystem();
110 double[] xvalues
= getXAxis(start
, end
, nb
);
111 if (xvalues
.length
== 0) {
116 boolean complete
= false;
117 long currentEnd
= Math
.max(ss
.getStartTime(), start
);
119 while (!complete
&& currentEnd
< end
) {
121 if (monitor
.isCanceled()) {
125 long traceStart
= Math
.max(getStartTime(), ss
.getStartTime());
126 long traceEnd
= getEndTime();
127 long offset
= getTimeOffset();
128 long selectedThread
= fSelectedThread
;
130 complete
= ss
.waitUntilBuilt(BUILD_UPDATE_TIMEOUT
);
131 currentEnd
= ss
.getCurrentEndTime();
133 /* Initialize the data */
134 Map
<String
, Long
> cpuUsageMap
= fModule
.getCpuUsageInRange(fCpus
, Math
.max(start
, traceStart
), Math
.min(end
, traceEnd
));
135 Map
<String
, String
> totalEntries
= new HashMap
<>();
137 fYValues
.put(Messages
.CpuUsageXYViewer_Total
, zeroFill(xvalues
.length
));
138 String stringSelectedThread
= Long
.toString(selectedThread
);
139 if (selectedThread
!= NOT_SELECTED
) {
140 fYValues
.put(stringSelectedThread
, zeroFill(xvalues
.length
));
143 for (Entry
<String
, Long
> entry
: cpuUsageMap
.entrySet()) {
145 * Process only entries representing the total of all CPUs
146 * and that have time on CPU
148 if (entry
.getValue() == 0) {
151 if (!entry
.getKey().startsWith(KernelCpuUsageAnalysis
.TOTAL
)) {
154 String
[] strings
= entry
.getKey().split(KernelCpuUsageAnalysis
.SPLIT_STRING
, 2);
156 if ((strings
.length
> 1) && !(strings
[1].equals(KernelCpuUsageAnalysis
.TID_ZERO
))) {
157 /* This is the total cpu usage for a thread */
158 totalEntries
.put(strings
[1], entry
.getKey());
162 double prevX
= xvalues
[0] - 1;
163 long prevTime
= (long) prevX
+ offset
;
165 * make sure that time is in the trace range after double to
168 prevTime
= Math
.max(traceStart
, prevTime
);
169 prevTime
= Math
.min(traceEnd
, prevTime
);
170 /* Get CPU usage statistics for each x value */
171 for (int i
= 0; i
< xvalues
.length
; i
++) {
172 if (monitor
.isCanceled()) {
176 double x
= xvalues
[i
];
177 long time
= (long) x
+ offset
;
178 time
= Math
.max(traceStart
, time
);
179 time
= Math
.min(traceEnd
, time
);
180 if (time
== prevTime
) {
182 * we need at least 1 time unit to be able to get cpu
183 * usage when zoomed in
188 cpuUsageMap
= fModule
.getCpuUsageInRange(fCpus
, prevTime
, time
);
191 * Calculate the sum of all total entries, and add a data
192 * point to the selected one
194 for (Entry
<String
, String
> entry
: totalEntries
.entrySet()) {
195 Long cpuEntry
= cpuUsageMap
.get(entry
.getValue());
196 cpuEntry
= cpuEntry
!= null ? cpuEntry
: 0L;
198 totalCpu
+= cpuEntry
;
200 if (entry
.getKey().equals(stringSelectedThread
)) {
201 /* This is the total cpu usage for a thread */
202 double[] key
= checkNotNull(fYValues
.get(entry
.getKey()));
203 key
[i
] = (double) cpuEntry
/ (double) (time
- prevTime
) * 100;
207 double[] key
= checkNotNull(fYValues
.get(Messages
.CpuUsageXYViewer_Total
));
208 key
[i
] = (double) totalCpu
/ (double) (time
- prevTime
) * 100;
211 for (Entry
<String
, double[]> entry
: fYValues
.entrySet()) {
212 setSeries(entry
.getKey(), entry
.getValue());
214 if (monitor
.isCanceled()) {
219 } catch (StateValueTypeException e
) {
220 Activator
.getDefault().logError("Error updating the data of the CPU usage view", e
); //$NON-NLS-1$
226 * Set the selected thread ID, which will be graphed in this viewer
229 * The selected thread ID
231 public void setSelectedThread(long tid
) {
233 deleteSeries(Long
.toString(fSelectedThread
));
234 fSelectedThread
= tid
;
239 * Gets the analysis module
241 * @return the {@link KernelCpuUsageAnalysis}
245 public KernelCpuUsageAnalysis
getModule() {
256 public void addCpu(int core
) {
260 getSwtChart().getTitle().setText(Messages
.CpuUsageView_Title
+ ' ' + getCpuList());
270 public void removeCpu(int core
) {
274 getSwtChart().getTitle().setText(Messages
.CpuUsageView_Title
+ ' ' + getCpuList());
277 private String
getCpuList() {
278 return Joiner
.on(", ").join(fCpus
); //$NON-NLS-1$
286 public void clearCpu() {
290 getSwtChart().getTitle().setText(Messages
.CpuUsageView_Title
);
295 public void traceSelected(TmfTraceSelectedSignal signal
) {
296 setSelectedThread(NOT_SELECTED
);
297 super.traceSelected(signal
);
302 public void traceOpened(TmfTraceOpenedSignal signal
) {
303 setSelectedThread(NOT_SELECTED
);
304 super.traceOpened(signal
);