Commit | Line | Data |
---|---|---|
c392540b | 1 | /******************************************************************************* |
ed902a2b | 2 | * Copyright (c) 2011, 2015 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 |
0fcf3b09 | 15 | * Patrick Tasse - Support selection range |
95aa81ef | 16 | * Jean-Christian Kouamé, Simon Delisle - Added support to manage lost events |
2fc582d2 | 17 | * Xavier Raynaud - Support multi-trace coloring |
c392540b FC |
18 | *******************************************************************************/ |
19 | ||
2bdf0193 | 20 | package org.eclipse.tracecompass.tmf.ui.views.histogram; |
c392540b FC |
21 | |
22 | import java.util.Arrays; | |
c14c0757 | 23 | import java.util.Collection; |
2fc582d2 XR |
24 | import java.util.LinkedHashMap; |
25 | import java.util.Map; | |
c392540b | 26 | |
fbd124dd | 27 | import org.eclipse.core.runtime.ListenerList; |
2bdf0193 AM |
28 | import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; |
29 | import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; | |
30 | import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager; | |
c392540b | 31 | |
c14c0757 GB |
32 | import com.google.common.base.Function; |
33 | import com.google.common.collect.FluentIterable; | |
34 | ||
c392540b | 35 | /** |
b544077e | 36 | * Histogram-independent data model. |
f8177ba2 | 37 | * |
b544077e | 38 | * It has the following characteristics: |
c392540b FC |
39 | * <ul> |
40 | * <li>The <i>basetime</i> is the timestamp of the first event | |
41 | * <li>There is a fixed number (<i>n</i>) of buckets of uniform duration | |
42 | * (<i>d</i>) | |
43 | * <li>The <i>timespan</i> of the model is thus: <i>n</i> * <i>d</i> time units | |
44 | * <li>Bucket <i>i</i> holds the number of events that occurred in time range: | |
45 | * [<i>basetime</i> + <i>i</i> * <i>d</i>, <i>basetime</i> + (<i>i</i> + 1) * | |
46 | * <i>d</i>) | |
47 | * </ul> | |
48 | * Initially, the bucket durations is set to 1ns. As the events are read, they | |
49 | * are tallied (using <i>countEvent()</i>) in the appropriate bucket (relative | |
50 | * to the <i>basetime</i>). | |
51 | * <p> | |
52 | * Eventually, an event will have a timestamp that exceeds the <i>timespan</i> | |
53 | * high end (determined by <i>n</i>, the number of buckets, and <i>d</i>, the | |
54 | * bucket duration). At this point, the histogram needs to be compacted. This is | |
55 | * done by simply merging adjacent buckets by pair, in effect doubling the | |
56 | * <i>timespan</i> (<i>timespan'</i> = <i>n</i> * <i>d'</i>, where <i>d'</i> = | |
57 | * 2<i>d</i>). This compaction happens as needed as the trace is read. | |
58 | * <p> | |
fbd124dd | 59 | * The model allows for timestamps in not increasing order. The timestamps can |
bfe038ff | 60 | * be fed to the model in any order. If an event has a timestamp less than the |
fbd124dd | 61 | * <i>basetime</i>, the buckets will be moved to the right to account for the |
bfe038ff | 62 | * new smaller timestamp. The new <i>basetime</i> is a multiple of the bucket |
95aa81ef JCK |
63 | * duration smaller then the previous <i>basetime</i>. Note that the |
64 | * <i>basetime</i> might no longer be the timestamp of an event. If necessary, | |
65 | * the buckets will be compacted before moving to the right. This might be | |
66 | * necessary to not lose any event counts at the end of the buckets array. | |
fbd124dd | 67 | * <p> |
c392540b FC |
68 | * The mapping from the model to the UI is performed by the <i>scaleTo()</i> |
69 | * method. By keeping the number of buckets <i>n</i> relatively large with | |
70 | * respect to to the number of pixels in the actual histogram, we should achieve | |
71 | * a nice result when visualizing the histogram. | |
72 | * <p> | |
f8177ba2 | 73 | * |
00419b1f | 74 | * @version 2.0 |
b544077e | 75 | * @author Francois Chouinard |
c392540b | 76 | */ |
fbd124dd | 77 | public class HistogramDataModel implements IHistogramDataModel { |
c392540b FC |
78 | |
79 | // ------------------------------------------------------------------------ | |
80 | // Constants | |
81 | // ------------------------------------------------------------------------ | |
82 | ||
b544077e | 83 | /** |
95aa81ef | 84 | * The default number of buckets |
b544077e | 85 | */ |
c392540b FC |
86 | public static final int DEFAULT_NUMBER_OF_BUCKETS = 16 * 1000; |
87 | ||
b544077e | 88 | /** |
f8177ba2 | 89 | * Number of events after which listeners will be notified. |
b544077e | 90 | */ |
fbd124dd | 91 | public static final int REFRESH_FREQUENCY = DEFAULT_NUMBER_OF_BUCKETS; |
bfe038ff | 92 | |
c392540b FC |
93 | // ------------------------------------------------------------------------ |
94 | // Attributes | |
95 | // ------------------------------------------------------------------------ | |
96 | ||
2fc582d2 XR |
97 | // Trace management |
98 | private ITmfTrace fTrace = null; | |
99 | private final Map<ITmfTrace, Integer> fTraceMap = new LinkedHashMap<>(); | |
100 | ||
c392540b FC |
101 | // Bucket management |
102 | private final int fNbBuckets; | |
2fc582d2 | 103 | private final HistogramBucket[] fBuckets; |
95aa81ef | 104 | private final long[] fLostEventsBuckets; |
c392540b FC |
105 | private long fBucketDuration; |
106 | private long fNbEvents; | |
107 | private int fLastBucket; | |
108 | ||
109 | // Timestamps | |
fbd124dd | 110 | private long fFirstBucketTime; // could be negative when analyzing events with descending order!!! |
c392540b | 111 | private long fFirstEventTime; |
d418423b | 112 | private long fEndTime; |
0fcf3b09 PT |
113 | private long fSelectionBegin; |
114 | private long fSelectionEnd; | |
c392540b | 115 | private long fTimeLimit; |
bfe038ff | 116 | |
e0752744 | 117 | // Private listener lists |
fbd124dd | 118 | private final ListenerList fModelListeners; |
bfe038ff | 119 | |
e0752744 FC |
120 | // ------------------------------------------------------------------------ |
121 | // Constructors | |
122 | // ------------------------------------------------------------------------ | |
123 | ||
b544077e BH |
124 | /** |
125 | * Default constructor with default number of buckets. | |
126 | */ | |
c392540b | 127 | public HistogramDataModel() { |
00419b1f FC |
128 | this(0, DEFAULT_NUMBER_OF_BUCKETS); |
129 | } | |
130 | ||
131 | /** | |
132 | * Default constructor with default number of buckets. | |
95aa81ef JCK |
133 | * |
134 | * @param startTime | |
135 | * The histogram start time | |
00419b1f FC |
136 | * @since 2.0 |
137 | */ | |
138 | public HistogramDataModel(long startTime) { | |
139 | this(startTime, DEFAULT_NUMBER_OF_BUCKETS); | |
c392540b FC |
140 | } |
141 | ||
b544077e BH |
142 | /** |
143 | * Constructor with non-default number of buckets. | |
95aa81ef JCK |
144 | * |
145 | * @param nbBuckets | |
146 | * A number of buckets. | |
b544077e | 147 | */ |
c392540b | 148 | public HistogramDataModel(int nbBuckets) { |
00419b1f FC |
149 | this(0, nbBuckets); |
150 | } | |
151 | ||
152 | /** | |
153 | * Constructor with non-default number of buckets. | |
95aa81ef JCK |
154 | * |
155 | * @param startTime | |
156 | * the histogram start time | |
157 | * @param nbBuckets | |
158 | * A number of buckets. | |
00419b1f FC |
159 | * @since 2.0 |
160 | */ | |
161 | public HistogramDataModel(long startTime, int nbBuckets) { | |
d418423b | 162 | fFirstBucketTime = fFirstEventTime = fEndTime = startTime; |
c392540b | 163 | fNbBuckets = nbBuckets; |
2fc582d2 | 164 | fBuckets = new HistogramBucket[nbBuckets]; |
95aa81ef | 165 | fLostEventsBuckets = new long[nbBuckets]; |
fbd124dd | 166 | fModelListeners = new ListenerList(); |
c392540b FC |
167 | clear(); |
168 | } | |
169 | ||
b544077e BH |
170 | /** |
171 | * Copy constructor. | |
95aa81ef JCK |
172 | * |
173 | * @param other | |
174 | * A model to copy. | |
b544077e | 175 | */ |
c392540b FC |
176 | public HistogramDataModel(HistogramDataModel other) { |
177 | fNbBuckets = other.fNbBuckets; | |
2fc582d2 XR |
178 | fBuckets = new HistogramBucket[fNbBuckets]; |
179 | for (int i = 0; i < fNbBuckets; i++) { | |
180 | fBuckets[i] = new HistogramBucket(other.fBuckets[i]); | |
181 | } | |
95aa81ef | 182 | fLostEventsBuckets = Arrays.copyOf(other.fLostEventsBuckets, fNbBuckets); |
00419b1f | 183 | fBucketDuration = Math.max(other.fBucketDuration, 1); |
c392540b FC |
184 | fNbEvents = other.fNbEvents; |
185 | fLastBucket = other.fLastBucket; | |
fbd124dd | 186 | fFirstBucketTime = other.fFirstBucketTime; |
c392540b | 187 | fFirstEventTime = other.fFirstEventTime; |
d418423b | 188 | fEndTime = other.fEndTime; |
0fcf3b09 PT |
189 | fSelectionBegin = other.fSelectionBegin; |
190 | fSelectionEnd = other.fSelectionEnd; | |
c392540b | 191 | fTimeLimit = other.fTimeLimit; |
fbd124dd BH |
192 | fModelListeners = new ListenerList(); |
193 | Object[] listeners = other.fModelListeners.getListeners(); | |
194 | for (Object listener : listeners) { | |
195 | fModelListeners.add(listener); | |
196 | } | |
c392540b FC |
197 | } |
198 | ||
c53a7992 BH |
199 | |
200 | /** | |
201 | * Disposes the data model | |
202 | * @since 3.0 | |
203 | */ | |
204 | public void dispose() { | |
205 | fTraceMap.clear(); | |
206 | fTrace = null; | |
207 | } | |
208 | ||
c392540b FC |
209 | // ------------------------------------------------------------------------ |
210 | // Accessors | |
211 | // ------------------------------------------------------------------------ | |
212 | ||
b544077e BH |
213 | /** |
214 | * Returns the number of events in the data model. | |
95aa81ef | 215 | * |
b544077e BH |
216 | * @return number of events. |
217 | */ | |
c392540b FC |
218 | public long getNbEvents() { |
219 | return fNbEvents; | |
220 | } | |
221 | ||
b544077e BH |
222 | /** |
223 | * Returns the number of buckets in the model. | |
95aa81ef | 224 | * |
b544077e BH |
225 | * @return number of buckets. |
226 | */ | |
c392540b FC |
227 | public int getNbBuckets() { |
228 | return fNbBuckets; | |
229 | } | |
230 | ||
95aa81ef JCK |
231 | /** |
232 | * Returns the current bucket duration. | |
233 | * | |
234 | * @return bucket duration | |
235 | */ | |
c392540b FC |
236 | public long getBucketDuration() { |
237 | return fBucketDuration; | |
238 | } | |
bfe038ff | 239 | |
b544077e BH |
240 | /** |
241 | * Returns the time value of the first bucket in the model. | |
95aa81ef | 242 | * |
b544077e BH |
243 | * @return time of first bucket. |
244 | */ | |
fbd124dd BH |
245 | public long getFirstBucketTime() { |
246 | return fFirstBucketTime; | |
247 | } | |
c392540b | 248 | |
b544077e BH |
249 | /** |
250 | * Returns the time of the first event in the model. | |
95aa81ef | 251 | * |
b544077e BH |
252 | * @return time of first event. |
253 | */ | |
c392540b FC |
254 | public long getStartTime() { |
255 | return fFirstEventTime; | |
256 | } | |
bfe038ff | 257 | |
2fc582d2 XR |
258 | /** |
259 | * Sets the trace of this model. | |
260 | * @param trace - a {@link ITmfTrace} | |
261 | * @since 3.0 | |
262 | */ | |
263 | public void setTrace(ITmfTrace trace) { | |
264 | this.fTrace = trace; | |
265 | fTraceMap.clear(); | |
c14c0757 GB |
266 | int i = 0; |
267 | for (ITmfTrace tr : TmfTraceManager.getTraceSet(fTrace)) { | |
268 | fTraceMap.put(tr, i); | |
269 | i++; | |
2fc582d2 XR |
270 | } |
271 | } | |
272 | ||
273 | /** | |
274 | * Gets the trace of this model. | |
275 | * @return a {@link ITmfTrace} | |
276 | * @since 3.0 | |
277 | */ | |
278 | public ITmfTrace getTrace() { | |
279 | return this.fTrace; | |
280 | } | |
281 | ||
282 | /** | |
283 | * Gets the traces names of this model. | |
284 | * @return an array of trace names | |
285 | * @since 3.0 | |
286 | */ | |
287 | public String[] getTraceNames() { | |
c14c0757 GB |
288 | FluentIterable<ITmfTrace> traces = FluentIterable.from(TmfTraceManager.getTraceSet(fTrace)); |
289 | FluentIterable<String> traceNames = traces.transform(new Function<ITmfTrace, String>() { | |
290 | @Override | |
291 | public String apply(ITmfTrace input) { | |
292 | return input.getName(); | |
293 | } | |
294 | }); | |
295 | return traceNames.toArray(String.class); | |
2fc582d2 XR |
296 | } |
297 | ||
298 | /** | |
299 | * Gets the number of traces of this model. | |
300 | * @return the number of traces of this model. | |
301 | * @since 3.0 | |
302 | */ | |
303 | public int getNbTraces() { | |
c14c0757 GB |
304 | Collection<ITmfTrace> traces = TmfTraceManager.getTraceSet(fTrace); |
305 | if (traces.isEmpty()) { | |
2fc582d2 XR |
306 | return 1; // |
307 | } | |
c14c0757 | 308 | return traces.size(); |
2fc582d2 XR |
309 | } |
310 | ||
00419b1f FC |
311 | /** |
312 | * Sets the model start time | |
95aa81ef JCK |
313 | * |
314 | * @param startTime | |
315 | * the histogram range start time | |
316 | * @param endTime | |
317 | * the histogram range end time | |
00419b1f FC |
318 | * @since 2.0 |
319 | */ | |
320 | public void setTimeRange(long startTime, long endTime) { | |
d418423b | 321 | fFirstBucketTime = fFirstEventTime = fEndTime = startTime; |
00419b1f FC |
322 | fBucketDuration = 1; |
323 | updateEndTime(); | |
324 | while (endTime >= fTimeLimit) { | |
325 | mergeBuckets(); | |
326 | } | |
327 | } | |
328 | ||
b544077e | 329 | /** |
d418423b PT |
330 | * Set the end time. Setting this ensures that the corresponding bucket is |
331 | * displayed regardless of the event counts. | |
95aa81ef | 332 | * |
d418423b PT |
333 | * @param endTime |
334 | * the time of the last used bucket | |
335 | * @since 2.2 | |
336 | */ | |
337 | public void setEndTime(long endTime) { | |
338 | fEndTime = endTime; | |
339 | fLastBucket = (int) ((endTime - fFirstBucketTime) / fBucketDuration); | |
340 | } | |
341 | ||
342 | /** | |
343 | * Returns the end time. | |
344 | * | |
345 | * @return the time of the last used bucket | |
b544077e | 346 | */ |
c392540b | 347 | public long getEndTime() { |
d418423b | 348 | return fEndTime; |
c392540b FC |
349 | } |
350 | ||
0fcf3b09 PT |
351 | /** |
352 | * Returns the begin time of the current selection in the model. | |
95aa81ef | 353 | * |
0fcf3b09 PT |
354 | * @return the begin time of the current selection. |
355 | * @since 2.1 | |
356 | */ | |
357 | public long getSelectionBegin() { | |
358 | return fSelectionBegin; | |
359 | } | |
360 | ||
361 | /** | |
362 | * Returns the end time of the current selection in the model. | |
95aa81ef | 363 | * |
0fcf3b09 PT |
364 | * @return the end time of the current selection. |
365 | * @since 2.1 | |
366 | */ | |
367 | public long getSelectionEnd() { | |
368 | return fSelectionEnd; | |
c392540b FC |
369 | } |
370 | ||
b544077e BH |
371 | /** |
372 | * Returns the time limit with is: start time + nbBuckets * bucketDuration | |
95aa81ef | 373 | * |
b544077e BH |
374 | * @return the time limit. |
375 | */ | |
c392540b FC |
376 | public long getTimeLimit() { |
377 | return fTimeLimit; | |
378 | } | |
bfe038ff | 379 | |
fbd124dd BH |
380 | // ------------------------------------------------------------------------ |
381 | // Listener handling | |
382 | // ------------------------------------------------------------------------ | |
bfe038ff | 383 | |
b544077e BH |
384 | /** |
385 | * Add a listener to the model to be informed about model changes. | |
95aa81ef JCK |
386 | * |
387 | * @param listener | |
388 | * A listener to add. | |
b544077e | 389 | */ |
fbd124dd | 390 | public void addHistogramListener(IHistogramModelListener listener) { |
bfe038ff | 391 | fModelListeners.add(listener); |
fbd124dd | 392 | } |
bfe038ff | 393 | |
b544077e BH |
394 | /** |
395 | * Remove a given model listener. | |
95aa81ef JCK |
396 | * |
397 | * @param listener | |
398 | * A listener to remove. | |
b544077e | 399 | */ |
fbd124dd BH |
400 | public void removeHistogramListener(IHistogramModelListener listener) { |
401 | fModelListeners.remove(listener); | |
402 | } | |
c392540b | 403 | |
f8177ba2 | 404 | // Notify listeners (always) |
fbd124dd BH |
405 | private void fireModelUpdateNotification() { |
406 | fireModelUpdateNotification(0); | |
407 | } | |
bfe038ff | 408 | |
b544077e | 409 | // Notify listener on boundary |
fbd124dd | 410 | private void fireModelUpdateNotification(long count) { |
bfe038ff | 411 | if ((count % REFRESH_FREQUENCY) == 0) { |
fbd124dd | 412 | Object[] listeners = fModelListeners.getListeners(); |
bfe038ff MK |
413 | for (Object listener2 : listeners) { |
414 | IHistogramModelListener listener = (IHistogramModelListener) listener2; | |
fbd124dd BH |
415 | listener.modelUpdated(); |
416 | } | |
417 | } | |
418 | } | |
bfe038ff | 419 | |
c392540b FC |
420 | // ------------------------------------------------------------------------ |
421 | // Operations | |
422 | // ------------------------------------------------------------------------ | |
e0752744 | 423 | |
fbd124dd BH |
424 | @Override |
425 | public void complete() { | |
426 | fireModelUpdateNotification(); | |
427 | } | |
c392540b FC |
428 | |
429 | /** | |
430 | * Clear the histogram model. | |
95aa81ef | 431 | * |
2bdf0193 | 432 | * @see org.eclipse.tracecompass.tmf.ui.views.distribution.model.IBaseDistributionModel#clear() |
c392540b | 433 | */ |
fbd124dd | 434 | @Override |
4540412a | 435 | public synchronized void clear() { |
2fc582d2 | 436 | Arrays.fill(fBuckets, null); |
95aa81ef | 437 | Arrays.fill(fLostEventsBuckets, 0); |
c392540b | 438 | fNbEvents = 0; |
fbd124dd | 439 | fFirstBucketTime = 0; |
d418423b | 440 | fEndTime = 0; |
0fcf3b09 PT |
441 | fSelectionBegin = 0; |
442 | fSelectionEnd = 0; | |
c392540b | 443 | fLastBucket = 0; |
f8177ba2 | 444 | fBucketDuration = 1; |
c392540b | 445 | updateEndTime(); |
fbd124dd | 446 | fireModelUpdateNotification(); |
c392540b FC |
447 | } |
448 | ||
0fcf3b09 PT |
449 | /** |
450 | * Sets the current selection time range (no notification of listeners) | |
451 | * | |
95aa81ef JCK |
452 | * @param beginTime |
453 | * The selection begin time. | |
454 | * @param endTime | |
455 | * The selection end time. | |
0fcf3b09 PT |
456 | * @since 2.1 |
457 | */ | |
458 | public void setSelection(long beginTime, long endTime) { | |
459 | fSelectionBegin = beginTime; | |
460 | fSelectionEnd = endTime; | |
461 | } | |
462 | ||
463 | /** | |
464 | * Sets the current selection time range with notification of listeners | |
465 | * | |
95aa81ef JCK |
466 | * @param beginTime |
467 | * The selection begin time. | |
468 | * @param endTime | |
469 | * The selection end time. | |
0fcf3b09 PT |
470 | * @since 2.1 |
471 | */ | |
472 | public void setSelectionNotifyListeners(long beginTime, long endTime) { | |
473 | fSelectionBegin = beginTime; | |
474 | fSelectionEnd = endTime; | |
fbd124dd BH |
475 | fireModelUpdateNotification(); |
476 | } | |
bfe038ff | 477 | |
c392540b FC |
478 | /** |
479 | * Add event to the correct bucket, compacting the if needed. | |
bfe038ff | 480 | * |
95aa81ef JCK |
481 | * @param eventCount |
482 | * The current event Count (for notification purposes) | |
483 | * @param timestamp | |
484 | * The timestamp of the event to count | |
2fc582d2 XR |
485 | * @param trace |
486 | * The event trace | |
487 | * @since 3.0 | |
c392540b | 488 | */ |
fbd124dd | 489 | @Override |
4540412a | 490 | public synchronized void countEvent(long eventCount, long timestamp, ITmfTrace trace) { |
bfe038ff | 491 | |
fbd124dd BH |
492 | // Validate |
493 | if (timestamp < 0) { | |
fbd124dd BH |
494 | return; |
495 | } | |
bfe038ff | 496 | |
c392540b | 497 | // Set the start/end time if not already done |
2fc582d2 | 498 | if ((fFirstBucketTime == 0) && (fLastBucket == 0) && (fBuckets[0] == null) && (timestamp > 0)) { |
fbd124dd | 499 | fFirstBucketTime = timestamp; |
c392540b FC |
500 | fFirstEventTime = timestamp; |
501 | updateEndTime(); | |
502 | } | |
bfe038ff | 503 | |
fbd124dd BH |
504 | if (timestamp < fFirstEventTime) { |
505 | fFirstEventTime = timestamp; | |
506 | } | |
bfe038ff | 507 | |
d418423b PT |
508 | if (fEndTime < timestamp) { |
509 | fEndTime = timestamp; | |
c392540b | 510 | } |
bfe038ff | 511 | |
fbd124dd | 512 | if (timestamp >= fFirstBucketTime) { |
c392540b | 513 | |
fbd124dd BH |
514 | // Compact as needed |
515 | while (timestamp >= fTimeLimit) { | |
516 | mergeBuckets(); | |
517 | } | |
c392540b | 518 | |
fbd124dd | 519 | } else { |
bfe038ff | 520 | |
fbd124dd | 521 | // get offset for adjustment |
4540412a | 522 | long preMergeOffset = getOffset(timestamp); |
fbd124dd BH |
523 | |
524 | // Compact as needed | |
4540412a | 525 | while ((fLastBucket + preMergeOffset) >= fNbBuckets) { |
fbd124dd | 526 | mergeBuckets(); |
4540412a | 527 | preMergeOffset = getOffset(timestamp); |
fbd124dd | 528 | } |
bfe038ff | 529 | |
4540412a PT |
530 | // after merging the offset should be less than number of buckets |
531 | int offset = (int) preMergeOffset; | |
fbd124dd BH |
532 | moveBuckets(offset); |
533 | ||
534 | fLastBucket = fLastBucket + offset; | |
c392540b | 535 | |
95aa81ef | 536 | fFirstBucketTime = fFirstBucketTime - (offset * fBucketDuration); |
fbd124dd BH |
537 | updateEndTime(); |
538 | } | |
bfe038ff | 539 | |
c392540b | 540 | // Increment the right bucket |
fbd124dd | 541 | int index = (int) ((timestamp - fFirstBucketTime) / fBucketDuration); |
2fc582d2 XR |
542 | if (fBuckets[index] == null) { |
543 | fBuckets[index] = new HistogramBucket(getNbTraces()); | |
544 | } | |
545 | Integer traceIndex = fTraceMap.get(trace); | |
546 | if (traceIndex == null) { | |
547 | traceIndex = 0; | |
548 | } | |
549 | fBuckets[index].addEvent(traceIndex); | |
c392540b | 550 | fNbEvents++; |
bfe038ff | 551 | if (fLastBucket < index) { |
c392540b | 552 | fLastBucket = index; |
bfe038ff MK |
553 | } |
554 | ||
fbd124dd | 555 | fireModelUpdateNotification(eventCount); |
c392540b FC |
556 | } |
557 | ||
95aa81ef JCK |
558 | /** |
559 | * Add lost event to the correct bucket, compacting the if needed. | |
560 | * | |
561 | * @param timeRange | |
562 | * time range of a lost event | |
563 | * @param nbLostEvents | |
564 | * the number of lost events | |
565 | * @param fullRange | |
566 | * Full range or time range for histogram request | |
46a59db7 | 567 | * @since 2.2 |
95aa81ef JCK |
568 | */ |
569 | public void countLostEvent(TmfTimeRange timeRange, long nbLostEvents, boolean fullRange) { | |
570 | ||
571 | // Validate | |
572 | if (timeRange.getStartTime().getValue() < 0 || timeRange.getEndTime().getValue() < 0) { | |
573 | return; | |
574 | } | |
575 | ||
576 | // Compact as needed | |
577 | if (fullRange) { | |
578 | while (timeRange.getEndTime().getValue() >= fTimeLimit) { | |
579 | mergeBuckets(); | |
580 | } | |
581 | } | |
582 | ||
583 | int indexStart = (int) ((timeRange.getStartTime().getValue() - fFirstBucketTime) / fBucketDuration); | |
584 | int indexEnd = (int) ((timeRange.getEndTime().getValue() - fFirstBucketTime) / fBucketDuration); | |
585 | int nbBucketRange = (indexEnd - indexStart) + 1; | |
586 | ||
587 | int lostEventPerBucket = (int) Math.ceil((double) nbLostEvents / nbBucketRange); | |
588 | long lastLostCol = Math.max(1, nbLostEvents - lostEventPerBucket * (nbBucketRange - 1)); | |
589 | ||
590 | // Increment the right bucket, bear in mind that ranges make it almost certain that some lost events are out of range | |
591 | for (int index = indexStart; index <= indexEnd && index < fLostEventsBuckets.length; index++) { | |
592 | if (index == (indexStart + nbBucketRange - 1)) { | |
593 | fLostEventsBuckets[index] += lastLostCol; | |
594 | } else { | |
595 | fLostEventsBuckets[index] += lostEventPerBucket; | |
596 | } | |
597 | } | |
598 | ||
599 | fNbEvents++; | |
600 | ||
601 | fireModelUpdateNotification(nbLostEvents); | |
602 | } | |
603 | ||
c392540b | 604 | /** |
fbd124dd | 605 | * Scale the model data to the width, height and bar width requested. |
bfe038ff | 606 | * |
95aa81ef JCK |
607 | * @param width |
608 | * A width of the histogram canvas | |
609 | * @param height | |
610 | * A height of the histogram canvas | |
611 | * @param barWidth | |
612 | * A width (in pixel) of a histogram bar | |
613 | * @return the result array of size [width] and where the highest value | |
614 | * doesn't exceed [height] | |
f8177ba2 | 615 | * |
2bdf0193 | 616 | * @see org.eclipse.tracecompass.tmf.ui.views.histogram.IHistogramDataModel#scaleTo(int, |
95aa81ef | 617 | * int, int) |
c392540b | 618 | */ |
fbd124dd BH |
619 | @Override |
620 | public HistogramScaledData scaleTo(int width, int height, int barWidth) { | |
c392540b | 621 | // Basic validation |
95aa81ef JCK |
622 | if ((width <= 0) || (height <= 0) || (barWidth <= 0)) |
623 | { | |
fbd124dd | 624 | throw new AssertionError("Invalid histogram dimensions (" + width + "x" + height + ", barWidth=" + barWidth + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
bfe038ff | 625 | } |
c392540b FC |
626 | |
627 | // The result structure | |
fbd124dd | 628 | HistogramScaledData result = new HistogramScaledData(width, height, barWidth); |
c392540b FC |
629 | |
630 | // Scale horizontally | |
74237cc3 | 631 | result.fMaxValue = 0; |
bfe038ff | 632 | |
fbd124dd | 633 | int nbBars = width / barWidth; |
bfe038ff | 634 | int bucketsPerBar = (fLastBucket / nbBars) + 1; |
95aa81ef | 635 | result.fBucketDuration = Math.max(bucketsPerBar * fBucketDuration, 1); |
fbd124dd | 636 | for (int i = 0; i < nbBars; i++) { |
c392540b | 637 | int count = 0; |
95aa81ef | 638 | int countLostEvent = 0; |
2fc582d2 | 639 | result.fData[i] = new HistogramBucket(getNbTraces()); |
bfe038ff MK |
640 | for (int j = i * bucketsPerBar; j < ((i + 1) * bucketsPerBar); j++) { |
641 | if (fNbBuckets <= j) { | |
c392540b | 642 | break; |
bfe038ff | 643 | } |
2fc582d2 XR |
644 | if (fBuckets[j] != null) { |
645 | count += fBuckets[j].getNbEvents(); | |
646 | result.fData[i].add(fBuckets[j]); | |
647 | } | |
95aa81ef | 648 | countLostEvent += fLostEventsBuckets[j]; |
c392540b | 649 | } |
95aa81ef | 650 | result.fLostEventsData[i] = countLostEvent; |
c392540b | 651 | result.fLastBucket = i; |
bfe038ff | 652 | if (result.fMaxValue < count) { |
c392540b | 653 | result.fMaxValue = count; |
bfe038ff | 654 | } |
95aa81ef JCK |
655 | if (result.fMaxCombinedValue < count + countLostEvent) { |
656 | result.fMaxCombinedValue = count + countLostEvent; | |
657 | } | |
c392540b FC |
658 | } |
659 | ||
660 | // Scale vertically | |
661 | if (result.fMaxValue > 0) { | |
662 | result.fScalingFactor = (double) height / result.fMaxValue; | |
663 | } | |
95aa81ef JCK |
664 | if (result.fMaxCombinedValue > 0) { |
665 | result.fScalingFactorCombined = (double) height / result.fMaxCombinedValue; | |
666 | } | |
c392540b | 667 | |
bfe038ff | 668 | fBucketDuration = Math.max(fBucketDuration, 1); |
0fcf3b09 | 669 | // Set selection begin and end index in the scaled histogram |
cc817e65 PT |
670 | result.fSelectionBeginBucket = (int) ((fSelectionBegin - fFirstBucketTime) / fBucketDuration) / bucketsPerBar; |
671 | result.fSelectionEndBucket = (int) ((fSelectionEnd - fFirstBucketTime) / fBucketDuration) / bucketsPerBar; | |
c392540b | 672 | |
fbd124dd BH |
673 | result.fFirstBucketTime = fFirstBucketTime; |
674 | result.fFirstEventTime = fFirstEventTime; | |
c392540b FC |
675 | return result; |
676 | } | |
677 | ||
678 | // ------------------------------------------------------------------------ | |
679 | // Helper functions | |
680 | // ------------------------------------------------------------------------ | |
681 | ||
682 | private void updateEndTime() { | |
bfe038ff | 683 | fTimeLimit = fFirstBucketTime + (fNbBuckets * fBucketDuration); |
c392540b FC |
684 | } |
685 | ||
686 | private void mergeBuckets() { | |
bfe038ff | 687 | for (int i = 0; i < (fNbBuckets / 2); i++) { |
2fc582d2 | 688 | fBuckets[i] = new HistogramBucket(fBuckets[2 * i], fBuckets[(2 * i) + 1]); |
95aa81ef | 689 | fLostEventsBuckets[i] = fLostEventsBuckets[2 * i] + fLostEventsBuckets[(2 * i) + 1]; |
c392540b | 690 | } |
2fc582d2 | 691 | Arrays.fill(fBuckets, fNbBuckets / 2, fNbBuckets, null); |
95aa81ef | 692 | Arrays.fill(fLostEventsBuckets, fNbBuckets / 2, fNbBuckets, 0); |
c392540b FC |
693 | fBucketDuration *= 2; |
694 | updateEndTime(); | |
bfe038ff | 695 | fLastBucket = (fNbBuckets / 2) - 1; |
c392540b | 696 | } |
bfe038ff | 697 | |
fbd124dd | 698 | private void moveBuckets(int offset) { |
95aa81ef | 699 | for (int i = fNbBuckets - 1; i >= offset; i--) { |
2fc582d2 | 700 | fBuckets[i] = new HistogramBucket(fBuckets[i - offset]); |
95aa81ef | 701 | fLostEventsBuckets[i] = fLostEventsBuckets[i - offset]; |
fbd124dd BH |
702 | } |
703 | ||
704 | for (int i = 0; i < offset; i++) { | |
2fc582d2 | 705 | fBuckets[i] = null; |
95aa81ef | 706 | fLostEventsBuckets[i] = 0; |
fbd124dd BH |
707 | } |
708 | } | |
709 | ||
4540412a PT |
710 | private long getOffset(long timestamp) { |
711 | long offset = (fFirstBucketTime - timestamp) / fBucketDuration; | |
bfe038ff | 712 | if (((fFirstBucketTime - timestamp) % fBucketDuration) != 0) { |
fbd124dd BH |
713 | offset++; |
714 | } | |
715 | return offset; | |
716 | } | |
c392540b | 717 | } |