| /******************************************************************************* |
| * Copyright (c) 2010, 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.tmf.ui.views.timechart; |
| |
| import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; |
| |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.Vector; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeEvent; |
| import org.eclipse.tracecompass.tmf.ui.widgets.timegraph.model.ITimeGraphEntry; |
| |
| /** |
| * An entry (row) in the time chart analysis view |
| * |
| * @version 1.0 |
| * @author Patrick Tasse |
| */ |
| public class TimeChartAnalysisEntry implements ITimeGraphEntry { |
| |
| private final ITmfTrace fTrace; |
| private final Vector<TimeChartEvent> fTraceEvents; |
| private int fPower = 0; // 2^fPower nanoseconds per vector position |
| private long fReferenceTime = -1; // time corresponding to beginning of index 0 |
| private long fStartTime = -1; // time of first event |
| private long fStopTime = -1; // time of last event |
| private long fLastRank = -1; // rank of last processed trace event |
| |
| TimeChartAnalysisEntry(ITmfTrace trace, int modelSize) { |
| fTrace = trace; |
| fTraceEvents = new Vector<>(modelSize); |
| } |
| |
| @Override |
| public List<@NonNull ITimeGraphEntry> getChildren() { |
| return null; |
| } |
| |
| @Override |
| public ITimeGraphEntry getParent() { |
| return null; |
| } |
| |
| @Override |
| public boolean hasChildren() { |
| return false; |
| } |
| |
| @Override |
| public String getName() { |
| return fTrace.getName(); |
| } |
| |
| @Override |
| public long getStartTime() { |
| return fStartTime; |
| } |
| |
| @Override |
| public long getEndTime() { |
| return fStopTime; |
| } |
| |
| @Override |
| public boolean hasTimeEvents() { |
| return true; |
| } |
| |
| @Override |
| public Iterator<@NonNull ITimeEvent> getTimeEventsIterator() { |
| return new EntryIterator(0, Long.MAX_VALUE, 0); |
| } |
| |
| @Override |
| public Iterator<@NonNull ITimeEvent> getTimeEventsIterator(long startTime, long stopTime, long maxDuration) { |
| return new EntryIterator(startTime, stopTime, maxDuration); |
| } |
| |
| private class EntryIterator implements Iterator<@NonNull ITimeEvent> { |
| private final long fIteratorStartTime; |
| private final long fIteratorStopTime; |
| private final long fIteratorMaxDuration; |
| private long lastTime = -1; |
| private TimeChartEvent next = null; |
| private Iterator<ITimeEvent> nestedIterator = null; |
| |
| public EntryIterator(long startTime, long stopTime, long maxDuration) { |
| fIteratorStartTime = startTime; |
| fIteratorStopTime = stopTime; |
| fIteratorMaxDuration = maxDuration; |
| } |
| |
| @Override |
| public boolean hasNext() { |
| synchronized (fTraceEvents) { |
| if (next != null) { |
| return true; |
| } |
| if (nestedIterator != null) { |
| if (nestedIterator.hasNext()) { |
| return true; |
| } |
| nestedIterator = null; |
| } |
| long time = (lastTime == -1) ? fStartTime : lastTime; |
| int index = (fReferenceTime == -1) ? 0 : (int) ((time - fReferenceTime) >> fPower); |
| while (index < fTraceEvents.size()) { |
| TimeChartEvent event = fTraceEvents.get(index++); |
| if (event != null && (lastTime == -1 || event.getTime() > time)) { |
| if (event.getTime() + event.getDuration() >= fIteratorStartTime && event.getTime() <= fIteratorStopTime) { |
| if (event.getItemizedEntry() == null || event.getDuration() <= fIteratorMaxDuration) { |
| lastTime = event.getTime() + event.getDuration(); |
| next = event; |
| return true; |
| } |
| nestedIterator = event.getItemizedEntry().getTimeEventsIterator(fIteratorStartTime, fIteratorStopTime, fIteratorMaxDuration); |
| return nestedIterator.hasNext(); |
| } |
| } |
| } |
| return false; |
| } |
| } |
| |
| @Override |
| public TimeChartEvent next() { |
| synchronized (fTraceEvents) { |
| if (nestedIterator != null) { |
| TimeChartEvent event = (TimeChartEvent) nestedIterator.next(); |
| lastTime = event.getTime() + event.getDuration(); |
| return event; |
| } |
| if (hasNext()) { |
| TimeChartEvent event = checkNotNull(next); |
| next = null; |
| return event; |
| } |
| throw new NoSuchElementException(); |
| } |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| } |
| |
| /** |
| * Add a time event to the time chart entry |
| * |
| * @param timeEvent |
| * The event to add |
| */ |
| public void addTraceEvent(ITimeEvent timeEvent) { |
| long time = timeEvent.getTime(); |
| synchronized (fTraceEvents) { |
| long index = (fReferenceTime == -1) ? 0 : (time - fReferenceTime) >> fPower; |
| if (index < 0) { |
| if (fTraceEvents.capacity() - fTraceEvents.size() < -index) { |
| int powershift = (-index + fTraceEvents.size() <= 2 * fTraceEvents.capacity()) ? 1 : |
| (int) Math.ceil(Math.log((double) (-index + fTraceEvents.size()) / fTraceEvents.capacity()) / Math.log(2)); |
| merge(powershift); |
| index = (int) ((time - fReferenceTime) >> fPower); |
| } |
| shift((int) -index); |
| index = 0; |
| fTraceEvents.set(0, (TimeChartEvent) timeEvent); |
| } else if (index < fTraceEvents.capacity()) { |
| if (index >= fTraceEvents.size()) { |
| fTraceEvents.setSize((int) index + 1); |
| } |
| } else { |
| int powershift = (index < 2 * fTraceEvents.capacity()) ? 1 : |
| (int) Math.ceil(Math.log((double) (index + 1) / fTraceEvents.capacity()) / Math.log(2)); |
| merge(powershift); |
| index = (int) ((time - fReferenceTime) >> fPower); |
| fTraceEvents.setSize((int) index + 1); |
| } |
| TimeChartEvent event = fTraceEvents.get((int) index); |
| if (event == null) { |
| fTraceEvents.set((int) index, (TimeChartEvent) timeEvent); |
| } else { |
| if (event.getItemizedEntry() == null) { |
| event.merge((TimeChartEvent) timeEvent); |
| } else { |
| event.mergeDecorations((TimeChartEvent) timeEvent); |
| event.getItemizedEntry().addTraceEvent(timeEvent); |
| } |
| } |
| if (fReferenceTime == -1 || time < fReferenceTime) { |
| fReferenceTime = (time >> fPower) << fPower; |
| } |
| if (fStartTime == -1 || time < fStartTime) { |
| fStartTime = time; |
| } |
| if (fStopTime == -1 || time > fStopTime) { |
| fStopTime = time; |
| } |
| } |
| } |
| |
| private void merge(int powershift) { |
| fPower += powershift; |
| fReferenceTime = (fReferenceTime >> fPower) << fPower; |
| int index = 0; |
| for (int i = 0; i < fTraceEvents.size(); i++) { |
| TimeChartEvent event = fTraceEvents.get(i); |
| if (event != null) { |
| index = (int) ((event.getTime() - fReferenceTime) >> fPower); |
| TimeChartEvent mergedEvent = fTraceEvents.get(index); |
| if (mergedEvent == null) { |
| fTraceEvents.set(index, event); |
| } else { |
| mergedEvent.merge(event); |
| } |
| if (i != index) { |
| fTraceEvents.set(i, null); |
| } |
| } |
| } |
| fTraceEvents.setSize(index + 1); |
| } |
| |
| private void shift(int indexshift) { |
| int oldSize = fTraceEvents.size(); |
| fTraceEvents.setSize(oldSize + indexshift); |
| for (int i = oldSize - 1; i >= 0; i--) { |
| fTraceEvents.set(i + indexshift, fTraceEvents.get(i)); |
| } |
| for (int i = 0; i < indexshift; i++) { |
| fTraceEvents.set(i, null); |
| } |
| } |
| |
| /** |
| * Retrieve the trace associated with this entry |
| * |
| * @return The trace object |
| */ |
| public ITmfTrace getTrace() { |
| return fTrace; |
| } |
| |
| /** |
| * Set the last rank of the entry |
| * |
| * @param rank |
| * The rank to set |
| */ |
| public void setLastRank(long rank) { |
| fLastRank = rank; |
| } |
| |
| /** |
| * Retrieve the last rank of the entry |
| * |
| * @return The last rank |
| */ |
| public long getLastRank() { |
| return fLastRank; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public boolean matches(@NonNull Pattern pattern) { |
| return getName() != null ? pattern.matcher(getName()).find() : false; |
| } |
| } |