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