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