a72d899ca3e08d6064e243f022ca35e421727494
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.os.linux.ui / src / org / eclipse / tracecompass / internal / analysis / os / linux / ui / views / cpuusage / CpuUsageComposite.java
1 /*******************************************************************************
2 * Copyright (c) 2014, 2017 École Polytechnique de Montréal
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 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.internal.analysis.os.linux.ui.views.cpuusage;
14
15 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
16
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Set;
24 import java.util.TreeSet;
25
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;
55
56 /**
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.
60 *
61 * @author Geneviève Bastien
62 */
63 public class CpuUsageComposite extends AbstractTmfTreeViewer {
64
65 // Timeout between to wait for in the updateElements method
66 private static final long BUILD_UPDATE_TIMEOUT = 500;
67
68 private KernelCpuUsageAnalysis fModule = null;
69 private String fSelectedThread = null;
70
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
76 };
77
78 /* A map that saves the mapping of a thread ID to its executable name */
79 private final Map<String, String> fProcessNameMap = new HashMap<>();
80
81 private final @NonNull Set<@NonNull Integer> fCpus = new TreeSet<>();
82
83 /** Provides label for the CPU usage tree viewer cells */
84 protected static class CpuLabelProvider extends TreeLabelProvider {
85
86 @Override
87 public String getColumnText(Object element, int columnIndex) {
88 CpuUsageEntry obj = (CpuUsageEntry) element;
89 if (columnIndex == 0) {
90 return obj.getTid();
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());
97 }
98
99 return element.toString();
100 }
101
102 }
103
104 /**
105 * Constructor
106 *
107 * @param parent
108 * The parent composite that holds this viewer
109 */
110 public CpuUsageComposite(Composite parent) {
111 super(parent, false);
112 setLabelProvider(new CpuLabelProvider());
113 }
114
115 @Override
116 protected ITmfTreeColumnDataProvider getColumnDataProvider() {
117 return new ITmfTreeColumnDataProvider() {
118
119 @Override
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() {
125 @Override
126 public int compare(Viewer viewer, Object e1, Object e2) {
127 CpuUsageEntry n1 = (CpuUsageEntry) e1;
128 CpuUsageEntry n2 = (CpuUsageEntry) e2;
129
130 return n1.getTid().compareTo(n2.getTid());
131
132 }
133 });
134 columns.add(column);
135 column = new TmfTreeColumnData(COLUMN_NAMES[1]);
136 column.setComparator(new ViewerComparator() {
137 @Override
138 public int compare(Viewer viewer, Object e1, Object e2) {
139 CpuUsageEntry n1 = (CpuUsageEntry) e1;
140 CpuUsageEntry n2 = (CpuUsageEntry) e2;
141
142 return n1.getProcessName().compareTo(n2.getProcessName());
143
144 }
145 });
146 columns.add(column);
147 column = new TmfTreeColumnData(COLUMN_NAMES[2]);
148 column.setComparator(new ViewerComparator() {
149 @Override
150 public int compare(Viewer viewer, Object e1, Object e2) {
151 CpuUsageEntry n1 = (CpuUsageEntry) e1;
152 CpuUsageEntry n2 = (CpuUsageEntry) e2;
153
154 return n1.getPercent().compareTo(n2.getPercent());
155
156 }
157 });
158 column.setPercentageProvider(new ITmfColumnPercentageProvider() {
159
160 @Override
161 public double getPercentage(Object data) {
162 CpuUsageEntry parent = (CpuUsageEntry) data;
163 return parent.getPercent() / 100;
164 }
165 });
166 columns.add(column);
167 column = new TmfTreeColumnData(COLUMN_NAMES[3]);
168 column.setComparator(new ViewerComparator() {
169 @Override
170 public int compare(Viewer viewer, Object e1, Object e2) {
171 CpuUsageEntry n1 = (CpuUsageEntry) e1;
172 CpuUsageEntry n2 = (CpuUsageEntry) e2;
173
174 return n1.getTime().compareTo(n2.getTime());
175
176 }
177 });
178 columns.add(column);
179
180 return columns;
181 }
182
183 };
184 }
185
186 @Override
187 protected ITmfTrace getTrace() {
188 return super.getTrace();
189 }
190
191 // ------------------------------------------------------------------------
192 // Operations
193 // ------------------------------------------------------------------------
194
195 @Override
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);
205 return;
206 }
207 }
208 }
209 }
210 }
211
212 @Override
213 public void initializeDataSource() {
214 /* Should not be called while trace is still null */
215 ITmfTrace trace = checkNotNull(getTrace());
216
217 fModule = TmfTraceUtils.getAnalysisModuleOfClass(trace, KernelCpuUsageAnalysis.class, KernelCpuUsageAnalysis.ID);
218 if (fModule == null) {
219 return;
220 }
221 fModule.schedule();
222 fModule.waitForInitialization();
223 fProcessNameMap.clear();
224 }
225
226 @Override
227 protected ITmfTreeViewerEntry updateElements(long start, long end, boolean isSelection) {
228 if (isSelection || (start == end)) {
229 return null;
230 }
231 if (getTrace() == null || fModule == null) {
232 return null;
233 }
234 fModule.waitForInitialization();
235 ITmfStateSystem ss = fModule.getStateSystem();
236 if (ss == null) {
237 return null;
238 }
239
240 boolean complete = false;
241 long currentEnd = Math.max(start, ss.getStartTime());
242
243 while (!complete && currentEnd < end) {
244 complete = ss.waitUntilBuilt(BUILD_UPDATE_TIMEOUT);
245 currentEnd = ss.getCurrentEndTime();
246 }
247
248 /* Initialize the data */
249 Map<String, Long> cpuUsageMap = fModule.getCpuUsageInRange(fCpus, Math.max(start, getStartTime()), Math.min(end, getEndTime()));
250
251 TmfTreeViewerEntry root = new TmfTreeViewerEntry(""); //$NON-NLS-1$
252 List<ITmfTreeViewerEntry> entryList = root.getChildren();
253
254 for (Entry<String, Long> entry : cpuUsageMap.entrySet()) {
255 /*
256 * Process only entries representing the total of all CPUs and that
257 * have time on CPU
258 */
259 if (entry.getValue() == 0) {
260 continue;
261 }
262 if (!entry.getKey().startsWith(KernelCpuUsageAnalysis.TOTAL)) {
263 continue;
264 }
265 String[] strings = entry.getKey().split(KernelCpuUsageAnalysis.SPLIT_STRING, 2);
266
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());
269 entryList.add(obj);
270 }
271 }
272
273 return root;
274 }
275
276 /*
277 * Get the process name from its TID by using the LTTng kernel analysis
278 * module
279 */
280 private String getProcessName(String tid) {
281 String execName = fProcessNameMap.get(tid);
282 if (execName != null) {
283 return execName;
284 }
285 ITmfTrace trace = getTrace();
286 if (trace == null) {
287 return tid;
288 }
289 ITmfStateSystem kernelSs = TmfStateSystemAnalysisModule.getStateSystem(trace, KernelAnalysisModule.ID);
290 if (kernelSs == null) {
291 return tid;
292 }
293
294 try {
295 int cpusNode = kernelSs.getQuarkAbsolute(Attributes.THREADS);
296
297 /* Get the quarks for each cpu */
298 List<Integer> cpuNodes = kernelSs.getSubAttributes(cpusNode, false);
299
300 for (Integer tidQuark : cpuNodes) {
301 if (kernelSs.getAttributeName(tidQuark).equals(tid)) {
302 int execNameQuark;
303 List<ITmfStateInterval> execNameIntervals;
304 try {
305 execNameQuark = kernelSs.getQuarkRelative(tidQuark, Attributes.EXEC_NAME);
306 execNameIntervals = StateSystemUtils.queryHistoryRange(kernelSs, execNameQuark, getStartTime(), getEndTime());
307 } catch (TimeRangeException | AttributeNotFoundException e) {
308 /*
309 * No information on this thread (yet?), skip it for now
310 */
311 continue;
312 } catch (StateSystemDisposedException e) {
313 /* State system is closing down, no point continuing */
314 break;
315 }
316
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);
322 return execName;
323 }
324 }
325 }
326 }
327
328 } catch (AttributeNotFoundException e) {
329 /* can't find the process name, just return the tid instead */
330 }
331 return tid;
332 }
333
334 /**
335 * Set the currently selected thread ID
336 *
337 * @param tid
338 * The selected thread ID
339 */
340 public void setSelectedThread(String tid) {
341 fSelectedThread = tid;
342 }
343
344 /**
345 * Add a core
346 *
347 * @param core
348 * the core to add
349 * @since 2.0
350 */
351 public void addCpu(int core) {
352 fCpus.add(core);
353 updateContent(getWindowStartTime(), getWindowEndTime(), false);
354 }
355
356 /**
357 * Remove a core
358 *
359 * @param core
360 * the core to remove
361 * @since 2.0
362 */
363 public void removeCpu(int core) {
364 fCpus.remove(core);
365 updateContent(getWindowStartTime(), getWindowEndTime(), false);
366 }
367
368 /**
369 * Clears the cores
370 *
371 * @since 2.0
372 *
373 */
374 public void clearCpu() {
375 fCpus.clear();
376 updateContent(getWindowStartTime(), getWindowEndTime(), false);
377 }
378
379 @Override
380 @TmfSignalHandler
381 public void traceSelected(TmfTraceSelectedSignal signal) {
382 initSelection();
383 initCPU();
384 super.traceSelected(signal);
385 }
386
387 @Override
388 @TmfSignalHandler
389 public void traceOpened(TmfTraceOpenedSignal signal) {
390 initSelection();
391 initCPU();
392 super.traceOpened(signal);
393 }
394
395 private void initSelection() {
396 TmfTraceContext ctx = TmfTraceManager.getInstance().getCurrentTraceContext();
397 String thread = (String) ctx.getData(CpuUsageView.CPU_USAGE_SELECTED_THREAD);
398 setSelectedThread(thread);
399 }
400
401 private void initCPU() {
402 clearCpu();
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;
410 if (core >= 0) {
411 addCpu(core);
412 }
413 }
414 }
415 }
416 }
417 }
This page took 0.060234 seconds and 4 git commands to generate.