1 /*******************************************************************************
2 * Copyright (c) 2014, 2016 É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
.kernel
.KernelAnalysisModule
;
22 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernel
.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
.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
.IVirtualMachineModel
;
29 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.VirtualCPU
;
30 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.VirtualMachine
;
31 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.qemukvm
.QemuKvmVmModel
;
32 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.trace
.layout
.LttngEventLayout
;
33 import org
.eclipse
.tracecompass
.lttng2
.kernel
.core
.trace
.LttngKernelTrace
;
34 import org
.eclipse
.tracecompass
.statesystem
.core
.ITmfStateSystemBuilder
;
35 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.AttributeNotFoundException
;
36 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.StateValueTypeException
;
37 import org
.eclipse
.tracecompass
.statesystem
.core
.exceptions
.TimeRangeException
;
38 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.ITmfStateValue
;
39 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
40 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
41 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEventField
;
42 import org
.eclipse
.tracecompass
.tmf
.core
.event
.aspect
.TmfCpuAspect
;
43 import org
.eclipse
.tracecompass
.tmf
.core
.statesystem
.AbstractTmfStateProvider
;
44 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.ITmfTrace
;
45 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
46 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperiment
;
47 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperimentUtils
;
49 import com
.google
.common
.collect
.HashBasedTable
;
50 import com
.google
.common
.collect
.Table
;
53 * This is the state provider which translates the virtual machine experiment
54 * events to a state system.
60 * | |- <Guest Host ID> -> Friendly name (trace name)
61 * | | |- <VCPU number>
62 * | | | |- Status -> <Status value>
65 * The status value of the VCPUs are either {@link VcpuStateValues#VCPU_IDLE},
66 * {@link VcpuStateValues#VCPU_UNKNOWN} or {@link VcpuStateValues#VCPU_RUNNING}.
67 * Those three values are ORed with flags {@link VcpuStateValues#VCPU_VMM}
68 * and/or {@link VcpuStateValues#VCPU_PREEMPT} to indicate respectively whether
69 * they are in hypervisor mode or preempted on the host.
71 * @author Mohamad Gebai
73 public class VirtualMachineStateProvider
extends AbstractTmfStateProvider
{
76 * Version number of this state provider. Please bump this if you modify the
77 * contents of the generated state history in some way.
79 private static final int VERSION
= 1;
81 private static final int SCHED_SWITCH_INDEX
= 0;
83 /* TODO: An analysis should support many hypervisor models */
84 private IVirtualMachineModel fModel
;
85 private final Table
<ITmfTrace
, String
, @Nullable Integer
> fEventNames
;
86 private final Map
<ITmfTrace
, IKernelAnalysisEventLayout
> fLayouts
;
88 // ------------------------------------------------------------------------
90 // ------------------------------------------------------------------------
96 * The virtual machine experiment
98 public VirtualMachineStateProvider(TmfExperiment experiment
) {
99 super(experiment
, "Virtual Machine State Provider"); //$NON-NLS-1$
101 fModel
= new QemuKvmVmModel(experiment
);
102 Table
<ITmfTrace
, String
, @Nullable Integer
> table
= HashBasedTable
.create();
104 fLayouts
= new HashMap
<>();
107 // ------------------------------------------------------------------------
108 // Event names management
109 // ------------------------------------------------------------------------
111 private void buildEventNames(ITmfTrace trace
) {
112 IKernelAnalysisEventLayout layout
;
113 if (trace
instanceof LttngKernelTrace
) {
114 layout
= ((LttngKernelTrace
) trace
).getKernelEventLayout();
116 /* Fall-back to the base LttngEventLayout */
117 layout
= LttngEventLayout
.getInstance();
119 fLayouts
.put(trace
, layout
);
120 fEventNames
.put(trace
, layout
.eventSchedSwitch(), SCHED_SWITCH_INDEX
);
123 // ------------------------------------------------------------------------
125 // ------------------------------------------------------------------------
128 public TmfExperiment
getTrace() {
129 ITmfTrace trace
= super.getTrace();
130 if (trace
instanceof TmfExperiment
) {
131 return (TmfExperiment
) trace
;
133 throw new IllegalStateException("VirtualMachineStateProvider: The associated trace should be an experiment"); //$NON-NLS-1$
137 public int getVersion() {
142 public VirtualMachineStateProvider
getNewInstance() {
143 TmfExperiment trace
= getTrace();
144 return new VirtualMachineStateProvider(trace
);
148 protected void eventHandle(@Nullable ITmfEvent event
) {
153 /* Is the event managed by this analysis */
154 final String eventName
= event
.getName();
156 /* TODO When requirements work again, don't hardcode this */
157 if (!eventName
.equals("sched_switch") && //$NON-NLS-1$
158 !fModel
.getRequiredEvents().contains(eventName
)) {
162 ITmfStateSystemBuilder ss
= checkNotNull(getStateSystemBuilder());
163 ITmfStateValue value
;
165 final ITmfEventField content
= event
.getContent();
166 final long ts
= event
.getTimestamp().getValue();
167 final String hostId
= event
.getTrace().getHostId();
169 /* Do we know this trace's role yet? */
170 VirtualMachine host
= fModel
.getCurrentMachine(event
);
175 /* Make sure guest traces are added to the state system */
176 if (host
.isGuest()) {
178 * If event from a guest OS, make sure the guest exists in the
183 vmQuark
= ss
.getQuarkRelative(getNodeVirtualMachines(), host
.getHostId());
184 } catch (AttributeNotFoundException e
) {
186 * We should enter this catch only once per machine, so it
187 * is not so costly to do compared with adding the trace's
188 * name for each guest event
190 vmQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host
.getHostId());
191 TmfStateValue machineName
= TmfStateValue
.newValueString(event
.getTrace().getName());
192 ss
.modifyAttribute(ts
, machineName
, vmQuark
);
196 /* Have the hypervisor models handle the event first */
197 fModel
.handleEvent(event
);
199 /* Handle the event here */
200 if (!fEventNames
.containsRow(event
.getTrace())) {
201 buildEventNames(event
.getTrace());
203 Integer idx
= fEventNames
.get(event
.getTrace(), eventName
);
204 int intval
= (idx
== null ?
-1 : idx
.intValue());
206 case SCHED_SWITCH_INDEX
: // "sched_switch":
208 * Fields: string prev_comm, int32 prev_tid, int32 prev_prio, int64
209 * prev_state, string next_comm, int32 next_tid, int32 next_prio
212 final IKernelAnalysisEventLayout eventLayout
= fLayouts
.get(event
.getTrace());
213 if (eventLayout
== null) {
216 int prevTid
= ((Long
) content
.getField(eventLayout
.fieldPrevTid()).getValue()).intValue();
217 int nextTid
= ((Long
) content
.getField(eventLayout
.fieldNextTid()).getValue()).intValue();
219 if (host
.isGuest()) {
220 /* Get the event's CPU */
221 Integer cpu
= TmfTraceUtils
.resolveIntEventAspectOfClassForEvent(event
.getTrace(), TmfCpuAspect
.class, event
);
224 * We couldn't find any CPU information, ignore this
231 * If sched switch is from a guest, just update the status
232 * of the virtual CPU to either idle or running
234 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host
.getHostId(),
235 cpu
.toString(), VmAttributes
.STATUS
);
236 value
= TmfStateValue
.newValueInt(VcpuStateValues
.VCPU_IDLE
);
238 value
= TmfStateValue
.newValueInt(VcpuStateValues
.VCPU_RUNNING
);
240 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
244 /* Event is not from a guest */
245 /* Verify if the previous thread corresponds to a virtual CPU */
246 HostThread ht
= new HostThread(hostId
, prevTid
);
247 VirtualCPU vcpu
= fModel
.getVirtualCpu(ht
);
250 * If previous thread is virtual CPU, update status of the
251 * virtual CPU to preempted
254 VirtualMachine vm
= vcpu
.getVm();
256 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm
.getHostId(),
257 vcpu
.getCpuId().toString(), VmAttributes
.STATUS
);
259 /* Add the preempted flag to the status */
260 value
= ss
.queryOngoingState(curStatusQuark
);
261 if ((value
.unboxInt() & VcpuStateValues
.VCPU_IDLE
) == 0) {
262 int newVal
= Math
.max(VcpuStateValues
.VCPU_UNKNOWN
, value
.unboxInt());
263 value
= TmfStateValue
.newValueInt(newVal
| VcpuStateValues
.VCPU_PREEMPT
);
264 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
268 /* Verify if the next thread corresponds to a virtual CPU */
269 ht
= new HostThread(hostId
, nextTid
);
270 vcpu
= fModel
.getVirtualCpu(ht
);
273 * If next thread is virtual CPU, update status of the virtual
274 * CPU the previous status
277 VirtualMachine vm
= vcpu
.getVm();
278 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm
.getHostId(),
279 vcpu
.getCpuId().toString(), VmAttributes
.STATUS
);
281 /* Remove the preempted flag from the status */
282 value
= ss
.queryOngoingState(curStatusQuark
);
283 int newVal
= Math
.max(VcpuStateValues
.VCPU_UNKNOWN
, value
.unboxInt());
284 value
= TmfStateValue
.newValueInt(newVal
& ~VcpuStateValues
.VCPU_PREEMPT
);
285 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
293 /* Other events not covered by the main switch */
295 HostThread ht
= getCurrentHostThread(event
, ts
);
301 * Are we entering the hypervisor mode and if so, which virtual
304 VirtualCPU virtualCpu
= fModel
.getVCpuEnteringHypervisorMode(event
, ht
);
305 if (virtualCpu
!= null) {
306 /* Add the hypervisor flag to the status */
307 VirtualMachine vm
= virtualCpu
.getVm();
308 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm
.getHostId(),
309 Long
.toString(virtualCpu
.getCpuId()), VmAttributes
.STATUS
);
310 value
= ss
.queryOngoingState(curStatusQuark
);
311 if ((value
.unboxInt() & VcpuStateValues
.VCPU_IDLE
) == 0) {
312 int newVal
= Math
.max(VcpuStateValues
.VCPU_UNKNOWN
, value
.unboxInt());
313 value
= TmfStateValue
.newValueInt(newVal
| VcpuStateValues
.VCPU_VMM
);
314 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
319 * Are we exiting the hypervisor mode and if so, which virtual
322 virtualCpu
= fModel
.getVCpuExitingHypervisorMode(event
, ht
);
323 if (virtualCpu
!= null) {
324 /* Remove the hypervisor flag from the status */
325 VirtualMachine vm
= virtualCpu
.getVm();
326 int curStatusQuark
= ss
.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm
.getHostId(),
327 Long
.toString(virtualCpu
.getCpuId()), VmAttributes
.STATUS
);
328 value
= ss
.queryOngoingState(curStatusQuark
);
329 int newVal
= Math
.max(VcpuStateValues
.VCPU_UNKNOWN
, value
.unboxInt());
330 value
= TmfStateValue
.newValueInt(newVal
& ~VcpuStateValues
.VCPU_VMM
);
331 ss
.modifyAttribute(ts
, value
, curStatusQuark
);
338 } catch (TimeRangeException
| StateValueTypeException e
) {
339 Activator
.getDefault().logError("Error handling event in VirtualMachineStateProvider", e
); //$NON-NLS-1$
343 // ------------------------------------------------------------------------
344 // Convenience methods for commonly-used attribute tree locations
345 // ------------------------------------------------------------------------
347 private int getNodeVirtualMachines() {
348 return checkNotNull(getStateSystemBuilder()).getQuarkAbsoluteAndAdd(VmAttributes
.VIRTUAL_MACHINES
);
351 private @Nullable HostThread
getCurrentHostThread(ITmfEvent event
, long ts
) {
352 /* Get the LTTng kernel analysis for the host */
353 String hostId
= event
.getTrace().getHostId();
354 KernelAnalysisModule module
= TmfExperimentUtils
.getAnalysisModuleOfClassForHost(getTrace(), hostId
, KernelAnalysisModule
.class);
355 if (module
== null) {
359 /* Get the CPU the event is running on */
360 Integer cpu
= TmfTraceUtils
.resolveIntEventAspectOfClassForEvent(event
.getTrace(), TmfCpuAspect
.class, event
);
362 /* We couldn't find any CPU information, ignore this event */
366 Integer currentTid
= KernelThreadInformationProvider
.getThreadOnCpu(module
, cpu
, ts
);
367 if (currentTid
== null) {
370 return new HostThread(hostId
, currentTid
);