Commit | Line | Data |
---|---|---|
4a74f111 MG |
1 | /******************************************************************************* |
2 | * Copyright (c) 2014 École Polytechnique de Montréal | |
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 | ||
15 | import java.util.HashMap; | |
16 | import java.util.Map; | |
17 | ||
18 | import org.eclipse.jdt.annotation.Nullable; | |
19 | import org.eclipse.tracecompass.common.core.NonNullUtils; | |
20 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.Activator; | |
21 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.VcpuStateValues; | |
22 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.VmAttributes; | |
23 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.model.HostThread; | |
24 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.model.IVirtualMachineModel; | |
25 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.model.VirtualCPU; | |
26 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.model.VirtualMachine; | |
27 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.analysis.vm.model.qemukvm.QemuKvmVmModel; | |
28 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.trace.layout.IKernelAnalysisEventLayout; | |
29 | import org.eclipse.tracecompass.internal.lttng2.kernel.core.trace.layout.LttngEventLayout; | |
30 | import org.eclipse.tracecompass.lttng2.kernel.core.analysis.kernel.LttngKernelAnalysis; | |
31 | import org.eclipse.tracecompass.lttng2.kernel.core.analysis.kernel.LttngKernelThreadInformationProvider; | |
32 | import org.eclipse.tracecompass.lttng2.kernel.core.trace.LttngKernelTrace; | |
33 | import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; | |
34 | import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException; | |
35 | import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException; | |
36 | import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; | |
37 | import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue; | |
38 | import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; | |
39 | import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; | |
40 | import org.eclipse.tracecompass.tmf.core.event.aspect.TmfCpuAspect; | |
41 | import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider; | |
42 | import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; | |
43 | import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils; | |
44 | import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment; | |
45 | import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperimentUtils; | |
46 | ||
47 | import com.google.common.collect.HashBasedTable; | |
48 | import com.google.common.collect.Table; | |
49 | ||
50 | /** | |
51 | * This is the state provider which translates the virtual machine experiment | |
52 | * events to a state system. | |
53 | * | |
54 | * Attribute tree: | |
55 | * | |
56 | * <pre> | |
57 | * |- Virtual Machines | |
58 | * | |- <Guest Host ID> -> Friendly name (trace name) | |
59 | * | | |- <VCPU number> | |
60 | * | | | |- Status -> <Status value> | |
61 | * </pre> | |
62 | * | |
63 | * The status value of the VCPUs are either {@link VcpuStateValues#VCPU_IDLE}, | |
64 | * {@link VcpuStateValues#VCPU_UNKNOWN} or {@link VcpuStateValues#VCPU_RUNNING}. | |
65 | * Those three values are ORed with flags {@link VcpuStateValues#VCPU_VMM} | |
66 | * and/or {@link VcpuStateValues#VCPU_PREEMPT} to indicate respectively whether | |
67 | * they are in hypervisor mode or preempted on the host. | |
68 | * | |
69 | * @author Mohamad Gebai | |
70 | */ | |
71 | public class VirtualMachineStateProvider extends AbstractTmfStateProvider { | |
72 | ||
73 | /** | |
74 | * Version number of this state provider. Please bump this if you modify the | |
75 | * contents of the generated state history in some way. | |
76 | */ | |
77 | private static final int VERSION = 1; | |
78 | ||
79 | private static final int SCHED_SWITCH_INDEX = 0; | |
80 | ||
81 | /* TODO: An analysis should support many hypervisor models */ | |
82 | private IVirtualMachineModel fModel; | |
83 | private final Table<ITmfTrace, String, Integer> fEventNames; | |
84 | private final Map<ITmfTrace, IKernelAnalysisEventLayout> fLayouts; | |
85 | ||
86 | // ------------------------------------------------------------------------ | |
87 | // Constructor | |
88 | // ------------------------------------------------------------------------ | |
89 | ||
90 | /** | |
91 | * Constructor | |
92 | * | |
93 | * @param experiment | |
94 | * The virtual machine experiment | |
95 | */ | |
96 | public VirtualMachineStateProvider(TmfExperiment experiment) { | |
97 | super(experiment, ITmfEvent.class, "Virtual Machine State Provider"); //$NON-NLS-1$ | |
98 | ||
99 | fModel = new QemuKvmVmModel(experiment); | |
100 | Table<ITmfTrace, String, Integer> table = NonNullUtils.checkNotNull(HashBasedTable.<ITmfTrace, String, Integer> create()); | |
101 | fEventNames = table; | |
102 | fLayouts = new HashMap<>(); | |
103 | } | |
104 | ||
105 | // ------------------------------------------------------------------------ | |
106 | // Event names management | |
107 | // ------------------------------------------------------------------------ | |
108 | ||
109 | private void buildEventNames(ITmfTrace trace) { | |
110 | IKernelAnalysisEventLayout layout; | |
111 | if (trace instanceof LttngKernelTrace) { | |
112 | layout = ((LttngKernelTrace) trace).getEventLayout(); | |
113 | } else { | |
114 | /* Fall-back to the base LttngEventLayout */ | |
115 | layout = LttngEventLayout.getInstance(); | |
116 | } | |
117 | fLayouts.put(trace, layout); | |
118 | fEventNames.put(trace, layout.eventSchedSwitch(), SCHED_SWITCH_INDEX); | |
119 | } | |
120 | ||
121 | // ------------------------------------------------------------------------ | |
122 | // IStateChangeInput | |
123 | // ------------------------------------------------------------------------ | |
124 | ||
125 | @Override | |
126 | public TmfExperiment getTrace() { | |
127 | ITmfTrace trace = super.getTrace(); | |
128 | if (trace instanceof TmfExperiment) { | |
129 | return (TmfExperiment) trace; | |
130 | } | |
131 | throw new IllegalStateException("VirtualMachineStateProvider: The associated trace should be an experiment"); //$NON-NLS-1$ | |
132 | } | |
133 | ||
134 | @Override | |
135 | public int getVersion() { | |
136 | return VERSION; | |
137 | } | |
138 | ||
139 | @Override | |
140 | public VirtualMachineStateProvider getNewInstance() { | |
141 | TmfExperiment trace = getTrace(); | |
142 | return new VirtualMachineStateProvider(trace); | |
143 | } | |
144 | ||
145 | @Override | |
146 | protected void eventHandle(@Nullable ITmfEvent event) { | |
147 | if (event == null) { | |
148 | return; | |
149 | } | |
150 | ||
151 | /* Is the event managed by this analysis */ | |
152 | final String eventName = event.getType().getName(); | |
153 | ||
154 | /* TODO When requirements work again, don't hardcode this */ | |
155 | if (!eventName.equals("sched_switch") && //$NON-NLS-1$ | |
156 | !fModel.getRequiredEvents().contains(eventName)) { | |
157 | return; | |
158 | } | |
159 | ||
160 | ITmfStateValue value; | |
161 | ||
162 | final ITmfEventField content = event.getContent(); | |
163 | final long ts = event.getTimestamp().getValue(); | |
164 | final String hostId = event.getTrace().getHostId(); | |
165 | try { | |
166 | /* Do we know this trace's role yet? */ | |
167 | VirtualMachine host = fModel.getCurrentMachine(event); | |
168 | if (host == null) { | |
169 | return; | |
170 | } | |
171 | ||
172 | /* Make sure guest traces are added to the state system */ | |
173 | if (host.isGuest()) { | |
174 | /* | |
175 | * If event from a guest OS, make sure the guest exists in the | |
176 | * state system | |
177 | */ | |
178 | int vmQuark = -1; | |
179 | try { | |
180 | vmQuark = ss.getQuarkRelative(getNodeVirtualMachines(), host.getHostId()); | |
181 | } catch (AttributeNotFoundException e) { | |
182 | /* | |
183 | * We should enter this catch only once per machine, so it | |
184 | * is not so costly to do compared with adding the trace's | |
185 | * name for each guest event | |
186 | */ | |
187 | vmQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host.getHostId()); | |
188 | TmfStateValue machineName = TmfStateValue.newValueString(event.getTrace().getName()); | |
189 | ss.modifyAttribute(ts, machineName, vmQuark); | |
190 | } | |
191 | } | |
192 | ||
193 | /* Have the hypervisor models handle the event first */ | |
194 | fModel.handleEvent(event); | |
195 | ||
196 | /* Handle the event here */ | |
197 | if (!fEventNames.containsRow(event.getTrace())) { | |
198 | buildEventNames(event.getTrace()); | |
199 | } | |
200 | Integer idx = fEventNames.get(event.getTrace(), eventName); | |
201 | int intval = (idx == null ? -1 : idx.intValue()); | |
202 | switch (intval) { | |
203 | case SCHED_SWITCH_INDEX: // "sched_switch": | |
204 | /* | |
205 | * Fields: string prev_comm, int32 prev_tid, int32 prev_prio, int64 | |
206 | * prev_state, string next_comm, int32 next_tid, int32 next_prio | |
207 | */ | |
208 | { | |
209 | Integer prevTid = ((Long) content.getField(fLayouts.get(event.getTrace()).fieldPrevTid()).getValue()).intValue(); | |
210 | Integer nextTid = ((Long) content.getField(fLayouts.get(event.getTrace()).fieldNextTid()).getValue()).intValue(); | |
211 | ||
212 | if (prevTid == null || nextTid == null) { | |
213 | break; | |
214 | } | |
215 | ||
216 | if (host.isGuest()) { | |
217 | /* Get the event's CPU */ | |
218 | Integer cpu = null; | |
219 | Iterable<TmfCpuAspect> aspects = TmfTraceUtils.getEventAspectsOfClass(event.getTrace(), TmfCpuAspect.class); | |
220 | for (TmfCpuAspect aspect : aspects) { | |
221 | if (!aspect.resolve(event).equals(TmfCpuAspect.CPU_UNAVAILABLE)) { | |
222 | cpu = aspect.resolve(event); | |
223 | break; | |
224 | } | |
225 | } | |
226 | if (cpu == null) { | |
227 | /* | |
228 | * We couldn't find any CPU information, ignore this | |
229 | * event | |
230 | */ | |
231 | break; | |
232 | } | |
233 | ||
234 | /* | |
235 | * If sched switch is from a guest, just update the status | |
236 | * of the virtual CPU to either idle or running | |
237 | */ | |
238 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), host.getHostId(), | |
239 | cpu.toString(), VmAttributes.STATUS); | |
240 | value = TmfStateValue.newValueInt(VcpuStateValues.VCPU_IDLE); | |
241 | if (nextTid > 0) { | |
242 | value = TmfStateValue.newValueInt(VcpuStateValues.VCPU_RUNNING); | |
243 | } | |
244 | ss.modifyAttribute(ts, value, curStatusQuark); | |
245 | break; | |
246 | } | |
247 | ||
248 | /* Event is not from a guest */ | |
249 | /* Verify if the previous thread corresponds to a virtual CPU */ | |
250 | HostThread ht = new HostThread(hostId, prevTid); | |
251 | VirtualCPU vcpu = fModel.getVirtualCpu(ht); | |
252 | ||
253 | /* | |
254 | * If previous thread is virtual CPU, update status of the | |
255 | * virtual CPU to preempted | |
256 | */ | |
257 | if (vcpu != null) { | |
258 | VirtualMachine vm = vcpu.getVm(); | |
259 | ||
260 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm.getHostId(), | |
261 | vcpu.getCpuId().toString(), VmAttributes.STATUS); | |
262 | ||
263 | /* Add the preempted flag to the status */ | |
264 | value = ss.queryOngoingState(curStatusQuark); | |
265 | int newVal = Math.max(VcpuStateValues.VCPU_UNKNOWN, value.unboxInt()); | |
266 | value = TmfStateValue.newValueInt(newVal | VcpuStateValues.VCPU_PREEMPT); | |
267 | ss.modifyAttribute(ts, value, curStatusQuark); | |
268 | } | |
269 | ||
270 | /* Verify if the next thread corresponds to a virtual CPU */ | |
271 | ht = new HostThread(hostId, nextTid); | |
272 | vcpu = fModel.getVirtualCpu(ht); | |
273 | ||
274 | /* | |
275 | * If next thread is virtual CPU, update status of the virtual | |
276 | * CPU the previous status | |
277 | */ | |
278 | if (vcpu != null) { | |
279 | VirtualMachine vm = vcpu.getVm(); | |
280 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm.getHostId(), | |
281 | vcpu.getCpuId().toString(), VmAttributes.STATUS); | |
282 | ||
283 | /* Remove the preempted flag from the status */ | |
284 | value = ss.queryOngoingState(curStatusQuark); | |
285 | int newVal = Math.max(VcpuStateValues.VCPU_UNKNOWN, value.unboxInt()); | |
286 | value = TmfStateValue.newValueInt(newVal & ~VcpuStateValues.VCPU_PREEMPT); | |
287 | ss.modifyAttribute(ts, value, curStatusQuark); | |
288 | ||
289 | } | |
290 | ||
291 | } | |
292 | break; | |
293 | ||
294 | default: | |
295 | /* Other events not covered by the main switch */ | |
296 | { | |
297 | HostThread ht = getCurrentHostThread(event, ts); | |
298 | if (ht == null) { | |
299 | break; | |
300 | } | |
301 | ||
302 | /* | |
303 | * Are we entering the hypervisor mode and if so, which virtual | |
304 | * CPU is concerned? | |
305 | */ | |
306 | VirtualCPU virtualCpu = fModel.getVCpuEnteringHypervisorMode(event, ht); | |
307 | if (virtualCpu != null) { | |
308 | /* Add the hypervisor flag to the status */ | |
309 | VirtualMachine vm = virtualCpu.getVm(); | |
310 | int curStatusQuark = ss.getQuarkRelativeAndAdd(getNodeVirtualMachines(), vm.getHostId(), | |
311 | Long.toString(virtualCpu.getCpuId()), VmAttributes.STATUS); | |
312 | value = ss.queryOngoingState(curStatusQuark); | |
313 | int newVal = Math.max(VcpuStateValues.VCPU_UNKNOWN, value.unboxInt()); | |
314 | value = TmfStateValue.newValueInt(newVal | VcpuStateValues.VCPU_VMM); | |
315 | ss.modifyAttribute(ts, value, curStatusQuark); | |
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() { | |
348 | return ss.getQuarkAbsoluteAndAdd(VmAttributes.VIRTUAL_MACHINES); | |
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(); | |
354 | LttngKernelAnalysis module = TmfExperimentUtils.getAnalysisModuleOfClassForHost(getTrace(), hostId, LttngKernelAnalysis.class); | |
355 | if (module == null) { | |
356 | return null; | |
357 | } | |
358 | ||
359 | /* Get the CPU the event is running on */ | |
360 | Integer cpu = null; | |
361 | Iterable<TmfCpuAspect> aspects = TmfTraceUtils.getEventAspectsOfClass(event.getTrace(), TmfCpuAspect.class); | |
362 | for (TmfCpuAspect aspect : aspects) { | |
363 | if (!aspect.resolve(event).equals(TmfCpuAspect.CPU_UNAVAILABLE)) { | |
364 | cpu = aspect.resolve(event); | |
365 | break; | |
366 | } | |
367 | } | |
368 | if (cpu == null) { | |
369 | /* We couldn't find any CPU information, ignore this event */ | |
370 | return null; | |
371 | } | |
372 | ||
373 | Integer currentTid = LttngKernelThreadInformationProvider.getThreadOnCpu(module, cpu, ts); | |
374 | if (currentTid == null) { | |
375 | return null; | |
376 | } | |
377 | return new HostThread(hostId, currentTid); | |
378 | } | |
379 | ||
380 | } |