blob: 00bf64593c060cd985da3e2106d78f53c39729dc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2016 Ericsson
*
* 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
*
* Contributors:
* Patrick Tasse - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem;
import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.osgi.util.NLS;
import org.eclipse.tracecompass.incubator.analysis.core.aspects.ProcessNameAspect;
import org.eclipse.tracecompass.incubator.analysis.core.aspects.ThreadNameAspect;
import org.eclipse.tracecompass.incubator.callstack.core.flamechart.Messages;
import org.eclipse.tracecompass.incubator.internal.callstack.core.Activator;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider;
import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
/**
* The default base state provider for traces that implement the default
* {@link InstrumentedCallStackAnalysis} with a process / thread grouping, using
* the default values.
*
* Specific analyses will need to override it to specify the function
* entry/exit, as well as how to get the process ID and thread ID.
*
* The attribute tree should have the following structure:
*
* <pre>
* (root)
* +-- Processes
* +-- (PID 1000)
* | +-- (TID 1000)
* | | +-- CallStack (stack-attribute)
* | | +-- 1
* | | +-- 2
* | | ...
* | | +-- n
* | +-- (TID 1001)
* | +-- CallStack (stack-attribute)
* | +-- 1
* | +-- 2
* | ...
* | +-- n
* |
* +-- (PID 2000)
* +-- (TID 2000)
* +-- CallStack (stack-attribute)
* +-- 1
* +-- 2
* ...
* +-- n
* </pre>
*
* where:
* <ul>
* <li>(PID n) is an attribute whose name is the display name of the process.
* Optionally, its value can be an int representing the process id. Otherwise,
* the attribute name can be set to the process id formatted as a string.</li>
* <li>(TID n) is an attribute whose name is the display name of the thread.
* Optionally, its value can be a long representing the thread id. Otherwise,
* the attribute name can be set to the thread id formatted as a string.</li>
* <li>"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.</li>
* </ul>
*
* TODO: Use the same state provider as the one currently in trace Compass. It
* just needs the ProcessName and ThreadName Aspects.
*
* @author Patrick Tasse
*/
public abstract class CallStackStateProvider extends AbstractTmfStateProvider {
/**
* Thread attribute
*
* @since 2.0
*/
public static final String PROCESSES = "Processes"; //$NON-NLS-1$
/**
* Unknown process ID
*
* @since 2.0
*/
public static final int UNKNOWN_PID = -1;
/**
* Unknown name
*
* @since 2.0
*/
public static final String UNKNOWN = "UNKNOWN"; //$NON-NLS-1$
/** CallStack state system ID */
private static final String ID = "org.eclipse.linuxtools.tmf.callstack"; //$NON-NLS-1$
private boolean fHasErrors = false;
/**
* Default constructor
*
* @param trace
* The trace for which we build this state system
*/
public CallStackStateProvider(ITmfTrace trace) {
super(trace, ID);
}
@Override
protected void eventHandle(ITmfEvent event) {
if (!considerEvent(event)) {
return;
}
ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder());
handleFunctionEntry(ss, event);
handleFunctionExit(ss, event);
}
private void handleFunctionEntry(ITmfStateSystemBuilder ss, ITmfEvent event) {
/* Check if the event is a function entry */
Object functionEntryName = functionEntry(event);
if (functionEntryName != null) {
long timestamp = event.getTimestamp().toNanos();
String processName = getProcessName(event);
int processId = getProcessId(event);
if (processName == null) {
processName = (processId == UNKNOWN_PID) ? UNKNOWN : Integer.toString(processId);
}
int processQuark = ss.getQuarkAbsoluteAndAdd(PROCESSES, processName);
ss.updateOngoingState(TmfStateValue.newValueInt(processId), processQuark);
String threadName = getThreadName(event);
long threadId = getThreadId(event);
if (threadName == null) {
threadName = Long.toString(threadId);
}
int threadQuark = ss.getQuarkRelativeAndAdd(processQuark, threadName);
ss.updateOngoingState(TmfStateValue.newValueLong(threadId), threadQuark);
int callStackQuark = ss.getQuarkRelativeAndAdd(threadQuark, InstrumentedCallStackAnalysis.CALL_STACK);
ss.pushAttribute(timestamp, functionEntryName, callStackQuark);
return;
}
}
private void handleFunctionExit(ITmfStateSystemBuilder ss, ITmfEvent event) {
/* Check if the event is a function exit */
Object functionExitState = functionExit(event);
// FIXME: since
if (functionExitState != null) {
long timestamp = event.getTimestamp().toNanos();
String processName = getProcessName(event);
if (processName == null) {
int processId = getProcessId(event);
processName = (processId == UNKNOWN_PID) ? UNKNOWN : Integer.toString(processId);
}
String threadName = getThreadName(event);
if (threadName == null) {
threadName = Long.toString(getThreadId(event));
}
int quark = ss.getQuarkAbsoluteAndAdd(PROCESSES, processName, threadName, InstrumentedCallStackAnalysis.CALL_STACK);
Object poppedValue = ss.popAttributeObject(timestamp, quark);
/*
* Verify that the value we are popping matches the one in the
* event field, unless the latter is undefined.
*/
if (!fHasErrors && !functionExitState.equals(poppedValue)) {
Activator.getInstance().logWarning(NLS.bind(Messages.CallStackStateProvider_EventDescription, event.getName(),
event.getTimestamp().getValue()) + ": " //$NON-NLS-1$
+ NLS.bind( Messages.CallStackStateProvider_UnmatchedPoppedValue,
functionExitState,
poppedValue));
fHasErrors = true;
}
}
}
/**
* Restrict the return type for {@link ITmfStateProvider#getNewInstance}.
*
* @since 2.0
*/
@Override
public abstract CallStackStateProvider getNewInstance();
/**
* 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.
*/
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 object representing the function being entered, or null
* if not a function entry
* @since 2.0
*/
protected abstract @Nullable Object functionEntry(ITmfEvent event);
/**
* Check an event if it indicates a function exit.
*
* @param event
* An event to check for function exit
* @return The object representing the function being exited, or
* TmfStateValue#nullValue() if the exited function is undefined,
* or null if not a function exit.
* @since 2.0
*/
protected abstract @Nullable Object functionExit(ITmfEvent event);
/**
* Return the process ID of a function entry event.
* <p>
* Use {@link #UNKNOWN_PID} if it is not known.
*
* @param event
* The event
* @return The process ID
* @since 2.0
*/
protected abstract int getProcessId(ITmfEvent event);
/**
* Return the process name of a function entry event.
*
* @param event
* The event
* @return The process name (as will be shown in the view) or null to use
* the process ID formatted as a string (or {@link #UNKNOWN})
* @since 2.0
*/
protected @Nullable String getProcessName(ITmfEvent event) {
/* Override to provide a process name */
Object resolved = TmfTraceUtils.resolveEventAspectOfClassForEvent(event.getTrace(), ProcessNameAspect.class, event);
return (resolved instanceof String) ? (String) resolved : null;
}
/**
* Return the thread id of a function entry event.
*
* @param event
* The event
* @return The thread id
* @since 2.0
*/
protected abstract long getThreadId(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) or null to use the
* thread id formatted as a string
*/
protected @Nullable String getThreadName(ITmfEvent event) {
/* Override to provide a thread name */
Object resolved = TmfTraceUtils.resolveEventAspectOfClassForEvent(event.getTrace(), ThreadNameAspect.class, event);
return (resolved instanceof String) ? (String) resolved : null;
}
}