tmf: Bug 495219: Fix NPE in checkpoint indexer seeking on disposed trace
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / trace / indexer / checkpoint / TmfCheckpointIndexer.java
index 1b0d9636c7f0a39d0b695dad65cec246aa2e79d7..355f67e04125323f5b4f9be867174b42dcaba7ce 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2012, 2014 Ericsson
+ * Copyright (c) 2012, 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
@@ -18,6 +18,7 @@ import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.internal.tmf.core.Activator;
 import org.eclipse.tracecompass.internal.tmf.core.Messages;
 import org.eclipse.tracecompass.internal.tmf.core.TmfCoreTracer;
 import org.eclipse.tracecompass.internal.tmf.core.trace.indexer.TmfMemoryIndex;
@@ -28,25 +29,25 @@ import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceUpdatedSignal;
 import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
 import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
-import org.eclipse.tracecompass.tmf.core.trace.ITmfTraceCompleteness;
 import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
 import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
 
 /**
  * A simple indexer that manages the trace index as an array of trace
- * checkpoints. Checkpoints are stored in memory at fixed intervals (event rank) in
- * ascending timestamp order.
+ * checkpoints. Checkpoints are stored in memory at fixed intervals (event rank)
+ * in ascending timestamp order.
  * <p>
  * The goal being to access a random trace event reasonably fast from the user's
- * standpoint, picking the right interval value becomes a trade-off between speed
- * and memory usage (a shorter inter-event interval is faster but requires more
- * checkpoints).
+ * standpoint, picking the right interval value becomes a trade-off between
+ * speed and memory usage (a shorter inter-event interval is faster but requires
+ * more checkpoints).
  * <p>
  * Locating a specific checkpoint is trivial for both rank (rank % interval) and
- * timestamp (bsearch in the array).
- * *
+ * timestamp (bsearch in the array). *
+ *
  * @see ITmfTrace
  * @see ITmfEvent
  *
@@ -78,6 +79,9 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
      */
     private ITmfEventRequest fIndexingRequest = null;
 
+    /** Whether or not the index was built once */
+    private boolean fBuiltOnce;
+
     // ------------------------------------------------------------------------
     // Construction
     // ------------------------------------------------------------------------
@@ -86,7 +90,8 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
      * Basic constructor that uses the default trace block size as checkpoints
      * intervals
      *
