Commit | Line | Data |
---|---|---|
4a74f111 | 1 | /******************************************************************************* |
ed902a2b | 2 | * Copyright (c) 2014, 2015 École Polytechnique de Montréal |
4a74f111 MG |
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 | * Mohamad Gebai - Initial API and implementation | |
11 | *******************************************************************************/ | |
12 | ||
13 | package org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.module; | |
14 | ||
d0c7e4ba AM |
15 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
16 | ||
4a74f111 MG |
17 | import java.util.HashMap; |
18 | import java.util.Map; | |
19 | ||
20 | import org.eclipse.jdt.annotation.Nullable; | |
6d16f5a9 | 21 | import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.KernelAnalysisModule; |
e363eae1 | 22 | import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.KernelThreadInformationProvider; |
a6145763 | 23 | import org.eclipse.tracecompass.analysis.os.linux.core.model.HostThread; |
e363eae1 | 24 | import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout; |
4a74f111 MG |
25 | import org.eclipse.tracecompass.common.core.NonNullUtils; |
26 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.Activator; | |
27 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.VcpuStateValues; | |
28 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.VmAttributes; | |
4a74f111 MG |
29 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.model.IVirtualMachineModel; |
30 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.model.VirtualCPU; | |
31 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.model.VirtualMachine; | |
32 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.model.qemukvm.QemuKvmVmModel; | |
4a74f111 | 33 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.trace.layout.LttngEventLayout; |
4a74f111 | 34 | import org.eclipse.tracecompass.lttng2.kernel.core.trace.LttngKernelTrace; |
d0c7e4ba | 35 | import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; |
4a74f111 MG |
36 | import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; |
37 | import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException; | |
38 | import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException; | |
39 | import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; | |
40 | import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue; | |
41 | import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; | |
42 | import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; | |
43 | import org.eclipse.tracecompass.tmf.core.event.aspect.TmfCpuAspect; | |
44 | import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider; | |
45 | import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; | |
46 | import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils; | |
47 | import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment; | |
48 | import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperimentUtils; | |
49 | ||
50 | import com.google.common.collect.HashBasedTable; | |
51 | import com.google.common.collect.Table; | |
52 | ||
53 | /** | |
54 | * This is the state provider which translates the virtual machine experiment | |
55 | * events to a state system. | |
56 | * | |
57 | * Attribute tree: | |
58 | * | |
59 | * <pre> | |
60 | * |- Virtual Machines | |
61 | * | |- <Guest Host ID> -> Friendly name (trace name) | |
62 | * | | |- <VCPU number> | |
63 | * | | | |- Status -> <Status value> | |
64 | * </pre> | |
65 | * | |
66 | * The status value of the VCPUs are either {@link VcpuStateValues#VCPU_IDLE}, | |
67 | * {@link VcpuStateValues#VCPU_UNKNOWN} or {@link VcpuStateValues#VCPU_RUNNING}. | |
68 | * Those three values are ORed with flags {@link VcpuStateValues#VCPU_VMM} | |
69 | * and/or {@link VcpuStateValues#VCPU_PREEMPT} to indicate respectively whether | |
70 | * they are in hypervisor mode or preempted on the host. | |
71 | * | |
72 | * @author Mohamad Gebai | |
73 | */ | |
74 | public class VirtualMachineStateProvider extends AbstractTmfStateProvider { | |
75 | ||
76 | /** | |
77 | * Version number of this state provider. Please bump this if you modify the | |
78 | * contents of the generated state history in some way. | |
79 | */ | |
80 | private static final int VERSION = 1; | |
81 | ||
82 | private static final int SCHED_SWITCH_INDEX = 0; | |
83 | ||
84 | /* TODO: An analysis should support many hypervisor models */ | |
85 | private IVirtualMachineModel fModel; | |
4c4e2816 | 86 | private final Table<ITmfTrace, String, @Nullable Integer> fEventNames; |
4a74f111 MG |
87 | private final Map<ITmfTrace, IKernelAnalysisEventLayout> fLayouts; |
88 | ||
89 | // ------------------------------------------------------------------------ | |
90 | // Constructor | |
91 | // ------------------------------------------------------------------------ | |
92 | ||
93 | /** | |
94 | * Constructor | |
95 | * | |
96 | * @param experiment | |
97 | * The virtual machine experiment | |
98 | */ | |
99 | public VirtualMachineStateProvider(TmfExperiment experiment) { | |
e2bcc8a5 | 100 | super(experiment, "Virtual Machine State Provider"); //$NON-NLS-1$ |
4a74f111 MG |
101 | |
102 | fModel = new QemuKvmVmModel(experiment); | |
4c4e2816 | 103 | Table<ITmfTrace, String, @Nullable Integer> table = NonNullUtils.checkNotNull(HashBasedTable.<ITmfTrace, String, Integer> create()); |
4a74f111 MG |
104 | fEventNames = table; |
105 | fLayouts = new HashMap<>(); | |
106 | } | |
107 | ||
108 | // ------------------------------------------------------------------------ | |
109 | // Event names management | |
110 | // ------------------------------------------------------------------------ | |
111 | ||
112 | private void buildEventNames(ITmfTrace trace) { | |
113 | IKernelAnalysisEventLayout layout; | |
114 | if (trace instanceof LttngKernelTrace) { | |
e363eae1 | 115 | layout = ((LttngKernelTrace) trace).getKernelEventLayout(); |
4a74f111 MG |
116 | } else { |
117 | /* Fall-back to the base LttngEventLayout */ | |
118 | layout = LttngEventLayout.getInstance(); | |
119 | } | |
120 | fLayouts.put(trace, layout); | |
121 | fEventNames.put(trace, layout.eventSchedSwitch(), SCHED_SWITCH_INDEX); | |
122 | } | |
123 | ||
124 | // ------------------------------------------------------------------------ | |
125 | // IStateChangeInput | |
126 | // ------------------------------------------------------------------------ | |
127 | ||
128 | @Override | |
129 | public TmfExperiment getTrace() { | |
130 | ITmfTrace trace = super.getTrace(); | |
131 | if (trace instanceof TmfExperiment) { | |
132 | return (TmfExperiment) trace; | |
133 | } | |
134 | throw new IllegalStateException("VirtualMachineStateProvider: The associated trace should be an experiment"); //$NON-NLS-1$ | |
135 | } | |
136 | ||
137 | @Override | |
138 | public int getVersion() { | |
139 | return VERSION; | |
140 | } | |
141 | ||
142 | @Override | |
143 | public VirtualMachineStateProvider getNewInstance() { | |
144 | TmfExperiment trace = getTrace(); | |
145 | return new VirtualMachineStateProvider(trace); | |
146 | } | |
147 | ||
148 | @Override | |
149 | protected void eventHandle(@Nullable ITmfEvent event) { | |
150 | if (event == null) { | |
151 | return; | |
152 | } | |
153 | ||
154 | /* Is the event managed by this analysis */ | |
578716e6 | 155 | final String eventName = event.getName(); |
4a74f111 MG |
156 | |
157 | /* TODO When requirements work again, don't hardcode this */ | |
158 | if (!eventName.equals("sched_switch") && //$NON-NLS-1$ | |
159 | !fModel.getRequiredEvents().contains(eventName)) { | |
160 | return; | |
161 | } | |
162 | ||
d0c7e4ba | 163 | ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder()); |
4a74f111 MG |
164 | ITmfStateValue value; |
165 | ||
166 | final ITmfEventField content = event.getContent(); | |
167 | final long ts = event.getTimestamp().getValue(); | |
168 | final String hostId = event.getTrace().getHostId(); | |
169 | try { | |
170 | /* Do we know this trace's role yet? */ | |
171 | VirtualMachine host = fModel.getCurrentMachine(event); | |
172 | if (host == null) { | |
173 | return; | |
174 | } | |
175 | ||
176 | /* Make sure guest traces are added to the state system */ | |
177 | if (host.isGuest()) { | |
178 | /* | |
179 | * If event from a guest OS, make sure the guest exists in the | |
180 | * state system | |
181 | */ | |
182 | int vmQuark = -1; | |
183 | try { | |
184 | vmQuark = ss.getQuarkRelative(getNodeVirtualMachines(), host.getHostId()); | |
185 | } catch (AttributeNotFoundException e) { | |
186 | /* | |
187 | * We should enter this catch only once per machine, so it | |
188 | * is not so costly to do compared with adding the trace's | |
189 | * name for each guest event | |
190 | */ | |
191 | vmQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host.getHostId()); | |
192 | TmfStateValue machineName = TmfStateValue.newValueString(event.getTrace().getName()); | |
193 | ss.modifyAttribute(ts, machineName, vmQuark); | |
194 | } | |
195 | } | |
196 | ||
197 | /* Have the hypervisor models handle the event first */ | |
198 | fModel.handleEvent(event); | |
199 | ||
200 | /* Handle the event here */ | |
201 | if (!fEventNames.containsRow(event.getTrace())) { | |
202 | buildEventNames(event.getTrace()); | |
203 | } | |
204 | Integer idx = fEventNames.get(event.getTrace(), eventName); | |
205 | int intval = (idx == null ? -1 : idx.intValue()); | |
206 | switch (intval) { | |
207 | case SCHED_SWITCH_INDEX: // "sched_switch": | |
208 | /* | |
209 | * Fields: string prev_comm, int32 prev_tid, int32 prev_prio, int64 | |
210 | * prev_state, string next_comm, int32 next_tid, int32 next_prio | |
211 | */ | |
212 | { | |
94411c58 AM |
213 | final IKernelAnalysisEventLayout eventLayout = fLayouts.get(event.getTrace()); |
214 | if (eventLayout == null) { | |
215 | return; | |
216 | } | |
217 | int prevTid = ((Long) content.getField(eventLayout.fieldPrevTid()).getValue()).intValue(); | |
218 | int nextTid = ((Long) content.getField(eventLayout.fieldNextTid()).getValue()).intValue(); | |
4a74f111 MG |
219 | |
220 | if (host.isGuest()) { | |
221 | /* Get the event's CPU */ | |
b1aad44e GB |
222 | Object cpuObj = TmfTraceUtils.resolveEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event); |
223 | if (cpuObj == null) { | |
52efabb6 CB |
224 | /* |
225 | * We couldn't find any CPU information, ignore this | |
226 | * event | |
227 | */ | |
4a74f111 MG |
228 | break; |
229 | } | |
b1aad44e | 230 | Integer cpu = (Integer) cpuObj; |
4a74f111 MG |
231 | |
232 | /* | |
233 | * If sched switch is from a guest, just update the status | |
234 | * of the virtual CPU to either idle or running | |
235 | */ | |
236 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host.getHostId(), | |
237 | cpu.toString(), VmAttributes.STATUS); | |
238 | value = TmfStateValue.newValueInt(VcpuStateValues.VCPU_IDLE); | |
239 | if (nextTid > 0) { | |
240 | value = TmfStateValue.newValueInt(VcpuStateValues.VCPU_RUNNING); | |
241 | } | |
242 | ss.modifyAttribute(ts, value, curStatusQuark); | |
243 | break; | |
244 | } | |
245 | ||
246 | /* Event is not from a guest */ | |
247 | /* Verify if the previous thread corresponds to a virtual CPU */ | |
248 | HostThread ht = new HostThread(hostId, prevTid); | |
249 | VirtualCPU vcpu = fModel.getVirtualCpu(ht); | |
250 | ||
251 | /* | |
252 | * If previous thread is virtual CPU, update status of the | |
253 | * virtual CPU to preempted | |
254 | */ | |
255 | if (vcpu != null) { | |
256 | VirtualMachine vm = vcpu.getVm(); | |
257 | ||
258 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm.getHostId(), | |
259 | vcpu.getCpuId().toString(), VmAttributes.STATUS); | |
260 | ||
261 | /* Add the preempted flag to the status */ | |
262 | value = ss.queryOngoingState(curStatusQuark); | |
52efabb6 CB |
263 | if ((value.unboxInt() & VcpuStateValues.VCPU_IDLE) == 0) { |
264 | int newVal = Math.max(VcpuStateValues.VCPU_UNKNOWN, value.unboxInt()); | |
265 | value = TmfStateValue.newValueInt(newVal | VcpuStateValues.VCPU_PREEMPT); | |
266 | ss.modifyAttribute(ts, value, curStatusQuark); | |
267 | } | |
4a74f111 MG |
268 | } |
269 | ||
270 | /* Verify if the next thread corresponds to a virtual CPU */ | |
271 | ht = new HostThread(hostId, nextTid); | |
272 | vcpu = fModel.getVirtualCpu(ht); | |
273 | ||
274 | /* | |
275 | * If next thread is virtual CPU, update status of the virtual | |
276 | * CPU the previous status | |
277 | */ | |
278 | if (vcpu != null) { | |
279 | VirtualMachine vm = vcpu.getVm(); | |
280 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm.getHostId(), | |
281 | vcpu.getCpuId().toString(), VmAttributes.STATUS); | |
282 | ||
283 | /* Remove the preempted flag from the status */ | |
284 | value = ss.queryOngoingState(curStatusQuark); | |
285 | int newVal = Math.max(VcpuStateValues.VCPU_UNKNOWN, value.unboxInt()); | |
286 | value = TmfStateValue.newValueInt(newVal & ~VcpuStateValues.VCPU_PREEMPT); | |
287 | ss.modifyAttribute(ts, value, curStatusQuark); | |
288 | ||
289 | } | |
290 | ||
291 | } | |
292 | break; | |
293 | ||
294 | default: | |
295 | /* Other events not covered by the main switch */ | |
296 | { | |
297 | HostThread ht = getCurrentHostThread(event, ts); | |
298 | if (ht == null) { | |
299 | break; | |
300 | } | |
301 | ||
302 | /* | |
303 | * Are we entering the hypervisor mode and if so, which virtual | |
304 | * CPU is concerned? | |
305 | */ | |
306 | VirtualCPU virtualCpu = fModel.getVCpuEnteringHypervisorMode(event, ht); | |
307 | if (virtualCpu != null) { | |
308 | /* Add the hypervisor flag to the status */ | |
309 | VirtualMachine vm = virtualCpu.getVm(); | |
310 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm.getHostId(), | |
311 | Long.toString(virtualCpu.getCpuId()), VmAttributes.STATUS); | |
312 | value = ss.queryOngoingState(curStatusQuark); | |
52efabb6 CB |
313 | if ((value.unboxInt() & VcpuStateValues.VCPU_IDLE) == 0) { |
314 | int newVal = Math.max(VcpuStateValues.VCPU_UNKNOWN, value.unboxInt()); | |
315 | value = TmfStateValue.newValueInt(newVal | VcpuStateValues.VCPU_VMM); | |
316 | ss.modifyAttribute(ts, value, curStatusQuark); | |
317 | } | |
4a74f111 MG |
318 | } |
319 | ||
320 | /* | |
321 | * Are we exiting the hypervisor mode and if so, which virtual | |
322 | * CPU is concerned? | |
323 | */ | |
324 | virtualCpu = fModel.getVCpuExitingHypervisorMode(event, ht); | |
325 | if (virtualCpu != null) { | |
326 | /* Remove the hypervisor flag from the status */ | |
327 | VirtualMachine vm = virtualCpu.getVm(); | |
328 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm.getHostId(), | |
329 | Long.toString(virtualCpu.getCpuId()), VmAttributes.STATUS); | |
330 | value = ss.queryOngoingState(curStatusQuark); | |
331 | int newVal = Math.max(VcpuStateValues.VCPU_UNKNOWN, value.unboxInt()); | |
332 | value = TmfStateValue.newValueInt(newVal & ~VcpuStateValues.VCPU_VMM); | |
333 | ss.modifyAttribute(ts, value, curStatusQuark); | |
334 | } | |
335 | ||
336 | } | |
337 | break; | |
338 | } | |
339 | ||
340 | } catch (AttributeNotFoundException | TimeRangeException | StateValueTypeException e) { | |
341 | Activator.getDefault().logError("Error handling event in VirtualMachineStateProvider", e); //$NON-NLS-1$ | |
342 | } | |
343 | } | |
344 | ||
345 | // ------------------------------------------------------------------------ | |
346 | // Convenience methods for commonly-used attribute tree locations | |
347 | // ------------------------------------------------------------------------ | |
348 | ||
349 | private int getNodeVirtualMachines() { | |
d0c7e4ba | 350 | return checkNotNull(getStateSystemBuilder()).getQuarkAbsoluteAndAdd(VmAttributes.VIRTUAL_MACHINES); |
4a74f111 MG |
351 | } |
352 | ||
353 | private @Nullable HostThread getCurrentHostThread(ITmfEvent event, long ts) { | |
354 | /* Get the LTTng kernel analysis for the host */ | |
355 | String hostId = event.getTrace().getHostId(); | |
6d16f5a9 | 356 | KernelAnalysisModule module = TmfExperimentUtils.getAnalysisModuleOfClassForHost(getTrace(), hostId, KernelAnalysisModule.class); |
4a74f111 MG |
357 | if (module == null) { |
358 | return null; | |
359 | } | |
360 | ||
361 | /* Get the CPU the event is running on */ | |
b1aad44e GB |
362 | Object cpuObj = TmfTraceUtils.resolveEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event); |
363 | if (cpuObj == null) { | |
4a74f111 MG |
364 | /* We couldn't find any CPU information, ignore this event */ |
365 | return null; | |
366 | } | |
b1aad44e | 367 | Integer cpu = (Integer) cpuObj; |
4a74f111 | 368 | |
e363eae1 | 369 | Integer currentTid = KernelThreadInformationProvider.getThreadOnCpu(module, cpu, ts); |
4a74f111 MG |
370 | if (currentTid == null) { |
371 | return null; | |
372 | } | |
373 | return new HostThread(hostId, currentTid); | |
374 | } | |
375 | ||
4c4e2816 | 376 | } |