/*******************************************************************************
* Copyright (c) 2013, 2014 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:
* Patrick Tasse - Initial API and implementation
*******************************************************************************/
package fr.inria.linuxtools.tmf.core.callstack;
import org.eclipse.osgi.util.NLS;
import fr.inria.linuxtools.internal.tmf.core.Activator;
import fr.inria.linuxtools.statesystem.core.exceptions.AttributeNotFoundException;
import fr.inria.linuxtools.statesystem.core.exceptions.StateValueTypeException;
import fr.inria.linuxtools.statesystem.core.exceptions.TimeRangeException;
import fr.inria.linuxtools.statesystem.core.statevalue.ITmfStateValue;
import fr.inria.linuxtools.statesystem.core.statevalue.TmfStateValue;
import fr.inria.linuxtools.tmf.core.event.ITmfEvent;
import fr.inria.linuxtools.tmf.core.statesystem.AbstractTmfStateProvider;
import fr.inria.linuxtools.tmf.core.timestamp.ITmfTimestamp;
import fr.inria.linuxtools.tmf.core.trace.ITmfTrace;
/**
* The state provider for traces that support the Call Stack view.
*
* The attribute tree should have the following structure:
*<pre>
* (root)
* \-- Threads
* |-- (Thread 1)
* | \-- CallStack (stack-attribute)
* | |-- 1
* | |-- 2
* | ...
* | \-- n
* |-- (Thread 2)
* | \-- CallStack (stack-attribute)
* | |-- 1
* | |-- 2
* | ...
* | \-- n
* ...
* \-- (Thread n)
* \-- CallStack (stack-attribute)
* |-- 1
* |-- 2
* ...
* \-- n
*</pre>
* where:
* <br>
* (Thread n) is an attribute whose name is the name of the thread
* <br>
* CallStack is a stack-attribute whose pushed values are either a string,
* int or long representing the function name or address in the call stack.
* The type of value used must be constant for a particular CallStack.
*
* @author Patrick Tasse
* @since 2.0
*/
public abstract class CallStackStateProvider extends AbstractTmfStateProvider {
/** Thread attribute */
public static final String THREADS = "Threads"; //$NON-NLS-1$
/** CallStack stack-attribute */
public static final String CALL_STACK = "CallStack"; //$NON-NLS-1$
/** Undefined function exit name */
public static final String UNDEFINED = "UNDEFINED"; //$NON-NLS-1$
/** CallStack state system ID */
private static final String ID = "fr.inria.linuxtools.tmf.callstack"; //$NON-NLS-1$
/** Dummy function name for when no function is expected */
private static final String NO_FUNCTION = "no function"; //$NON-NLS-1$
/**
* Default constructor
*
* @param trace
* The trace for which we build this state system
*/
public CallStackStateProvider(ITmfTrace trace) {
super(trace, ITmfEvent.class, ID);
}
@Override
protected void eventHandle(ITmfEvent event) {
if (!considerEvent(event)) {
return;
}
try {
/* Check if the event is a function entry */
String functionEntryName = functionEntry(event);
if (functionEntryName != null) {
long timestamp = event.getTimestamp().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
String thread = getThreadName(event);
int quark = ss.getQuarkAbsoluteAndAdd(THREADS, thread, CALL_STACK);
ITmfStateValue value = TmfStateValue.newValueString(functionEntryName);
ss.pushAttribute(timestamp, value, quark);
return;
}
/* Check if the event is a function exit */
String functionExitName = functionExit(event);
if (functionExitName != null) {
long timestamp = event.getTimestamp().normalize(0, ITmfTimestamp.NANOSECOND_SCALE).getValue();
String thread = getThreadName(event);
int quark = ss.getQuarkAbsoluteAndAdd(THREADS, thread, CALL_STACK);
ITmfStateValue poppedValue = ss.popAttribute(timestamp, quark);
String poppedName = (poppedValue == null ? NO_FUNCTION : poppedValue.unboxStr());
/*
* Verify that the value we are popping matches the one in the
* event field, unless the latter is undefined.
*/
if (!functionExitName.equals(UNDEFINED) &&
!functionExitName.equals(poppedName)) {
Activator.logWarning(NLS.bind(
Messages.CallStackStateProvider_UnmatchedPoppedValue,
functionExitName,
poppedName));
}
}
} catch (TimeRangeException e) {
e.printStackTrace();
} catch (AttributeNotFoundException e) {
e.printStackTrace();
} catch (StateValueTypeException e) {
e.printStackTrace();
}
}
/**
* Check if this event should be considered at all for function entry/exit
* analysis. This check is only run once per event, before
* {@link #functionEntry} and {@link #functionExit} (to avoid repeating
* checks in those methods).
*
* @param event
* The event to check
* @return If false, the event will be ignored by the state provider. If
* true processing will continue.
* @since 3.0
*/
protected abstract boolean considerEvent(ITmfEvent event);
/**
* Check an event if it indicates a function entry.
*
* @param event
* An event to check for function entry
* @return The function name of the function entry, or null if not a
* function entry.
*/
protected abstract String functionEntry(ITmfEvent event);
/**
* Check an event if it indicates a function exit.
*
* @param event
* An event to check for function exit
* @return The function name, or UNDEFINED, for a function exit, or null if
* not a function exit.
*/
protected abstract String functionExit(ITmfEvent event);
/**
* Return the thread name of a function entry or exit event.
*
* @param event
* The event
* @return The thread name (as will be shown in the view)
* @since 3.0
*/
protected abstract String getThreadName(ITmfEvent event);
}