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
.KernelAnalysis
;
22 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernelanalysis
.KernelThreadInformationProvider
;
23 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.trace
.IKernelAnalysisEventLayout
;
24 import org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
;
25 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.Activator
;
26 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.VcpuStateValues
;
27 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.VmAttributes
;
28 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.HostThread
;
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
, Integer
> fEventNames
;
87 private final Map
<ITmfTrace
, IKernelAnalysisEventLayout
> fLayouts
;
89 // ------------------------------------------------------------------------
91 // ------------------------------------------------------------------------
97 * The virtual machine experiment
99 public VirtualMachineStateProvider(TmfExperiment experiment
) {
100 super(experiment
, ITmfEvent
.class, "Virtual Machine State Provider"); //$NON-NLS-1$
102 fModel
= new QemuKvmVmModel(experiment
);
103 Table
<ITmfTrace
, String
, Integer
> table
= NonNullUtils
.checkNotNull(HashBasedTable
.<ITmfTrace
, String
, Integer
> 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
.getType().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 Integer prevTid
= ((Long
) content
.getField(fLayouts
.get(event
.getTrace()).fieldPrevTid()).getValue()).intValue();
214 Integer nextTid
= ((Long
) content
.getField(fLayouts
.get(event
.getTrace()).fieldNextTid()).getValue()).intValue();
216 if (prevTid
== null || nextTid
== null) {
220 if (host
.isGuest()) {
221 /* Get the event's CPU */
223 Iterable
<TmfCpuAspect
> aspects
= TmfTraceUtils
.getEventAspectsOfClass(event
.getTrace(), TmfCpuAspect
.class);
224 for (TmfCpuAspect aspect
: aspects
) {
225 Integer aspectRes
= aspect
.resolve(event
);
226 if (aspectRes
!= null) {
233 * We couldn't find any CPU information, ignore this
240 * If sched switch is from a guest, just update the status
241 * of the virtual CPU to either idle or running
243 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host
.getHostId(),
244 cpu
.toString(), VmAttributes
.STATUS
);
245 value
= TmfStateValue
.newValueInt(VcpuStateValues
.VCPU_IDLE
);
247 value
= TmfStateValue
.newValueInt(VcpuStateValues
.VCPU_RUNNING
);
249 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
253 /* Event is not from a guest */
254 /* Verify if the previous thread corresponds to a virtual CPU */
255 HostThread ht
= new HostThread(hostId
, prevTid
);
256 VirtualCPU vcpu
= fModel
.getVirtualCpu(ht
);
259 * If previous thread is virtual CPU, update status of the
260 * virtual CPU to preempted
263 VirtualMachine vm
= vcpu
.getVm();
265 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm
.getHostId(),
266 vcpu
.getCpuId().toString(), VmAttributes
.STATUS
);
268 /* Add the preempted flag to the status */
269 value
= ss
.queryOngoingState(curStatusQuark
);
270 int newVal
= Math
.max(VcpuStateValues
.VCPU_UNKNOWN
, value
.unboxInt());
271 value
= TmfStateValue
.newValueInt(newVal
| VcpuStateValues
.VCPU_PREEMPT
);
272 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
275 /* Verify if the next thread corresponds to a virtual CPU */
276 ht
= new HostThread(hostId
, nextTid
);
277 vcpu
= fModel
.getVirtualCpu(ht
);
280 * If next thread is virtual CPU, update status of the virtual
281 * CPU the previous status
284 VirtualMachine vm
= vcpu
.getVm();
285 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm
.getHostId(),
286 vcpu
.getCpuId().toString(), VmAttributes
.STATUS
);
288 /* Remove the preempted flag from the status */
289 value
= ss
.queryOngoingState(curStatusQuark
);
290 int newVal
= Math
.max(VcpuStateValues
.VCPU_UNKNOWN
, value
.unboxInt());
291 value
= TmfStateValue
.newValueInt(newVal
& ~VcpuStateValues
.VCPU_PREEMPT
);
292 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
300 /* Other events not covered by the main switch */
302 HostThread ht
= getCurrentHostThread(event
, ts
);
308 * Are we entering the hypervisor mode and if so, which virtual
311 VirtualCPU virtualCpu
= fModel
.getVCpuEnteringHypervisorMode(event
, ht
);
312 if (virtualCpu
!= null) {
313 /* Add the hypervisor flag to the status */
314 VirtualMachine vm
= virtualCpu
.getVm();
315 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm
.getHostId(),
316 Long
.toString(virtualCpu
.getCpuId()), VmAttributes
.STATUS
);
317 value
= ss
.queryOngoingState(curStatusQuark
);
318 int newVal
= Math
.max(VcpuStateValues
.VCPU_UNKNOWN
, value
.unboxInt());
319 value
= TmfStateValue
.newValueInt(newVal
| VcpuStateValues
.VCPU_VMM
);
320 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
324 * Are we exiting the hypervisor mode and if so, which virtual
327 virtualCpu
= fModel
.getVCpuExitingHypervisorMode(event
, ht
);
328 if (virtualCpu
!= null) {
329 /* Remove the hypervisor flag from the status */
330 VirtualMachine vm
= virtualCpu
.getVm();
331 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm
.getHostId(),
332 Long
.toString(virtualCpu
.getCpuId()), VmAttributes
.STATUS
);
333 value
= ss
.queryOngoingState(curStatusQuark
);
334 int newVal
= Math
.max(VcpuStateValues
.VCPU_UNKNOWN
, value
.unboxInt());
335 value
= TmfStateValue
.newValueInt(newVal
& ~VcpuStateValues
.VCPU_VMM
);
336 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
343 } catch (AttributeNotFoundException
| TimeRangeException
| StateValueTypeException e
) {
344 Activator
.getDefault().logError("Error handling event in VirtualMachineStateProvider", e
); //$NON-NLS-1$
348 // ------------------------------------------------------------------------
349 // Convenience methods for commonly-used attribute tree locations
350 // ------------------------------------------------------------------------
352 private int getNodeVirtualMachines() {
353 return checkNotNull(getStateSystemBuilder()).getQuarkAbsoluteAndAdd(VmAttributes
.VIRTUAL_MACHINES
);
356 private @Nullable HostThread
getCurrentHostThread(ITmfEvent event
, long ts
) {
357 /* Get the LTTng kernel analysis for the host */
358 String hostId
= event
.getTrace().getHostId();
359 KernelAnalysis module
= TmfExperimentUtils
.getAnalysisModuleOfClassForHost(getTrace(), hostId
, KernelAnalysis
.class);
360 if (module
== null) {
364 /* Get the CPU the event is running on */
366 Iterable
<TmfCpuAspect
> aspects
= TmfTraceUtils
.getEventAspectsOfClass(event
.getTrace(), TmfCpuAspect
.class);
367 for (TmfCpuAspect aspect
: aspects
) {
368 Integer aspectRes
= aspect
.resolve(event
);
369 if (aspectRes
!= null) {
375 /* We couldn't find any CPU information, ignore this event */
379 Integer currentTid
= KernelThreadInformationProvider
.getThreadOnCpu(module
, cpu
, ts
);
380 if (currentTid
== null) {
383 return new HostThread(hostId
, currentTid
);