blob: 1f34c5ce84cf927214827940d2dba55deb39c96e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2014 Ericsson
* Copyright (c) 2010, 2011 École Polytechnique de Montréal
* Copyright (c) 2010, 2011 Alexandre Montplaisir <alexandre.montplaisir@gmail.com>
*
* 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
*
*******************************************************************************/
package org.eclipse.linuxtools.internal.lttng2.kernel.core.stateprovider;
import org.eclipse.linuxtools.internal.lttng2.kernel.core.Attributes;
import org.eclipse.linuxtools.internal.lttng2.kernel.core.LttngStrings;
import org.eclipse.linuxtools.internal.lttng2.kernel.core.StateValues;
import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
import org.eclipse.linuxtools.tmf.core.event.ITmfEventField;
import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException;
import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException;
import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException;
import org.eclipse.linuxtools.tmf.core.statesystem.AbstractTmfStateProvider;
import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystemBuilder;
import org.eclipse.linuxtools.tmf.core.statevalue.ITmfStateValue;
import org.eclipse.linuxtools.tmf.core.statevalue.TmfStateValue;
import org.eclipse.linuxtools.tmf.ctf.core.CtfTmfEvent;
import org.eclipse.linuxtools.tmf.ctf.core.CtfTmfTrace;
/**
* This is the state change input plugin for TMF's state system which handles
* the LTTng 2.0 kernel traces in CTF format.
*
* It uses the reference handler defined in CTFKernelHandler.java.
*
* @author alexmont
*
*/
public class LttngKernelStateProvider extends AbstractTmfStateProvider {
/**
* Version number of this state provider. Please bump this if you modify the
* contents of the generated state history in some way.
*/
private static final int VERSION = 4;
// ------------------------------------------------------------------------
// Constructor
// ------------------------------------------------------------------------
/**
* Instantiate a new state provider plugin.
*
* @param trace
* The LTTng 2.0 kernel trace directory
*/
public LttngKernelStateProvider(CtfTmfTrace trace) {
super(trace, CtfTmfEvent.class, "LTTng Kernel"); //$NON-NLS-1$
}
// ------------------------------------------------------------------------
// IStateChangeInput
// ------------------------------------------------------------------------
@Override
public int getVersion() {
return VERSION;
}
@Override
public void assignTargetStateSystem(ITmfStateSystemBuilder ssb) {
/* We can only set up the locations once the state system is assigned */
super.assignTargetStateSystem(ssb);
}
@Override
public LttngKernelStateProvider getNewInstance() {
return new LttngKernelStateProvider((CtfTmfTrace) this.getTrace());
}
@Override
protected void eventHandle(ITmfEvent ev) {
/*
* AbstractStateChangeInput should have already checked for the correct
* class type
*/
final CtfTmfEvent event = (CtfTmfEvent) ev;
final ITmfEventField content = event.getContent();
final String eventName = event.getType().getName();
final long ts = event.getTimestamp().getValue();
try {
/* Shortcut for the "current CPU" attribute node */
final Integer currentCPUNode = ss.getQuarkRelativeAndAdd(getNodeCPUs(), String.valueOf(event.getCPU()));
/*
* Shortcut for the "current thread" attribute node. It requires
* querying the current CPU's current thread.
*/
int quark = ss.getQuarkRelativeAndAdd(currentCPUNode, Attributes.CURRENT_THREAD);
ITmfStateValue value = ss.queryOngoingState(quark);
int thread = value.isNull() ? -1 : value.unboxInt();
final Integer currentThreadNode = ss.getQuarkRelativeAndAdd(getNodeThreads(), String.valueOf(thread));
/*
* Feed event to the history system if it's known to cause a state
* transition.
*/
switch (eventName) {
case LttngStrings.EXIT_SYSCALL:
/* Fields: int64 ret */
{
/* Clear the current system call on the process */
quark = ss.getQuarkRelativeAndAdd(currentThreadNode, Attributes.SYSTEM_CALL);
value = TmfStateValue.nullValue();
ss.modifyAttribute(ts, value, quark);
/* Put the process' status back to user mode */
quark = ss.getQuarkRelativeAndAdd(currentThreadNode, Attributes.STATUS);
value = StateValues.PROCESS_STATUS_RUN_USERMODE_VALUE;
ss.modifyAttribute(ts, value, quark);
/* Put the CPU's status back to user mode */
quark = ss.getQuarkRelativeAndAdd(currentCPUNode, Attributes.STATUS);
value = StateValues.CPU_STATUS_RUN_USERMODE_VALUE;
ss.modifyAttribute(ts, value, quark);
}
break;
case LttngStrings.IRQ_HANDLER_ENTRY:
/* Fields: int32 irq, string name */
{
Integer irqId = ((Long) content.getField(LttngStrings.IRQ).getValue()).intValue();
/* Mark this IRQ as active in the resource tree.
* The state value = the CPU on which this IRQ is sitting */
quark = ss.getQuarkRelativeAndAdd(getNodeIRQs(), irqId.toString());
value = TmfStateValue.newValueInt(event.getCPU());
ss.modifyAttribute(ts, value, quark);
/* Change the status of the running process to interrupted */
quark = ss.getQuarkRelativeAndAdd(currentThreadNode, Attributes.STATUS);
value = StateValues.PROCESS_STATUS_INTERRUPTED_VALUE;
ss.modifyAttribute(ts, value, quark);
/* Change the status of the CPU to interrupted */
quark = ss.getQuarkRelativeAndAdd(currentCPUNode, Attributes.STATUS);
value = StateValues.CPU_STATUS_IRQ_VALUE;
ss.modifyAttribute(ts, value, quark);
}
break;
case LttngStrings.IRQ_HANDLER_EXIT:
/* Fields: int32 irq, int32 ret */
{
Integer irqId = ((Long) content.getField(LttngStrings.IRQ).getValue()).intValue();
/* Put this IRQ back to inactive in the resource tree */
quark = ss.getQuarkRelativeAndAdd(getNodeIRQs(), irqId.toString());
value = TmfStateValue.nullValue();
ss.modifyAttribute(ts, value, quark);
/* Set the previous process back to running */
setProcessToRunning(ts, currentThreadNode);
/* Set the CPU status back to running or "idle" */
cpuExitInterrupt(ts, currentCPUNode, currentThreadNode);
}
break;
case LttngStrings.SOFTIRQ_ENTRY:
/* Fields: int32 vec */
{
Integer softIrqId = ((Long) content.getField(LttngStrings.VEC).getValue()).intValue();
/* Mark this SoftIRQ as active in the resource tree.
* The state value = the CPU on which this SoftIRQ is processed */
quark = ss.getQuarkRelativeAndAdd(getNodeSoftIRQs(), softIrqId.toString());
value = TmfStateValue.newValueInt(event.getCPU());
ss.modifyAttribute(ts, value, quark);
/* Change the status of the running process to interrupted */
quark = ss.getQuarkRelativeAndAdd(currentThreadNode, Attributes.STATUS);
value = StateValues.PROCESS_STATUS_INTERRUPTED_VALUE;
ss.modifyAttribute(ts, value, quark);
/* Change the status of the CPU to interrupted */
quark = ss.getQuarkRelativeAndAdd(currentCPUNode, Attributes.STATUS);
value = StateValues.CPU_STATUS_SOFTIRQ_VALUE;
ss.modifyAttribute(ts, value, quark);
}
break;
case LttngStrings.SOFTIRQ_EXIT:
/* Fields: int32 vec */
{
Integer softIrqId = ((Long) content.getField(LttngStrings.VEC).getValue()).intValue();
/* Put this SoftIRQ back to inactive (= -1) in the resource tree */
quark = ss.getQuarkRelativeAndAdd(getNodeSoftIRQs(), softIrqId.toString());
value = TmfStateValue.nullValue();
ss.modifyAttribute(ts, value, quark);
/* Set the previous process back to running */
setProcessToRunning(ts, currentThreadNode);
/* Set the CPU status back to "busy" or "idle" */
cpuExitInterrupt(ts, currentCPUNode, currentThreadNode);
}
break;
case LttngStrings.SOFTIRQ_RAISE:
/* Fields: int32 vec */
{
Integer softIrqId = ((Long) content.getField(LttngStrings.VEC).getValue()).intValue();
/* Mark this SoftIRQ as *raised* in the resource tree.
* State value = -2 */
quark = ss.getQuarkRelativeAndAdd(getNodeSoftIRQs(), softIrqId.toString());
value = StateValues.SOFT_IRQ_RAISED_VALUE;
ss.modifyAttribute(ts, value, quark);
}
break;
case LttngStrings.SCHED_SWITCH:
/*
* Fields: string prev_comm, int32 prev_tid, int32 prev_prio, int64 prev_state,
* string next_comm, int32 next_tid, int32 next_prio
*/
{
Integer prevTid = ((Long) content.getField(LttngStrings.PREV_TID).getValue()).intValue();
Long prevState = (Long) content.getField(LttngStrings.PREV_STATE).getValue();
String nextProcessName = (String) content.getField(LttngStrings.NEXT_COMM).getValue();
Integer nextTid = ((Long) content.getField(LttngStrings.NEXT_TID).getValue()).intValue();
Integer formerThreadNode = ss.getQuarkRelativeAndAdd(getNodeThreads(), prevTid.toString());
Integer newCurrentThreadNode = ss.getQuarkRelativeAndAdd(getNodeThreads(), nextTid.toString());
/* Set the status of the process that got scheduled out. */
quark = ss.getQuarkRelativeAndAdd(formerThreadNode, Attributes.STATUS);
if (prevState != 0) {
value = StateValues.PROCESS_STATUS_WAIT_BLOCKED_VALUE;
} else {
value = StateValues.PROCESS_STATUS_WAIT_FOR_CPU_VALUE;
}
ss.modifyAttribute(ts, value, quark);
/* Set the status of the new scheduled process */
setProcessToRunning(ts, newCurrentThreadNode);
/* Set the exec name of the new process */
quark = ss.getQuarkRelativeAndAdd(newCurrentThreadNode, Attributes.EXEC_NAME);
value = TmfStateValue.newValueString(nextProcessName);
ss.modifyAttribute(ts, value, quark);
/* Make sure the PPID and system_call sub-attributes exist */
ss.getQuarkRelativeAndAdd(newCurrentThreadNode, Attributes.SYSTEM_CALL);
ss.getQuarkRelativeAndAdd(newCurrentThreadNode, Attributes.PPID);
/* Set the current scheduled process on the relevant CPU */
quark = ss.getQuarkRelativeAndAdd(currentCPUNode, Attributes.CURRENT_THREAD);
value = TmfStateValue.newValueInt(nextTid);
ss.modifyAttribute(ts, value, quark);
/* Set the status of the CPU itself */
if (nextTid > 0) {
/* Check if the entering process is in kernel or user mode */
quark = ss.getQuarkRelativeAndAdd(newCurrentThreadNode, Attributes.SYSTEM_CALL);
if (ss.queryOngoingState(quark).isNull()) {
value = StateValues.CPU_STATUS_RUN_USERMODE_VALUE;
} else {
value = StateValues.CPU_STATUS_RUN_SYSCALL_VALUE;
}
} else {
value = StateValues.CPU_STATUS_IDLE_VALUE;
}
quark = ss.getQuarkRelativeAndAdd(currentCPUNode, Attributes.STATUS);
ss.modifyAttribute(ts, value, quark);
}
break;
case LttngStrings.SCHED_PROCESS_FORK:
/* Fields: string parent_comm, int32 parent_tid,
* string child_comm, int32 child_tid */
{
// String parentProcessName = (String) event.getFieldValue("parent_comm");
String childProcessName = (String) content.getField(LttngStrings.CHILD_COMM).getValue();
// assert ( parentProcessName.equals(childProcessName) );
Integer parentTid = ((Long) content.getField(LttngStrings.PARENT_TID).getValue()).intValue();
Integer childTid = ((Long) content.getField(LttngStrings.CHILD_TID).getValue()).intValue();
Integer parentTidNode = ss.getQuarkRelativeAndAdd(getNodeThreads(), parentTid.toString());
Integer childTidNode = ss.getQuarkRelativeAndAdd(getNodeThreads(), childTid.toString());
/* Assign the PPID to the new process */
quark = ss.getQuarkRelativeAndAdd(childTidNode, Attributes.PPID);
value = TmfStateValue.newValueInt(parentTid);
ss.modifyAttribute(ts, value, quark);
/* Set the new process' exec_name */
quark = ss.getQuarkRelativeAndAdd(childTidNode, Attributes.EXEC_NAME);
value = TmfStateValue.newValueString(childProcessName);
ss.modifyAttribute(ts, value, quark);
/* Set the new process' status */
quark = ss.getQuarkRelativeAndAdd(childTidNode, Attributes.STATUS);
value = StateValues.PROCESS_STATUS_WAIT_FOR_CPU_VALUE;
ss.modifyAttribute(ts, value, quark);
/* Set the process' syscall name, to be the same as the parent's */
quark = ss.getQuarkRelativeAndAdd(parentTidNode, Attributes.SYSTEM_CALL);
value = ss.queryOngoingState(quark);
if (value.isNull()) {
/*
* Maybe we were missing info about the parent? At least we
* will set the child right. Let's suppose "sys_clone".
*/
value = TmfStateValue.newValueString(LttngStrings.SYS_CLONE);
}
quark = ss.getQuarkRelativeAndAdd(childTidNode, Attributes.SYSTEM_CALL);
ss.modifyAttribute(ts, value, quark);
}
break;
case LttngStrings.SCHED_PROCESS_EXIT:
/* Fields: string comm, int32 tid, int32 prio */
break;
case LttngStrings.SCHED_PROCESS_FREE:
/* Fields: string comm, int32 tid, int32 prio */
/*
* A sched_process_free will always happen after the sched_switch
* that will remove the process from the cpu for the last time. So
* this is when we should delete everything wrt to the process.
*/
{
Integer tid = ((Long) content.getField(LttngStrings.TID).getValue()).intValue();
/*
* Remove the process and all its sub-attributes from the
* current state
*/
quark = ss.getQuarkRelativeAndAdd(getNodeThreads(), tid.toString());
ss.removeAttribute(ts, quark);
}
break;
case LttngStrings.STATEDUMP_PROCESS_STATE:
/* Fields:
* int32 type, int32 mode, int32 pid, int32 submode, int32 vpid,
* int32 ppid, int32 tid, string name, int32 status, int32 vtid */
{
int tid = ((Long) content.getField(LttngStrings.TID).getValue()).intValue();
int pid = ((Long) content.getField(LttngStrings.PID).getValue()).intValue();
int ppid = ((Long) content.getField(LttngStrings.PPID).getValue()).intValue();
int status = ((Long) content.getField(LttngStrings.STATUS).getValue()).intValue();
String name = (String) content.getField(LttngStrings.NAME).getValue();
/*
* "mode" could be interesting too, but it doesn't seem to be
* populated with anything relevant for now.
*/
int curThreadNode = ss.getQuarkRelativeAndAdd(getNodeThreads(), String.valueOf(tid));
/* Set the process' name */
quark = ss.getQuarkRelativeAndAdd(curThreadNode, Attributes.EXEC_NAME);
if (ss.queryOngoingState(quark).isNull()) {
/* If the value didn't exist previously, set it */
value = TmfStateValue.newValueString(name);
ss.modifyAttribute(ts, value, quark);
}
/* Set the process' PPID */
quark = ss.getQuarkRelativeAndAdd(curThreadNode, Attributes.PPID);
if (ss.queryOngoingState(quark).isNull()) {
if (pid == tid) {
/* We have a process. Use the 'PPID' field. */
value = TmfStateValue.newValueInt(ppid);
} else {
/* We have a thread, use the 'PID' field for the parent. */
value = TmfStateValue.newValueInt(pid);
}
ss.modifyAttribute(ts, value, quark);
}
/* Set the process' status */
quark = ss.getQuarkRelativeAndAdd(curThreadNode, Attributes.STATUS);
if (ss.queryOngoingState(quark).isNull()) {
/* "2" here means "WAIT_FOR_CPU", and "5" "WAIT_BLOCKED" in the LTTng kernel. */
if (status == 2) {
value = StateValues.PROCESS_STATUS_WAIT_FOR_CPU_VALUE;
} else if (status == 5) {
value = StateValues.PROCESS_STATUS_WAIT_BLOCKED_VALUE;
} else {
value = StateValues.PROCESS_STATUS_UNKNOWN_VALUE;
}
ss.modifyAttribute(ts, value, quark);
}
}
break;
case LttngStrings.SCHED_WAKEUP:
case LttngStrings.SCHED_WAKEUP_NEW:
/* Fields (same fields for both types):
* string comm, int32 pid, int32 prio, int32 success,
* int32 target_cpu */
{
final int tid = ((Long) content.getField(LttngStrings.TID).getValue()).intValue();
final int threadNode = ss.getQuarkRelativeAndAdd(getNodeThreads(), String.valueOf(tid));
/*
* The process indicated in the event's payload is now ready to
* run. Assign it to the "wait for cpu" state, but only if it
* was not already running.
*/
quark = ss.getQuarkRelativeAndAdd(threadNode, Attributes.STATUS);
int status = ss.queryOngoingState(quark).unboxInt();
if (status != StateValues.PROCESS_STATUS_RUN_SYSCALL &&
status != StateValues.PROCESS_STATUS_RUN_USERMODE) {
value = StateValues.PROCESS_STATUS_WAIT_FOR_CPU_VALUE;
ss.modifyAttribute(ts, value, quark);
}
}
break;
default:
/* Other event types not covered by the main switch */
{
if (eventName.startsWith(LttngStrings.SYSCALL_PREFIX)
|| eventName.startsWith(LttngStrings.COMPAT_SYSCALL_PREFIX)) {
/*
* This is a replacement for the old sys_enter event. Now
* syscall names are listed into the event type
*/
/* Assign the new system call to the process */
quark = ss.getQuarkRelativeAndAdd(currentThreadNode, Attributes.SYSTEM_CALL);
value = TmfStateValue.newValueString(eventName);
ss.modifyAttribute(ts, value, quark);
/* Put the process in system call mode */
quark = ss.getQuarkRelativeAndAdd(currentThreadNode, Attributes.STATUS);
value = StateValues.PROCESS_STATUS_RUN_SYSCALL_VALUE;
ss.modifyAttribute(ts, value, quark);
/* Put the CPU in system call (kernel) mode */
quark = ss.getQuarkRelativeAndAdd(currentCPUNode, Attributes.STATUS);
value = StateValues.CPU_STATUS_RUN_SYSCALL_VALUE;
ss.modifyAttribute(ts, value, quark);
}
}
break;
} // End of big switch
} catch (AttributeNotFoundException ae) {
/*
* This would indicate a problem with the logic of the manager here,
* so it shouldn't happen.
*/
ae.printStackTrace();
} catch (TimeRangeException tre) {
/*
* This would happen if the events in the trace aren't ordered
* chronologically, which should never be the case ...
*/
System.err.println("TimeRangeExcpetion caught in the state system's event manager."); //$NON-NLS-1$
System.err.println("Are the events in the trace correctly ordered?"); //$NON-NLS-1$
tre.printStackTrace();
} catch (StateValueTypeException sve) {
/*
* This would happen if we were trying to push/pop attributes not of
* type integer. Which, once again, should never happen.
*/
sve.printStackTrace();
}
}
// ------------------------------------------------------------------------
// Convenience methods for commonly-used attribute tree locations
// ------------------------------------------------------------------------
private int getNodeCPUs() {
return ss.getQuarkAbsoluteAndAdd(Attributes.CPUS);
}
private int getNodeThreads() {
return ss.getQuarkAbsoluteAndAdd(Attributes.THREADS);
}
private int getNodeIRQs() {
return ss.getQuarkAbsoluteAndAdd(Attributes.RESOURCES, Attributes.IRQS);
}
private int getNodeSoftIRQs() {
return ss.getQuarkAbsoluteAndAdd(Attributes.RESOURCES, Attributes.SOFT_IRQS);
}
// ------------------------------------------------------------------------
// Advanced state-setting methods
// ------------------------------------------------------------------------
/**
* When we want to set a process back to a "running" state, first check
* its current System_call attribute. If there is a system call active, we
* put the process back in the syscall state. If not, we put it back in
* user mode state.
*/
private void setProcessToRunning(long ts, int currentThreadNode)
throws AttributeNotFoundException, TimeRangeException,
StateValueTypeException {
int quark;
ITmfStateValue value;
quark = ss.getQuarkRelativeAndAdd(currentThreadNode, Attributes.SYSTEM_CALL);
if (ss.queryOngoingState(quark).isNull()) {
/* We were in user mode before the interruption */
value = StateValues.PROCESS_STATUS_RUN_USERMODE_VALUE;
} else {
/* We were previously in kernel mode */
value = StateValues.PROCESS_STATUS_RUN_SYSCALL_VALUE;
}
quark = ss.getQuarkRelativeAndAdd(currentThreadNode, Attributes.STATUS);
ss.modifyAttribute(ts, value, quark);
}
/**
* Similar logic as above, but to set the CPU's status when it's coming out
* of an interruption.
*/
private void cpuExitInterrupt(long ts, int currentCpuNode, int currentThreadNode)
throws StateValueTypeException, AttributeNotFoundException,
TimeRangeException {
int quark;
ITmfStateValue value;
quark = ss.getQuarkRelativeAndAdd(currentCpuNode, Attributes.CURRENT_THREAD);
if (ss.queryOngoingState(quark).unboxInt() > 0) {
/* There was a process on the CPU */
quark = ss.getQuarkRelative(currentThreadNode, Attributes.SYSTEM_CALL);
if (ss.queryOngoingState(quark).isNull()) {
/* That process was in user mode */
value = StateValues.CPU_STATUS_RUN_USERMODE_VALUE;
} else {
/* That process was in a system call */
value = StateValues.CPU_STATUS_RUN_SYSCALL_VALUE;
}
} else {
/* There was no real process scheduled, CPU was idle */
value = StateValues.CPU_STATUS_IDLE_VALUE;
}
quark = ss.getQuarkRelativeAndAdd(currentCpuNode, Attributes.STATUS);
ss.modifyAttribute(ts, value, quark);
}
}