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