blob: e8b05f24b8e245bbecd0db149e94fcc53891180b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 Ericsson, É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.internal.analysis.os.linux.core.resourcesstatus;
import java.text.FieldPosition;
import java.text.Format;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.osgi.util.NLS;
import org.eclipse.tracecompass.analysis.os.linux.core.kernel.KernelAnalysisModule;
import org.eclipse.tracecompass.analysis.os.linux.core.kernel.StateValues;
import org.eclipse.tracecompass.common.core.format.DecimalUnitFormat;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.Attributes;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.resourcesstatus.ResourcesEntryModel.Type;
import org.eclipse.tracecompass.internal.tmf.core.model.filters.TimeGraphStateQueryFilter;
import org.eclipse.tracecompass.internal.tmf.core.model.timegraph.AbstractTimeGraphDataProvider;
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.tmf.core.model.CommonStatusMessage;
import org.eclipse.tracecompass.tmf.core.model.filters.SelectionTimeQueryFilter;
import org.eclipse.tracecompass.tmf.core.model.filters.TimeQueryFilter;
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphArrow;
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphRowModel;
import org.eclipse.tracecompass.tmf.core.model.timegraph.ITimeGraphState;
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphRowModel;
import org.eclipse.tracecompass.tmf.core.model.timegraph.TimeGraphState;
import org.eclipse.tracecompass.tmf.core.response.ITmfResponse;
import org.eclipse.tracecompass.tmf.core.response.TmfModelResponse;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;
import com.google.common.primitives.Ints;
/**
* Resources status data provider, used by the Resources view for example.
*
* @author Loic Prieur-Drevon
*/
public class ResourcesStatusDataProvider extends AbstractTimeGraphDataProvider<@NonNull KernelAnalysisModule, @NonNull ResourcesEntryModel> {
/**
* Extension point ID.
*/
public static final @NonNull String ID = "org.eclipse.tracecompass.internal.analysis.os.linux.core.threadstatus.ResourcesStatusDataProvider"; //$NON-NLS-1$
private static final String WILDCARD = "*"; //$NON-NLS-1$
private static final @NonNull String SEPARATOR = ""; //$NON-NLS-1$
private static final @NonNull Format FREQUENCY_FORMATTER = new DecimalUnitFormat() {
/**
*
*/
private static final long serialVersionUID = 2101980732073309988L;
@Override
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
return super.format(obj, toAppendTo, pos).append("Hz"); //$NON-NLS-1$
}
};
private static final long FREQUENCY_MULTIPLIER = 1000;
/** Map of attribute quark to its ResourcesEntryModel type */
private final HashMap<Integer, Type> fEntryModelTypes = new HashMap<>();
/** Map of CPU number to its separator entry's model id */
private final Map<Integer, Long> fSeparatorIds = new HashMap<>();
/**
* BiMap of IRQ/SoftIRQ twin model id to quark, the key is the model id of the
* CPU entry under an aggregate IRQ/SoftIRQ entry, the value is the quark of its
* twin IRQ/SoftIRQ entry under a CPU. These entries share the same states.
*/
private final BiMap<Long, Integer> fTwinIdsToQuark = HashBiMap.create();
private static final Comparator<ITmfStateInterval> CACHE_COMPARATOR = (a, b) -> {
if (a.getEndTime() < b.getStartTime()) {
return -1;
} else if (a.getStartTime() > b.getEndTime()) {
return 1;
}
return 0;
};
/** Map of thread id to Exec_name intervals */
private final TreeMultimap<Integer, ITmfStateInterval> fExecNamesCache = TreeMultimap.create(Integer::compare, CACHE_COMPARATOR);
/**
* Constructor
*
* @param trace
* The trace for which this provider will be built.
* @param module
* the {@link KernelAnalysisModule} to access the underlying
* {@link ITmfStateSystem}
*/
protected ResourcesStatusDataProvider(@NonNull ITmfTrace trace, @NonNull KernelAnalysisModule module) {
super(trace, module);
}
@Override
protected @NonNull List<@NonNull ResourcesEntryModel> getTree(@NonNull ITmfStateSystem ss,
@NonNull TimeQueryFilter filter, @Nullable IProgressMonitor monitor) throws StateSystemDisposedException {
long start = ss.getStartTime();
long end = ss.getCurrentEndTime();
List<@NonNull ResourcesEntryModel> builder = new ArrayList<>();
long traceId = getId(ITmfStateSystem.ROOT_ATTRIBUTE);
ResourcesEntryModel resourcesEntryModel = new ResourcesEntryModel(traceId, -1, getTrace().getName(), start, end, -1, Type.GROUP);
builder.add(resourcesEntryModel);
for (Integer cpuQuark : ss.getQuarks(Attributes.CPUS, WILDCARD)) {
final @NonNull String cpuName = ss.getAttributeName(cpuQuark);
int cpu = Integer.parseInt(cpuName);
Integer currentThreadQuark = ss.optQuarkRelative(cpuQuark, Attributes.CURRENT_THREAD);
if (currentThreadQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
ResourcesEntryModel currentThreadEntry = new ResourcesEntryModel(getId(currentThreadQuark), traceId, computeEntryName(Type.CURRENT_THREAD, cpu), start, end, cpu, Type.CURRENT_THREAD);
builder.add(currentThreadEntry);
fEntryModelTypes.put(currentThreadQuark, Type.CURRENT_THREAD);
}
ResourcesEntryModel cpuEntry = new ResourcesEntryModel(getId(cpuQuark), traceId, computeEntryName(Type.CPU, cpu), start, end, cpu, Type.CPU);
builder.add(cpuEntry);
fEntryModelTypes.put(cpuQuark, Type.CPU);
// Add a line for the frequency if available
Integer currentFreqQuark = ss.optQuarkRelative(cpuQuark, Attributes.CURRENT_FREQUENCY);
if (currentFreqQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
// Get the minimum and maximum frequencies of the CPU, if avaible. If not, these values won't be used anyway
long minFrequency = getCpuFrequency(ss, cpuQuark, Attributes.MIN_FREQUENCY);
long maxFrequency = getCpuFrequency(ss, cpuQuark, Attributes.MAX_FREQUENCY);
ResourcesEntryModel currentFreqEntry = new ResourcesEntryModelWeighted(getId(currentFreqQuark), traceId, computeEntryName(Type.FREQUENCY, cpu), start, end, cpu, Type.FREQUENCY, minFrequency, maxFrequency);
builder.add(currentFreqEntry);
fEntryModelTypes.put(currentFreqQuark, Type.FREQUENCY);
}
// Add a separator entry after each CPU entry
long id = fSeparatorIds.computeIfAbsent(cpu, key -> getEntryId());
builder.add(new ResourcesEntryModel(id, traceId, SEPARATOR, start, end, cpu, Type.GROUP));
List<Integer> irqQuarks = ss.getQuarks(cpuQuark, Attributes.IRQS, WILDCARD);
createInterrupt(ss, start, end, cpuEntry, irqQuarks, Type.IRQ, builder);
List<Integer> softIrqQuarks = ss.getQuarks(cpuQuark, Attributes.SOFT_IRQS, WILDCARD);
createInterrupt(ss, start, end, cpuEntry, softIrqQuarks, Type.SOFT_IRQ, builder);
}
return ImmutableList.copyOf(builder);
}
private static long getCpuFrequency(@NonNull ITmfStateSystem ss, int cpuQuark, @NonNull String freqAttribute) throws StateSystemDisposedException {
int quark = ss.optQuarkRelative(cpuQuark, freqAttribute);
if (quark == ITmfStateSystem.INVALID_ATTRIBUTE) {
// This value will not be used if unavailable, so just return something
return 1;
}
Object value = ss.querySingleState(ss.getStartTime(), quark).getValue();
// The frequency needs to fit in an int, so divide by 1000
return value instanceof Long ? ((Long) value).longValue() / FREQUENCY_MULTIPLIER : 1;
}
/**
* Create and add execution contexts to a cpu entry. Also creates an aggregate
* entry in the root trace entry. The execution context is basically what the
* cpu is doing in its execution stack. It can be in an IRQ, Soft IRQ. MCEs,
* NMIs, Userland and Kernel execution is not yet supported.
*
* @param ssq
* the state system
* @param startTime
* the start time in nanoseconds
* @param endTime
* the end time in nanoseconds
* @param cpuEntry
* the cpu timegraph entry (the entry under the trace entry
* @param irqQuarks
* the quarks to add to cpu entry
* @param type
* the type of entry being added
* @param builder
* list of entries to return
*/
private void createInterrupt(final ITmfStateSystem ssq, long startTime, long endTime,
ResourcesEntryModel cpuEntry, List<Integer> irqQuarks, Type type, List<ResourcesEntryModel> builder) {
for (Integer irqQuark : irqQuarks) {
String resourceName = ssq.getAttributeName(irqQuark);
int resourceId = Integer.parseInt(resourceName);
long irqId = getId(irqQuark);
builder.add(new ResourcesEntryModel(irqId, cpuEntry.getId(),
computeEntryName(type, resourceId), startTime, endTime, resourceId, type));
fEntryModelTypes.put(irqQuark, type);
/*
* Search for the aggregate interrupt entry in the list. If it does not exist
* yet, create it.
*/
String aggregateIrqtype = type == Type.IRQ ? Attributes.IRQS : Attributes.SOFT_IRQS;
int aggregateQuark = ssq.optQuarkAbsolute(aggregateIrqtype, resourceName);
if (aggregateQuark == ITmfStateSystem.INVALID_ATTRIBUTE) {
continue;
}
long aggregateId = getId(aggregateQuark);
if (!Iterables.any(builder, entry -> entry.getId() == aggregateId)) {
builder.add(new ResourcesEntryModel(aggregateId, cpuEntry.getParentId(),
computeEntryName(type, resourceId),
startTime, endTime, resourceId, type));
}
fEntryModelTypes.put(aggregateQuark, type);
/*
* Create an IRQ or SOFT_IRQ entry under the aggregate interrupt entry, but name
* it like a CPU entry. Add the mapping to its twin IRQ/SOFT_IRQ entry's quark
* under the CPU entry. The twin entries share the same quark.
*/
long id = fTwinIdsToQuark.inverse().computeIfAbsent(irqQuark, key -> getEntryId());
builder.add(new ResourcesEntryModel(id, aggregateId,
computeEntryName(Type.CPU, cpuEntry.getResourceId()),
startTime, endTime, cpuEntry.getResourceId(), type));
}
}
private static @NonNull String computeEntryName(Type type, int id) {
if (type == Type.SOFT_IRQ) {
return type.toString() + ' ' + id + ' ' + SoftIrqLabelProvider.getSoftIrq(id);
} else if (type == Type.CURRENT_THREAD) {
String threadEntryName = NLS.bind(Messages.ThreadEntry, id);
if (threadEntryName != null) {
return threadEntryName;
}
} else if (type == Type.CPU) {
String cpuEntryName = NLS.bind(Messages.CpuEntry, id);
if (cpuEntryName != null) {
return cpuEntryName;
}
} else if (type == Type.FREQUENCY) {
String cpuEntryName = NLS.bind(Messages.FrequencyEntry, id);
if (cpuEntryName != null) {
return cpuEntryName;
}
}
return type.toString() + ' ' + id;
}
@Override
public List<ITimeGraphRowModel> getRowModel(ITmfStateSystem ss, SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor)
throws StateSystemDisposedException {
TreeMultimap<Integer, ITmfStateInterval> intervals = TreeMultimap.create(Comparator.naturalOrder(),
Comparator.comparing(ITmfStateInterval::getStartTime));
Map<@NonNull Long, @NonNull Integer> idsToQuark = getSelectedEntries(filter);
/* Add the mapping for twin entries as they are not in the parent class BiMap */
addTwinIrqIds(filter, idsToQuark);
Collection<Long> times = getTimes(filter, ss.getStartTime(), ss.getCurrentEndTime());
/* Do the actual query */
Collection<@NonNull Integer> quarks = addThreadStatus(ss, idsToQuark.values());
for (ITmfStateInterval interval : ss.query2D(quarks, times)) {
if (monitor != null && monitor.isCanceled()) {
return null;
}
intervals.put(interval.getAttribute(), interval);
}
Map<@NonNull Integer, @NonNull Predicate<@NonNull Multimap<@NonNull String, @NonNull String>>> predicates = new HashMap<>();
if (filter instanceof TimeGraphStateQueryFilter) {
TimeGraphStateQueryFilter timeEventFilter = (TimeGraphStateQueryFilter) filter;
predicates.putAll(computeRegexPredicate(timeEventFilter));
}
List<ITimeGraphRowModel> rows = new ArrayList<>();
for (Map.Entry<Long, Integer> idToQuark : idsToQuark.entrySet()) {
if (monitor != null && monitor.isCanceled()) {
return null;
}
Long key = Objects.requireNonNull(idToQuark.getKey());
List<ITimeGraphState> eventList = new ArrayList<>();
for (ITmfStateInterval interval : intervals.get(idToQuark.getValue())) {
long startTime = interval.getStartTime();
long duration = interval.getEndTime() - startTime + 1;
Object status = interval.getValue();
Type type = fEntryModelTypes.get(interval.getAttribute());
if (status instanceof Integer) {
int s = (int) status;
int currentThreadQuark = ss.optQuarkRelative(interval.getAttribute(), Attributes.CURRENT_THREAD);
if (type == Type.CPU && s == StateValues.CPU_STATUS_RUN_SYSCALL) {
// add events for all the sampled current threads.
List<@NonNull ITimeGraphState> syscalls = getSyscalls(ss, interval, intervals.get(currentThreadQuark));
syscalls.forEach(timeGraphState -> applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor));
} else if (type == Type.CPU && s == StateValues.CPU_STATUS_RUN_USERMODE) {
// add events for all the sampled current threads.
List<@NonNull TimeGraphState> currentThreads = getCurrentThreads(ss, interval, intervals.get(currentThreadQuark));
currentThreads.forEach(timeGraphState -> applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor));
} else if (type == Type.CURRENT_THREAD && s != 0) {
String execName = null;
synchronized (fExecNamesCache) {
if (fExecNamesCache.containsEntry(status, interval)) {
NavigableSet<ITmfStateInterval> intervalSet = fExecNamesCache.get(s);
ITmfStateInterval execNameInterval = intervalSet.ceiling(interval);
if (execNameInterval != null && CACHE_COMPARATOR.compare(execNameInterval, interval) == 0) {
execName = (String) execNameInterval.getValue();
}
} else {
int quark = ss.optQuarkAbsolute(Attributes.THREADS, Integer.toString(s), Attributes.EXEC_NAME);
if (quark != ITmfStateSystem.INVALID_ATTRIBUTE) {
ITmfStateInterval namedInterval = ss.querySingleState(interval.getEndTime(), quark);
fExecNamesCache.put(s, namedInterval);
execName = (String) namedInterval.getValue();
}
}
}
TimeGraphState timeGraphState = new TimeGraphState(startTime, duration, s, execName != null ? execName + ' ' + '(' + String.valueOf(s) + ')' : String.valueOf(s));
applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
} else if (type == Type.CURRENT_THREAD) {
// add null state when current thread is 0
ITimeGraphState timeGraphState = new TimeGraphState(startTime, duration, Integer.MIN_VALUE);
applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
} else {
TimeGraphState timeGraphState = new TimeGraphState(startTime, duration, s);
applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
}
} else if ((status instanceof Long) && (type == Type.FREQUENCY)) {
long s = (long) status;
// The value needs to fit in an integer
TimeGraphState timeGraphState = new TimeGraphState(startTime, duration, (int) (s / FREQUENCY_MULTIPLIER), String.valueOf(FREQUENCY_FORMATTER.format(s)));
applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
} else {
ITimeGraphState timeGraphState = new TimeGraphState(startTime, duration, Integer.MIN_VALUE);
applyFilterAndAddState(eventList, timeGraphState, key, predicates, monitor);
}
}
rows.add(new TimeGraphRowModel(idToQuark.getKey(), eventList));
}
synchronized (fExecNamesCache) {
fExecNamesCache.clear();
}
return rows;
}
/**
* Add the quarks for the thread status attribute, for all the CPU quarks in
* values
*
* @param ss
* backing state system
* @param values
* original quarks
* @return set containing values, and Current Thread attributes for all the
* quarks
*/
private static @NonNull Set<@NonNull Integer> addThreadStatus(@NonNull ITmfStateSystem ss, @NonNull Collection<@NonNull Integer> values) {
Set<@NonNull Integer> set = new HashSet<>(values);
for (Integer quark : values) {
int parentAttributeQuark = ss.getParentAttributeQuark(quark);
if (ss.getAttributeName(parentAttributeQuark).equals(Attributes.CPUS)) {
int threadStatus = ss.optQuarkRelative(quark, Attributes.CURRENT_THREAD);
if (threadStatus != ITmfStateSystem.INVALID_ATTRIBUTE) {
set.add(threadStatus);
}
}
}
return set;
}
private void addTwinIrqIds(SelectionTimeQueryFilter filter, Map<@NonNull Long, @NonNull Integer> idsToQuark) {
for (Long id : filter.getSelectedItems()) {
Integer quark = fTwinIdsToQuark.get(id);
if (quark != null) {
/*
* The selected id is a twin id. Add it to the idsToQuark map, using its
* corresponding twin entry's quark.
*/
idsToQuark.put(id, quark);
}
}
}
/**
* Get a list of all the current threads over the duration of the current
* usermode interval, as several threads can be scheduled over that interval
*
* @param ss
* backing state system
* @param userModeInterval
* interval representing the CPUs current user mode state.
* @param currentThreadIntervals
* Current Threads intervals for the same CPU with the time query
* filter sampling
* @return a List of intervals with the current thread name label.
*/
private static List<@NonNull TimeGraphState> getCurrentThreads(@NonNull ITmfStateSystem ss, ITmfStateInterval userModeInterval,
@NonNull NavigableSet<ITmfStateInterval> currentThreadIntervals) throws StateSystemDisposedException {
List<@NonNull TimeGraphState> list = new ArrayList<>();
for (ITmfStateInterval currentThread : currentThreadIntervals) {
// filter the current thread intervals which overlap the usermode interval
if (currentThread.getStartTime() <= userModeInterval.getEndTime()
&& currentThread.getEndTime() >= userModeInterval.getStartTime()) {
long start = Long.max(userModeInterval.getStartTime(), currentThread.getStartTime());
long end = Long.min(userModeInterval.getEndTime(), currentThread.getEndTime());
long duration = end - start + 1;
Object tid = currentThread.getValue();
if (tid instanceof Integer) {
int execNameQuark = ss.optQuarkAbsolute(Attributes.THREADS, String.valueOf(tid), Attributes.EXEC_NAME);
if (execNameQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
Object currentThreadName = ss.querySingleState(currentThread.getEndTime(), execNameQuark).getValue();
if (currentThreadName instanceof String) {
list.add(new TimeGraphState(start, duration, StateValues.CPU_STATUS_RUN_USERMODE, (String) currentThreadName));
continue;
}
}
}
list.add(new TimeGraphState(start, duration, StateValues.CPU_STATUS_RUN_USERMODE));
}
}
return list;
}
/**
* Get a list of all the system call states over the duration of the current
* syscall interval, as several threads can be scheduled over that interval
*
* @param ss
* backing state system
* @param syscallInterval
* current syscall interval
* @param currentThreadIntervals
* sampled current thread intervals for the CPU
* @return a List of intervals with the system call name label.
*/
private static List<@NonNull ITimeGraphState> getSyscalls(@NonNull ITmfStateSystem ss, ITmfStateInterval syscallInterval,
@NonNull NavigableSet<ITmfStateInterval> currentThreadIntervals) throws StateSystemDisposedException {
List<@NonNull ITimeGraphState> list = new ArrayList<>();
for (ITmfStateInterval currentThread : currentThreadIntervals) {
// filter the current thread intervals which overlap the syscall interval
if (currentThread.getStartTime() <= syscallInterval.getEndTime()
&& currentThread.getEndTime() >= syscallInterval.getStartTime()) {
long start = Long.max(syscallInterval.getStartTime(), currentThread.getStartTime());
long end = Long.min(syscallInterval.getEndTime(), currentThread.getEndTime());
long duration = end - start + 1;
Object tid = currentThread.getValue();
if (tid instanceof Integer) {
int syscallQuark = ss.optQuarkAbsolute(Attributes.THREADS, String.valueOf(tid), Attributes.SYSTEM_CALL);
if (syscallQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
Object syscallName = ss.querySingleState(start, syscallQuark).getValue();
if (syscallName instanceof String) {
list.add(new TimeGraphState(start, duration, StateValues.CPU_STATUS_RUN_SYSCALL, String.valueOf(syscallName)));
continue;
}
}
}
list.add(new TimeGraphState(start, duration, StateValues.CPU_STATUS_RUN_SYSCALL));
}
}
return list;
}
@Override
public TmfModelResponse<List<ITimeGraphArrow>> fetchArrows(TimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
@Override
public @NonNull String getId() {
return ID;
}
@Override
public TmfModelResponse<Map<String, String>> fetchTooltip(@NonNull SelectionTimeQueryFilter filter, @Nullable IProgressMonitor monitor) {
ITmfStateSystem ss = getAnalysisModule().getStateSystem();
Collection<@NonNull Integer> quarks = getSelectedEntries(filter).values();
boolean isACopy = false;
if (quarks.size() != 1) {
Map<Long, Integer> selectedEntries = new HashMap<>();
for (Long selectedItem : filter.getSelectedItems()) {
Integer quark = fTwinIdsToQuark.get(selectedItem);
if (quark != null && quark >= 0) {
selectedEntries.put(selectedItem, quark);
isACopy = true;
}
}
quarks = selectedEntries.values();
}
long start = filter.getStart();
if (ss == null || quarks.size() != 1 || !getAnalysisModule().isQueryable(start)) {
/*
* We need the ss to query, we should only be querying one attribute and the
* query times should be valid.
*/
return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
int quark = quarks.iterator().next();
// assert that it is a CPU quark
String attributeName = ss.getAttributeName(quark);
Integer cpuNumber = Ints.tryParse(attributeName);
String parent = ss.getAttributeName(ss.getParentAttributeQuark(quark));
if (cpuNumber == null && !(attributeName.equals(Attributes.CURRENT_THREAD) || attributeName.equals(Attributes.CURRENT_FREQUENCY))) {
return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
try {
Map<String, String> retMap = new LinkedHashMap<>(1);
List<ITmfStateInterval> full = ss.queryFullState(start);
Object object = full.get(quark).getValue();
if (object instanceof Integer) {
int status = (int) object;
if (isACopy) {
retMap.put(parent.equals(Attributes.IRQS) ? Messages.ResourcesStatusDataProvider_attributeIrqName :
Messages.ResourcesStatusDataProvider_attributeSoftIrqName,
attributeName);
} else if (parent.equals(Attributes.IRQS) || parent.equals(Attributes.SOFT_IRQS)) {
putCpus(ss, quark, retMap, full, parent, status);
} else if (status == StateValues.CPU_STATUS_IRQ) {
putIrq(ss, attributeName, retMap, full, Attributes.IRQS, status);
} else if (status == StateValues.CPU_STATUS_SOFTIRQ) {
putIrq(ss, attributeName, retMap, full, Attributes.SOFT_IRQS, status);
} else if (status == StateValues.CPU_STATUS_RUN_USERMODE || status == StateValues.CPU_STATUS_RUN_SYSCALL) {
putCpuTooltip(ss, attributeName, retMap, full, status);
} else if (attributeName.equals(Attributes.CURRENT_THREAD)) {
putCurrentThreadTooltip(ss, retMap, full, status);
}
} else if (object instanceof Long && attributeName.equals(Attributes.CURRENT_FREQUENCY)) {
retMap.put("Frequency", FREQUENCY_FORMATTER.format(object)); //$NON-NLS-1$
}
return new TmfModelResponse<>(retMap, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
} catch (StateSystemDisposedException e) {
}
return new TmfModelResponse<>(null, ITmfResponse.Status.COMPLETED, CommonStatusMessage.COMPLETED);
}
private static void putCpus(ITmfStateSystem ss, int quark, Map<String, String> retMap,
List<ITmfStateInterval> full, String irqs, int status) {
List<@NonNull String> cpuList = new ArrayList<>();
int grandParentAttribute = ss.getParentAttributeQuark(ss.getParentAttributeQuark(quark));
if (grandParentAttribute != org.eclipse.tracecompass.statesystem.core.ITmfStateSystem.ROOT_ATTRIBUTE
&& ss.getAttributeName(ss.getParentAttributeQuark(ss.getParentAttributeQuark(ss.getParentAttributeQuark(quark)))).equals(Attributes.CPUS)) {
cpuList.add(ss.getAttributeName(grandParentAttribute));
} else {
for (int cpuQuark : ss.getQuarks(Attributes.CPUS, "*", irqs, ss.getAttributeName(quark))) { //$NON-NLS-1$
ITmfStateInterval interval = full.get(cpuQuark);
if (interval.getValue() != null) {
Object object = full.get(cpuQuark).getValue();
if (object instanceof Integer) {
int objectStatus = (int) object;
if (objectStatus == status) {
String cpu = ss.getAttributeName(ss.getParentAttributeQuark(ss.getParentAttributeQuark(cpuQuark)));
cpuList.add(cpu);
}
}
}
}
}
if (!cpuList.isEmpty()) {
Collections.sort(cpuList, (s1, s2) -> Integer.compare(Integer.parseInt(s1), Integer.parseInt(s2)));
retMap.put(Messages.ResourcesStatusDataProvider_attributeCpuName, String.join(", ", cpuList)); //$NON-NLS-1$
}
}
private static void putIrq(ITmfStateSystem ss, String attributeName,
Map<String, String> retMap, List<ITmfStateInterval> full, String irqs, int status) {
for (int irqQuark : ss.getQuarks(Attributes.CPUS, attributeName, irqs, "*")) { //$NON-NLS-1$
ITmfStateInterval interval = full.get(irqQuark);
if (interval.getValue() != null) {
Object object = full.get(irqQuark).getValue();
if (object instanceof Integer) {
int objectStatus = (int) object;
if (objectStatus == status) {
retMap.put(status == StateValues.CPU_STATUS_IRQ ? Messages.ResourcesStatusDataProvider_attributeIrqName :
Messages.ResourcesStatusDataProvider_attributeSoftIrqName,
ss.getAttributeName(irqQuark));
return;
}
}
}
}
}
private static void putCpuTooltip(ITmfStateSystem ss, String attributeName,
Map<String, String> retMap, List<ITmfStateInterval> full, int status) {
int currentThreadQuark = ss.optQuarkAbsolute(Attributes.CPUS, attributeName, Attributes.CURRENT_THREAD);
if (currentThreadQuark == ITmfStateSystem.INVALID_ATTRIBUTE) {
return;
}
Object currentThreadObject = full.get(currentThreadQuark).getValue();
if (currentThreadObject instanceof Number) {
String currentThread = currentThreadObject.toString();
retMap.put(Messages.ResourcesStatusDataProvider_attributeTidName, currentThread);
int execNameQuark = ss.optQuarkAbsolute(Attributes.THREADS, currentThread, Attributes.EXEC_NAME);
if (execNameQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
Object processName = full.get(execNameQuark).getValue();
if (processName instanceof String) {
retMap.put(Messages.ResourcesStatusDataProvider_attributeProcessName, (String) processName);
}
}
int syscallQuark = ss.optQuarkAbsolute(Attributes.THREADS, currentThread, Attributes.SYSTEM_CALL);
if (status == StateValues.CPU_STATUS_RUN_SYSCALL
&& syscallQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
Object syscall = full.get(syscallQuark).getValue();
if (syscall instanceof String) {
retMap.put(Attributes.SYSTEM_CALL, (String) syscall);
}
}
}
}
private static void putCurrentThreadTooltip(ITmfStateSystem ss,
Map<String, String> retMap, List<ITmfStateInterval> full, int tid) {
String currentThread = String.valueOf(tid);
retMap.put(org.eclipse.tracecompass.analysis.os.linux.core.event.aspect.Messages.AspectName_Tid, currentThread);
int execNameQuark = ss.optQuarkAbsolute(Attributes.THREADS, currentThread, Attributes.EXEC_NAME);
if (execNameQuark != ITmfStateSystem.INVALID_ATTRIBUTE) {
Object processName = full.get(execNameQuark).getValue();
if (processName instanceof String) {
retMap.put(Attributes.EXEC_NAME, (String) processName);
}
}
}
@Override
protected boolean isCacheable() {
return true;
}
@Override
public @NonNull Multimap<@NonNull String, @NonNull String> getFilterData(long entryId, long time, @Nullable IProgressMonitor monitor) {
Multimap<@NonNull String, @NonNull String> data = HashMultimap.create();
data.putAll(super.getFilterData(entryId, time, monitor));
SelectionTimeQueryFilter filter = new SelectionTimeQueryFilter(Collections.singletonList(time), Collections.singleton(Objects.requireNonNull(entryId)));
TmfModelResponse<Map<String, String>> response = fetchTooltip(filter, monitor);
Map<@NonNull String, @NonNull String> model = response.getModel();
if (model != null) {
for (Entry<String, String> entry : model.entrySet()) {
data.put(entry.getKey(), entry.getValue());
}
}
return data;
}
}