Merge branch 'lttng-kepler'
[deliverable/tracecompass.git] / org.eclipse.linuxtools.tmf.ui / src / org / eclipse / linuxtools / tmf / ui / views / histogram / HistogramDataModel.java
CommitLineData
c392540b 1/*******************************************************************************
e0752744 2 * Copyright (c) 2011, 2012 Ericsson
bfe038ff 3 *
c392540b
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
bfe038ff 8 *
c392540b
FC
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
bfe038ff 11 * Bernd Hufmann - Implementation of new interfaces/listeners and support for
fbd124dd 12 * time stamp in any order
e0752744 13 * Francois Chouinard - Moved from LTTng to TMF
00419b1f 14 * Francois Chouinard - Added support for empty initial buckets
c392540b
FC
15 *******************************************************************************/
16
e0752744 17package org.eclipse.linuxtools.tmf.ui.views.histogram;
c392540b
FC
18
19import java.util.Arrays;
20
fbd124dd 21import org.eclipse.core.runtime.ListenerList;
c392540b
FC
22
23/**
b544077e 24 * Histogram-independent data model.
f8177ba2 25 *
b544077e 26 * It has the following characteristics:
c392540b
FC
27 * <ul>
28 * <li>The <i>basetime</i> is the timestamp of the first event
29 * <li>There is a fixed number (<i>n</i>) of buckets of uniform duration
30 * (<i>d</i>)
31 * <li>The <i>timespan</i> of the model is thus: <i>n</i> * <i>d</i> time units
32 * <li>Bucket <i>i</i> holds the number of events that occurred in time range:
33 * [<i>basetime</i> + <i>i</i> * <i>d</i>, <i>basetime</i> + (<i>i</i> + 1) *
34 * <i>d</i>)
35 * </ul>
36 * Initially, the bucket durations is set to 1ns. As the events are read, they
37 * are tallied (using <i>countEvent()</i>) in the appropriate bucket (relative
38 * to the <i>basetime</i>).
39 * <p>
40 * Eventually, an event will have a timestamp that exceeds the <i>timespan</i>
41 * high end (determined by <i>n</i>, the number of buckets, and <i>d</i>, the
42 * bucket duration). At this point, the histogram needs to be compacted. This is
43 * done by simply merging adjacent buckets by pair, in effect doubling the
44 * <i>timespan</i> (<i>timespan'</i> = <i>n</i> * <i>d'</i>, where <i>d'</i> =
45 * 2<i>d</i>). This compaction happens as needed as the trace is read.
46 * <p>
fbd124dd 47 * The model allows for timestamps in not increasing order. The timestamps can
bfe038ff 48 * be fed to the model in any order. If an event has a timestamp less than the
fbd124dd 49 * <i>basetime</i>, the buckets will be moved to the right to account for the
bfe038ff 50 * new smaller timestamp. The new <i>basetime</i> is a multiple of the bucket
fbd124dd
BH
51 * duration smaller then the previous <i>basetime</i>. Note that the <i>basetime</i>
52 * might not be anymore a timestamp of an event. If necessary, the buckets will
bfe038ff 53 * be compacted before moving to the right. This might be necessary to not
fbd124dd
BH
54 * loose any event counts at the end of the buckets array.
55 * <p>
c392540b
FC
56 * The mapping from the model to the UI is performed by the <i>scaleTo()</i>
57 * method. By keeping the number of buckets <i>n</i> relatively large with
58 * respect to to the number of pixels in the actual histogram, we should achieve
59 * a nice result when visualizing the histogram.
60 * <p>
f8177ba2 61 *
00419b1f 62 * @version 2.0
b544077e 63 * @author Francois Chouinard
c392540b 64 */
fbd124dd 65public class HistogramDataModel implements IHistogramDataModel {
c392540b
FC
66
67 // ------------------------------------------------------------------------
68 // Constants
69 // ------------------------------------------------------------------------
70
b544077e
BH
71 /**
72 * The default number of buckets
73 */
c392540b
FC
74 public static final int DEFAULT_NUMBER_OF_BUCKETS = 16 * 1000;
75
b544077e 76 /**
f8177ba2 77 * Number of events after which listeners will be notified.
b544077e 78 */
fbd124dd 79 public static final int REFRESH_FREQUENCY = DEFAULT_NUMBER_OF_BUCKETS;
bfe038ff 80
c392540b
FC
81 // ------------------------------------------------------------------------
82 // Attributes
83 // ------------------------------------------------------------------------
84
85 // Bucket management
86 private final int fNbBuckets;
87 private final long[] fBuckets;
88 private long fBucketDuration;
89 private long fNbEvents;
90 private int fLastBucket;
91
92 // Timestamps
fbd124dd 93 private long fFirstBucketTime; // could be negative when analyzing events with descending order!!!
c392540b
FC
94 private long fFirstEventTime;
95 private long fLastEventTime;
96 private long fCurrentEventTime;
97 private long fTimeLimit;
bfe038ff 98
e0752744 99 // Private listener lists
fbd124dd 100 private final ListenerList fModelListeners;
bfe038ff 101
e0752744
FC
102 // ------------------------------------------------------------------------
103 // Constructors
104 // ------------------------------------------------------------------------
105
b544077e
BH
106 /**
107 * Default constructor with default number of buckets.
108 */
c392540b 109 public HistogramDataModel() {
00419b1f
FC
110 this(0, DEFAULT_NUMBER_OF_BUCKETS);
111 }
112
113 /**
114 * Default constructor with default number of buckets.
115 * @param startTime The histogram start time
116 * @since 2.0
117 */
118 public HistogramDataModel(long startTime) {
119 this(startTime, DEFAULT_NUMBER_OF_BUCKETS);
c392540b
FC
120 }
121
b544077e
BH
122 /**
123 * Constructor with non-default number of buckets.
124 * @param nbBuckets A number of buckets.
125 */
c392540b 126 public HistogramDataModel(int nbBuckets) {
00419b1f
FC
127 this(0, nbBuckets);
128 }
129
130 /**
131 * Constructor with non-default number of buckets.
132 * @param startTime the histogram start time
133 * @param nbBuckets A number of buckets.
134 * @since 2.0
135 */
136 public HistogramDataModel(long startTime, int nbBuckets) {
137 fFirstBucketTime = fFirstEventTime = fLastEventTime = startTime;
c392540b
FC
138 fNbBuckets = nbBuckets;
139 fBuckets = new long[nbBuckets];
fbd124dd 140 fModelListeners = new ListenerList();
c392540b
FC
141 clear();
142 }
143
b544077e
BH
144 /**
145 * Copy constructor.
146 * @param other A model to copy.
147 */
c392540b
FC
148 public HistogramDataModel(HistogramDataModel other) {
149 fNbBuckets = other.fNbBuckets;
150 fBuckets = Arrays.copyOf(other.fBuckets, fNbBuckets);
00419b1f 151 fBucketDuration = Math.max(other.fBucketDuration, 1);
c392540b
FC
152 fNbEvents = other.fNbEvents;
153 fLastBucket = other.fLastBucket;
fbd124dd 154 fFirstBucketTime = other.fFirstBucketTime;
c392540b
FC
155 fFirstEventTime = other.fFirstEventTime;
156 fLastEventTime = other.fLastEventTime;
157 fCurrentEventTime = other.fCurrentEventTime;
158 fTimeLimit = other.fTimeLimit;
fbd124dd
BH
159 fModelListeners = new ListenerList();
160 Object[] listeners = other.fModelListeners.getListeners();
161 for (Object listener : listeners) {
162 fModelListeners.add(listener);
163 }
c392540b
FC
164 }
165
166 // ------------------------------------------------------------------------
167 // Accessors
168 // ------------------------------------------------------------------------
169
b544077e
BH
170 /**
171 * Returns the number of events in the data model.
172 * @return number of events.
173 */
c392540b
FC
174 public long getNbEvents() {
175 return fNbEvents;
176 }
177
b544077e
BH
178 /**
179 * Returns the number of buckets in the model.
180 * @return number of buckets.
181 */
c392540b
FC
182 public int getNbBuckets() {
183 return fNbBuckets;
184 }
185
b544077e
BH
186 /**
187 * Returns the current bucket duration.
188 * @return bucket duration
189 */
c392540b
FC
190 public long getBucketDuration() {
191 return fBucketDuration;
192 }
bfe038ff 193
b544077e
BH
194 /**
195 * Returns the time value of the first bucket in the model.
196 * @return time of first bucket.
197 */
fbd124dd
BH
198 public long getFirstBucketTime() {
199 return fFirstBucketTime;
200 }
c392540b 201
b544077e
BH
202 /**
203 * Returns the time of the first event in the model.
204 * @return time of first event.
205 */
c392540b
FC
206 public long getStartTime() {
207 return fFirstEventTime;
208 }
bfe038ff 209
00419b1f
FC
210 /**
211 * Sets the model start time
212 * @param startTime the histogram range start time
213 * @param endTime the histogram range end time
214 * @since 2.0
215 */
216 public void setTimeRange(long startTime, long endTime) {
217 fFirstBucketTime = fFirstEventTime = fLastEventTime = startTime;
218 fBucketDuration = 1;
219 updateEndTime();
220 while (endTime >= fTimeLimit) {
221 mergeBuckets();
222 }
223 }
224
b544077e
BH
225 /**
226 * Returns the time of the last event in the model.
227 * @return the time of last event.
228 */
c392540b
FC
229 public long getEndTime() {
230 return fLastEventTime;
231 }
232
b544077e 233 /**
f8177ba2 234 * Returns the time of the current event in the model.
b544077e
BH
235 * @return the time of the current event.
236 */
c392540b
FC
237 public long getCurrentEventTime() {
238 return fCurrentEventTime;
239 }
240
b544077e
BH
241 /**
242 * Returns the time limit with is: start time + nbBuckets * bucketDuration
243 * @return the time limit.
244 */
c392540b
FC
245 public long getTimeLimit() {
246 return fTimeLimit;
247 }
bfe038ff 248
fbd124dd
BH
249 // ------------------------------------------------------------------------
250 // Listener handling
251 // ------------------------------------------------------------------------
bfe038ff 252
b544077e
BH
253 /**
254 * Add a listener to the model to be informed about model changes.
255 * @param listener A listener to add.
256 */
fbd124dd 257 public void addHistogramListener(IHistogramModelListener listener) {
bfe038ff 258 fModelListeners.add(listener);
fbd124dd 259 }
bfe038ff 260
b544077e
BH
261 /**
262 * Remove a given model listener.
263 * @param listener A listener to remove.
264 */
fbd124dd
BH
265 public void removeHistogramListener(IHistogramModelListener listener) {
266 fModelListeners.remove(listener);
267 }
c392540b 268
f8177ba2 269 // Notify listeners (always)
fbd124dd
BH
270 private void fireModelUpdateNotification() {
271 fireModelUpdateNotification(0);
272 }
bfe038ff 273
b544077e 274 // Notify listener on boundary
fbd124dd 275 private void fireModelUpdateNotification(long count) {
bfe038ff 276 if ((count % REFRESH_FREQUENCY) == 0) {
fbd124dd 277 Object[] listeners = fModelListeners.getListeners();
bfe038ff
MK
278 for (Object listener2 : listeners) {
279 IHistogramModelListener listener = (IHistogramModelListener) listener2;
fbd124dd
BH
280 listener.modelUpdated();
281 }
282 }
283 }
bfe038ff 284
c392540b
FC
285 // ------------------------------------------------------------------------
286 // Operations
287 // ------------------------------------------------------------------------
e0752744 288
b544077e
BH
289 /*
290 * (non-Javadoc)
291 * @see org.eclipse.linuxtools.tmf.ui.views.distribution.model.IBaseDistributionModel#complete()
292 */
fbd124dd
BH
293 @Override
294 public void complete() {
295 fireModelUpdateNotification();
296 }
c392540b
FC
297
298 /**
299 * Clear the histogram model.
b544077e 300 * @see org.eclipse.linuxtools.tmf.ui.views.distribution.model.IBaseDistributionModel#clear()
c392540b 301 */
fbd124dd 302 @Override
c392540b
FC
303 public void clear() {
304 Arrays.fill(fBuckets, 0);
305 fNbEvents = 0;
fbd124dd 306 fFirstBucketTime = 0;
c392540b
FC
307 fLastEventTime = 0;
308 fCurrentEventTime = 0;
309 fLastBucket = 0;
f8177ba2 310 fBucketDuration = 1;
c392540b 311 updateEndTime();
fbd124dd 312 fireModelUpdateNotification();
c392540b
FC
313 }
314
315 /**
f8177ba2 316 * Sets the current event time (no notification of listeners)
bfe038ff 317 *
b544077e 318 * @param timestamp A time stamp to set.
c392540b
FC
319 */
320 public void setCurrentEvent(long timestamp) {
321 fCurrentEventTime = timestamp;
322 }
323
fbd124dd 324 /**
f8177ba2 325 * Sets the current event time with notification of listeners
bfe038ff 326 *
b544077e 327 * @param timestamp A time stamp to set.
fbd124dd
BH
328 */
329 public void setCurrentEventNotifyListeners(long timestamp) {
330 fCurrentEventTime = timestamp;
331 fireModelUpdateNotification();
332 }
bfe038ff 333
c392540b
FC
334 /**
335 * Add event to the correct bucket, compacting the if needed.
bfe038ff 336 *
b544077e
BH
337 * @param eventCount The current event Count (for notification purposes)
338 * @param timestamp The timestamp of the event to count
f8177ba2 339 *
c392540b 340 */
fbd124dd
BH
341 @Override
342 public void countEvent(long eventCount, long timestamp) {
bfe038ff 343
fbd124dd
BH
344 // Validate
345 if (timestamp < 0) {
fbd124dd
BH
346 return;
347 }
bfe038ff 348
c392540b 349 // Set the start/end time if not already done
00419b1f 350 if ((fFirstBucketTime == 0) && (fLastBucket == 0) && (fBuckets[0] == 0) && (timestamp > 0)) {
fbd124dd 351 fFirstBucketTime = timestamp;
c392540b
FC
352 fFirstEventTime = timestamp;
353 updateEndTime();
354 }
bfe038ff 355
fbd124dd
BH
356 if (timestamp < fFirstEventTime) {
357 fFirstEventTime = timestamp;
358 }
bfe038ff 359
c392540b
FC
360 if (fLastEventTime < timestamp) {
361 fLastEventTime = timestamp;
362 }
bfe038ff 363
fbd124dd 364 if (timestamp >= fFirstBucketTime) {
c392540b 365
fbd124dd
BH
366 // Compact as needed
367 while (timestamp >= fTimeLimit) {
368 mergeBuckets();
369 }
c392540b 370
fbd124dd 371 } else {
bfe038ff 372
fbd124dd
BH
373 // get offset for adjustment
374 int offset = getOffset(timestamp);
375
376 // Compact as needed
bfe038ff 377 while((fLastBucket + offset) >= fNbBuckets) {
fbd124dd
BH
378 mergeBuckets();
379 offset = getOffset(timestamp);
380 }
bfe038ff 381
fbd124dd
BH
382 moveBuckets(offset);
383
384 fLastBucket = fLastBucket + offset;
c392540b 385
bfe038ff 386 fFirstBucketTime = fFirstBucketTime - (offset*fBucketDuration);
fbd124dd
BH
387 updateEndTime();
388 }
bfe038ff 389
c392540b 390 // Increment the right bucket
fbd124dd 391 int index = (int) ((timestamp - fFirstBucketTime) / fBucketDuration);
c392540b
FC
392 fBuckets[index]++;
393 fNbEvents++;
bfe038ff 394 if (fLastBucket < index) {
c392540b 395 fLastBucket = index;
bfe038ff
MK
396 }
397
fbd124dd 398 fireModelUpdateNotification(eventCount);
c392540b
FC
399 }
400
401 /**
fbd124dd 402 * Scale the model data to the width, height and bar width requested.
bfe038ff 403 *
b544077e
BH
404 * @param width A width of the histogram canvas
405 * @param height A height of the histogram canvas
406 * @param barWidth A width (in pixel) of a histogram bar
407 * @return the result array of size [width] and where the highest value doesn't exceed [height]
f8177ba2 408 *
b544077e 409 * @see org.eclipse.linuxtools.tmf.ui.views.histogram.IHistogramDataModel#scaleTo(int, int, int)
c392540b 410 */
fbd124dd
BH
411 @Override
412 public HistogramScaledData scaleTo(int width, int height, int barWidth) {
c392540b 413 // Basic validation
bfe038ff
MK
414 if ((width <= 0) || (height <= 0) || (barWidth <= 0))
415 {
fbd124dd 416 throw new AssertionError("Invalid histogram dimensions (" + width + "x" + height + ", barWidth=" + barWidth + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
bfe038ff 417 }
c392540b
FC
418
419 // The result structure
fbd124dd 420 HistogramScaledData result = new HistogramScaledData(width, height, barWidth);
c392540b
FC
421
422 // Scale horizontally
74237cc3 423 result.fMaxValue = 0;
bfe038ff 424
fbd124dd 425 int nbBars = width / barWidth;
bfe038ff
MK
426 int bucketsPerBar = (fLastBucket / nbBars) + 1;
427 result.fBucketDuration = Math.max(bucketsPerBar * fBucketDuration,1);
fbd124dd 428 for (int i = 0; i < nbBars; i++) {
c392540b 429 int count = 0;
bfe038ff
MK
430 for (int j = i * bucketsPerBar; j < ((i + 1) * bucketsPerBar); j++) {
431 if (fNbBuckets <= j) {
c392540b 432 break;
bfe038ff 433 }
c392540b
FC
434 count += fBuckets[j];
435 }
436 result.fData[i] = count;
437 result.fLastBucket = i;
bfe038ff 438 if (result.fMaxValue < count) {
c392540b 439 result.fMaxValue = count;
bfe038ff 440 }
c392540b
FC
441 }
442
443 // Scale vertically
444 if (result.fMaxValue > 0) {
445 result.fScalingFactor = (double) height / result.fMaxValue;
446 }
447
bfe038ff 448 fBucketDuration = Math.max(fBucketDuration, 1);
c392540b 449 // Set the current event index in the scaled histogram
bfe038ff 450 if ((fCurrentEventTime >= fFirstBucketTime) && (fCurrentEventTime <= fLastEventTime)) {
fbd124dd 451 result.fCurrentBucket = (int) ((fCurrentEventTime - fFirstBucketTime) / fBucketDuration) / bucketsPerBar;
bfe038ff 452 } else {
c392540b 453 result.fCurrentBucket = HistogramScaledData.OUT_OF_RANGE_BUCKET;
bfe038ff 454 }
c392540b 455
fbd124dd
BH
456 result.fFirstBucketTime = fFirstBucketTime;
457 result.fFirstEventTime = fFirstEventTime;
c392540b
FC
458 return result;
459 }
460
461 // ------------------------------------------------------------------------
462 // Helper functions
463 // ------------------------------------------------------------------------
464
465 private void updateEndTime() {
bfe038ff 466 fTimeLimit = fFirstBucketTime + (fNbBuckets * fBucketDuration);
c392540b
FC
467 }
468
469 private void mergeBuckets() {
bfe038ff
MK
470 for (int i = 0; i < (fNbBuckets / 2); i++) {
471 fBuckets[i] = fBuckets[2 * i] + fBuckets[(2 * i) + 1];
c392540b
FC
472 }
473 Arrays.fill(fBuckets, fNbBuckets / 2, fNbBuckets, 0);
474 fBucketDuration *= 2;
475 updateEndTime();
bfe038ff 476 fLastBucket = (fNbBuckets / 2) - 1;
c392540b 477 }
bfe038ff 478
fbd124dd
BH
479 private void moveBuckets(int offset) {
480 for(int i = fNbBuckets - 1; i >= offset; i--) {
bfe038ff 481 fBuckets[i] = fBuckets[i-offset];
fbd124dd
BH
482 }
483
484 for (int i = 0; i < offset; i++) {
485 fBuckets[i] = 0;
486 }
487 }
488
489 private int getOffset(long timestamp) {
490 int offset = (int) ((fFirstBucketTime - timestamp) / fBucketDuration);
bfe038ff 491 if (((fFirstBucketTime - timestamp) % fBucketDuration) != 0) {
fbd124dd
BH
492 offset++;
493 }
494 return offset;
495 }
c392540b
FC
496
497}
This page took 0.078409 seconds and 5 git commands to generate.