1 /*******************************************************************************
2 * Copyright (c) 2014, 2015 É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 * Mohamad Gebai - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.module
;
15 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
17 import java
.util
.HashMap
;
20 import org
.eclipse
.jdt
.annotation
.Nullable
;
21 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernelanalysis
.KernelAnalysisModule
;
22 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernelanalysis
.KernelThreadInformationProvider
;
23 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.model
.HostThread
;
24 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.trace
.IKernelAnalysisEventLayout
;
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
;
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
;
33 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.trace
.layout
.LttngEventLayout
;
34 import org
.eclipse
.tracecompass
.lttng2
.kernel
.core
.trace
.LttngKernelTrace
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystemBuilder
;
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
;
50 import com
.google
.common
.collect
.HashBasedTable
;
51 import com
.google
.common
.collect
.Table
;
54 * This is the state provider which translates the virtual machine experiment
55 * events to a state system.
61 * | |- <Guest Host ID> -> Friendly name (trace name)
62 * | | |- <VCPU number>
63 * | | | |- Status -> <Status value>
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.
72 * @author Mohamad Gebai
74 public class VirtualMachineStateProvider
extends AbstractTmfStateProvider
{
77 * Version number of this state provider. Please bump this if you modify the
78 * contents of the generated state history in some way.
80 private static final int VERSION
= 1;
82 private static final int SCHED_SWITCH_INDEX
= 0;
84 /* TODO: An analysis should support many hypervisor models */
85 private IVirtualMachineModel fModel
;
86 private final Table
<ITmfTrace
, String
, @Nullable Integer
> fEventNames
;
87 private final Map
<ITmfTrace
, IKernelAnalysisEventLayout
> fLayouts
;
89 // ------------------------------------------------------------------------
91 // ------------------------------------------------------------------------
97 * The virtual machine experiment
99 public VirtualMachineStateProvider(TmfExperiment experiment
) {
100 super(experiment
, "Virtual Machine State Provider"); //$NON-NLS-1$
102 fModel
= new QemuKvmVmModel(experiment
);
103 Table
<ITmfTrace
, String
, @Nullable Integer
> table
= NonNullUtils
.checkNotNull(HashBasedTable
.create());
105 fLayouts
= new HashMap
<>();
108 // ------------------------------------------------------------------------
109 // Event names management
110 // ------------------------------------------------------------------------
112 private void buildEventNames(ITmfTrace trace
) {
113 IKernelAnalysisEventLayout layout
;
114 if (trace
instanceof LttngKernelTrace
) {
115 layout
= ((LttngKernelTrace
) trace
).getKernelEventLayout();
117 /* Fall-back to the base LttngEventLayout */
118 layout
= LttngEventLayout
.getInstance();
120 fLayouts
.put(trace
, layout
);
121 fEventNames
.put(trace
, layout
.eventSchedSwitch(), SCHED_SWITCH_INDEX
);
124 // ------------------------------------------------------------------------
126 // ------------------------------------------------------------------------
129 public TmfExperiment
getTrace() {
130 ITmfTrace trace
= super.getTrace();
131 if (trace
instanceof TmfExperiment
) {
132 return (TmfExperiment
) trace
;
134 throw new IllegalStateException("VirtualMachineStateProvider: The associated trace should be an experiment"); //$NON-NLS-1$
138 public int getVersion() {
143 public VirtualMachineStateProvider
getNewInstance() {
144 TmfExperiment trace
= getTrace();
145 return new VirtualMachineStateProvider(trace
);
149 protected void eventHandle(@Nullable ITmfEvent event
) {
154 /* Is the event managed by this analysis */
155 final String eventName
= event
.getName();
157 /* TODO When requirements work again, don't hardcode this */
158 if (!eventName
.equals("sched_switch") && //$NON-NLS-1$
159 !fModel
.getRequiredEvents().contains(eventName
)) {
163 ITmfStateSystemBuilder ss
= checkNotNull(getStateSystemBuilder());
164 ITmfStateValue value
;
166 final ITmfEventField content
= event
.getContent();
167 final long ts
= event
.getTimestamp().getValue();
168 final String hostId
= event
.getTrace().getHostId();
170 /* Do we know this trace's role yet? */
171 VirtualMachine host
= fModel
.getCurrentMachine(event
);
176 /* Make sure guest traces are added to the state system */
177 if (host
.isGuest()) {
179 * If event from a guest OS, make sure the guest exists in the
184 vmQuark
= ss
.getQuarkRelative(getNodeVirtualMachines(), host
.getHostId());
185 } catch (AttributeNotFoundException e
) {
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
191 vmQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host
.getHostId());
192 TmfStateValue machineName
= TmfStateValue
.newValueString(event
.getTrace().getName());
193 ss
.modifyAttribute(ts
, machineName
, vmQuark
);
197 /* Have the hypervisor models handle the event first */
198 fModel
.handleEvent(event
);
200 /* Handle the event here */
201 if (!fEventNames
.containsRow(event
.getTrace())) {
202 buildEventNames(event
.getTrace());
204 Integer idx
= fEventNames
.get(event
.getTrace(), eventName
);
205 int intval
= (idx
== null ?
-1 : idx
.intValue());
207 case SCHED_SWITCH_INDEX
: // "sched_switch":
209 * Fields: string prev_comm, int32 prev_tid, int32 prev_prio, int64
210 * prev_state, string next_comm, int32 next_tid, int32 next_prio
213 final IKernelAnalysisEventLayout eventLayout
= fLayouts
.get(event
.getTrace());
214 if (eventLayout
== null) {
217 int prevTid
= ((Long
) content
.getField(eventLayout
.fieldPrevTid()).getValue()).intValue();
218 int nextTid
= ((Long
) content
.getField(eventLayout
.fieldNextTid()).getValue()).intValue();
220 if (host
.isGuest()) {
221 /* Get the event's CPU */
222 Object cpuObj
= TmfTraceUtils
.resolveEventAspectOfClassForEvent(event
.getTrace(), TmfCpuAspect
.class, event
);
223 if (cpuObj
== null) {
225 * We couldn't find any CPU information, ignore this
230 Integer cpu
= (Integer
) cpuObj
;
233 * If sched switch is from a guest, just update the status
234 * of the virtual CPU to either idle or running
236 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host
.getHostId(),
237 cpu
.toString(), VmAttributes
.STATUS
);
238 value
= TmfStateValue
.newValueInt(VcpuStateValues
.VCPU_IDLE
);
240 value
= TmfStateValue
.newValueInt(VcpuStateValues
.VCPU_RUNNING
);
242 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
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
);
252 * If previous thread is virtual CPU, update status of the
253 * virtual CPU to preempted
256 VirtualMachine vm
= vcpu
.getVm();
258 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm
.getHostId(),
259 vcpu
.getCpuId().toString(), VmAttributes
.STATUS
);
261 /* Add the preempted flag to the status */
262 value
= ss
.queryOngoingState(curStatusQuark
);
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
);
270 /* Verify if the next thread corresponds to a virtual CPU */
271 ht
= new HostThread(hostId
, nextTid
);
272 vcpu
= fModel
.getVirtualCpu(ht
);
275 * If next thread is virtual CPU, update status of the virtual
276 * CPU the previous status
279 VirtualMachine vm
= vcpu
.getVm();
280 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm
.getHostId(),
281 vcpu
.getCpuId().toString(), VmAttributes
.STATUS
);
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
);
295 /* Other events not covered by the main switch */
297 HostThread ht
= getCurrentHostThread(event
, ts
);
303 * Are we entering the hypervisor mode and if so, which virtual
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
);
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
);
321 * Are we exiting the hypervisor mode and if so, which virtual
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
);
340 } catch (AttributeNotFoundException
| TimeRangeException
| StateValueTypeException e
) {
341 Activator
.getDefault().logError("Error handling event in VirtualMachineStateProvider", e
); //$NON-NLS-1$
345 // ------------------------------------------------------------------------
346 // Convenience methods for commonly-used attribute tree locations
347 // ------------------------------------------------------------------------
349 private int getNodeVirtualMachines() {
350 return checkNotNull(getStateSystemBuilder()).getQuarkAbsoluteAndAdd(VmAttributes
.VIRTUAL_MACHINES
);
353 private @Nullable HostThread
getCurrentHostThread(ITmfEvent event
, long ts
) {
354 /* Get the LTTng kernel analysis for the host */
355 String hostId
= event
.getTrace().getHostId();
356 KernelAnalysisModule module
= TmfExperimentUtils
.getAnalysisModuleOfClassForHost(getTrace(), hostId
, KernelAnalysisModule
.class);
357 if (module
== null) {
361 /* Get the CPU the event is running on */
362 Object cpuObj
= TmfTraceUtils
.resolveEventAspectOfClassForEvent(event
.getTrace(), TmfCpuAspect
.class, event
);
363 if (cpuObj
== null) {
364 /* We couldn't find any CPU information, ignore this event */
367 Integer cpu
= (Integer
) cpuObj
;
369 Integer currentTid
= KernelThreadInformationProvider
.getThreadOnCpu(module
, cpu
, ts
);
370 if (currentTid
== null) {
373 return new HostThread(hostId
, currentTid
);