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