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 * Geneviève Bastien - Initial API and implementation
12 *******************************************************************************/
14 package org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.qemukvm
;
16 import java
.util
.HashMap
;
18 import java
.util
.Map
.Entry
;
21 import org
.eclipse
.jdt
.annotation
.Nullable
;
22 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernel
.KernelAnalysisModule
;
23 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernel
.KernelThreadInformationProvider
;
24 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.model
.HostThread
;
25 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.IVirtualMachineModel
;
26 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.VirtualCPU
;
27 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.VirtualMachine
;
28 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
29 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEventField
;
30 import org
.eclipse
.tracecompass
.tmf
.core
.event
.TmfEventField
;
31 import org
.eclipse
.tracecompass
.tmf
.core
.event
.aspect
.TmfCpuAspect
;
32 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
33 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperiment
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperimentUtils
;
36 import com
.google
.common
.collect
.ImmutableSet
;
39 * The virtual machine model corresponding to the Qemu/KVM hypervisor. It uses
40 * the kvm_exit/kvm_entry events to identify entry to and exit from the
41 * hypervisor. It also requires vmsync_* events from both guests and hosts to
42 * identify which thread from a host belongs to which machine.
44 * @author Mohamad Gebai
46 public class QemuKvmVmModel
implements IVirtualMachineModel
{
48 private static final String KVM
= "kvm_"; //$NON-NLS-1$
50 /* Associate a host's thread to a virtual CPU */
51 private final Map
<HostThread
, VirtualCPU
> fTidToVcpu
= new HashMap
<>();
52 /* Associate a host's thread to a virtual machine */
53 private final Map
<HostThread
, VirtualMachine
> fTidToVm
= new HashMap
<>();
54 /* Maps a virtual machine name to a virtual machine */
55 private final Map
<String
, VirtualMachine
> fKnownMachines
= new HashMap
<>();
57 private final TmfExperiment fExperiment
;
59 static final ImmutableSet
<String
> REQUIRED_EVENTS
= ImmutableSet
.of(
60 QemuKvmStrings
.KVM_ENTRY
,
61 QemuKvmStrings
.KVM_EXIT
,
62 QemuKvmStrings
.VMSYNC_GH_GUEST
,
63 QemuKvmStrings
.VMSYNC_GH_HOST
,
64 QemuKvmStrings
.VMSYNC_HG_GUEST
,
65 QemuKvmStrings
.VMSYNC_HG_HOST
72 * The experiment this model applies to
74 public QemuKvmVmModel(TmfExperiment exp
) {
79 public @Nullable VirtualMachine
getCurrentMachine(ITmfEvent event
) {
80 final String hostId
= event
.getTrace().getHostId();
81 VirtualMachine machine
= fKnownMachines
.get(hostId
);
82 if (machine
!= null) {
87 * TODO: This model wouldn't support a machine (same hostId) being both
92 * This code path would be used only at the beginning of the analysis.
93 * Once all the hostIds have been associated with a virtual machine, the
94 * machines are all cached and the method returns earlier
96 /* Try to get the virtual machine from the event */
97 String eventName
= event
.getName();
98 if (eventName
.startsWith(KVM
)) {
99 /* Only the host machine has kvm_* events, so this is a host */
100 machine
= VirtualMachine
.newHostMachine(hostId
);
101 } else if (eventName
.equals(QemuKvmStrings
.VMSYNC_GH_GUEST
) || eventName
.equals(QemuKvmStrings
.VMSYNC_HG_GUEST
)) {
102 /* Those events are only present in the guests */
103 TmfEventField field
= (TmfEventField
) event
.getContent();
104 ITmfEventField data
= field
.getField(QemuKvmStrings
.VM_UID_PAYLOAD
);
106 machine
= VirtualMachine
.newGuestMachine((Long
) data
.getValue(), hostId
);
109 if (machine
!= null) {
110 /* Associate the machine to the hostID here, for cached access later */
111 fKnownMachines
.put(hostId
, machine
);
117 public Set
<String
> getRequiredEvents() {
118 return REQUIRED_EVENTS
;
121 private @Nullable VirtualMachine
findVmFromParent(ITmfEvent event
, HostThread ht
) {
123 * Maybe the parent of the current thread has a VM associated, see if we
124 * can infer the VM for this thread
126 KernelAnalysisModule module
= getLttngKernelModuleFor(ht
.getHost());
127 if (module
== null) {
131 Integer ppid
= KernelThreadInformationProvider
.getParentPid(module
, ht
.getTid(), event
.getTimestamp().getValue());
136 HostThread parentHt
= new HostThread(ht
.getHost(), ppid
);
137 VirtualMachine vm
= fTidToVm
.get(parentHt
);
141 fTidToVm
.put(ht
, vm
);
147 public @Nullable VirtualCPU
getVCpuExitingHypervisorMode(ITmfEvent event
, HostThread ht
) {
148 final String eventName
= event
.getName();
150 /* TODO: Use event layouts for this part also */
152 * The KVM_ENTRY event means we are entering a virtual CPU, so exiting
155 if (!eventName
.equals(QemuKvmStrings
.KVM_ENTRY
)) {
160 * Are we entering the hypervisor and if so, which virtual CPU is
163 VirtualMachine vm
= fTidToVm
.get(ht
);
165 vm
= findVmFromParent(event
, ht
);
170 /* Associate this thread with the virtual CPU that is going to be run */
171 final ITmfEventField content
= event
.getContent();
172 long vcpu_id
= (Long
) content
.getField(QemuKvmStrings
.VCPU_ID
).getValue();
174 VirtualCPU virtualCPU
= VirtualCPU
.getVirtualCPU(vm
, vcpu_id
);
175 fTidToVcpu
.put(ht
, virtualCPU
);
181 public @Nullable VirtualCPU
getVCpuEnteringHypervisorMode(ITmfEvent event
, HostThread ht
) {
182 final String eventName
= event
.getName();
184 * The KVM_EXIT event means we are exiting a virtual CPU, so entering
187 if (!eventName
.equals(QemuKvmStrings
.KVM_EXIT
)) {
191 return getVirtualCpu(ht
);
195 public @Nullable VirtualCPU
getVirtualCpu(HostThread ht
) {
196 return fTidToVcpu
.get(ht
);
200 public void handleEvent(ITmfEvent event
) {
201 /* Is the event handled by this model */
202 final String eventName
= event
.getName();
203 if (!eventName
.equals(QemuKvmStrings
.VMSYNC_GH_HOST
)) {
207 final ITmfEventField content
= event
.getContent();
208 final long ts
= event
.getTimestamp().getValue();
209 final String hostId
= event
.getTrace().getHostId();
211 final Integer cpu
= TmfTraceUtils
.resolveIntEventAspectOfClassForEvent(event
.getTrace(), TmfCpuAspect
.class, event
);
213 /* We couldn't find any CPU information, ignore this event */
217 /* Find a virtual machine with the vm uid payload value */
218 ITmfEventField data
= content
.getField(QemuKvmStrings
.VM_UID_PAYLOAD
);
222 long vmUid
= (Long
) data
.getValue();
223 for (Entry
<String
, VirtualMachine
> entry
: fKnownMachines
.entrySet()) {
224 if (entry
.getValue().getVmUid() == vmUid
) {
226 * We found the VM being run, let's associate it with the thread
229 KernelAnalysisModule module
= getLttngKernelModuleFor(hostId
);
230 if (module
== null) {
233 Integer tid
= KernelThreadInformationProvider
.getThreadOnCpu(module
, cpu
, ts
);
236 * We do not know which process is running at this point. It
237 * may happen at the beginning of the trace.
241 HostThread ht
= new HostThread(hostId
, tid
);
242 fTidToVm
.put(ht
, entry
.getValue());
245 * To make sure siblings are also associated with this VM, also
246 * add an entry for the parent TID
248 Integer ppid
= KernelThreadInformationProvider
.getParentPid(module
, tid
, ts
);
250 HostThread parentHt
= new HostThread(hostId
, ppid
);
251 fTidToVm
.put(parentHt
, entry
.getValue());
257 private @Nullable KernelAnalysisModule
getLttngKernelModuleFor(String hostId
) {
258 return TmfExperimentUtils
.getAnalysisModuleOfClassForHost(fExperiment
, hostId
, KernelAnalysisModule
.class);