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
1 /*******************************************************************************
2 * Copyright (c) 2011, 2012 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 * Bernd Hufmann - Implementation of new interfaces/listeners and support for
12 * time stamp in any order
13 * Francois Chouinard - Moved from LTTng to TMF
14 *******************************************************************************/
15
16 package org.eclipse.linuxtools.tmf.ui.views.histogram;
17
18 import java.util.Arrays;
19
20 import org.eclipse.core.runtime.ListenerList;
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>
46 * The model allows for timestamps in not increasing order. The timestamps can
47 * be fed to the model in any order. If an event has a timestamp less than the
48 * <i>basetime</i>, the buckets will be moved to the right to account for the
49 * new smaller timestamp. The new <i>basetime</i> is a multiple of the bucket
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
52 * be compacted before moving to the right. This might be necessary to not
53 * loose any event counts at the end of the buckets array.
54 * <p>
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>
63 * TODO: Cut-off eccentric values? TODO: Support for going back in time?
64 */
65 public class HistogramDataModel implements IHistogramDataModel {
66
67 // ------------------------------------------------------------------------
68 // Constants
69 // ------------------------------------------------------------------------
70
71 // The default number of buckets
72 public static final int DEFAULT_NUMBER_OF_BUCKETS = 16 * 1000;
73
74 public static final int REFRESH_FREQUENCY = DEFAULT_NUMBER_OF_BUCKETS;
75
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
88 private long fFirstBucketTime; // could be negative when analyzing events with descending order!!!
89 private long fFirstEventTime;
90 private long fLastEventTime;
91 private long fCurrentEventTime;
92 private long fTimeLimit;
93
94 // Private listener lists
95 private final ListenerList fModelListeners;
96
97 // ------------------------------------------------------------------------
98 // Constructors
99 // ------------------------------------------------------------------------
100
101 public HistogramDataModel() {
102 this(DEFAULT_NUMBER_OF_BUCKETS);
103 }
104
105 public HistogramDataModel(int nbBuckets) {
106 fNbBuckets = nbBuckets;
107 fBuckets = new long[nbBuckets];
108 fModelListeners = new ListenerList();
109 clear();
110 }
111
112 public HistogramDataModel(HistogramDataModel other) {
113 fNbBuckets = other.fNbBuckets;
114 fBuckets = Arrays.copyOf(other.fBuckets, fNbBuckets);
115 fBucketDuration = Math.max(other.fBucketDuration,1);
116 fNbEvents = other.fNbEvents;
117 fLastBucket = other.fLastBucket;
118 fFirstBucketTime = other.fFirstBucketTime;
119 fFirstEventTime = other.fFirstEventTime;
120 fLastEventTime = other.fLastEventTime;
121 fCurrentEventTime = other.fCurrentEventTime;
122 fTimeLimit = other.fTimeLimit;
123 fModelListeners = new ListenerList();
124 Object[] listeners = other.fModelListeners.getListeners();
125 for (Object listener : listeners) {
126 fModelListeners.add(listener);
127 }
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 }
145
146 public long getFirstBucketTime() {
147 return fFirstBucketTime;
148 }
149
150 public long getStartTime() {
151 return fFirstEventTime;
152 }
153
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 }
165
166 // ------------------------------------------------------------------------
167 // Listener handling
168 // ------------------------------------------------------------------------
169
170 public void addHistogramListener(IHistogramModelListener listener) {
171 fModelListeners.add(listener);
172 }
173
174 public void removeHistogramListener(IHistogramModelListener listener) {
175 fModelListeners.remove(listener);
176 }
177
178 private void fireModelUpdateNotification() {
179 fireModelUpdateNotification(0);
180 }
181
182 private void fireModelUpdateNotification(long count) {
183 if ((count % REFRESH_FREQUENCY) == 0) {
184 Object[] listeners = fModelListeners.getListeners();
185 for (Object listener2 : listeners) {
186 IHistogramModelListener listener = (IHistogramModelListener) listener2;
187 listener.modelUpdated();
188 }
189 }
190 }
191
192 // ------------------------------------------------------------------------
193 // Operations
194 // ------------------------------------------------------------------------
195
196 @Override
197 public void complete() {
198 fireModelUpdateNotification();
199 }
200
201 /**
202 * Clear the histogram model.
203 */
204 @Override
205 public void clear() {
206 Arrays.fill(fBuckets, 0);
207 fNbEvents = 0;
208 fFirstBucketTime = 0;
209 fLastEventTime = 0;
210 fCurrentEventTime = 0;
211 fLastBucket = 0;
212 fBucketDuration = 1; // 1ns
213 updateEndTime();
214 fireModelUpdateNotification();
215 }
216
217 /**
218 * Sets the current event time
219 *
220 * @param timestamp
221 */
222 public void setCurrentEvent(long timestamp) {
223 fCurrentEventTime = timestamp;
224 }
225
226 /**
227 * Sets the current event time
228 *
229 * @param timestamp
230 */
231 public void setCurrentEventNotifyListeners(long timestamp) {
232 fCurrentEventTime = timestamp;
233 fireModelUpdateNotification();
234 }
235
236 /**
237 * Add event to the correct bucket, compacting the if needed.
238 *
239 * @param timestamp the timestamp of the event to count
240 */
241 @Override
242 public void countEvent(long eventCount, long timestamp) {
243
244 // Validate
245 if (timestamp < 0) {
246 return;
247 }
248
249 // Set the start/end time if not already done
250 if ((fLastBucket == 0) && (fBuckets[0] == 0) && (timestamp > 0)) {
251 fFirstBucketTime = timestamp;
252 fFirstEventTime = timestamp;
253 updateEndTime();
254 }
255
256 if (timestamp < fFirstEventTime) {
257 fFirstEventTime = timestamp;
258 }
259
260 if (fLastEventTime < timestamp) {
261 fLastEventTime = timestamp;
262 }
263
264 if (timestamp >= fFirstBucketTime) {
265
266 // Compact as needed
267 while (timestamp >= fTimeLimit) {
268 mergeBuckets();
269 }
270
271 } else {
272
273 // get offset for adjustment
274 int offset = getOffset(timestamp);
275
276 // Compact as needed
277 while((fLastBucket + offset) >= fNbBuckets) {
278 mergeBuckets();
279 offset = getOffset(timestamp);
280 }
281
282 moveBuckets(offset);
283
284 fLastBucket = fLastBucket + offset;
285
286 fFirstBucketTime = fFirstBucketTime - (offset*fBucketDuration);
287 updateEndTime();
288 }
289
290 // Increment the right bucket
291 int index = (int) ((timestamp - fFirstBucketTime) / fBucketDuration);
292 fBuckets[index]++;
293 fNbEvents++;
294 if (fLastBucket < index) {
295 fLastBucket = index;
296 }
297
298 fireModelUpdateNotification(eventCount);
299 }
300
301 /**
302 * Scale the model data to the width, height and bar width requested.
303 *
304 * @param width
305 * @param height
306 * @param barWidth
307 * @return the result array of size [width] and where the highest value
308 * doesn't exceed [height]
309 */
310 @Override
311 public HistogramScaledData scaleTo(int width, int height, int barWidth) {
312 // Basic validation
313 if ((width <= 0) || (height <= 0) || (barWidth <= 0))
314 {
315 throw new AssertionError("Invalid histogram dimensions (" + width + "x" + height + ", barWidth=" + barWidth + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
316 }
317
318 // The result structure
319 HistogramScaledData result = new HistogramScaledData(width, height, barWidth);
320
321 // Scale horizontally
322 result.fMaxValue = 0;
323
324 int nbBars = width / barWidth;
325 int bucketsPerBar = (fLastBucket / nbBars) + 1;
326 result.fBucketDuration = Math.max(bucketsPerBar * fBucketDuration,1);
327 for (int i = 0; i < nbBars; i++) {
328 int count = 0;
329 for (int j = i * bucketsPerBar; j < ((i + 1) * bucketsPerBar); j++) {
330 if (fNbBuckets <= j) {
331 break;
332 }
333 count += fBuckets[j];
334 }
335 result.fData[i] = count;
336 result.fLastBucket = i;
337 if (result.fMaxValue < count) {
338 result.fMaxValue = count;
339 }
340 }
341
342 // Scale vertically
343 if (result.fMaxValue > 0) {
344 result.fScalingFactor = (double) height / result.fMaxValue;
345 }
346
347 fBucketDuration = Math.max(fBucketDuration, 1);
348 // Set the current event index in the scaled histogram
349 if ((fCurrentEventTime >= fFirstBucketTime) && (fCurrentEventTime <= fLastEventTime)) {
350 result.fCurrentBucket = (int) ((fCurrentEventTime - fFirstBucketTime) / fBucketDuration) / bucketsPerBar;
351 } else {
352 result.fCurrentBucket = HistogramScaledData.OUT_OF_RANGE_BUCKET;
353 }
354
355 result.fFirstBucketTime = fFirstBucketTime;
356 result.fFirstEventTime = fFirstEventTime;
357 return result;
358 }
359
360 // ------------------------------------------------------------------------
361 // Helper functions
362 // ------------------------------------------------------------------------
363
364 private void updateEndTime() {
365 fTimeLimit = fFirstBucketTime + (fNbBuckets * fBucketDuration);
366 }
367
368 private void mergeBuckets() {
369 for (int i = 0; i < (fNbBuckets / 2); i++) {
370 fBuckets[i] = fBuckets[2 * i] + fBuckets[(2 * i) + 1];
371 }
372 Arrays.fill(fBuckets, fNbBuckets / 2, fNbBuckets, 0);
373 fBucketDuration *= 2;
374 updateEndTime();
375 fLastBucket = (fNbBuckets / 2) - 1;
376 }
377
378 private void moveBuckets(int offset) {
379 for(int i = fNbBuckets - 1; i >= offset; i--) {
380 fBuckets[i] = fBuckets[i-offset];
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);
390 if (((fFirstBucketTime - timestamp) % fBucketDuration) != 0) {
391 offset++;
392 }
393 return offset;
394 }
395
396 }
This page took 0.040319 seconds and 6 git commands to generate.