Commit | Line | Data |
---|---|---|
8c8bf09f | 1 | /******************************************************************************* |
e31e01e8 | 2 | * Copyright (c) 2009, 2010 Ericsson |
8c8bf09f ASL |
3 | * |
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 | |
8 | * | |
9 | * Contributors: | |
10 | * Francois Chouinard - Initial API and implementation | |
11 | *******************************************************************************/ | |
12 | ||
13 | package org.eclipse.linuxtools.tmf.trace; | |
14 | ||
b0a282fb | 15 | import java.io.File; |
62d1696a | 16 | import java.io.FileNotFoundException; |
62d1696a | 17 | import java.util.Collections; |
8c8bf09f ASL |
18 | import java.util.Vector; |
19 | ||
62d1696a FC |
20 | import org.eclipse.core.runtime.IProgressMonitor; |
21 | import org.eclipse.core.runtime.IStatus; | |
22 | import org.eclipse.core.runtime.Status; | |
23 | import org.eclipse.core.runtime.jobs.Job; | |
fc6ccf6f | 24 | import org.eclipse.linuxtools.tmf.component.TmfEventProvider; |
8c8bf09f ASL |
25 | import org.eclipse.linuxtools.tmf.event.TmfEvent; |
26 | import org.eclipse.linuxtools.tmf.event.TmfTimeRange; | |
27 | import org.eclipse.linuxtools.tmf.event.TmfTimestamp; | |
951d134a | 28 | import org.eclipse.linuxtools.tmf.request.TmfCoalescedEventRequest; |
4e3aa37d | 29 | import org.eclipse.linuxtools.tmf.request.TmfDataRequest; |
e31e01e8 | 30 | import org.eclipse.linuxtools.tmf.request.TmfEventRequest; |
8c8bf09f ASL |
31 | |
32 | /** | |
146a887c | 33 | * <b><u>TmfTrace</u></b> |
8c8bf09f | 34 | * <p> |
146a887c FC |
35 | * Abstract implementation of ITmfTrace. It should be sufficient to extend this |
36 | * class and provide implementation for <code>getCurrentLocation()</code> and | |
37 | * <code>seekLocation()</code>, as well as a proper parser, to have a working | |
4e3aa37d FC |
38 | * concrete implementation. |
39 | * | |
54d55ced FC |
40 | * Note: The notion of event rank is still under heavy discussion. Although |
41 | * used for by the Events View and probably useful in then general case, there | |
42 | * is no easy way to implement it for LTTng (actually a strong case is being | |
43 | * made that this is useless). Therefore, this is a minimal and partial | |
44 | * implementation and rank should not be relied upon in the general case (i.e. | |
45 | * it was hacked to work for the Events View). | |
46 | * | |
4e3aa37d | 47 | * TODO: Add support for live streaming (notifications, incremental indexing, ...) |
8c8bf09f | 48 | */ |
fc6ccf6f | 49 | public abstract class TmfTrace<T extends TmfEvent> extends TmfEventProvider<T> implements ITmfTrace { |
62d1696a | 50 | |
e31e01e8 | 51 | // ------------------------------------------------------------------------ |
62d1696a | 52 | // Constants |
e31e01e8 | 53 | // ------------------------------------------------------------------------ |
62d1696a FC |
54 | |
55 | // The default number of events to cache | |
e31e01e8 | 56 | // TODO: Make the DEFAULT_CACHE_SIZE a preference |
8d2e2848 | 57 | public static final int DEFAULT_CACHE_SIZE = 1000; |
8c8bf09f | 58 | |
e31e01e8 | 59 | // ------------------------------------------------------------------------ |
8c8bf09f | 60 | // Attributes |
e31e01e8 | 61 | // ------------------------------------------------------------------------ |
8c8bf09f | 62 | |
b0a282fb FC |
63 | // The trace path |
64 | private final String fPath; | |
65 | ||
66 | // The trace name | |
62d1696a FC |
67 | private final String fName; |
68 | ||
8d2e2848 | 69 | // The cache page size AND checkpoints interval |
9f584e4c | 70 | protected int fIndexPageSize; |
62d1696a FC |
71 | |
72 | // The set of event stream checkpoints (for random access) | |
9f584e4c | 73 | protected Vector<TmfCheckpoint> fCheckpoints = new Vector<TmfCheckpoint>(); |
62d1696a FC |
74 | |
75 | // The number of events collected | |
a3fe52fc | 76 | protected long fNbEvents = 0; |
62d1696a FC |
77 | |
78 | // The time span of the event stream | |
79 | private TmfTimeRange fTimeRange = new TmfTimeRange(TmfTimestamp.BigBang, TmfTimestamp.BigBang); | |
80 | ||
e31e01e8 | 81 | // ------------------------------------------------------------------------ |
50adc88e | 82 | // Constructors |
e31e01e8 | 83 | // ------------------------------------------------------------------------ |
8c8bf09f | 84 | |
62d1696a | 85 | /** |
e31e01e8 FC |
86 | * @param path |
87 | * @param cacheSize | |
62d1696a FC |
88 | * @throws FileNotFoundException |
89 | */ | |
e31e01e8 FC |
90 | protected TmfTrace(Class<T> type, String path, int cacheSize) throws FileNotFoundException { |
91 | super(type); | |
b0a282fb FC |
92 | int sep = path.lastIndexOf(File.separator); |
93 | fName = (sep >= 0) ? path.substring(sep + 1) : path; | |
94 | fPath = path; | |
9f584e4c | 95 | fIndexPageSize = (cacheSize > 0) ? cacheSize : DEFAULT_CACHE_SIZE; |
8c8bf09f ASL |
96 | } |
97 | ||
62d1696a | 98 | /** |
e31e01e8 | 99 | * @param path |
62d1696a FC |
100 | * @throws FileNotFoundException |
101 | */ | |
e31e01e8 FC |
102 | protected TmfTrace(Class<T> type, String path) throws FileNotFoundException { |
103 | this(type, path, DEFAULT_CACHE_SIZE); | |
8c8bf09f ASL |
104 | } |
105 | ||
e31e01e8 | 106 | // ------------------------------------------------------------------------ |
8c8bf09f | 107 | // Accessors |
e31e01e8 | 108 | // ------------------------------------------------------------------------ |
8c8bf09f | 109 | |
62d1696a | 110 | /** |
b0a282fb | 111 | * @return the trace path |
62d1696a | 112 | */ |
b0a282fb FC |
113 | public String getPath() { |
114 | return fPath; | |
8c8bf09f ASL |
115 | } |
116 | ||
62d1696a | 117 | /** |
146a887c | 118 | * @return the trace name |
62d1696a | 119 | */ |
fc6ccf6f FC |
120 | @Override |
121 | public String getName() { | |
62d1696a | 122 | return fName; |
8c8bf09f ASL |
123 | } |
124 | ||
62d1696a FC |
125 | /* (non-Javadoc) |
126 | * @see org.eclipse.linuxtools.tmf.stream.ITmfEventStream#getNbEvents() | |
127 | */ | |
4e3aa37d | 128 | public long getNbEvents() { |
62d1696a | 129 | return fNbEvents; |
8c8bf09f ASL |
130 | } |
131 | ||
b0a282fb FC |
132 | /** |
133 | * @return the size of the cache | |
134 | */ | |
8d2e2848 | 135 | public int getCacheSize() { |
9f584e4c | 136 | return fIndexPageSize; |
b0a282fb FC |
137 | } |
138 | ||
62d1696a FC |
139 | /* (non-Javadoc) |
140 | * @see org.eclipse.linuxtools.tmf.stream.ITmfEventStream#getTimeRange() | |
141 | */ | |
8c8bf09f | 142 | public TmfTimeRange getTimeRange() { |
62d1696a | 143 | return fTimeRange; |
8c8bf09f ASL |
144 | } |
145 | ||
e31e01e8 FC |
146 | /* (non-Javadoc) |
147 | * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#getStartTime() | |
148 | */ | |
146a887c FC |
149 | public TmfTimestamp getStartTime() { |
150 | return fTimeRange.getStartTime(); | |
151 | } | |
152 | ||
e31e01e8 FC |
153 | /* (non-Javadoc) |
154 | * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#getEndTime() | |
155 | */ | |
146a887c FC |
156 | public TmfTimestamp getEndTime() { |
157 | return fTimeRange.getEndTime(); | |
158 | } | |
159 | ||
54d55ced FC |
160 | public Vector<TmfCheckpoint> getCheckpoints() { |
161 | return fCheckpoints; | |
162 | } | |
163 | ||
e31e01e8 | 164 | // ------------------------------------------------------------------------ |
8c8bf09f | 165 | // Operators |
e31e01e8 | 166 | // ------------------------------------------------------------------------ |
8c8bf09f | 167 | |
4e3aa37d FC |
168 | protected void setTimeRange(TmfTimeRange range) { |
169 | fTimeRange = range; | |
170 | } | |
171 | ||
172 | protected void setStartTime(TmfTimestamp startTime) { | |
173 | fTimeRange = new TmfTimeRange(startTime, fTimeRange.getEndTime()); | |
174 | } | |
175 | ||
176 | protected void setEndTime(TmfTimestamp endTime) { | |
177 | fTimeRange = new TmfTimeRange(fTimeRange.getStartTime(), endTime); | |
178 | } | |
179 | ||
e31e01e8 FC |
180 | // ------------------------------------------------------------------------ |
181 | // TmfProvider | |
182 | // ------------------------------------------------------------------------ | |
183 | ||
184 | @Override | |
fc6ccf6f | 185 | public ITmfContext armRequest(TmfDataRequest<T> request) { |
e31e01e8 FC |
186 | if (request instanceof TmfEventRequest<?>) { |
187 | return seekEvent(((TmfEventRequest<T>) request).getRange().getStartTime()); | |
188 | } | |
951d134a FC |
189 | if (request instanceof TmfCoalescedEventRequest<?>) { |
190 | return seekEvent(((TmfCoalescedEventRequest<T>) request).getRange().getStartTime()); | |
191 | } | |
e31e01e8 FC |
192 | return null; |
193 | } | |
194 | ||
195 | /** | |
196 | * Return the next piece of data based on the context supplied. The context | |
197 | * would typically be updated for the subsequent read. | |
198 | * | |
199 | * @param context | |
200 | * @return | |
201 | */ | |
202 | @SuppressWarnings("unchecked") | |
203 | @Override | |
204 | public T getNext(ITmfContext context) { | |
9f584e4c FC |
205 | if (context instanceof TmfContext) { |
206 | return (T) getNextEvent((TmfContext) context); | |
e31e01e8 FC |
207 | } |
208 | return null; | |
209 | } | |
210 | ||
e31e01e8 FC |
211 | // ------------------------------------------------------------------------ |
212 | // ITmfTrace | |
213 | // ------------------------------------------------------------------------ | |
214 | ||
146a887c FC |
215 | /* (non-Javadoc) |
216 | * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#seekEvent(org.eclipse.linuxtools.tmf.event.TmfTimestamp) | |
217 | */ | |
9f584e4c | 218 | public TmfContext seekEvent(TmfTimestamp timestamp) { |
62d1696a | 219 | |
4e3aa37d FC |
220 | if (timestamp == null) { |
221 | timestamp = TmfTimestamp.BigBang; | |
222 | } | |
223 | ||
224 | // First, find the right checkpoint | |
9f584e4c | 225 | int index = Collections.binarySearch(fCheckpoints, new TmfCheckpoint(timestamp, null)); |
62d1696a | 226 | |
8d2e2848 | 227 | // In the very likely case that the checkpoint was not found, bsearch |
62d1696a FC |
228 | // returns its negated would-be location (not an offset...). From that |
229 | // index, we can then position the stream and get the event. | |
230 | if (index < 0) { | |
231 | index = Math.max(0, -(index + 2)); | |
232 | } | |
233 | ||
234 | // Position the stream at the checkpoint | |
452ad365 | 235 | ITmfLocation<?> location; |
e31e01e8 FC |
236 | synchronized (fCheckpoints) { |
237 | if (fCheckpoints.size() > 0) { | |
238 | if (index >= fCheckpoints.size()) { | |
239 | index = fCheckpoints.size() - 1; | |
240 | } | |
241 | location = fCheckpoints.elementAt(index).getLocation(); | |
242 | } | |
243 | else { | |
244 | location = null; | |
245 | } | |
8d2e2848 | 246 | } |
54d55ced FC |
247 | TmfContext context = seekLocation(location); |
248 | context.setRank(index * fIndexPageSize); | |
62d1696a | 249 | |
54d55ced FC |
250 | // And locate the event |
251 | TmfContext nextEventContext = new TmfContext(context); | |
62d1696a FC |
252 | TmfEvent event = getNextEvent(nextEventContext); |
253 | while (event != null && event.getTimestamp().compareTo(timestamp, false) < 0) { | |
54d55ced FC |
254 | context.setLocation(nextEventContext.getLocation().clone()); |
255 | context.updateRank(1); | |
62d1696a FC |
256 | event = getNextEvent(nextEventContext); |
257 | } | |
258 | ||
54d55ced | 259 | return context; |
62d1696a FC |
260 | } |
261 | ||
146a887c FC |
262 | /* (non-Javadoc) |
263 | * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#seekEvent(int) | |
264 | */ | |
9f584e4c | 265 | public TmfContext seekEvent(long rank) { |
62d1696a FC |
266 | |
267 | // Position the stream at the previous checkpoint | |
9f584e4c | 268 | int index = (int) rank / fIndexPageSize; |
452ad365 | 269 | ITmfLocation<?> location; |
e31e01e8 | 270 | synchronized (fCheckpoints) { |
54d55ced FC |
271 | if (fCheckpoints.size() == 0) { |
272 | location = null; | |
273 | } | |
274 | else { | |
e31e01e8 | 275 | if (index >= fCheckpoints.size()) { |
54d55ced | 276 | index = fCheckpoints.size() - 1; |
e31e01e8 FC |
277 | } |
278 | location = fCheckpoints.elementAt(index).getLocation(); | |
279 | } | |
8d2e2848 | 280 | } |
54d55ced | 281 | |
9f584e4c FC |
282 | TmfContext context = seekLocation(location); |
283 | long pos = index * fIndexPageSize; | |
284 | context.setRank(pos); | |
e31e01e8 | 285 | |
9f584e4c | 286 | if (pos < rank) { |
e31e01e8 | 287 | TmfEvent event = getNextEvent(context); |
9f584e4c | 288 | while (event != null && ++pos < rank) { |
e31e01e8 FC |
289 | event = getNextEvent(context); |
290 | } | |
165c977c | 291 | } |
62d1696a | 292 | |
9f584e4c | 293 | return new TmfContext(context.getLocation(), context.getRank()); |
8c8bf09f ASL |
294 | } |
295 | ||
146a887c FC |
296 | /* (non-Javadoc) |
297 | * @see org.eclipse.linuxtools.tmf.trace.ITmfTrace#getNextEvent(org.eclipse.linuxtools.tmf.trace.ITmfTrace.TraceContext) | |
298 | */ | |
9f584e4c | 299 | public synchronized TmfEvent getNextEvent(TmfContext context) { |
e31e01e8 | 300 | // parseEvent() does not update the context |
cc6eec3e | 301 | TmfEvent event = parseEvent(context); |
4e3aa37d | 302 | if (event != null) { |
54d55ced FC |
303 | context.setLocation(getCurrentLocation()); |
304 | context.updateRank(1); | |
4e3aa37d FC |
305 | processEvent(event); |
306 | } | |
146a887c FC |
307 | return event; |
308 | } | |
8c8bf09f | 309 | |
4e3aa37d | 310 | /** |
e31e01e8 FC |
311 | * Hook for "special" processing by the concrete class |
312 | * (called by getNextEvent()) | |
313 | * | |
146a887c FC |
314 | * @param event |
315 | */ | |
316 | public void processEvent(TmfEvent event) { | |
317 | // Do nothing by default | |
62d1696a | 318 | } |
4e3aa37d | 319 | |
e31e01e8 FC |
320 | /** |
321 | * To be implemented by the concrete class | |
4e3aa37d | 322 | */ |
452ad365 FC |
323 | public abstract TmfContext seekLocation(ITmfLocation<?> location); |
324 | public abstract ITmfLocation<?> getCurrentLocation(); | |
9f584e4c | 325 | public abstract TmfEvent parseEvent(TmfContext context); |
4e3aa37d | 326 | |
e31e01e8 FC |
327 | // ------------------------------------------------------------------------ |
328 | // toString | |
329 | // ------------------------------------------------------------------------ | |
8d2e2848 FC |
330 | |
331 | /* (non-Javadoc) | |
332 | * @see java.lang.Object#toString() | |
333 | */ | |
334 | @Override | |
335 | public String toString() { | |
336 | return "[TmfTrace (" + fName + "]"; | |
337 | } | |
338 | ||
e31e01e8 FC |
339 | // ------------------------------------------------------------------------ |
340 | // Indexing | |
341 | // ------------------------------------------------------------------------ | |
146a887c | 342 | |
e31e01e8 FC |
343 | /* |
344 | * The purpose of the index is to keep the information needed to rapidly | |
345 | * access a trace event based on its timestamp or rank. | |
346 | * | |
347 | * NOTE: As it is, doesn't work for streaming traces. | |
348 | */ | |
349 | ||
350 | private IndexingJob job; | |
9f584e4c FC |
351 | |
352 | // Indicates that an indexing job is already running | |
cbd4ad82 FC |
353 | private Object fIndexingLock = new Object(); |
354 | private boolean fIndexing = false; | |
9f584e4c | 355 | private Boolean fIndexed = false; |
e31e01e8 FC |
356 | |
357 | public void indexTrace(boolean waitForCompletion) { | |
cbd4ad82 | 358 | synchronized (fIndexingLock) { |
9f584e4c | 359 | if (fIndexed || fIndexing) { |
4e3aa37d FC |
360 | return; |
361 | } | |
362 | fIndexing = true; | |
363 | } | |
364 | ||
e31e01e8 | 365 | job = new IndexingJob("Indexing " + fName); |
62d1696a | 366 | job.schedule(); |
4e3aa37d | 367 | |
e31e01e8 | 368 | if (waitForCompletion) { |
4e3aa37d FC |
369 | try { |
370 | job.join(); | |
371 | } catch (InterruptedException e) { | |
4e3aa37d FC |
372 | e.printStackTrace(); |
373 | } | |
52e28818 | 374 | } |
62d1696a FC |
375 | } |
376 | ||
377 | private class IndexingJob extends Job { | |
378 | ||
379 | public IndexingJob(String name) { | |
380 | super(name); | |
381 | } | |
382 | ||
383 | /* (non-Javadoc) | |
384 | * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) | |
385 | */ | |
386 | @Override | |
387 | protected IStatus run(IProgressMonitor monitor) { | |
388 | ||
62d1696a FC |
389 | monitor.beginTask("Indexing " + fName, IProgressMonitor.UNKNOWN); |
390 | ||
e31e01e8 FC |
391 | int nbEvents = 0; |
392 | TmfTimestamp startTime = null; | |
393 | TmfTimestamp lastTime = null; | |
394 | ||
9f584e4c FC |
395 | // Reset the index |
396 | fCheckpoints = new Vector<TmfCheckpoint>(); | |
e31e01e8 | 397 | |
98029bc9 FC |
398 | try { |
399 | // Position the trace at the beginning | |
9f584e4c | 400 | TmfContext context = seekLocation(null); |
54d55ced | 401 | ITmfLocation<?> location = context.getLocation().clone(); |
e31e01e8 | 402 | |
9f584e4c | 403 | // Get the first event |
e31e01e8 | 404 | TmfEvent event = getNextEvent(context); |
9f584e4c FC |
405 | if (event != null) { |
406 | startTime = new TmfTimestamp(event.getTimestamp()); | |
407 | } | |
408 | ||
409 | // Index the trace | |
410 | while (event != null) { | |
e31e01e8 | 411 | lastTime = event.getTimestamp(); |
9f584e4c | 412 | if ((nbEvents++ % fIndexPageSize) == 0) { |
e31e01e8 | 413 | lastTime = new TmfTimestamp(event.getTimestamp()); |
9f584e4c | 414 | fCheckpoints.add(new TmfCheckpoint(lastTime, location.clone())); |
e31e01e8 | 415 | fNbEvents = nbEvents; |
98029bc9 FC |
416 | fTimeRange = new TmfTimeRange(startTime, lastTime); |
417 | notifyListeners(new TmfTimeRange(startTime, lastTime)); | |
4e3aa37d | 418 | |
62d1696a | 419 | monitor.worked(1); |
4e3aa37d | 420 | |
62d1696a FC |
421 | // Check monitor *after* fCheckpoints has been updated |
422 | if (monitor.isCanceled()) { | |
4e3aa37d | 423 | monitor.done(); |
62d1696a FC |
424 | return Status.CANCEL_STATUS; |
425 | } | |
165c977c | 426 | } |
165c977c | 427 | |
e31e01e8 | 428 | // We will need this location at the next iteration |
9f584e4c | 429 | if ((nbEvents % fIndexPageSize) == 0) { |
54d55ced | 430 | location = context.getLocation().clone(); |
e31e01e8 FC |
431 | } |
432 | ||
433 | event = getNextEvent(context); | |
62d1696a FC |
434 | } |
435 | } | |
436 | finally { | |
437 | synchronized(this) { | |
98029bc9 | 438 | fNbEvents = nbEvents; |
62d1696a | 439 | fTimeRange = new TmfTimeRange(startTime, lastTime); |
4e3aa37d | 440 | fIndexing = false; |
9f584e4c | 441 | fIndexed = true; |
62d1696a | 442 | } |
98029bc9 | 443 | notifyListeners(new TmfTimeRange(startTime, lastTime)); |
62d1696a | 444 | monitor.done(); |
8c8bf09f | 445 | } |
62d1696a | 446 | |
4e3aa37d | 447 | return Status.OK_STATUS; |
62d1696a | 448 | } |
8c8bf09f ASL |
449 | } |
450 | ||
a3fe52fc | 451 | protected void notifyListeners(TmfTimeRange range) { |
e31e01e8 | 452 | broadcast(new TmfTraceUpdatedSignal(this, this, range)); |
146a887c FC |
453 | } |
454 | ||
8c8bf09f | 455 | } |