/******************************************************************************* * 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 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 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; } }