Commit | Line | Data |
---|---|---|
4a74f111 | 1 | /******************************************************************************* |
ed902a2b | 2 | * Copyright (c) 2014, 2015 École Polytechnique de Montréal |
4a74f111 MG |
3 | * |
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 | |
8 | * | |
9 | * Contributors: | |
10 | * Mohamad Gebai - Initial API and implementation | |
11 | * Geneviève Bastien - Initial API and implementation | |
12 | *******************************************************************************/ | |
13 | ||
14 | package org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.model.qemukvm; | |
15 | ||
16 | import java.util.HashMap; | |
17 | import java.util.Map; | |
18 | import java.util.Map.Entry; | |
19 | import java.util.Set; | |
20 | ||
21 | import org.eclipse.jdt.annotation.Nullable; | |
6d16f5a9 | 22 | import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.KernelAnalysisModule; |
e363eae1 | 23 | import org.eclipse.tracecompass.analysis.os.linux.core.kernelanalysis.KernelThreadInformationProvider; |
a6145763 | 24 | import org.eclipse.tracecompass.analysis.os.linux.core.model.HostThread; |
4a74f111 MG |
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; | |
4a74f111 MG |
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; | |
35 | ||
36 | import com.google.common.collect.ImmutableSet; | |
37 | ||
38 | /** | |
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. | |
43 | * | |
44 | * @author Mohamad Gebai | |
45 | */ | |
46 | public class QemuKvmVmModel implements IVirtualMachineModel { | |
47 | ||
48 | private static final String KVM = "kvm_"; //$NON-NLS-1$ | |
49 | ||
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<>(); | |
56 | ||
57 | private final TmfExperiment fExperiment; | |
58 | ||
0e4f957e | 59 | static final ImmutableSet<String> REQUIRED_EVENTS = ImmutableSet.of( |
4a74f111 MG |
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 | |
0e4f957e | 66 | ); |
4a74f111 MG |
67 | |
68 | /** | |
69 | * Constructor | |
70 | * | |
71 | * @param exp | |
72 | * The experiment this model applies to | |
73 | */ | |
74 | public QemuKvmVmModel(TmfExperiment exp) { | |
75 | fExperiment = exp; | |
76 | } | |
77 | ||
78 | @Override | |
79 | public @Nullable VirtualMachine getCurrentMachine(ITmfEvent event) { | |
80 | final String hostId = event.getTrace().getHostId(); | |
81 | VirtualMachine machine = fKnownMachines.get(hostId); | |
82 | if (machine != null) { | |
83 | return machine; | |
84 | } | |
85 | ||
86 | /* | |
87 | * TODO: This model wouldn't support a machine (same hostId) being both | |
88 | * a guest and a host | |
89 | */ | |
90 | ||
91 | /* | |
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 | |
95 | */ | |
96 | /* Try to get the virtual machine from the event */ | |
578716e6 | 97 | String eventName = event.getName(); |
4a74f111 MG |
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); | |
105 | if (data != null) { | |
106 | machine = VirtualMachine.newGuestMachine((Long) data.getValue(), hostId); | |
107 | } | |
108 | } | |
109 | if (machine != null) { | |
110 | /* Associate the machine to the hostID here, for cached access later */ | |
111 | fKnownMachines.put(hostId, machine); | |
112 | } | |
113 | return machine; | |
114 | } | |
115 | ||
116 | @Override | |
117 | public Set<String> getRequiredEvents() { | |
118 | return REQUIRED_EVENTS; | |
119 | } | |
120 | ||
121 | private @Nullable VirtualMachine findVmFromParent(ITmfEvent event, HostThread ht) { | |
122 | /* | |
123 | * Maybe the parent of the current thread has a VM associated, see if we | |
124 | * can infer the VM for this thread | |
125 | */ | |
6d16f5a9 | 126 | KernelAnalysisModule module = getLttngKernelModuleFor(ht.getHost()); |
4a74f111 MG |
127 | if (module == null) { |
128 | return null; | |
129 | } | |
130 | ||
e363eae1 | 131 | Integer ppid = KernelThreadInformationProvider.getParentPid(module, ht.getTid(), event.getTimestamp().getValue()); |
4a74f111 MG |
132 | if (ppid == null) { |
133 | return null; | |
134 | } | |
135 | ||
136 | HostThread parentHt = new HostThread(ht.getHost(), ppid); | |
137 | VirtualMachine vm = fTidToVm.get(parentHt); | |
138 | if (vm == null) { | |
139 | return null; | |
140 | } | |
141 | fTidToVm.put(ht, vm); | |
142 | ||
143 | return vm; | |
144 | } | |
145 | ||
146 | @Override | |
147 | public @Nullable VirtualCPU getVCpuExitingHypervisorMode(ITmfEvent event, HostThread ht) { | |
578716e6 | 148 | final String eventName = event.getName(); |
4a74f111 MG |
149 | |
150 | /* TODO: Use event layouts for this part also */ | |
151 | /* | |
152 | * The KVM_ENTRY event means we are entering a virtual CPU, so exiting | |
153 | * hypervisor mode | |
154 | */ | |
155 | if (!eventName.equals(QemuKvmStrings.KVM_ENTRY)) { | |
156 | return null; | |
157 | } | |
158 | ||
159 | /* | |
160 | * Are we entering the hypervisor and if so, which virtual CPU is | |
161 | * concerned? | |
162 | */ | |
163 | VirtualMachine vm = fTidToVm.get(ht); | |
164 | if (vm == null) { | |
165 | vm = findVmFromParent(event, ht); | |
166 | if (vm == null) { | |
167 | return null; | |
168 | } | |
169 | } | |
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(); | |
173 | ||
174 | VirtualCPU virtualCPU = VirtualCPU.getVirtualCPU(vm, vcpu_id); | |
175 | fTidToVcpu.put(ht, virtualCPU); | |
176 | ||
177 | return virtualCPU; | |
178 | } | |
179 | ||
180 | @Override | |
181 | public @Nullable VirtualCPU getVCpuEnteringHypervisorMode(ITmfEvent event, HostThread ht) { | |
578716e6 | 182 | final String eventName = event.getName(); |
4a74f111 MG |
183 | /* |
184 | * The KVM_EXIT event means we are exiting a virtual CPU, so entering | |
185 | * hypervisor mode | |
186 | */ | |
187 | if (!eventName.equals(QemuKvmStrings.KVM_EXIT)) { | |
188 | return null; | |
189 | } | |
190 | ||
191 | return getVirtualCpu(ht); | |
192 | } | |
193 | ||
194 | @Override | |
195 | public @Nullable VirtualCPU getVirtualCpu(HostThread ht) { | |
196 | return fTidToVcpu.get(ht); | |
197 | } | |
198 | ||
199 | @Override | |
200 | public void handleEvent(ITmfEvent event) { | |
201 | /* Is the event handled by this model */ | |
578716e6 | 202 | final String eventName = event.getName(); |
4a74f111 MG |
203 | if (!eventName.equals(QemuKvmStrings.VMSYNC_GH_HOST)) { |
204 | return; | |
205 | } | |
206 | ||
207 | final ITmfEventField content = event.getContent(); | |
208 | final long ts = event.getTimestamp().getValue(); | |
209 | final String hostId = event.getTrace().getHostId(); | |
210 | ||
b3867ecc MAL |
211 | final Integer cpu = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event); |
212 | if (cpu == null) { | |
4a74f111 MG |
213 | /* We couldn't find any CPU information, ignore this event */ |
214 | return; | |
215 | } | |
216 | ||
217 | /* Find a virtual machine with the vm uid payload value */ | |
218 | ITmfEventField data = content.getField(QemuKvmStrings.VM_UID_PAYLOAD); | |
219 | if (data == null) { | |
220 | return; | |
221 | } | |
222 | long vmUid = (Long) data.getValue(); | |
223 | for (Entry<String, VirtualMachine> entry : fKnownMachines.entrySet()) { | |
224 | if (entry.getValue().getVmUid() == vmUid) { | |
225 | /* | |
226 | * We found the VM being run, let's associate it with the thread | |
227 | * ID | |
228 | */ | |
6d16f5a9 | 229 | KernelAnalysisModule module = getLttngKernelModuleFor(hostId); |
4a74f111 MG |
230 | if (module == null) { |
231 | break; | |
232 | } | |
e363eae1 | 233 | Integer tid = KernelThreadInformationProvider.getThreadOnCpu(module, cpu, ts); |
4a74f111 MG |
234 | if (tid == null) { |
235 | /* | |
236 | * We do not know which process is running at this point. It | |
237 | * may happen at the beginning of the trace. | |
238 | */ | |
239 | break; | |
240 | } | |
241 | HostThread ht = new HostThread(hostId, tid); | |
242 | fTidToVm.put(ht, entry.getValue()); | |
243 | ||
244 | /* | |
245 | * To make sure siblings are also associated with this VM, also | |
246 | * add an entry for the parent TID | |
247 | */ | |
e363eae1 | 248 | Integer ppid = KernelThreadInformationProvider.getParentPid(module, tid, ts); |
4a74f111 MG |
249 | if (ppid != null) { |
250 | HostThread parentHt = new HostThread(hostId, ppid); | |
251 | fTidToVm.put(parentHt, entry.getValue()); | |
252 | } | |
253 | } | |
254 | } | |
255 | } | |
256 | ||
6d16f5a9 AM |
257 | private @Nullable KernelAnalysisModule getLttngKernelModuleFor(String hostId) { |
258 | return TmfExperimentUtils.getAnalysisModuleOfClassForHost(fExperiment, hostId, KernelAnalysisModule.class); | |
4a74f111 MG |
259 | } |
260 | ||
261 | } |