| /******************************************************************************* |
| * Copyright (c) 2018 École Polytechnique de Montréal |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.model.handlers; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.analysis.os.linux.core.event.aspect.LinuxPidAspect; |
| import org.eclipse.tracecompass.analysis.os.linux.core.model.HostThread; |
| import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.IVirtualMachineEventHandler; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.model.VirtualCPU; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.model.VirtualMachine; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.model.analysis.VirtualEnvironmentBuilder; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.model.analysis.VirtualMachineModelAnalysis; |
| import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.model.qemukvm.QemuKvmStrings; |
| import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; |
| import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils; |
| |
| import com.google.common.collect.ImmutableSet; |
| |
| /** |
| * Handle events coming from a qemu/kvm hypervisor |
| * |
| * @author Geneviève Bastien |
| */ |
| public class QemuKvmEventHandler implements IVirtualMachineModelBuilderEventHandler { |
| |
| private Map<IKernelAnalysisEventLayout, Set<String>> fRequiredEvents = new HashMap<>(); |
| |
| private static final ImmutableSet<String> VMSYNC_EVENTS = ImmutableSet.of( |
| QemuKvmStrings.VMSYNC_GH_GUEST, |
| QemuKvmStrings.VMSYNC_GH_HOST, |
| QemuKvmStrings.VMSYNC_HG_GUEST, |
| QemuKvmStrings.VMSYNC_HG_HOST); |
| |
| /** |
| * Constructor |
| */ |
| public QemuKvmEventHandler() { |
| } |
| |
| @Override |
| public Set<String> getRequiredEvents(IKernelAnalysisEventLayout layout) { |
| Set<String> events = fRequiredEvents.get(layout); |
| if (events == null) { |
| events = new HashSet<>(); |
| events.addAll(layout.eventsKVMEntry()); |
| events.addAll(layout.eventsKVMExit()); |
| events.addAll(VMSYNC_EVENTS); |
| fRequiredEvents.put(layout, events); |
| } |
| return events; |
| } |
| |
| /** |
| * Handle the event for a Qemu/kvm model |
| * |
| * @param ss |
| * The state system builder to fill |
| * @param event |
| * The event to handle |
| */ |
| @Override |
| public void handleBuilderEvent(ITmfStateSystemBuilder ss, ITmfEvent event, VirtualEnvironmentBuilder virtEnv, IKernelAnalysisEventLayout layout) { |
| String eventName = event.getName(); |
| VirtualMachine machine = virtEnv.getCurrentMachineBuild(event); |
| long ts = event.getTimestamp().toNanos(); |
| if (layout.eventsKVMEntry().contains(eventName)) { |
| setMachineHost(ss, machine); |
| handleKvmEntry(ss, ts, event, virtEnv, machine); |
| } else if (layout.eventsKVMExit().contains(eventName)) { |
| setMachineHost(ss, machine); |
| } else if (eventName.equals(QemuKvmStrings.VMSYNC_GH_GUEST) || eventName.equals(QemuKvmStrings.VMSYNC_HG_GUEST)) { |
| if (machine.isGuest()) { |
| // There's nothing more we can learn from these events, return |
| return; |
| } |
| handleGuestEvent(event, machine); |
| } else if (eventName.equals(QemuKvmStrings.VMSYNC_GH_HOST)) { |
| setMachineHost(ss, machine); |
| handleHostEvent(ss, ts, event, virtEnv, machine); |
| } |
| } |
| |
| private static void handleHostEvent(ITmfStateSystemBuilder ss, long ts, ITmfEvent event, VirtualEnvironmentBuilder virtEnv, VirtualMachine hostMachine) { |
| HostThread ht = IVirtualMachineEventHandler.getCurrentHostThread(event, ts); |
| if (ht == null) { |
| return; |
| } |
| |
| VirtualMachine vm = virtEnv.getGuestMachineBuild(event, ht); |
| if (vm != null) { |
| // Machine is already known, exit |
| return; |
| } |
| |
| Long vmUid = event.getContent().getFieldValue(Long.class, QemuKvmStrings.VM_UID_PAYLOAD); |
| if (vmUid == null) { |
| return; |
| } |
| |
| for (VirtualMachine machine : virtEnv.getMachines()) { |
| if (machine.getVmUid() == vmUid) { |
| /* |
| * We found the VM being run, let's associate it with the |
| * thread ID and set its hypervisor |
| */ |
| hostMachine.addChild(machine); |
| int guestQuark = ss.getQuarkAbsoluteAndAdd(hostMachine.getHostId(), VirtualMachineModelAnalysis.GUEST_VMS, machine.getHostId()); |
| ss.modifyAttribute(ts, machine.getTraceName(), guestQuark); |
| |
| virtEnv.setGuestMachine(machine, ht); |
| |
| int hypervisorQuark = ss.getQuarkRelativeAndAdd(guestQuark, VirtualMachineModelAnalysis.HYPERVISOR); |
| ss.modifyAttribute(ts, "Qemu/KVM", hypervisorQuark); //$NON-NLS-1$ |
| |
| // we have the thread running, its associated process represents the guest |
| Integer pid = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), LinuxPidAspect.class, event); |
| if (pid == null) { |
| return; |
| } |
| HostThread parentHt = new HostThread(ht.getHost(), pid); |
| // Update guest process if not known |
| if (virtEnv.getGuestMachineBuild(event, parentHt) == null) { |
| virtEnv.setGuestMachine(machine, parentHt); |
| int guestProcessQuark = ss.getQuarkRelativeAndAdd(guestQuark, VirtualMachineModelAnalysis.PROCESS); |
| ss.modifyAttribute(ts, pid, guestProcessQuark); |
| } |
| } |
| } |
| } |
| |
| private static void handleGuestEvent(ITmfEvent event, VirtualMachine machine) { |
| // Set the machine as guest and add its uid |
| Long uid = event.getContent().getFieldValue(Long.class, QemuKvmStrings.VM_UID_PAYLOAD); |
| if (uid != null) { |
| machine.setGuest(uid); |
| } |
| } |
| |
| private static void setMachineHost(ITmfStateSystemBuilder ss, VirtualMachine machine) { |
| // Machine is already a host, ignore |
| if (machine.isHost()) { |
| return; |
| } |
| // Set the machine as host and add the quark for guests |
| machine.setHost(); |
| ss.getQuarkAbsoluteAndAdd(machine.getHostId(), VirtualMachineModelAnalysis.GUEST_VMS); |
| } |
| |
| private static void handleKvmEntry(ITmfStateSystemBuilder ss, long ts, ITmfEvent event, VirtualEnvironmentBuilder virtEnv, VirtualMachine machine) { |
| HostThread ht = IVirtualMachineEventHandler.getCurrentHostThread(event, ts); |
| if (ht == null) { |
| return; |
| } |
| VirtualCPU vcpu = virtEnv.getVirtualCpuBuild(event, ht); |
| if (vcpu != null) { |
| // The current thread has a vcpu configured, ignore |
| return; |
| } |
| // Try to find the guest that corresponds to this one |
| VirtualMachine vm = virtEnv.getGuestMachineBuild(event, ht); |
| if (vm == null) { |
| vm = findVmFromProcess(event, ht, virtEnv); |
| if (vm == null) { |
| return; |
| } |
| } |
| /* Associate this thread with the virtual CPU that is going to be run */ |
| final ITmfEventField content = event.getContent(); |
| Long vcpuId = content.getFieldValue(Long.class, QemuKvmStrings.VCPU_ID); |
| if (vcpuId == null) { |
| return; |
| } |
| VirtualCPU virtualCPU = VirtualCPU.getVirtualCPU(vm, vcpuId); |
| virtEnv.setGuestCpu(virtualCPU, ht); |
| int vcpuQuark = ss.getQuarkAbsoluteAndAdd(machine.getHostId(), VirtualMachineModelAnalysis.GUEST_VMS, vm.getHostId(), VirtualMachineModelAnalysis.CPUS, vcpuId.toString()); |
| ss.modifyAttribute(ts, ht.getTid(), vcpuQuark); |
| } |
| |
| private @Nullable static VirtualMachine findVmFromProcess(ITmfEvent event, HostThread ht, VirtualEnvironmentBuilder virtEnv) { |
| /* |
| * Maybe the process of the current thread has a VM associated, see if we |
| * can infer the VM for this thread |
| */ |
| Integer pid = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), LinuxPidAspect.class, event); |
| if (pid == null) { |
| return null; |
| } |
| HostThread parentHt = new HostThread(ht.getHost(), pid); |
| VirtualMachine vm = virtEnv.getGuestMachineBuild(event, parentHt); |
| if (vm != null) { |
| virtEnv.setGuestMachine(vm, ht); |
| } |
| |
| return vm; |
| } |
| |
| } |