blob: 4c52927d3543603ae8b8bc421e310075937e84aa [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.overhead.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.model.HostThread;
import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout;
import org.eclipse.tracecompass.incubator.analysis.core.model.IHostModel;
import org.eclipse.tracecompass.incubator.analysis.core.model.ModelManager;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.InstrumentedCallStackAnalysis;
import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.IVirtualMachineEventHandler;
import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.model.IVirtualEnvironmentModel;
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.overhead.VmOverheadAnalysis;
import org.eclipse.tracecompass.incubator.internal.virtual.machine.analysis.core.overhead.VmOverheadStateProvider;
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.event.aspect.TmfCpuAspect;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
/**
* Handle sched switches
*
* @author Geneviève Bastien
*/
public class SchedSwitchEventHandler implements IVirtualMachineEventHandler {
private Map<IKernelAnalysisEventLayout, Set<String>> fRequiredEvents = new HashMap<>();
private final Map<HostThread, OverheadStatus> fPrevStatus = new HashMap<>();
private final VmOverheadStateProvider fProvider;
private class OverheadStatus {
private final @Nullable Object fLevel2;
private final @Nullable Object fLevel3;
public OverheadStatus(@Nullable Object level2, @Nullable Object level3) {
fLevel2 = level2;
fLevel3 = level3;
}
}
/**
* Constructor
* @param provider The state provider running the analysis
*/
public SchedSwitchEventHandler(VmOverheadStateProvider provider) {
fProvider = provider;
}
@Override
public Set<String> getRequiredEvents(IKernelAnalysisEventLayout layout) {
Set<String> events = fRequiredEvents.get(layout);
if (events == null) {
events = new HashSet<>();
events.add(layout.eventSchedSwitch());
fRequiredEvents.put(layout, events);
}
return events;
}
@Override
public void handleEvent(ITmfStateSystemBuilder ss, ITmfEvent event, IVirtualEnvironmentModel virtEnv, IKernelAnalysisEventLayout eventLayout) {
VirtualMachine currentMachine = virtEnv.getCurrentMachine(event);
/*
* If sched switch is from a guest, just update the status of the virtual CPU to
* either idle or running
*/
if (currentMachine.isHost()) {
handleHostSchedSwitch(ss, event, virtEnv, eventLayout);
}
if (currentMachine.isGuest()) {
handleGuestSchedSwitch(ss, event, virtEnv, eventLayout, currentMachine);
}
}
/*
* If the previous or next TID is one of a vcpu, update the preemption status of
* the thread running on that CPU in the guest
*/
private void handleHostSchedSwitch(ITmfStateSystemBuilder ss, ITmfEvent event, IVirtualEnvironmentModel virtEnv, IKernelAnalysisEventLayout eventLayout) {
final ITmfEventField content = event.getContent();
final String hostId = event.getTrace().getHostId();
final long ts = event.getTimestamp().getValue();
int prevTid = ((Long) content.getField(eventLayout.fieldPrevTid()).getValue()).intValue();
int nextTid = ((Long) content.getField(eventLayout.fieldNextTid()).getValue()).intValue();
Long prevState = content.getFieldValue(Long.class, eventLayout.fieldPrevState());
/* Verify if the previous thread corresponds to a virtual CPU */
/*
* If previous thread is virtual CPU, update status of the virtual CPU to
* preempted
*/
VirtualCPU vcpu = prevTid == 0 ? null : virtEnv.getVirtualCpu(event, new HostThread(hostId, prevTid));
if (vcpu != null) {
VirtualMachine vm = vcpu.getVm();
IHostModel model = ModelManager.getModelFor(vm.getHostId());
int guestTid = model.getThreadOnCpu(vcpu.getCpuId().intValue(), ts);
if (guestTid != IHostModel.UNKNOWN_TID) {
int quark = ss.getQuarkAbsoluteAndAdd(VmOverheadStateProvider.TRACES, vm.getTraceName(), VmOverheadStateProvider.THREADS, VmOverheadStateProvider.buildThreadAttributeName(guestTid, vcpu.getCpuId().intValue()),
InstrumentedCallStackAnalysis.CALL_STACK);
// Just make sure this attribute exists, at the beginning of trace or if lost
// events, it may not
int tidQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_1);
if (ss.queryOngoing(tidQuark) == null) {
HostThread hostThread = new HostThread(event.getTrace().getHostId(), guestTid);
fProvider.createGuestThreadStatus(ss, hostThread, ts, tidQuark);
}
int preemptQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_2);
int statusQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_3);
// Save the previous statuses for this thread
fPrevStatus.put(new HostThread(vm.getHostId(), guestTid), new OverheadStatus(ss.queryOngoing(preemptQuark), ss.queryOngoing(statusQuark)));
ss.modifyAttribute(ts, VmOverheadStateProvider.STATUS_VCPU_PREEMPTED, preemptQuark);
if (prevState != null) {
ss.modifyAttribute(ts, String.valueOf(prevState), statusQuark);
}
}
}
vcpu = nextTid == 0 ? null : virtEnv.getVirtualCpu(event, new HostThread(hostId, nextTid));
if (vcpu != null) {
VirtualMachine vm = vcpu.getVm();
IHostModel model = ModelManager.getModelFor(vm.getHostId());
int guestTid = model.getThreadOnCpu(vcpu.getCpuId().intValue(), ts);
if (guestTid != IHostModel.UNKNOWN_TID) {
int quark = ss.getQuarkAbsoluteAndAdd(VmOverheadStateProvider.TRACES, vm.getTraceName(), VmOverheadStateProvider.THREADS, VmOverheadStateProvider.buildThreadAttributeName(guestTid, vcpu.getCpuId().intValue()),
InstrumentedCallStackAnalysis.CALL_STACK);
int tidQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_1);
if (ss.queryOngoing(tidQuark) == null) {
ss.modifyAttribute(ts, VmOverheadStateProvider.STATUS_RUNNING, tidQuark);
}
int preemptQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_2);
int statusQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_3);
OverheadStatus overheadStatus = fPrevStatus.remove(new HostThread(vm.getHostId(), guestTid));
if (overheadStatus == null) {
ss.removeAttribute(ts, preemptQuark);
ss.removeAttribute(ts, statusQuark);
} else {
ss.modifyAttribute(ts, overheadStatus.fLevel2, preemptQuark);
ss.modifyAttribute(ts, overheadStatus.fLevel3, statusQuark);
}
}
}
}
private void handleGuestSchedSwitch(ITmfStateSystemBuilder ss, ITmfEvent event, IVirtualEnvironmentModel virtEnv, IKernelAnalysisEventLayout eventLayout, VirtualMachine host) {
final ITmfEventField content = event.getContent();
final long ts = event.getTimestamp().getValue();
int prevTid = ((Long) content.getField(eventLayout.fieldPrevTid()).getValue()).intValue();
int nextTid = ((Long) content.getField(eventLayout.fieldNextTid()).getValue()).intValue();
Object cpuObj = TmfTraceUtils.resolveEventAspectOfClassForEvent(event.getTrace(), TmfCpuAspect.class, event);
int cpu = (cpuObj == null ? -1 : (Integer) cpuObj);
// Handle the process being scheduled out
int baseQuark = ss.getQuarkAbsoluteAndAdd(VmOverheadStateProvider.TRACES, event.getTrace().getName(), VmOverheadStateProvider.THREADS, VmOverheadStateProvider.buildThreadAttributeName(prevTid, cpu));
// Remove host tid running the previous thread
int quark = ss.getQuarkRelativeAndAdd(baseQuark, VmOverheadAnalysis.HOST_CPU_TID);
ss.removeAttribute(ts, quark);
// The thread is not running anymore, remove all stack
quark = ss.getQuarkRelativeAndAdd(baseQuark, InstrumentedCallStackAnalysis.CALL_STACK);
int tidQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_1);
ss.removeAttribute(ts, tidQuark);
int preemptQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_2);
ss.removeAttribute(ts, preemptQuark);
int statusQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_3);
ss.removeAttribute(ts, statusQuark);
// Stop handling the guest thread status for previous thread
fProvider.removeGuestThreadStatus(new HostThread(event.getTrace().getHostId(), prevTid));
// Handle the next thread
baseQuark = ss.getQuarkAbsoluteAndAdd(VmOverheadStateProvider.TRACES, event.getTrace().getName(), VmOverheadStateProvider.THREADS, VmOverheadStateProvider.buildThreadAttributeName(nextTid, cpu));
if (cpu >= 0) {
// Find the host thread of the current cpu and set it as host thread
VirtualCPU virtualCPU = VirtualCPU.getVirtualCPU(host, Long.valueOf(cpu));
HostThread vcpuTid = virtEnv.getVirtualCpuTid(virtualCPU);
if (vcpuTid != null) {
quark = ss.getQuarkRelativeAndAdd(baseQuark, VmOverheadAnalysis.HOST_CPU_TID);
ss.modifyAttribute(ts, vcpuTid.getTid(), quark);
}
}
quark = ss.getQuarkRelativeAndAdd(baseQuark, InstrumentedCallStackAnalysis.CALL_STACK);
tidQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_1);
if (ss.queryOngoing(tidQuark) == null) {
ss.modifyAttribute(ts, VmOverheadStateProvider.STATUS_RUNNING, tidQuark);
}
preemptQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_2);
ss.removeAttribute(ts, preemptQuark);
statusQuark = ss.getQuarkRelativeAndAdd(quark, VmOverheadStateProvider.LEVEL_3);
ss.removeAttribute(ts, statusQuark);
// Create the iterator to update guest thread statuses
HostThread hostThread = new HostThread(event.getTrace().getHostId(), nextTid);
fProvider.createGuestThreadStatus(ss, hostThread, ts, tidQuark);
}
}