-     * @param trace the trace to index
+     * @param trace
+     *            the trace to index
      */
     public TmfCheckpointIndexer(final ITmfTrace trace) {
         this(trace, TmfEventProvider.DEFAULT_BLOCK_SIZE);
@@ -95,8 +100,10 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
     /**
      * Full trace indexer
      *
-     * @param trace the trace to index
-     * @param interval the checkpoints interval
+     * @param trace
+     *            the trace to index
+     * @param interval
+     *            the checkpoints interval
      */
     public TmfCheckpointIndexer(final ITmfTrace trace, final int interval) {
         fTrace = trace;
@@ -106,10 +113,11 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
     }
 
     /**
-     * Creates the index instance. Classes extending this class
-     * can override this to provide a different index implementation.
+     * Creates the index instance. Classes extending this class can override
+     * this to provide a different index implementation.
      *
-     * @param trace the trace to index
+     * @param trace
+     *            the trace to index
      * @return the index
      */
     protected ITmfCheckpointIndex createIndex(final ITmfTrace trace) {
@@ -141,6 +149,9 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
     @Override
     public void buildIndex(final long offset, final TmfTimeRange range, final boolean waitForCompletion) {
 
+        long indexingOffset = offset;
+        TmfTimeRange indexingTimeRange = range;
+
         // Don't do anything if we are already indexing
         synchronized (fTraceIndex) {
             if (fIsIndexing) {
@@ -149,51 +160,28 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
             fIsIndexing = true;
         }
 
-        // No need to build the index, it has been restored
-        if (!fTraceIndex.isCreatedFromScratch()) {
+        // Restore previously built index values
+        if (!fTraceIndex.isCreatedFromScratch() && !fBuiltOnce && fTraceIndex.getNbEvents() > 0) {
+            indexingOffset = fTraceIndex.getNbEvents();
+            indexingTimeRange = new TmfTimeRange(fTraceIndex.getTimeRange().getStartTime(), TmfTimestamp.BIG_CRUNCH);
+            TmfCoreTracer.traceIndexer("restoring index. nbEvents: " + fTraceIndex.getNbEvents() + " time range: " + fTraceIndex.getTimeRange()); //$NON-NLS-1$ //$NON-NLS-2$
             // Set some trace attributes that depends on indexing
-            TmfCoreTracer.traceIndexer("Restoring index. nbEvents: " + fTraceIndex.getNbEvents() + " time range: " + fTraceIndex.getTimeRange()); //$NON-NLS-1$ //$NON-NLS-2$
-            TmfTraceUpdatedSignal signal = new TmfTraceUpdatedSignal(this, fTrace, new TmfTimeRange(fTraceIndex.getTimeRange().getStartTime(), fTraceIndex.getTimeRange().getEndTime()), fTraceIndex.getNbEvents());
-            if (waitForCompletion) {
-                fTrace.broadcast(signal);
-            } else {
-                fTrace.broadcastAsync(signal);
-            }
-            fIsIndexing = false;
-            return;
+            TmfTraceUpdatedSignal signal = new TmfTraceUpdatedSignal(this, fTrace, new TmfTimeRange(fTraceIndex.getTimeRange().getStartTime(), fTraceIndex.getTimeRange().getEndTime()), indexingOffset);
+            fTrace.broadcast(signal);
         }
 
-        TmfCoreTracer.traceIndexer("buildIndex. offset: " + offset + " time range: " + range); //$NON-NLS-1$ //$NON-NLS-2$
+        TmfCoreTracer.traceIndexer("buildIndex. offset: " + indexingOffset + " (requested " + offset + ")" + " time range: " + range); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 
         // The monitoring job
-        final Job job = new Job("Indexing " + fTrace.getName() + "...") { //$NON-NLS-1$ //$NON-NLS-2$
-            @Override
-            protected IStatus run(final IProgressMonitor monitor) {
-                monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
-                while (!monitor.isCanceled()) {
-                    try {
-                        long prevNbEvents = fTrace.getNbEvents();
-                        Thread.sleep(250);
-                        long nbEvents = fTrace.getNbEvents();
-                        setName(Messages.TmfCheckpointIndexer_Indexing + ' ' + fTrace.getName() + " (" + String.format("%,d", nbEvents) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-                        // setName doesn't refresh the UI, setTaskName does
-                        long rate = (nbEvents - prevNbEvents) * 4;
-                        monitor.setTaskName(String.format("%,d", rate) + " " + Messages.TmfCheckpointIndexer_EventsPerSecond); //$NON-NLS-1$ //$NON-NLS-2$
-                    } catch (final InterruptedException e) {
-                        return Status.OK_STATUS;
-                    }
-                }
-                monitor.done();
-                return Status.OK_STATUS;
-            }
-        };
-        job.setSystem(!isCompleteTrace(fTrace));
+        TmfIndexingJob job = new TmfIndexingJob("Indexing " + fTrace.getName() + "..."); //$NON-NLS-1$ //$NON-NLS-2$
+        job.setSystem(fBuiltOnce);
+        fBuiltOnce = true;
         job.schedule();
 
         // Build a background request for all the trace data. The index is
         // updated as we go by readNextEvent().
         fIndexingRequest = new TmfEventRequest(ITmfEvent.class,
-                range, offset, ITmfEventRequest.ALL_DATA,
+                indexingTimeRange, indexingOffset, ITmfEventRequest.ALL_DATA,
                 ITmfEventRequest.ExecutionType.BACKGROUND) {
             @Override
             public void handleData(final ITmfEvent event) {
@@ -206,20 +194,23 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
 
             @Override
             public void handleSuccess() {
-                fTraceIndex.setTimeRange(fTrace.getTimeRange());
-                fTraceIndex.setNbEvents(fTrace.getNbEvents());
-                if (isCompleteTrace(fTrace)) {
-                    fTraceIndex.setIndexComplete();
-                }
                 updateTraceStatus();
             }
 
             @Override
             public void handleCompleted() {
                 job.cancel();
+                fTraceIndex.setTimeRange(fTrace.getTimeRange());
+                fTraceIndex.setNbEvents(fTrace.getNbEvents());
                 super.handleCompleted();
                 fIsIndexing = false;
-                TmfCoreTracer.traceIndexer("Build index request done. nbEvents: " + fTraceIndex.getNbEvents() + " time range: " + fTraceIndex.getTimeRange()); //$NON-NLS-1$ //$NON-NLS-2$
+                TmfCoreTracer.traceIndexer("Build index request completed. nbEvents: " + fTraceIndex.getNbEvents() + " time range: " + fTraceIndex.getTimeRange()); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+
+            @Override
+            public void fail(Exception e) {
+                super.fail(e);
+                job.setException(e);
             }
 
             private void updateTraceStatus() {
@@ -242,8 +233,10 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
     /**
      * Notify the interested parties that the trace time range has changed
      *
-     * @param startTime the new start time
-     * @param endTime the new end time
+     * @param startTime
+     *            the new start time
+     * @param endTime
+     *            the new end time
      */
     private void signalNewTimeRange(final @NonNull ITmfTimestamp startTime, final @NonNull ITmfTimestamp endTime) {
         fTrace.broadcast(new TmfTraceUpdatedSignal(fTrace, fTrace, new TmfTimeRange(startTime, endTime), fTrace.getNbEvents()));
@@ -287,7 +280,8 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
         if (index < 0) {
             index = Math.max(0, -(index + 2));
         } else {
-            // If timestamp was in the list, use previous index to be able to find the
+            // If timestamp was in the list, use previous index to be able to
+            // find the
             // first event with the same timestamp before the checkpoint
             index = Math.max(0, index - 1);
         }
@@ -314,19 +308,25 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
     /**
      * Position the trace at the given checkpoint
      *
-     * @param checkpoint the checkpoint index
+     * @param checkpointIndex
+     *            the checkpoint index
      * @return the corresponding context
      */
-    private ITmfContext restoreCheckpoint(final long checkpoint) {
+    private ITmfContext restoreCheckpoint(final long checkpointIndex) {
         ITmfLocation location = null;
         long index = 0;
         synchronized (fTraceIndex) {
             if (!fTraceIndex.isEmpty()) {
-                index = checkpoint;
+                index = checkpointIndex;
                 if (index >= fTraceIndex.size()) {
                     index = fTraceIndex.size() - 1;
                 }
-                location = fTraceIndex.get(index).getLocation();
+                ITmfCheckpoint checkpoint = fTraceIndex.get(index);
+                TmfCoreTracer.traceIndexer("Restored checkpoint: " + checkpoint); //$NON-NLS-1$
+                if (checkpoint == null) {
+                    return fTrace.seekEvent((ITmfLocation) null);
+                }
+                location = checkpoint.getLocation();
             }
         }
         final ITmfContext context = fTrace.seekEvent(location);
@@ -345,7 +345,36 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer {
         return fTraceIndex;
     }
 
-    private static boolean isCompleteTrace(ITmfTrace trace) {
-        return !(trace instanceof ITmfTraceCompleteness) || ((ITmfTraceCompleteness)trace).isComplete();
+    private final class TmfIndexingJob extends Job {
+        private Exception fException = null;
+
+        private TmfIndexingJob(String name) {
+            super(name);
+        }
+
+        @Override
+        protected IStatus run(final IProgressMonitor monitor) {
+            monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
+            while (!monitor.isCanceled()) {
+                try {
+                    long prevNbEvents = fTrace.getNbEvents();
+                    Thread.sleep(250);
+                    long nbEvents = fTrace.getNbEvents();
+                    setName(Messages.TmfCheckpointIndexer_Indexing + ' ' + fTrace.getName() + " (" + String.format("%,d", nbEvents) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+                    // setName doesn't refresh the UI, setTaskName does
+                    long rate = (nbEvents - prevNbEvents) * 4;
+                    monitor.setTaskName(String.format("%,d", rate) + " " + Messages.TmfCheckpointIndexer_EventsPerSecond); //$NON-NLS-1$ //$NON-NLS-2$
+                } catch (final InterruptedException e) {
+                    return Status.OK_STATUS;
+                }
+            }
+            monitor.done();
+            return fException != null ? new Status(IStatus.ERROR, Activator.PLUGIN_ID, fException.getMessage(), fException) : Status.OK_STATUS;
+        }
+
+        public void setException(Exception e) {
+            fException = e;
+        }
+
     }
 }
This page took 0.033013 seconds and 5 git commands to generate.