/******************************************************************************* * Copyright (c) 2015 Ericsson * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Matthew Khouzam - Initial API and implementation *******************************************************************************/ package org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.handlers; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; import org.eclipse.tracecompass.analysis.os.linux.core.kernel.LinuxValues; import org.eclipse.tracecompass.analysis.os.linux.core.kernel.StateValues; import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout; import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attributes; import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder; import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue; import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue; import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; import org.eclipse.tracecompass.tmf.core.event.ITmfEventField; /** * Scheduler switch event handler */ public class SchedSwitchHandler extends KernelEventHandler { /** * Constructor * * @param layout * event layout */ public SchedSwitchHandler(IKernelAnalysisEventLayout layout) { super(layout); } @Override public void handleEvent(ITmfStateSystemBuilder ss, ITmfEvent event) throws AttributeNotFoundException { Integer cpu = KernelEventHandlerUtils.getCpu(event); if (cpu == null) { return; } ITmfEventField content = event.getContent(); String prevProcessName = checkNotNull((String) content.getField(getLayout().fieldPrevComm()).getValue()); Integer prevTid = ((Long) content.getField(getLayout().fieldPrevTid()).getValue()).intValue(); Long prevState = checkNotNull((Long) content.getField(getLayout().fieldPrevState()).getValue()); Integer prevPrio = ((Long) content.getField(getLayout().fieldPrevPrio()).getValue()).intValue(); String nextProcessName = checkNotNull((String) content.getField(getLayout().fieldNextComm()).getValue()); Integer nextTid = ((Long) content.getField(getLayout().fieldNextTid()).getValue()).intValue(); Integer nextPrio = ((Long) content.getField(getLayout().fieldNextPrio()).getValue()).intValue(); /* Will never return null since "cpu" is null checked */ String formerThreadAttributeName = Attributes.buildThreadAttributeName(prevTid, cpu); String currenThreadAttributeName = Attributes.buildThreadAttributeName(nextTid, cpu); int nodeThreads = KernelEventHandlerUtils.getNodeThreads(ss); int formerThreadNode = ss.getQuarkRelativeAndAdd(nodeThreads, formerThreadAttributeName); int newCurrentThreadNode = ss.getQuarkRelativeAndAdd(nodeThreads, currenThreadAttributeName); long timestamp = KernelEventHandlerUtils.getTimestamp(event); /* Set the status of the process that got scheduled out. */ setOldProcessStatus(ss, prevState, formerThreadNode, timestamp); /* Set the status of the new scheduled process */ KernelEventHandlerUtils.setProcessToRunning(timestamp, newCurrentThreadNode, ss); /* Set the exec name of the former process */ setProcessExecName(ss, prevProcessName, formerThreadNode, timestamp); /* Set the exec name of the new process */ setProcessExecName(ss, nextProcessName, newCurrentThreadNode, timestamp); /* Set the current prio for the former process */ setProcessPrio(ss, prevPrio, formerThreadNode, timestamp); /* Set the current prio for the new process */ setProcessPrio(ss, nextPrio, newCurrentThreadNode, timestamp); /* Set the current scheduled process on the relevant CPU */ int currentCPUNode = KernelEventHandlerUtils.getCurrentCPUNode(cpu, ss); setCpuProcess(ss, nextTid, timestamp, currentCPUNode); /* Set the status of the CPU itself */ setCpuStatus(ss, nextTid, newCurrentThreadNode, timestamp, currentCPUNode); } private static void setOldProcessStatus(ITmfStateSystemBuilder ss, Long prevState, Integer formerThreadNode, long timestamp) { ITmfStateValue value; /* * Empirical observations and look into the linux code have * shown that the TASK_STATE_MAX flag is used internally and * |'ed with other states, most often the running state, so it * is ignored from the prevState value. * * Since Linux 4.1, the TASK_NOLOAD state was created and * TASK_STATE_MAX is now 2048. We use TASK_NOLOAD as the new max * because it does not modify the displayed state value. */ int state = (int) (prevState & (LinuxValues.TASK_NOLOAD - 1)); if (isRunning(state)) { value = StateValues.PROCESS_STATUS_WAIT_FOR_CPU_VALUE; } else if (isWaiting(state)) { value = StateValues.PROCESS_STATUS_WAIT_BLOCKED_VALUE; } else if (isDead(state)) { value = TmfStateValue.nullValue(); } else { value = StateValues.PROCESS_STATUS_WAIT_UNKNOWN_VALUE; } ss.modifyAttribute(timestamp, value, formerThreadNode); } private static boolean isDead(int state) { return (state & LinuxValues.TASK_DEAD) != 0; } private static boolean isWaiting(int state) { return (state & (LinuxValues.TASK_INTERRUPTIBLE | LinuxValues.TASK_UNINTERRUPTIBLE)) != 0; } private static boolean isRunning(int state) { // special case, this means ALL STATES ARE 0 // this is effectively an anti-state return state == 0; } private static void setCpuStatus(ITmfStateSystemBuilder ss, Integer nextTid, Integer newCurrentThreadNode, long timestamp, int currentCPUNode) { int quark; ITmfStateValue value; if (nextTid > 0) { /* Check if the entering process is in kernel or user mode */ quark = ss.getQuarkRelativeAndAdd(newCurrentThreadNode, Attributes.SYSTEM_CALL); ITmfStateValue queryOngoingState = ss.queryOngoingState(quark); if (queryOngoingState.isNull()) { value = StateValues.CPU_STATUS_RUN_USERMODE_VALUE; } else { value = StateValues.CPU_STATUS_RUN_SYSCALL_VALUE; } } else { value = StateValues.CPU_STATUS_IDLE_VALUE; } ss.modifyAttribute(timestamp, value, currentCPUNode); } private static void setCpuProcess(ITmfStateSystemBuilder ss, Integer nextTid, long timestamp, int currentCPUNode) { int quark; ITmfStateValue value; quark = ss.getQuarkRelativeAndAdd(currentCPUNode, Attributes.CURRENT_THREAD); value = TmfStateValue.newValueInt(nextTid); ss.modifyAttribute(timestamp, value, quark); } private static void setProcessPrio(ITmfStateSystemBuilder ss, Integer prio, Integer threadNode, long timestamp) { int quark; ITmfStateValue value; quark = ss.getQuarkRelativeAndAdd(threadNode, Attributes.PRIO); value = TmfStateValue.newValueInt(prio); ss.modifyAttribute(timestamp, value, quark); } private static void setProcessExecName(ITmfStateSystemBuilder ss, String processName, Integer threadNode, long timestamp) { int quark; ITmfStateValue value; quark = ss.getQuarkRelativeAndAdd(threadNode, Attributes.EXEC_NAME); value = TmfStateValue.newValueString(processName); ss.modifyAttribute(timestamp, value, quark); } }