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
CommitLineData
20658947 1/*******************************************************************************
7b3400bd 2 * Copyright (c) 2012, 2016 Ericsson
9b749023 3 *
20658947
FC
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
9b749023 8 *
20658947
FC
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
089a4872 11 * Bernd Hufmann - Update way of broadcasting of TmfTraceUpdatedSignal
20658947
FC
12 *******************************************************************************/
13
2bdf0193 14package org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint;
20658947 15
20658947
FC
16import org.eclipse.core.runtime.IProgressMonitor;
17import org.eclipse.core.runtime.IStatus;
18import org.eclipse.core.runtime.Status;
19import org.eclipse.core.runtime.jobs.Job;
6cfc180e 20import org.eclipse.jdt.annotation.NonNull;
12c61248 21import org.eclipse.tracecompass.internal.tmf.core.Activator;
2bdf0193 22import org.eclipse.tracecompass.internal.tmf.core.Messages;
5cd3530c 23import org.eclipse.tracecompass.internal.tmf.core.TmfCoreTracer;
2bdf0193
AM
24import org.eclipse.tracecompass.internal.tmf.core.trace.indexer.TmfMemoryIndex;
25import org.eclipse.tracecompass.tmf.core.component.TmfEventProvider;
26import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
27import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
28import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
29import org.eclipse.tracecompass.tmf.core.signal.TmfTraceUpdatedSignal;
30import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
31import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
0861e8e8 32import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
2bdf0193
AM
33import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
34import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
2bdf0193
AM
35import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer;
36import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
20658947
FC
37
38/**
2848c377 39 * A simple indexer that manages the trace index as an array of trace
12c61248
MK
40 * checkpoints. Checkpoints are stored in memory at fixed intervals (event rank)
41 * in ascending timestamp order.
20658947
FC
42 * <p>
43 * The goal being to access a random trace event reasonably fast from the user's
12c61248
MK
44 * standpoint, picking the right interval value becomes a trade-off between
45 * speed and memory usage (a shorter inter-event interval is faster but requires
46 * more checkpoints).
20658947
FC
47 * <p>
48 * Locating a specific checkpoint is trivial for both rank (rank % interval) and
12c61248
MK
49 * timestamp (bsearch in the array). *
50 *
f7703ed6
FC
51 * @see ITmfTrace
52 * @see ITmfEvent
a3db8436
AM
53 *
54 * @author Francois Chouinard
20658947 55 */
6256d8ad 56public class TmfCheckpointIndexer implements ITmfTraceIndexer {
20658947
FC
57
58 // ------------------------------------------------------------------------
59 // Attributes
60 // ------------------------------------------------------------------------
61
6f4e8ec0 62 /** The event trace to index */
6256d8ad 63 protected final ITmfTrace fTrace;
20658947 64
6f4e8ec0 65 /** The interval between checkpoints */
0316808c 66 private final int fCheckpointInterval;
20658947 67
6f4e8ec0 68 /** The event trace to index */
9e0640dc
FC
69 private boolean fIsIndexing;
70
20658947
FC
71 /**
72 * The trace index. It is composed of checkpoints taken at intervals of
73 * fCheckpointInterval events.
74 */
032ecd45 75 protected final ITmfCheckpointIndex fTraceIndex;
20658947 76
b5ee6881 77 /**
9b749023 78 * The indexing request
b5ee6881 79 */
5419a136 80 private ITmfEventRequest fIndexingRequest = null;
9b749023 81
0861e8e8
MAL
82 /** Whether or not the index was built once */
83 private boolean fBuiltOnce;
84
20658947
FC
85 // ------------------------------------------------------------------------
86 // Construction
87 // ------------------------------------------------------------------------
88
89 /**
90 * Basic constructor that uses the default trace block size as checkpoints
91 * intervals
9b749023 92 *
12c61248
MK
93 * @param trace
94 * the trace to index
20658947 95 */
6256d8ad 96 public TmfCheckpointIndexer(final ITmfTrace trace) {
fd3f1eff 97 this(trace, TmfEventProvider.DEFAULT_BLOCK_SIZE);
20658947
FC
98 }
99
100 /**
101 * Full trace indexer
9b749023 102 *
12c61248
MK
103 * @param trace
104 * the trace to index
105 * @param interval
106 * the checkpoints interval
20658947 107 */
6256d8ad 108 public TmfCheckpointIndexer(final ITmfTrace trace, final int interval) {
20658947
FC
109 fTrace = trace;
110 fCheckpointInterval = interval;
032ecd45 111 fTraceIndex = createIndex(trace);
9e0640dc
FC
112 fIsIndexing = false;
113 }
114
032ecd45 115 /**
12c61248
MK
116 * Creates the index instance. Classes extending this class can override
117 * this to provide a different index implementation.
032ecd45 118 *
12c61248
MK
119 * @param trace
120 * the trace to index
032ecd45 121 * @return the index
032ecd45
MAL
122 */
123 protected ITmfCheckpointIndex createIndex(final ITmfTrace trace) {
124 return new TmfMemoryIndex(trace);
125 }
126
b5ee6881
FC
127 @Override
128 public void dispose() {
129 if ((fIndexingRequest != null) && !fIndexingRequest.isCompleted()) {
130 fIndexingRequest.cancel();
b5ee6881 131 }
032ecd45
MAL
132
133 fTraceIndex.dispose();
b5ee6881
FC
134 }
135
9e0640dc
FC
136 // ------------------------------------------------------------------------
137 // ITmfTraceIndexer - isIndexing
138 // ------------------------------------------------------------------------
139
9e0640dc
FC
140 @Override
141 public boolean isIndexing() {
142 return fIsIndexing;
20658947
FC
143 }
144
145 // ------------------------------------------------------------------------
1703b536 146 // ITmfTraceIndexer - buildIndex
20658947
FC
147 // ------------------------------------------------------------------------
148
20658947 149 @Override
9e0640dc
FC
150 public void buildIndex(final long offset, final TmfTimeRange range, final boolean waitForCompletion) {
151
0861e8e8
MAL
152 long indexingOffset = offset;
153 TmfTimeRange indexingTimeRange = range;
154
9b749023 155 // Don't do anything if we are already indexing
9e0640dc
FC
156 synchronized (fTraceIndex) {
157 if (fIsIndexing) {
158 return;
159 }
160 fIsIndexing = true;
161 }
20658947 162
0861e8e8
MAL
163 // Restore previously built index values
164 if (!fTraceIndex.isCreatedFromScratch() && !fBuiltOnce && fTraceIndex.getNbEvents() > 0) {
165 indexingOffset = fTraceIndex.getNbEvents();
166 indexingTimeRange = new TmfTimeRange(fTraceIndex.getTimeRange().getStartTime(), TmfTimestamp.BIG_CRUNCH);
167 TmfCoreTracer.traceIndexer("restoring index. nbEvents: " + fTraceIndex.getNbEvents() + " time range: " + fTraceIndex.getTimeRange()); //$NON-NLS-1$ //$NON-NLS-2$
089a4872 168 // Set some trace attributes that depends on indexing
0861e8e8
MAL
169 TmfTraceUpdatedSignal signal = new TmfTraceUpdatedSignal(this, fTrace, new TmfTimeRange(fTraceIndex.getTimeRange().getStartTime(), fTraceIndex.getTimeRange().getEndTime()), indexingOffset);
170 fTrace.broadcast(signal);
089a4872
BH
171 }
172
0861e8e8 173 TmfCoreTracer.traceIndexer("buildIndex. offset: " + indexingOffset + " (requested " + offset + ")" + " time range: " + range); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
5cd3530c 174
20658947 175 // The monitoring job
12c61248 176 TmfIndexingJob job = new TmfIndexingJob("Indexing " + fTrace.getName() + "..."); //$NON-NLS-1$ //$NON-NLS-2$
0861e8e8
MAL
177 job.setSystem(fBuiltOnce);
178 fBuiltOnce = true;
20658947
FC
179 job.schedule();
180
089a4872
BH
181 // Build a background request for all the trace data. The index is
182 // updated as we go by readNextEvent().
183 fIndexingRequest = new TmfEventRequest(ITmfEvent.class,
0861e8e8 184 indexingTimeRange, indexingOffset, ITmfEventRequest.ALL_DATA,
089a4872
BH
185 ITmfEventRequest.ExecutionType.BACKGROUND) {
186 @Override
187 public void handleData(final ITmfEvent event) {
188 super.handleData(event);
41f3b36b
AM
189 // Update the trace status at regular intervals
190 if ((getNbRead() % fCheckpointInterval) == 0) {
191 updateTraceStatus();
20658947 192 }
089a4872 193 }
20658947 194
089a4872
BH
195 @Override
196 public void handleSuccess() {
089a4872
BH
197 updateTraceStatus();
198 }
20658947 199
089a4872
BH
200 @Override
201 public void handleCompleted() {
202 job.cancel();
0861e8e8
MAL
203 fTraceIndex.setTimeRange(fTrace.getTimeRange());
204 fTraceIndex.setNbEvents(fTrace.getNbEvents());
089a4872
BH
205 super.handleCompleted();
206 fIsIndexing = false;
0861e8e8 207 TmfCoreTracer.traceIndexer("Build index request completed. nbEvents: " + fTraceIndex.getNbEvents() + " time range: " + fTraceIndex.getTimeRange()); //$NON-NLS-1$ //$NON-NLS-2$
089a4872 208 }
20658947 209
12c61248
MK
210 @Override
211 public void fail(Exception e) {
212 super.fail(e);
213 job.setException(e);
214 }
215
089a4872
BH
216 private void updateTraceStatus() {
217 if (fTrace.getNbEvents() > 0) {
218 signalNewTimeRange(fTrace.getStartTime(), fTrace.getEndTime());
83f3a737 219 }
089a4872
BH
220 }
221 };
20658947 222
d337369a 223 // Submit the request and wait for completion if required
b5ee6881 224 fTrace.sendRequest(fIndexingRequest);
d337369a
FC
225 if (waitForCompletion) {
226 try {
b5ee6881 227 fIndexingRequest.waitForCompletion();
d337369a
FC
228 } catch (final InterruptedException e) {
229 }
230 }
20658947
FC
231 }
232
7e6347b0
FC
233 /**
234 * Notify the interested parties that the trace time range has changed
9b749023 235 *
12c61248
MK
236 * @param startTime
237 * the new start time
238 * @param endTime
239 * the new end time
7e6347b0 240 */
6cfc180e 241 private void signalNewTimeRange(final @NonNull ITmfTimestamp startTime, final @NonNull ITmfTimestamp endTime) {
032ecd45 242 fTrace.broadcast(new TmfTraceUpdatedSignal(fTrace, fTrace, new TmfTimeRange(startTime, endTime), fTrace.getNbEvents()));
1703b536
FC
243 }
244
245 // ------------------------------------------------------------------------
246 // ITmfTraceIndexer - updateIndex
247 // ------------------------------------------------------------------------
248
249 @Override
d337369a 250 public synchronized void updateIndex(final ITmfContext context, final ITmfTimestamp timestamp) {
ea271da6 251 if ((context.getRank() % fCheckpointInterval) == 0) {
1703b536 252 // Determine the table position
ea271da6 253 final long position = context.getRank() / fCheckpointInterval;
1703b536
FC
254 // Add new entry at proper location (if empty)
255 if (fTraceIndex.size() == position) {
5cd3530c
MAL
256 TmfCheckpoint checkpoint = new TmfCheckpoint(timestamp, context.getLocation(), position);
257 TmfCoreTracer.traceIndexer("Inserting checkpoint: " + checkpoint); //$NON-NLS-1$
258 fTraceIndex.insert(checkpoint);
1703b536
FC
259 }
260 }
261 }
262
263 // ------------------------------------------------------------------------
264 // ITmfTraceIndexer - seekIndex
265 // ------------------------------------------------------------------------
20658947 266
20658947 267 @Override
1703b536 268 public synchronized ITmfContext seekIndex(final ITmfTimestamp timestamp) {
20658947 269
1703b536 270 // A null timestamp indicates to seek the first event
0316808c 271 if (timestamp == null) {
7e6347b0 272 return fTrace.seekEvent(0);
0316808c 273 }
20658947 274
1703b536
FC
275 // Find the checkpoint at or before the requested timestamp.
276 // In the very likely event that the timestamp is not at a checkpoint
277 // boundary, bsearch will return index = (- (insertion point + 1)).
278 // It is then trivial to compute the index of the previous checkpoint.
032ecd45 279 long index = fTraceIndex.binarySearch(new TmfCheckpoint(timestamp, null, 0));
20658947
FC
280 if (index < 0) {
281 index = Math.max(0, -(index + 2));
3eade83c 282 } else {
12c61248
MK
283 // If timestamp was in the list, use previous index to be able to
284 // find the
3eade83c
BH
285 // first event with the same timestamp before the checkpoint
286 index = Math.max(0, index - 1);
20658947
FC
287 }
288
289 // Position the trace at the checkpoint
408e65d2 290 return restoreCheckpoint(index);
20658947
FC
291 }
292
293 @Override
294 public ITmfContext seekIndex(final long rank) {
295
f6ad2e3d
FC
296 // A rank < 0 indicates to seek the first event
297 if (rank < 0) {
298 return fTrace.seekEvent(0);
299 }
1703b536 300
f6ad2e3d
FC
301 // Find the checkpoint at or before the requested rank.
302 final int index = (int) rank / fCheckpointInterval;
1703b536 303
f6ad2e3d 304 // Position the trace at the checkpoint
408e65d2 305 return restoreCheckpoint(index);
1703b536
FC
306 }
307
308 /**
309 * Position the trace at the given checkpoint
9b749023 310 *
12c61248
MK
311 * @param checkpointIndex
312 * the checkpoint index
1703b536
FC
313 * @return the corresponding context
314 */
21924e03 315 private ITmfContext restoreCheckpoint(final long checkpointIndex) {
1e1bef82 316 ITmfLocation location = null;
032ecd45 317 long index = 0;
20658947 318 synchronized (fTraceIndex) {
1703b536 319 if (!fTraceIndex.isEmpty()) {
21924e03 320 index = checkpointIndex;
20658947
FC
321 if (index >= fTraceIndex.size()) {
322 index = fTraceIndex.size() - 1;
323 }
21924e03
MAL
324 ITmfCheckpoint checkpoint = fTraceIndex.get(index);
325 TmfCoreTracer.traceIndexer("Restored checkpoint: " + checkpoint); //$NON-NLS-1$
7b3400bd
PT
326 if (checkpoint == null) {
327 return fTrace.seekEvent((ITmfLocation) null);
328 }
21924e03 329 location = checkpoint.getLocation();
20658947
FC
330 }
331 }
7e6347b0 332 final ITmfContext context = fTrace.seekEvent(location);
032ecd45 333 context.setRank(index * fCheckpointInterval);
20658947
FC
334 return context;
335 }
336
0316808c
FC
337 // ------------------------------------------------------------------------
338 // Getters
339 // ------------------------------------------------------------------------
340
341 /**
342 * @return the trace index
343 */
032ecd45 344 protected ITmfCheckpointIndex getTraceIndex() {
0316808c
FC
345 return fTraceIndex;
346 }
12c61248
MK
347
348 private final class TmfIndexingJob extends Job {
349 private Exception fException = null;
350
351 private TmfIndexingJob(String name) {
352 super(name);
353 }
354
355 @Override
356 protected IStatus run(final IProgressMonitor monitor) {
357 monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
358 while (!monitor.isCanceled()) {
359 try {
360 long prevNbEvents = fTrace.getNbEvents();
361 Thread.sleep(250);
362 long nbEvents = fTrace.getNbEvents();
363 setName(Messages.TmfCheckpointIndexer_Indexing + ' ' + fTrace.getName() + " (" + String.format("%,d", nbEvents) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
364 // setName doesn't refresh the UI, setTaskName does
365 long rate = (nbEvents - prevNbEvents) * 4;
366 monitor.setTaskName(String.format("%,d", rate) + " " + Messages.TmfCheckpointIndexer_EventsPerSecond); //$NON-NLS-1$ //$NON-NLS-2$
367 } catch (final InterruptedException e) {
368 return Status.OK_STATUS;
369 }
370 }
371 monitor.done();
372 return fException != null ? new Status(IStatus.ERROR, Activator.PLUGIN_ID, fException.getMessage(), fException) : Status.OK_STATUS;
373 }
374
375 public void setException(Exception e) {
376 fException = e;
377 }
378
379 }
20658947 380}
This page took 0.122062 seconds and 5 git commands to generate.