1 /*******************************************************************************
2 * Copyright (c) 2014 É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
.kernelanalysis
.KernelAnalysis
;
23 import org
.eclipse
.tracecompass
.analysis
.os
.linux
.core
.kernelanalysis
.KernelThreadInformationProvider
;
24 import org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
;
25 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.HostThread
;
26 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.IVirtualMachineModel
;
27 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.VirtualCPU
;
28 import org
.eclipse
.tracecompass
.internal
.lttng2
.kernel
.core
.analysis
.vm
.model
.VirtualMachine
;
29 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEvent
;
30 import org
.eclipse
.tracecompass
.tmf
.core
.event
.ITmfEventField
;
31 import org
.eclipse
.tracecompass
.tmf
.core
.event
.TmfEventField
;
32 import org
.eclipse
.tracecompass
.tmf
.core
.event
.aspect
.TmfCpuAspect
;
33 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.TmfTraceUtils
;
34 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperiment
;
35 import org
.eclipse
.tracecompass
.tmf
.core
.trace
.experiment
.TmfExperimentUtils
;
37 import com
.google
.common
.collect
.ImmutableSet
;
40 * The virtual machine model corresponding to the Qemu/KVM hypervisor. It uses
41 * the kvm_exit/kvm_entry events to identify entry to and exit from the
42 * hypervisor. It also requires vmsync_* events from both guests and hosts to
43 * identify which thread from a host belongs to which machine.
45 * @author Mohamad Gebai
47 public class QemuKvmVmModel
implements IVirtualMachineModel
{
49 private static final String KVM
= "kvm_"; //$NON-NLS-1$
51 /* Associate a host's thread to a virtual CPU */
52 private final Map
<HostThread
, VirtualCPU
> fTidToVcpu
= new HashMap
<>();
53 /* Associate a host's thread to a virtual machine */
54 private final Map
<HostThread
, VirtualMachine
> fTidToVm
= new HashMap
<>();
55 /* Maps a virtual machine name to a virtual machine */
56 private final Map
<String
, VirtualMachine
> fKnownMachines
= new HashMap
<>();
58 private final TmfExperiment fExperiment
;
60 static final ImmutableSet
<String
> REQUIRED_EVENTS
= NonNullUtils
.checkNotNull(ImmutableSet
.of(
61 QemuKvmStrings
.KVM_ENTRY
,
62 QemuKvmStrings
.KVM_EXIT
,
63 QemuKvmStrings
.VMSYNC_GH_GUEST
,
64 QemuKvmStrings
.VMSYNC_GH_HOST
,
65 QemuKvmStrings
.VMSYNC_HG_GUEST
,
66 QemuKvmStrings
.VMSYNC_HG_HOST
73 * The experiment this model applies to
75 public QemuKvmVmModel(TmfExperiment exp
) {
80 public @Nullable VirtualMachine
getCurrentMachine(ITmfEvent event
) {
81 final String hostId
= event
.getTrace().getHostId();
82 VirtualMachine machine
= fKnownMachines
.get(hostId
);
83 if (machine
!= null) {
88 * TODO: This model wouldn't support a machine (same hostId) being both
93 * This code path would be used only at the beginning of the analysis.
94 * Once all the hostIds have been associated with a virtual machine, the
95 * machines are all cached and the method returns earlier
97 /* Try to get the virtual machine from the event */
98 String eventName
= event
.getType().getName();
99 if (eventName
.startsWith(KVM
)) {
100 /* Only the host machine has kvm_* events, so this is a host */
101 machine
= VirtualMachine
.newHostMachine(hostId
);
102 } else if (eventName
.equals(QemuKvmStrings
.VMSYNC_GH_GUEST
) || eventName
.equals(QemuKvmStrings
.VMSYNC_HG_GUEST
)) {
103 /* Those events are only present in the guests */
104 TmfEventField field
= (TmfEventField
) event
.getContent();
105 ITmfEventField data
= field
.getField(QemuKvmStrings
.VM_UID_PAYLOAD
);
107 machine
= VirtualMachine
.newGuestMachine((Long
) data
.getValue(), hostId
);
110 if (machine
!= null) {
111 /* Associate the machine to the hostID here, for cached access later */
112 fKnownMachines
.put(hostId
, machine
);
118 public Set
<String
> getRequiredEvents() {
119 return REQUIRED_EVENTS
;
122 private @Nullable VirtualMachine
findVmFromParent(ITmfEvent event
, HostThread ht
) {
124 * Maybe the parent of the current thread has a VM associated, see if we
125 * can infer the VM for this thread
127 KernelAnalysis module
= getLttngKernelModuleFor(ht
.getHost());
128 if (module
== null) {
132 Integer ppid
= KernelThreadInformationProvider
.getParentPid(module
, ht
.getTid(), event
.getTimestamp().getValue());
137 HostThread parentHt
= new HostThread(ht
.getHost(), ppid
);
138 VirtualMachine vm
= fTidToVm
.get(parentHt
);
142 fTidToVm
.put(ht
, vm
);
148 public @Nullable VirtualCPU
getVCpuExitingHypervisorMode(ITmfEvent event
, HostThread ht
) {
149 final String eventName
= event
.getType().getName();
151 /* TODO: Use event layouts for this part also */
153 * The KVM_ENTRY event means we are entering a virtual CPU, so exiting
156 if (!eventName
.equals(QemuKvmStrings
.KVM_ENTRY
)) {
161 * Are we entering the hypervisor and if so, which virtual CPU is
164 VirtualMachine vm
= fTidToVm
.get(ht
);
166 vm
= findVmFromParent(event
, ht
);
171 /* Associate this thread with the virtual CPU that is going to be run */
172 final ITmfEventField content
= event
.getContent();
173 long vcpu_id
= (Long
) content
.getField(QemuKvmStrings
.VCPU_ID
).getValue();
175 VirtualCPU virtualCPU
= VirtualCPU
.getVirtualCPU(vm
, vcpu_id
);
176 fTidToVcpu
.put(ht
, virtualCPU
);
182 public @Nullable VirtualCPU
getVCpuEnteringHypervisorMode(ITmfEvent event
, HostThread ht
) {
183 final String eventName
= event
.getType().getName();
185 * The KVM_EXIT event means we are exiting a virtual CPU, so entering
188 if (!eventName
.equals(QemuKvmStrings
.KVM_EXIT
)) {
192 return getVirtualCpu(ht
);
196 public @Nullable VirtualCPU
getVirtualCpu(HostThread ht
) {
197 return fTidToVcpu
.get(ht
);
201 public void handleEvent(ITmfEvent event
) {
202 /* Is the event handled by this model */
203 final String eventName
= event
.getType().getName();
204 if (!eventName
.equals(QemuKvmStrings
.VMSYNC_GH_HOST
)) {
208 final ITmfEventField content
= event
.getContent();
209 final long ts
= event
.getTimestamp().getValue();
210 final String hostId
= event
.getTrace().getHostId();
213 Iterable
<TmfCpuAspect
> aspects
= TmfTraceUtils
.getEventAspectsOfClass(event
.getTrace(), TmfCpuAspect
.class);
214 for (TmfCpuAspect aspect
: aspects
) {
215 Integer thisCpu
= aspect
.resolve(event
);
216 if (thisCpu
!= null) {
222 /* We couldn't find any CPU information, ignore this event */
226 /* Find a virtual machine with the vm uid payload value */
227 ITmfEventField data
= content
.getField(QemuKvmStrings
.VM_UID_PAYLOAD
);
231 long vmUid
= (Long
) data
.getValue();
232 for (Entry
<String
, VirtualMachine
> entry
: fKnownMachines
.entrySet()) {
233 if (entry
.getValue().getVmUid() == vmUid
) {
235 * We found the VM being run, let's associate it with the thread
238 KernelAnalysis module
= getLttngKernelModuleFor(hostId
);
239 if (module
== null) {
242 Integer tid
= KernelThreadInformationProvider
.getThreadOnCpu(module
, cpu
, ts
);
245 * We do not know which process is running at this point. It
246 * may happen at the beginning of the trace.
250 HostThread ht
= new HostThread(hostId
, tid
);
251 fTidToVm
.put(ht
, entry
.getValue());
254 * To make sure siblings are also associated with this VM, also
255 * add an entry for the parent TID
257 Integer ppid
= KernelThreadInformationProvider
.getParentPid(module
, tid
, ts
);
259 HostThread parentHt
= new HostThread(hostId
, ppid
);
260 fTidToVm
.put(parentHt
, entry
.getValue());
266 private @Nullable KernelAnalysis
getLttngKernelModuleFor(String hostId
) {
267 return TmfExperimentUtils
.getAnalysisModuleOfClassForHost(fExperiment
, hostId
, KernelAnalysis
.class);