Remove unneeded checkNotNull() calls
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.os.linux.core / src / org / eclipse / tracecompass / analysis / os / linux / core / cpuusage / KernelCpuUsageAnalysis.java
CommitLineData
e693075d 1/*******************************************************************************
04927a83 2 * Copyright (c) 2014, 2015 École Polytechnique de Montréal and others.
e693075d
FR
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
e363eae1 13package org.eclipse.tracecompass.analysis.os.linux.core.cpuusage;
e693075d 14
d0c7e4ba
AM
15import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
16
e693075d 17import java.util.HashMap;
0b4160dc 18import java.util.HashSet;
e693075d
FR
19import java.util.List;
20import java.util.Map;
21import java.util.Map.Entry;
0b4160dc 22import java.util.Set;
e693075d 23
17a3454d 24import org.eclipse.jdt.annotation.NonNull;
e363eae1 25import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.Attributes;
6d16f5a9 26import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.KernelAnalysisModule;
e363eae1
AM
27import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout;
28import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelTrace;
29import org.eclipse.tracecompass.internal.analysis.os.linux.core.Activator;
e894a508
AM
30import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
31import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
32import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
33import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
34import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
35import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
0b4160dc 36import org.eclipse.tracecompass.tmf.core.analysis.IAnalysisModule;
2bdf0193
AM
37import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
38import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
39import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
b8585c7c 40import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
e693075d
FR
41
42/**
43 * This analysis module computes the CPU usage of a system from a kernel trace.
44 * It requires the LTTng Kernel analysis module to have accurate CPU usage data.
45 *
46 * @author Geneviève Bastien
e693075d 47 */
e363eae1 48public class KernelCpuUsageAnalysis extends TmfStateSystemAnalysisModule {
e693075d
FR
49
50 /** The ID of this analysis */
e363eae1 51 public static final String ID = "org.eclipse.tracecompass.analysis.os.linux.cpuusage"; //$NON-NLS-1$
e693075d
FR
52
53 /** Text used to identify 'total' entries in the returned maps */
54 public static final String TOTAL = "total"; //$NON-NLS-1$
55 /** String used to separate elements in the returned maps */
56 public static final String SPLIT_STRING = "/"; //$NON-NLS-1$
57 /** Idle process thread ID */
58 public static final String TID_ZERO = "0"; //$NON-NLS-1$
59
60 @Override
61 protected ITmfStateProvider createStateProvider() {
d0c7e4ba 62 ITmfTrace trace = checkNotNull(getTrace());
7411cd67
AM
63 IKernelAnalysisEventLayout layout;
64
e363eae1
AM
65 if (trace instanceof IKernelTrace) {
66 layout = ((IKernelTrace) trace).getKernelEventLayout();
7411cd67
AM
67 } else {
68 /* Fall-back to the base LttngEventLayout */
e363eae1 69 layout = IKernelAnalysisEventLayout.DEFAULT_LAYOUT;
7411cd67
AM
70 }
71
e363eae1 72 return new KernelCpuUsageStateProvider(trace, layout);
e693075d
FR
73 }
74
75 @Override
76 protected StateSystemBackendType getBackendType() {
77 return StateSystemBackendType.FULL;
78 }
79
80 @Override
0b4160dc
GB
81 protected Iterable<IAnalysisModule> getDependentAnalyses() {
82 Set<IAnalysisModule> modules = new HashSet<>();
83
ba27dd38
GB
84 ITmfTrace trace = getTrace();
85 if (trace == null) {
86 throw new IllegalStateException();
87 }
e693075d 88 /*
0b4160dc
GB
89 * This analysis depends on the LTTng kernel analysis, so it's added to
90 * dependent modules.
e693075d 91 */
6d16f5a9
AM
92 Iterable<KernelAnalysisModule> kernelModules = TmfTraceUtils.getAnalysisModulesOfClass(trace, KernelAnalysisModule.class);
93 for (KernelAnalysisModule kernelModule : kernelModules) {
b8585c7c
AM
94 /* Only add the first one we find, if there is one */
95 modules.add(kernelModule);
96 break;
e693075d 97 }
0b4160dc 98 return modules;
e693075d
FR
99 }
100
17a3454d
MK
101 /**
102 * Gets the maximum number of cores detected
103 *
104 * @return the number of cores
105 * @since 2.0
106 */
107 public int getNumberOfCores() {
108
109 ITmfStateSystem cpuSs = getStateSystem();
110 if (cpuSs != null) {
111 try {
112 int cpusNode = cpuSs.getQuarkAbsolute(Attributes.CPUS);
113 final @NonNull List<@NonNull Integer> subAttributes = cpuSs.getSubAttributes(cpusNode, false);
114 int cpus = Integer.MIN_VALUE;
115 for (Integer quark : subAttributes) {
116 cpus = Math.max(Integer.parseInt(cpuSs.getAttributeName(quark)), cpus);
117 }
118 return Math.max(subAttributes.size(), cpus);
119 } catch (AttributeNotFoundException e) {
120 Activator.getDefault().logError(e.getMessage(), e);
121 }
122 }
123 return -1;
124
125 }
126
e693075d
FR
127 /**
128 * Get a map of time spent on CPU by various threads during a time range.
129 *
17a3454d
MK
130 * @param cpus
131 * A set of the desired CPUs to get. An empty set gets all the
132 * cores
e693075d
FR
133 * @param start
134 * Start time of requested range
135 * @param end
136 * End time of requested range
137 * @return A map of TID -> time spent on CPU in the [start, end] interval
17a3454d 138 * @since 2.0
e693075d 139 */
17a3454d 140 public Map<String, Long> getCpuUsageInRange(Set<@NonNull Integer> cpus, long start, long end) {
e693075d
FR
141 Map<String, Long> map = new HashMap<>();
142 Map<String, Long> totalMap = new HashMap<>();
143
72221aa4 144 ITmfTrace trace = getTrace();
e693075d 145 ITmfStateSystem cpuSs = getStateSystem();
72221aa4 146 if (trace == null || cpuSs == null) {
e693075d
FR
147 return map;
148 }
6d16f5a9 149 ITmfStateSystem kernelSs = TmfStateSystemAnalysisModule.getStateSystem(trace, KernelAnalysisModule.ID);
e693075d
FR
150 if (kernelSs == null) {
151 return map;
152 }
153
154 /*
155 * Make sure the start/end times are within the state history, so we
156 * don't get TimeRange exceptions.
157 */
158 long startTime = Math.max(start, cpuSs.getStartTime());
dffc234f 159 startTime = Math.max(startTime, kernelSs.getStartTime());
e693075d 160 long endTime = Math.min(end, cpuSs.getCurrentEndTime());
dffc234f 161 endTime = Math.min(endTime, kernelSs.getCurrentEndTime());
e693075d
FR
162 long totalTime = 0;
163 if (endTime < startTime) {
164 return map;
165 }
166
167 try {
168 /* Get the list of quarks for each CPU and CPU's TIDs */
169 int cpusNode = cpuSs.getQuarkAbsolute(Attributes.CPUS);
170 Map<Integer, List<Integer>> tidsPerCpu = new HashMap<>();
171 for (int cpuNode : cpuSs.getSubAttributes(cpusNode, false)) {
17a3454d
MK
172 final @NonNull List<@NonNull Integer> cpuSubAttributes = cpuSs.getSubAttributes(cpuNode, false);
173 if (cpus.isEmpty() || cpus.contains(Integer.parseInt(cpuSs.getAttributeName(cpuNode)))) {
174 tidsPerCpu.put(cpuNode, cpuSubAttributes);
175 }
e693075d
FR
176 }
177
178 /* Query full states at start and end times */
179 List<ITmfStateInterval> kernelEndState = kernelSs.queryFullState(endTime);
180 List<ITmfStateInterval> endState = cpuSs.queryFullState(endTime);
181 List<ITmfStateInterval> kernelStartState = kernelSs.queryFullState(startTime);
182 List<ITmfStateInterval> startState = cpuSs.queryFullState(startTime);
183
184 long countAtStart, countAtEnd;
185
186 for (Entry<Integer, List<Integer>> entry : tidsPerCpu.entrySet()) {
187 int cpuNode = entry.getKey();
188 List<Integer> tidNodes = entry.getValue();
189
190 String curCpuName = cpuSs.getAttributeName(cpuNode);
191 long cpuTotal = 0;
192
193 /* Get the quark of the thread running on this CPU */
194 int currentThreadQuark = kernelSs.getQuarkAbsolute(Attributes.CPUS, curCpuName, Attributes.CURRENT_THREAD);
195 /* Get the currently running thread on this CPU */
196 int startThread = kernelStartState.get(currentThreadQuark).getStateValue().unboxInt();
197 int endThread = kernelEndState.get(currentThreadQuark).getStateValue().unboxInt();
198
199 for (int tidNode : tidNodes) {
200 String curTidName = cpuSs.getAttributeName(tidNode);
201 int tid = Integer.parseInt(curTidName);
202
203 countAtEnd = endState.get(tidNode).getStateValue().unboxLong();
204 countAtStart = startState.get(tidNode).getStateValue().unboxLong();
205 if (countAtStart == -1) {
206 countAtStart = 0;
207 }
208 if (countAtEnd == -1) {
209 countAtEnd = 0;
210 }
211
212 /*
213 * Interpolate start and end time of threads running at
214 * those times
215 */
216 if (tid == startThread || startThread == -1) {
217 long runningTime = kernelStartState.get(currentThreadQuark).getEndTime() - kernelStartState.get(currentThreadQuark).getStartTime();
218 long runningEnd = kernelStartState.get(currentThreadQuark).getEndTime();
219
220 countAtStart = interpolateCount(countAtStart, startTime, runningEnd, runningTime);
221 }
222 if (tid == endThread) {
223 long runningTime = kernelEndState.get(currentThreadQuark).getEndTime() - kernelEndState.get(currentThreadQuark).getStartTime();
224 long runningEnd = kernelEndState.get(currentThreadQuark).getEndTime();
225
226 countAtEnd = interpolateCount(countAtEnd, endTime, runningEnd, runningTime);
227 }
228 /*
229 * If startThread is -1, we made the hypothesis that the
230 * process running at start was the current one. If the
231 * count is negative, we were wrong in this hypothesis. Also
232 * if the time at end is 0, it either means the process
233 * hasn't been on the CPU or that we still don't know who is
234 * running. In both cases, that invalidates the hypothesis.
235 */
236 if ((startThread == -1) && ((countAtEnd - countAtStart < 0) || (countAtEnd == 0))) {
237 countAtStart = 0;
238 }
239
240 long currentCount = countAtEnd - countAtStart;
241 if (currentCount < 0) {
0e4f957e 242 Activator.getDefault().logWarning(String.format("Negative count: start %d, end %d", countAtStart, countAtEnd)); //$NON-NLS-1$
e693075d
FR
243 currentCount = 0;
244 } else if (currentCount > endTime - startTime) {
0e4f957e 245 Activator.getDefault().logWarning(String.format("CPU Usage: Spent more time on CPU than allowed: %s spent %d when max should be %d", curTidName, currentCount, endTime - startTime)); //$NON-NLS-1$
e693075d
FR
246 currentCount = 0;
247 }
248 cpuTotal += currentCount;
249 map.put(curCpuName + SPLIT_STRING + curTidName, currentCount);
250 addToMap(totalMap, curTidName, currentCount);
251 totalTime += (currentCount);
252 }
253 map.put(curCpuName, cpuTotal);
254 }
255
256 /* Add the totals to the map */
257 for (Entry<String, Long> entry : totalMap.entrySet()) {
258 map.put(TOTAL + SPLIT_STRING + entry.getKey(), entry.getValue());
259 }
260 map.put(TOTAL, totalTime);
261
262 } catch (TimeRangeException | AttributeNotFoundException e) {
263 /*
264 * Assume there is no events or the attribute does not exist yet,
265 * nothing will be put in the map.
266 */
267 } catch (StateValueTypeException | StateSystemDisposedException e) {
268 /*
269 * These other exception types would show a logic problem, so they
270 * should not happen.
271 */
272 Activator.getDefault().logError("Error getting CPU usage in a time range", e); //$NON-NLS-1$
273 }
274
275 return map;
276 }
277
278 private static long interpolateCount(long count, long ts, long runningEnd, long runningTime) {
279 long newCount = count;
280
281 /* sanity check */
282 if (runningTime > 0) {
283
284 long runningStart = runningEnd - runningTime;
285
286 if (ts < runningStart) {
287 /*
288 * This interval was not started, this can happen if the current
289 * running thread is unknown and we execute this method. It just
290 * means that this process was not the one running
291 */
292 return newCount;
293 }
294 newCount += (ts - runningStart);
295 }
296 return newCount;
297 }
298
299 /*
300 * Add the value to the previous value in the map. If the key was not set,
301 * assume 0
302 */
303 private static void addToMap(Map<String, Long> map, String key, Long value) {
304 Long addTo = map.get(key);
305 if (addTo == null) {
306 map.put(key, value);
307 } else {
308 map.put(key, addTo + value);
309 }
310 }
311
312}
This page took 0.068539 seconds and 5 git commands to generate.