blob: 8c02a8ecb29027f956ed1ee648a5461040a03d95 [file] [log] [blame]
/*******************************************************************************
* 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;
}
}