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