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