1 /*******************************************************************************
2 * Copyright (c) 2014 École Polytechnique de Montréal
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
10 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.lttng2
.kernel
.core
.analysis
.cpuusage
;
15 import java
.util
.HashMap
;
16 import java
.util
.List
;
18 import java
.util
.Map
.Entry
;
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
.internal
.lttng2
.kernel
.core
.trace
.layout
.IKernelAnalysisEventLayout
;
24 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.trace
.layout
.LttngEventLayout
;
25 import org
.eclipse
.tracecompass
.lttng2
.kernel
.core
.analysis
.kernel
.LttngKernelAnalysis
;
26 import org
.eclipse
.tracecompass
.lttng2
.kernel
.core
.trace
.LttngKernelTrace
;
27 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystem
;
28 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
29 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateSystemDisposedException
;
30 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
31 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
32 import org
.eclipse
.tracecompass
.statesystem
.core
.interval
.ITmfStateInterval
;
33 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.ITmfStateProvider
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.TmfStateSystemAnalysisModule
;
35 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
38 * This analysis module computes the CPU usage of a system from a kernel trace.
39 * It requires the LTTng Kernel analysis module to have accurate CPU usage data.
41 * @author Geneviève Bastien
44 public class LttngKernelCpuUsageAnalysis
extends TmfStateSystemAnalysisModule
{
46 /** The ID of this analysis */
47 public static final String ID
= "org.eclipse.linuxtools.lttng2.kernel.core.cpuusage"; //$NON-NLS-1$
49 /** Text used to identify 'total' entries in the returned maps */
50 public static final String TOTAL
= "total"; //$NON-NLS-1$
51 /** String used to separate elements in the returned maps */
52 public static final String SPLIT_STRING
= "/"; //$NON-NLS-1$
53 /** Idle process thread ID */
54 public static final String TID_ZERO
= "0"; //$NON-NLS-1$
57 protected ITmfStateProvider
createStateProvider() {
58 ITmfTrace trace
= getTrace();
59 IKernelAnalysisEventLayout layout
;
61 if (trace
instanceof LttngKernelTrace
) {
62 layout
= ((LttngKernelTrace
) trace
).getEventLayout();
64 /* Fall-back to the base LttngEventLayout */
65 layout
= LttngEventLayout
.getInstance();
68 return new LttngKernelCpuUsageStateProvider(trace
, layout
);
72 protected StateSystemBackendType
getBackendType() {
73 return StateSystemBackendType
.FULL
;
77 protected boolean executeAnalysis(IProgressMonitor monitor
) {
79 * This analysis depends on the LTTng kernel analysis, so we'll start
80 * that build at the same time
82 LttngKernelAnalysis module
= getTrace().getAnalysisModuleOfClass(LttngKernelAnalysis
.class, LttngKernelAnalysis
.ID
);
86 return super.executeAnalysis(monitor
);
90 * Get a map of time spent on CPU by various threads during a time range.
93 * Start time of requested range
95 * End time of requested range
96 * @return A map of TID -> time spent on CPU in the [start, end] interval
98 public Map
<String
, Long
> getCpuUsageInRange(long start
, long end
) {
99 Map
<String
, Long
> map
= new HashMap
<>();
100 Map
<String
, Long
> totalMap
= new HashMap
<>();
102 ITmfTrace trace
= getTrace();
103 ITmfStateSystem cpuSs
= getStateSystem();
104 if (trace
== null || cpuSs
== null) {
107 ITmfStateSystem kernelSs
= TmfStateSystemAnalysisModule
.getStateSystem(trace
, LttngKernelAnalysis
.ID
);
108 if (kernelSs
== null) {
113 * Make sure the start/end times are within the state history, so we
114 * don't get TimeRange exceptions.
116 long startTime
= Math
.max(start
, cpuSs
.getStartTime());
117 startTime
= Math
.max(startTime
, kernelSs
.getStartTime());
118 long endTime
= Math
.min(end
, cpuSs
.getCurrentEndTime());
119 endTime
= Math
.min(endTime
, kernelSs
.getCurrentEndTime());
121 if (endTime
< startTime
) {
126 /* Get the list of quarks for each CPU and CPU's TIDs */
127 int cpusNode
= cpuSs
.getQuarkAbsolute(Attributes
.CPUS
);
128 Map
<Integer
, List
<Integer
>> tidsPerCpu
= new HashMap
<>();
129 for (int cpuNode
: cpuSs
.getSubAttributes(cpusNode
, false)) {
130 tidsPerCpu
.put(cpuNode
, cpuSs
.getSubAttributes(cpuNode
, false));
133 /* Query full states at start and end times */
134 List
<ITmfStateInterval
> kernelEndState
= kernelSs
.queryFullState(endTime
);
135 List
<ITmfStateInterval
> endState
= cpuSs
.queryFullState(endTime
);
136 List
<ITmfStateInterval
> kernelStartState
= kernelSs
.queryFullState(startTime
);
137 List
<ITmfStateInterval
> startState
= cpuSs
.queryFullState(startTime
);
139 long countAtStart
, countAtEnd
;
141 for (Entry
<Integer
, List
<Integer
>> entry
: tidsPerCpu
.entrySet()) {
142 int cpuNode
= entry
.getKey();
143 List
<Integer
> tidNodes
= entry
.getValue();
145 String curCpuName
= cpuSs
.getAttributeName(cpuNode
);
148 /* Get the quark of the thread running on this CPU */
149 int currentThreadQuark
= kernelSs
.getQuarkAbsolute(Attributes
.CPUS
, curCpuName
, Attributes
.CURRENT_THREAD
);
150 /* Get the currently running thread on this CPU */
151 int startThread
= kernelStartState
.get(currentThreadQuark
).getStateValue().unboxInt();
152 int endThread
= kernelEndState
.get(currentThreadQuark
).getStateValue().unboxInt();
154 for (int tidNode
: tidNodes
) {
155 String curTidName
= cpuSs
.getAttributeName(tidNode
);
156 int tid
= Integer
.parseInt(curTidName
);
158 countAtEnd
= endState
.get(tidNode
).getStateValue().unboxLong();
159 countAtStart
= startState
.get(tidNode
).getStateValue().unboxLong();
160 if (countAtStart
== -1) {
163 if (countAtEnd
== -1) {
168 * Interpolate start and end time of threads running at
171 if (tid
== startThread
|| startThread
== -1) {
172 long runningTime
= kernelStartState
.get(currentThreadQuark
).getEndTime() - kernelStartState
.get(currentThreadQuark
).getStartTime();
173 long runningEnd
= kernelStartState
.get(currentThreadQuark
).getEndTime();
175 countAtStart
= interpolateCount(countAtStart
, startTime
, runningEnd
, runningTime
);
177 if (tid
== endThread
) {
178 long runningTime
= kernelEndState
.get(currentThreadQuark
).getEndTime() - kernelEndState
.get(currentThreadQuark
).getStartTime();
179 long runningEnd
= kernelEndState
.get(currentThreadQuark
).getEndTime();
181 countAtEnd
= interpolateCount(countAtEnd
, endTime
, runningEnd
, runningTime
);
184 * If startThread is -1, we made the hypothesis that the
185 * process running at start was the current one. If the
186 * count is negative, we were wrong in this hypothesis. Also
187 * if the time at end is 0, it either means the process
188 * hasn't been on the CPU or that we still don't know who is
189 * running. In both cases, that invalidates the hypothesis.
191 if ((startThread
== -1) && ((countAtEnd
- countAtStart
< 0) || (countAtEnd
== 0))) {
195 long currentCount
= countAtEnd
- countAtStart
;
196 if (currentCount
< 0) {
197 Activator
.getDefault().logWarning(String
.format("Negative count: start %d, end %d", countAtStart
, countAtEnd
)); //$NON-NLS-1$
199 } else if (currentCount
> endTime
- startTime
) {
200 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$
203 cpuTotal
+= currentCount
;
204 map
.put(curCpuName
+ SPLIT_STRING
+ curTidName
, currentCount
);
205 addToMap(totalMap
, curTidName
, currentCount
);
206 totalTime
+= (currentCount
);
208 map
.put(curCpuName
, cpuTotal
);
211 /* Add the totals to the map */
212 for (Entry
<String
, Long
> entry
: totalMap
.entrySet()) {
213 map
.put(TOTAL
+ SPLIT_STRING
+ entry
.getKey(), entry
.getValue());
215 map
.put(TOTAL
, totalTime
);
217 } catch (TimeRangeException
| AttributeNotFoundException e
) {
219 * Assume there is no events or the attribute does not exist yet,
220 * nothing will be put in the map.
222 } catch (StateValueTypeException
| StateSystemDisposedException e
) {
224 * These other exception types would show a logic problem, so they
227 Activator
.getDefault().logError("Error getting CPU usage in a time range", e
); //$NON-NLS-1$
233 private static long interpolateCount(long count
, long ts
, long runningEnd
, long runningTime
) {
234 long newCount
= count
;
237 if (runningTime
> 0) {
239 long runningStart
= runningEnd
- runningTime
;
241 if (ts
< runningStart
) {
243 * This interval was not started, this can happen if the current
244 * running thread is unknown and we execute this method. It just
245 * means that this process was not the one running
249 newCount
+= (ts
- runningStart
);
255 * Add the value to the previous value in the map. If the key was not set,
258 private static void addToMap(Map
<String
, Long
> map
, String key
, Long value
) {
259 Long addTo
= map
.get(key
);
263 map
.put(key
, addTo
+ value
);