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