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 | *******************************************************************************/ | |
12 | ||
13 | package org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.module; | |
14 | ||
d0c7e4ba AM |
15 | import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
16 | ||
4a74f111 MG |
17 | import java.util.HashMap; |
18 | import java.util.Map; | |
19 | ||
20 | import org.eclipse.jdt.annotation.Nullable; | |
0f7a12d3 AM |
21 | import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule; |
22 | import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelThreadInformationProvider; | |
a6145763 | 23 | import org.eclipse.tracecompass.analysis.os.linux.core.model.HostThread; |
e363eae1 | 24 | import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout; |
4a74f111 MG |
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; | |
4a74f111 MG |
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; | |
4a74f111 | 32 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.trace.layout.LttngEventLayout; |
4a74f111 | 33 | import org.eclipse.tracecompass.lttng2.kernel.core.trace.LttngKernelTrace; |
d0c7e4ba | 34 | import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; |
4a74f111 MG |
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; | |
48 | ||
49 | import com.google.common.collect.HashBasedTable; | |
50 | import com.google.common.collect.Table; | |
51 | ||
52 | /** | |
53 | * This is the state provider which translates the virtual machine experiment | |
54 | * events to a state system. | |
55 | * | |
56 | * Attribute tree: | |
57 | * | |
58 | * <pre> | |
59 | * |- Virtual Machines | |
60 | * | |- <Guest Host ID> -> Friendly name (trace name) | |
61 | * | | |- <VCPU number> | |
62 | * | | | |- Status -> <Status value> | |
63 | * </pre> | |
64 | * | |
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. | |
70 | * | |
71 | * @author Mohamad Gebai | |
72 | */ | |
73 | public class VirtualMachineStateProvider extends AbstractTmfStateProvider { | |
74 | ||
75 | /** | |
76 | * Version number of this state provider. Please bump this if you modify the | |
77 | * contents of the generated state history in some way. | |
78 | */ | |
79 | private static final int VERSION = 1; | |
80 | ||
81 | private static final int SCHED_SWITCH_INDEX = 0; | |
82 | ||
83 | /* TODO: An analysis should support many hypervisor models */ | |
84 | private IVirtualMachineModel fModel; | |
4c4e2816 | 85 | private final Table<ITmfTrace, String, @Nullable Integer> fEventNames; |
4a74f111 MG |
86 | private final Map<ITmfTrace, IKernelAnalysisEventLayout> fLayouts; |
87 | ||
88 | // ------------------------------------------------------------------------ | |
89 | // Constructor | |
90 | // ------------------------------------------------------------------------ | |
91 | ||
92 | /** | |
93 | * Constructor | |
94 | * | |
95 | * @param experiment | |
96 | * The virtual machine experiment | |
97 | */ | |
98 | public VirtualMachineStateProvider(TmfExperiment experiment) { | |
e2bcc8a5 | 99 | super(experiment, "Virtual Machine State Provider"); //$NON-NLS-1$ |
4a74f111 MG |
100 | |
101 | fModel = new QemuKvmVmModel(experiment); | |
dc303fab | 102 | Table<ITmfTrace, String, @Nullable Integer> table = HashBasedTable.create(); |
4a74f111 MG |
103 | fEventNames = table; |
104 | fLayouts = new HashMap<>(); | |
105 | } | |
106 | ||
107 | // ------------------------------------------------------------------------ | |
108 | // Event names management | |
109 | // ------------------------------------------------------------------------ | |
110 | ||
111 | private void buildEventNames(ITmfTrace trace) { | |
112 | IKernelAnalysisEventLayout layout; | |
113 | if (trace instanceof LttngKernelTrace) { | |
e363eae1 | 114 | layout = ((LttngKernelTrace) trace).getKernelEventLayout(); |
4a74f111 MG |
115 | } else { |
116 | /* Fall-back to the base LttngEventLayout */ | |
117 | layout = LttngEventLayout.getInstance(); | |
118 | } | |
119 | fLayouts.put(trace, layout); | |
120 | fEventNames.put(trace, layout.eventSchedSwitch(), SCHED_SWITCH_INDEX); | |
121 | } | |
122 | ||
123 | // ------------------------------------------------------------------------ | |
124 | // IStateChangeInput | |
125 | // ------------------------------------------------------------------------ | |
126 | ||
127 | @Override | |
128 | public TmfExperiment getTrace() { | |
129 | ITmfTrace trace = super.getTrace(); | |
130 | if (trace instanceof TmfExperiment) { | |
131 | return (TmfExperiment) trace; | |
132 | } | |
133 | throw new IllegalStateException("VirtualMachineStateProvider: The associated trace should be an experiment"); //$NON-NLS-1$ | |
134 | } | |
135 | ||
136 | @Override | |
137 | public int getVersion() { | |
138 | return VERSION; | |
139 | } | |
140 | ||
141 | @Override | |
142 | public VirtualMachineStateProvider getNewInstance() { | |
143 | TmfExperiment trace = getTrace(); | |
144 | return new VirtualMachineStateProvider(trace); | |
145 | } | |
146 | ||
147 | @Override | |
148 | protected void eventHandle(@Nullable ITmfEvent event) { | |
149 | if (event == null) { | |
150 | return; | |
151 | } | |
152 | ||
153 | /* Is the event managed by this analysis */ | |
578716e6 | 154 | final String eventName = event.getName(); |
4a74f111 MG |
155 | |
156 | /* TODO When requirements work again, don't hardcode this */ | |
157 | if (!eventName.equals("sched_switch") && //$NON-NLS-1$ | |
158 | !fModel.getRequiredEvents().contains(eventName)) { | |
159 | return; | |
160 | } | |
161 | ||
d0c7e4ba | 162 | ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder()); |
4a74f111 MG |
163 | ITmfStateValue value; |
164 | ||
165 | final ITmfEventField content = event.getContent(); | |
166 | final long ts = event.getTimestamp().getValue(); | |
167 | final String hostId = event.getTrace().getHostId(); | |
168 | try { | |
169 | /* Do we know this trace's role yet? */ | |
170 | VirtualMachine host = fModel.getCurrentMachine(event); | |
171 | if (host == null) { | |
172 | return; | |
173 | } | |
174 | ||
175 | /* Make sure guest traces are added to the state system */ | |
176 | if (host.isGuest()) { | |
177 | /* | |
178 | * If event from a guest OS, make sure the guest exists in the | |
179 | * state system | |
180 | */ | |
181 | int vmQuark = -1; | |
182 | try { | |
183 | vmQuark = ss.getQuarkRelative(getNodeVirtualMachines(), host.getHostId()); | |
184 | } catch (AttributeNotFoundException e) { | |
185 | /* | |
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 | |
189 | */ | |
190 | vmQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host.getHostId()); | |
191 | TmfStateValue machineName = TmfStateValue.newValueString(event.getTrace().getName()); | |
192 | ss.modifyAttribute(ts, machineName, vmQuark); | |
193 | } | |
194 | } | |
195 | ||
196 | /* Have the hypervisor models handle the event first */ | |
197 | fModel.handleEvent(event); | |
198 | ||
199 | /* Handle the event here */ | |
200 | if (!fEventNames.containsRow(event.getTrace())) { | |
201 | buildEventNames(event.getTrace()); | |
202 | } | |
203 | Integer idx = fEventNames.get(event.getTrace(), eventName); | |
204 | int intval = (idx == null ? -1 : idx.intValue()); | |
205 | switch (intval) { | |
206 | case SCHED_SWITCH_INDEX: // "sched_switch": | |
207 | /* | |
208 | * Fields: string prev_comm, int32 prev_tid, int32 prev_prio, int64 | |
209 | * prev_state, string next_comm, int32 next_tid, int32 next_prio | |
210 | */ | |
211 | { | |
94411c58 AM |
212 | final IKernelAnalysisEventLayout eventLayout = fLayouts.get(event.getTrace()); |
213 | if (eventLayout == null) { | |
214 | return; | |
215 | } | |
216 | int prevTid = ((Long) content.getField(eventLayout.fieldPrevTid()).getValue()).intValue(); | |
217 | int nextTid = ((Long) content.getField(eventLayout.fieldNextTid()).getValue()).intValue(); | |
4a74f111 MG |
218 | |
219 | if (host.isGuest()) { | |
220 | /* Get the event's CPU */ | |
b3867ecc MAL |
221 | Integer cpu = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event); |
222 | if (cpu == null) { | |
52efabb6 CB |
223 | /* |
224 | * We couldn't find any CPU information, ignore this | |
225 | * event | |
226 | */ | |
4a74f111 MG |
227 | break; |
228 | } | |
229 | ||
230 | /* | |
231 | * If sched switch is from a guest, just update the status | |
232 | * of the virtual CPU to either idle or running | |
233 | */ | |
234 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host.getHostId(), | |
235 | cpu.toString(), VmAttributes.STATUS); | |
236 | value = TmfStateValue.newValueInt(VcpuStateValues.VCPU_IDLE); | |
237 | if (nextTid > 0) { | |
238 | value = TmfStateValue.newValueInt(VcpuStateValues.VCPU_RUNNING); | |
239 | } | |
240 | ss.modifyAttribute(ts, value, curStatusQuark); | |
241 | break; | |
242 | } | |
243 | ||
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); | |
248 | ||
249 | /* | |
250 | * If previous thread is virtual CPU, update status of the | |
251 | * virtual CPU to preempted | |
252 | */ | |
253 | if (vcpu != null) { | |
254 | VirtualMachine vm = vcpu.getVm(); | |
255 | ||
256 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm.getHostId(), | |
257 | vcpu.getCpuId().toString(), VmAttributes.STATUS); | |
258 | ||
259 | /* Add the preempted flag to the status */ | |
260 | value = ss.queryOngoingState(curStatusQuark); | |
52efabb6 CB |
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); | |
265 | } | |
4a74f111 MG |
266 | } |
267 | ||
268 | /* Verify if the next thread corresponds to a virtual CPU */ | |
269 | ht = new HostThread(hostId, nextTid); | |
270 | vcpu = fModel.getVirtualCpu(ht); | |
271 | ||
272 | /* | |
273 | * If next thread is virtual CPU, update status of the virtual | |
274 | * CPU the previous status | |
275 | */ | |
276 | if (vcpu != null) { | |
277 | VirtualMachine vm = vcpu.getVm(); | |
278 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm.getHostId(), | |
279 | vcpu.getCpuId().toString(), VmAttributes.STATUS); | |
280 | ||
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); | |
286 | ||
287 | } | |
288 | ||
289 | } | |
290 | break; | |
291 | ||
292 | default: | |
293 | /* Other events not covered by the main switch */ | |
294 | { | |
295 | HostThread ht = getCurrentHostThread(event, ts); | |
296 | if (ht == null) { | |
297 | break; | |
298 | } | |
299 | ||
300 | /* | |
301 | * Are we entering the hypervisor mode and if so, which virtual | |
302 | * CPU is concerned? | |
303 | */ | |
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); | |
52efabb6 CB |
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); | |
315 | } | |
4a74f111 MG |
316 | } |
317 | ||
318 | /* | |
319 | * Are we exiting the hypervisor mode and if so, which virtual | |
320 | * CPU is concerned? | |
321 | */ | |
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); | |
332 | } | |
333 | ||
334 | } | |
335 | break; | |
336 | } | |
337 | ||
338 | } catch (AttributeNotFoundException | TimeRangeException | StateValueTypeException e) { | |
339 | Activator.getDefault().logError("Error handling event in VirtualMachineStateProvider", e); //$NON-NLS-1$ | |
340 | } | |
341 | } | |
342 | ||
343 | // ------------------------------------------------------------------------ | |
344 | // Convenience methods for commonly-used attribute tree locations | |
345 | // ------------------------------------------------------------------------ | |
346 | ||
347 | private int getNodeVirtualMachines() { | |
d0c7e4ba | 348 | return checkNotNull(getStateSystemBuilder()).getQuarkAbsoluteAndAdd(VmAttributes.VIRTUAL_MACHINES); |
4a74f111 MG |
349 | } |
350 | ||
351 | private @Nullable HostThread getCurrentHostThread(ITmfEvent event, long ts) { | |
352 | /* Get the LTTng kernel analysis for the host */ | |
353 | String hostId = event.getTrace().getHostId(); | |
6d16f5a9 | 354 | KernelAnalysisModule module = TmfExperimentUtils.getAnalysisModuleOfClassForHost(getTrace(), hostId, KernelAnalysisModule.class); |
4a74f111 MG |
355 | if (module == null) { |
356 | return null; | |
357 | } | |
358 | ||
359 | /* Get the CPU the event is running on */ | |
b3867ecc MAL |
360 | Integer cpu = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event); |
361 | if (cpu == null) { | |
4a74f111 MG |
362 | /* We couldn't find any CPU information, ignore this event */ |
363 | return null; | |
364 | } | |
365 | ||
e363eae1 | 366 | Integer currentTid = KernelThreadInformationProvider.getThreadOnCpu(module, cpu, ts); |
4a74f111 MG |
367 | if (currentTid == null) { |
368 | return null; | |
369 | } | |
370 | return new HostThread(hostId, currentTid); | |
371 | } | |
372 | ||
4c4e2816 | 373 | } |