blob: 33fcf66c0a3b7a099fe7f20818e7ba1741406667 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 É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 v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.tracecompass.incubator.internal.callstack.core.instrumented;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.incubator.analysis.core.weighted.tree.IWeightedTreeGroupDescriptor;
import org.eclipse.tracecompass.incubator.callstack.core.base.CallStackElement;
import org.eclipse.tracecompass.incubator.callstack.core.base.ICallStackElement;
import org.eclipse.tracecompass.incubator.callstack.core.flamechart.CallStack;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackHostUtils.IHostIdProvider;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackHostUtils.IHostIdResolver;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackSeries.IThreadIdProvider;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.CallStackSeries.IThreadIdResolver;
import org.eclipse.tracecompass.incubator.callstack.core.instrumented.statesystem.InstrumentedCallStackAnalysis;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException;
import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue.Type;
/**
* A callstack element corresponding to an attribute in the state system
*
* @author Geneviève Bastien
*/
public class InstrumentedCallStackElement extends CallStackElement {
private static final String INSTRUMENTED = "instrumented"; //$NON-NLS-1$
private final ITmfStateSystem fStateSystem;
private final int fQuark;
private final IHostIdResolver fHostResolver;
private final @Nullable IThreadIdResolver fThreadIdResolver;
private final Map<Integer, ICallStackElement> fNextElements = new HashMap<>();
private @Nullable CallStack fCallstack = null;
/**
* Constructor
*
* @param hostResolver
* The resolver for the host ID for the callstack
* @param stateSystem
* The state system containing the callstack
* @param quark
* The quark corresponding to this element
* @param group
* The group descriptor of this element
* @param nextGroup
* The group descriptor of the next group of elements
* @param threadIdResolver
* The object describing how to resolve the thread ID
* @param parent
* The parent element or <code>null</code> if this is the root
* element
*/
public InstrumentedCallStackElement(IHostIdResolver hostResolver, ITmfStateSystem stateSystem, Integer quark,
IWeightedTreeGroupDescriptor group,
@Nullable IWeightedTreeGroupDescriptor nextGroup,
@Nullable IThreadIdResolver threadIdResolver,
@Nullable InstrumentedCallStackElement parent) {
super(INSTRUMENTED, group, nextGroup, parent);
fStateSystem = stateSystem;
fQuark = quark;
fHostResolver = hostResolver;
fThreadIdResolver = threadIdResolver;
}
@Override
public Collection<ICallStackElement> getChildrenElements() {
// Get the elements from the next group in the hierarchy
@Nullable IWeightedTreeGroupDescriptor nextGroup = getNextGroup();
if (!(nextGroup instanceof InstrumentedGroupDescriptor)) {
return Collections.emptyList();
}
return getNextGroupElements((InstrumentedGroupDescriptor) nextGroup);
}
@Override
public @Nullable InstrumentedCallStackElement getParentElement() {
return (InstrumentedCallStackElement) super.getParentElement();
}
/**
* Create the root elements from a root group and its thread ID resolver
*
* @param rootGroup
* The root group descriptor
* @param hostResolver
* The host ID resolver
* @param resolver
* the thread ID resolver
* @param cache
* A cache of elements already built. It maps a quark to an element
* and the element will be returned if it has already been computed
* @return A collection of elements that are roots of the given callstack
* grouping
*/
public static Collection<ICallStackElement> getRootElements(InstrumentedGroupDescriptor rootGroup, IHostIdResolver hostResolver, @Nullable IThreadIdResolver resolver, Map<Integer, ICallStackElement> cache) {
return getNextElements(rootGroup, rootGroup.getStateSystem(), ITmfStateSystem.ROOT_ATTRIBUTE, hostResolver, resolver, null, cache);
}
private Collection<ICallStackElement> getNextGroupElements(InstrumentedGroupDescriptor nextGroup) {
return getNextElements(nextGroup, fStateSystem, fQuark, fHostResolver, fThreadIdResolver, this, fNextElements);
}
private static Collection<ICallStackElement> getNextElements(InstrumentedGroupDescriptor nextGroup, ITmfStateSystem stateSystem, int baseQuark, IHostIdResolver hostResolver, @Nullable IThreadIdResolver threadIdProvider, @Nullable InstrumentedCallStackElement parent, Map<Integer, ICallStackElement> cache) {
// Get the elements from the base quark at the given pattern
List<Integer> quarks = stateSystem.getQuarks(baseQuark, nextGroup.getSubPattern());
if (quarks.isEmpty()) {
return Collections.emptyList();
}
InstrumentedGroupDescriptor nextLevel = nextGroup.getNextGroup();
// If the next level is null, then this is a callstack final element
List<ICallStackElement> elements = new ArrayList<>(quarks.size());
for (Integer quark : quarks) {
ICallStackElement element = cache.get(quark);
if (element == null) {
element = new InstrumentedCallStackElement(hostResolver, stateSystem, quark,
nextGroup, nextLevel, threadIdProvider, parent);
if (nextGroup.isSymbolKeyGroup()) {
element.setSymbolKeyElement(element);
}
if (parent != null) {
parent.addChild(element);
}
cache.put(quark, element);
}
elements.add(element);
}
return elements;
}
/**
* Get the thread ID resolver, the object that will retrieve the thread ID of a
* given stack
*
* @return The thread ID provider or <code>null</code> if unavailable
*/
protected @Nullable IThreadIdResolver getThreadIdResolver() {
return fThreadIdResolver;
}
@Override
public @NonNull String getName() {
if (fQuark == ITmfStateSystem.ROOT_ATTRIBUTE) {
return StringUtils.EMPTY;
}
return fStateSystem.getAttributeName(fQuark);
}
@Override
public int retrieveSymbolKeyAt(long startTime) {
int processId = CallStackElement.DEFAULT_SYMBOL_KEY;
if (fQuark != ITmfStateSystem.ROOT_ATTRIBUTE) {
try {
// Query a time that is within the bounds of the state system
long start = Math.max(fStateSystem.getStartTime(), startTime);
start = Math.max(start, fStateSystem.getCurrentEndTime());
// Query the value of the quark at the requested time
ITmfStateInterval interval = fStateSystem.querySingleState(start, fQuark);
ITmfStateValue processStateValue = interval.getStateValue();
// If the state value is an integer, assume it is the symbol we
// are looking for
if (processStateValue.getType() == Type.INTEGER) {
processId = processStateValue.unboxInt();
} else {
try {
// Otherwise, try to take the attribute name as the key
String processName = fStateSystem.getAttributeName(fQuark);
processId = Integer.parseInt(processName);
} catch (NumberFormatException e) {
/* use default processId */
}
}
} catch (StateSystemDisposedException e) {
// ignore
}
}
return processId;
}
/**
* Get the state system containing the callstack data
*
* @return The state system
*/
public ITmfStateSystem getStateSystem() {
return fStateSystem;
}
/**
* Get the quark corresponding to this element
*
* @return The quark
*/
public int getQuark() {
return fQuark;
}
/**
* Get the callstack associated with this element if this is a leaf element. If
* it is not a leaf, it throw a {@link NoSuchElementException}
*
* @return The call stack
*/
public CallStack getCallStack() {
CallStack callstack = fCallstack;
List<Integer> subAttributes = getStackQuarks();
if (callstack == null) {
IHostIdProvider hostProvider = fHostResolver.apply(this);
IThreadIdResolver threadIdResolver = fThreadIdResolver;
IThreadIdProvider threadIdProvider = threadIdResolver == null ? null : threadIdResolver.resolve(hostProvider, this);
callstack = new CallStack(getStateSystem(), subAttributes, this, hostProvider, threadIdProvider);
fCallstack = callstack;
} else {
synchronized (fCallstack) {
// Update the callstack if attributes were added
if (callstack.getMaxDepth() < subAttributes.size()) {
callstack.updateAttributes(subAttributes);
}
}
}
return Objects.requireNonNull(callstack);
}
/**
* Get the stack quarks that contain the data of the function calls
*
* This is meant to remain internal. It is public only so that the segment store
* from the CallStackSeries can access this
*
* @return The list of quarks containing the data
*/
public List<Integer> getStackQuarks() {
if (!isLeaf()) {
throw new NoSuchElementException();
}
int stackQuark = getStateSystem().optQuarkRelative(getQuark(), InstrumentedCallStackAnalysis.CALL_STACK);
if (stackQuark == ITmfStateSystem.INVALID_ATTRIBUTE) {
// No CallStack element underneath, assume a flat chart: the current quark has
// the status to show
return Collections.singletonList(getQuark());
}
List<Integer> subAttributes = getStateSystem().getSubAttributes(stackQuark, false);
return subAttributes;
}
@Override
public InstrumentedCallStackElement copyElement() {
return new InstrumentedCallStackElement(fHostResolver, fStateSystem, fQuark, super.getGroup(), null, fThreadIdResolver, null);
}
}