Update Javadoc for the public API in legacy LTTng
[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
c392540b
FC
14 *******************************************************************************/
15
e0752744 16package org.eclipse.linuxtools.tmf.ui.views.histogram;
c392540b
FC
17
18import java.util.Arrays;
19
fbd124dd 20import org.eclipse.core.runtime.ListenerList;
c392540b
FC
21
22/**
23 * <b><u>HistogramDataModel</u></b>
24 * <p>
25 * Histogram-independent data model with the following characteristics:
26 * <ul>
27 * <li>The <i>basetime</i> is the timestamp of the first event
28 * <li>There is a fixed number (<i>n</i>) of buckets of uniform duration
29 * (<i>d</i>)
30 * <li>The <i>timespan</i> of the model is thus: <i>n</i> * <i>d</i> time units
31 * <li>Bucket <i>i</i> holds the number of events that occurred in time range:
32 * [<i>basetime</i> + <i>i</i> * <i>d</i>, <i>basetime</i> + (<i>i</i> + 1) *
33 * <i>d</i>)
34 * </ul>
35 * Initially, the bucket durations is set to 1ns. As the events are read, they
36 * are tallied (using <i>countEvent()</i>) in the appropriate bucket (relative
37 * to the <i>basetime</i>).
38 * <p>
39 * Eventually, an event will have a timestamp that exceeds the <i>timespan</i>
40 * high end (determined by <i>n</i>, the number of buckets, and <i>d</i>, the
41 * bucket duration). At this point, the histogram needs to be compacted. This is
42 * done by simply merging adjacent buckets by pair, in effect doubling the
43 * <i>timespan</i> (<i>timespan'</i> = <i>n</i> * <i>d'</i>, where <i>d'</i> =
44 * 2<i>d</i>). This compaction happens as needed as the trace is read.
45 * <p>
fbd124dd 46 * The model allows for timestamps in not increasing order. The timestamps can
bfe038ff 47 * be fed to the model in any order. If an event has a timestamp less than the
fbd124dd 48 * <i>basetime</i>, the buckets will be moved to the right to account for the
bfe038ff 49 * new smaller timestamp. The new <i>basetime</i> is a multiple of the bucket
fbd124dd
BH
50 * duration smaller then the previous <i>basetime</i>. Note that the <i>basetime</i>
51 * might not be anymore a timestamp of an event. If necessary, the buckets will
bfe038ff 52 * be compacted before moving to the right. This might be necessary to not
fbd124dd
BH
53 * loose any event counts at the end of the buckets array.
54 * <p>
c392540b
FC
55 * The mapping from the model to the UI is performed by the <i>scaleTo()</i>
56 * method. By keeping the number of buckets <i>n</i> relatively large with
57 * respect to to the number of pixels in the actual histogram, we should achieve
58 * a nice result when visualizing the histogram.
59 * <p>
60 * TODO: Add filter support for more refined event counting (e.g. by trace,
61 * event type, etc.)
62 * <p>
74237cc3 63 * TODO: Cut-off eccentric values? TODO: Support for going back in time?
c392540b 64 */
fbd124dd 65public class HistogramDataModel implements IHistogramDataModel {
c392540b
FC
66
67 // ------------------------------------------------------------------------
68 // Constants
69 // ------------------------------------------------------------------------
70
71 // The default number of buckets
72 public static final int DEFAULT_NUMBER_OF_BUCKETS = 16 * 1000;
73
fbd124dd 74 public static final int REFRESH_FREQUENCY = DEFAULT_NUMBER_OF_BUCKETS;
bfe038ff 75
c392540b
FC
76 // ------------------------------------------------------------------------
77 // Attributes
78 // ------------------------------------------------------------------------
79
80 // Bucket management
81 private final int fNbBuckets;
82 private final long[] fBuckets;
83 private long fBucketDuration;
84 private long fNbEvents;
85 private int fLastBucket;
86
87 // Timestamps
fbd124dd 88 private long fFirstBucketTime; // could be negative when analyzing events with descending order!!!
c392540b
FC
89 private long fFirstEventTime;
90 private long fLastEventTime;
91 private long fCurrentEventTime;
92 private long fTimeLimit;
bfe038ff 93
e0752744 94 // Private listener lists
fbd124dd 95 private final ListenerList fModelListeners;
bfe038ff 96
e0752744
FC
97 // ------------------------------------------------------------------------
98 // Constructors
99 // ------------------------------------------------------------------------
100
c392540b
FC
101 public HistogramDataModel() {
102 this(DEFAULT_NUMBER_OF_BUCKETS);
103 }
104
105 public HistogramDataModel(int nbBuckets) {
106 fNbBuckets = nbBuckets;
107 fBuckets = new long[nbBuckets];
fbd124dd 108 fModelListeners = new ListenerList();
c392540b
FC
109 clear();
110 }
111
112 public HistogramDataModel(HistogramDataModel other) {
113 fNbBuckets = other.fNbBuckets;
114 fBuckets = Arrays.copyOf(other.fBuckets, fNbBuckets);
bfe038ff 115 fBucketDuration = Math.max(other.fBucketDuration,1);
c392540b
FC
116 fNbEvents = other.fNbEvents;
117 fLastBucket = other.fLastBucket;
fbd124dd 118 fFirstBucketTime = other.fFirstBucketTime;
c392540b
FC
119 fFirstEventTime = other.fFirstEventTime;
120 fLastEventTime = other.fLastEventTime;
121 fCurrentEventTime = other.fCurrentEventTime;
122 fTimeLimit = other.fTimeLimit;
fbd124dd
BH
123 fModelListeners = new ListenerList();
124 Object[] listeners = other.fModelListeners.getListeners();
125 for (Object listener : listeners) {
126 fModelListeners.add(listener);
127 }
c392540b
FC
128 }
129
130 // ------------------------------------------------------------------------
131 // Accessors
132 // ------------------------------------------------------------------------
133
134 public long getNbEvents() {
135 return fNbEvents;
136 }
137
138 public int getNbBuckets() {
139 return fNbBuckets;
140 }
141
142 public long getBucketDuration() {
143 return fBucketDuration;
144 }
bfe038ff 145
fbd124dd
BH
146 public long getFirstBucketTime() {
147 return fFirstBucketTime;
148 }
c392540b
FC
149
150 public long getStartTime() {
151 return fFirstEventTime;
152 }
bfe038ff 153
c392540b
FC
154 public long getEndTime() {
155 return fLastEventTime;
156 }
157
158 public long getCurrentEventTime() {
159 return fCurrentEventTime;
160 }
161
162 public long getTimeLimit() {
163 return fTimeLimit;
164 }
bfe038ff 165
fbd124dd
BH
166 // ------------------------------------------------------------------------
167 // Listener handling
168 // ------------------------------------------------------------------------
bfe038ff 169
fbd124dd 170 public void addHistogramListener(IHistogramModelListener listener) {
bfe038ff 171 fModelListeners.add(listener);
fbd124dd 172 }
bfe038ff 173
fbd124dd
BH
174 public void removeHistogramListener(IHistogramModelListener listener) {
175 fModelListeners.remove(listener);
176 }
c392540b 177
fbd124dd
BH
178 private void fireModelUpdateNotification() {
179 fireModelUpdateNotification(0);
180 }
bfe038ff 181
fbd124dd 182 private void fireModelUpdateNotification(long count) {
bfe038ff 183 if ((count % REFRESH_FREQUENCY) == 0) {
fbd124dd 184 Object[] listeners = fModelListeners.getListeners();
bfe038ff
MK
185 for (Object listener2 : listeners) {
186 IHistogramModelListener listener = (IHistogramModelListener) listener2;
fbd124dd
BH
187 listener.modelUpdated();
188 }
189 }
190 }
bfe038ff 191
c392540b
FC
192 // ------------------------------------------------------------------------
193 // Operations
194 // ------------------------------------------------------------------------
e0752744 195
fbd124dd
BH
196 @Override
197 public void complete() {
198 fireModelUpdateNotification();
199 }
c392540b
FC
200
201 /**
202 * Clear the histogram model.
203 */
fbd124dd 204 @Override
c392540b
FC
205 public void clear() {
206 Arrays.fill(fBuckets, 0);
207 fNbEvents = 0;
fbd124dd 208 fFirstBucketTime = 0;
c392540b
FC
209 fLastEventTime = 0;
210 fCurrentEventTime = 0;
211 fLastBucket = 0;
212 fBucketDuration = 1; // 1ns
213 updateEndTime();
fbd124dd 214 fireModelUpdateNotification();
c392540b
FC
215 }
216
217 /**
218 * Sets the current event time
bfe038ff 219 *
c392540b
FC
220 * @param timestamp
221 */
222 public void setCurrentEvent(long timestamp) {
223 fCurrentEventTime = timestamp;
224 }
225
fbd124dd
BH
226 /**
227 * Sets the current event time
bfe038ff 228 *
fbd124dd
BH
229 * @param timestamp
230 */
231 public void setCurrentEventNotifyListeners(long timestamp) {
232 fCurrentEventTime = timestamp;
233 fireModelUpdateNotification();
234 }
bfe038ff 235
c392540b
FC
236 /**
237 * Add event to the correct bucket, compacting the if needed.
bfe038ff 238 *
c392540b
FC
239 * @param timestamp the timestamp of the event to count
240 */
fbd124dd
BH
241 @Override
242 public void countEvent(long eventCount, long timestamp) {
bfe038ff 243
fbd124dd
BH
244 // Validate
245 if (timestamp < 0) {
fbd124dd
BH
246 return;
247 }
bfe038ff 248
c392540b 249 // Set the start/end time if not already done
bfe038ff 250 if ((fLastBucket == 0) && (fBuckets[0] == 0) && (timestamp > 0)) {
fbd124dd 251 fFirstBucketTime = timestamp;
c392540b
FC
252 fFirstEventTime = timestamp;
253 updateEndTime();
254 }
bfe038ff 255
fbd124dd
BH
256 if (timestamp < fFirstEventTime) {
257 fFirstEventTime = timestamp;
258 }
bfe038ff 259
c392540b
FC
260 if (fLastEventTime < timestamp) {
261 fLastEventTime = timestamp;
262 }
bfe038ff 263
fbd124dd 264 if (timestamp >= fFirstBucketTime) {
c392540b 265
fbd124dd
BH
266 // Compact as needed
267 while (timestamp >= fTimeLimit) {
268 mergeBuckets();
269 }
c392540b 270
fbd124dd 271 } else {
bfe038ff 272
fbd124dd
BH
273 // get offset for adjustment
274 int offset = getOffset(timestamp);
275
276 // Compact as needed
bfe038ff 277 while((fLastBucket + offset) >= fNbBuckets) {
fbd124dd
BH
278 mergeBuckets();
279 offset = getOffset(timestamp);
280 }
bfe038ff 281
fbd124dd
BH
282 moveBuckets(offset);
283
284 fLastBucket = fLastBucket + offset;
c392540b 285
bfe038ff 286 fFirstBucketTime = fFirstBucketTime - (offset*fBucketDuration);
fbd124dd
BH
287 updateEndTime();
288 }
bfe038ff 289
c392540b 290 // Increment the right bucket
fbd124dd 291 int index = (int) ((timestamp - fFirstBucketTime) / fBucketDuration);
c392540b
FC
292 fBuckets[index]++;
293 fNbEvents++;
bfe038ff 294 if (fLastBucket < index) {
c392540b 295 fLastBucket = index;
bfe038ff
MK
296 }
297
fbd124dd 298 fireModelUpdateNotification(eventCount);
c392540b
FC
299 }
300
301 /**
fbd124dd 302 * Scale the model data to the width, height and bar width requested.
bfe038ff 303 *
c392540b
FC
304 * @param width
305 * @param height
0d9a6d76 306 * @param barWidth
c392540b
FC
307 * @return the result array of size [width] and where the highest value
308 * doesn't exceed [height]
309 */
fbd124dd
BH
310 @Override
311 public HistogramScaledData scaleTo(int width, int height, int barWidth) {
c392540b 312 // Basic validation
bfe038ff
MK
313 if ((width <= 0) || (height <= 0) || (barWidth <= 0))
314 {
fbd124dd 315 throw new AssertionError("Invalid histogram dimensions (" + width + "x" + height + ", barWidth=" + barWidth + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
bfe038ff 316 }
c392540b
FC
317
318 // The result structure
fbd124dd 319 HistogramScaledData result = new HistogramScaledData(width, height, barWidth);
c392540b
FC
320
321 // Scale horizontally
74237cc3 322 result.fMaxValue = 0;
bfe038ff 323
fbd124dd 324 int nbBars = width / barWidth;
bfe038ff
MK
325 int bucketsPerBar = (fLastBucket / nbBars) + 1;
326 result.fBucketDuration = Math.max(bucketsPerBar * fBucketDuration,1);
fbd124dd 327 for (int i = 0; i < nbBars; i++) {
c392540b 328 int count = 0;
bfe038ff
MK
329 for (int j = i * bucketsPerBar; j < ((i + 1) * bucketsPerBar); j++) {
330 if (fNbBuckets <= j) {
c392540b 331 break;
bfe038ff 332 }
c392540b
FC
333 count += fBuckets[j];
334 }
335 result.fData[i] = count;
336 result.fLastBucket = i;
bfe038ff 337 if (result.fMaxValue < count) {
c392540b 338 result.fMaxValue = count;
bfe038ff 339 }
c392540b
FC
340 }
341
342 // Scale vertically
343 if (result.fMaxValue > 0) {
344 result.fScalingFactor = (double) height / result.fMaxValue;
345 }
346
bfe038ff 347 fBucketDuration = Math.max(fBucketDuration, 1);
c392540b 348 // Set the current event index in the scaled histogram
bfe038ff 349 if ((fCurrentEventTime >= fFirstBucketTime) && (fCurrentEventTime <= fLastEventTime)) {
fbd124dd 350 result.fCurrentBucket = (int) ((fCurrentEventTime - fFirstBucketTime) / fBucketDuration) / bucketsPerBar;
bfe038ff 351 } else {
c392540b 352 result.fCurrentBucket = HistogramScaledData.OUT_OF_RANGE_BUCKET;
bfe038ff 353 }
c392540b 354
fbd124dd
BH
355 result.fFirstBucketTime = fFirstBucketTime;
356 result.fFirstEventTime = fFirstEventTime;
c392540b
FC
357 return result;
358 }
359
360 // ------------------------------------------------------------------------
361 // Helper functions
362 // ------------------------------------------------------------------------
363
364 private void updateEndTime() {
bfe038ff 365 fTimeLimit = fFirstBucketTime + (fNbBuckets * fBucketDuration);
c392540b
FC
366 }
367
368 private void mergeBuckets() {
bfe038ff
MK
369 for (int i = 0; i < (fNbBuckets / 2); i++) {
370 fBuckets[i] = fBuckets[2 * i] + fBuckets[(2 * i) + 1];
c392540b
FC
371 }
372 Arrays.fill(fBuckets, fNbBuckets / 2, fNbBuckets, 0);
373 fBucketDuration *= 2;
374 updateEndTime();
bfe038ff 375 fLastBucket = (fNbBuckets / 2) - 1;
c392540b 376 }
bfe038ff 377
fbd124dd
BH
378 private void moveBuckets(int offset) {
379 for(int i = fNbBuckets - 1; i >= offset; i--) {
bfe038ff 380 fBuckets[i] = fBuckets[i-offset];
fbd124dd
BH
381 }
382
383 for (int i = 0; i < offset; i++) {
384 fBuckets[i] = 0;
385 }
386 }
387
388 private int getOffset(long timestamp) {
389 int offset = (int) ((fFirstBucketTime - timestamp) / fBucketDuration);
bfe038ff 390 if (((fFirstBucketTime - timestamp) % fBucketDuration) != 0) {
fbd124dd
BH
391 offset++;
392 }
393 return offset;
394 }
c392540b
FC
395
396}
This page took 0.086208 seconds and 5 git commands to generate.