Commit | Line | Data |
---|---|---|
200789b3 | 1 | /******************************************************************************* |
61759503 | 2 | * Copyright (c) 2012, 2013 Ericsson |
200789b3 AM |
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 | * Alexandre Montplaisir - Initial API and implementation | |
11 | ******************************************************************************/ | |
12 | ||
13 | package org.eclipse.linuxtools.tmf.core.statistics; | |
14 | ||
15 | import java.io.File; | |
16 | import java.util.HashMap; | |
f3f93fa6 | 17 | import java.util.LinkedList; |
200789b3 AM |
18 | import java.util.List; |
19 | import java.util.Map; | |
20 | ||
200789b3 | 21 | import org.eclipse.linuxtools.tmf.core.exceptions.AttributeNotFoundException; |
96345c5a | 22 | import org.eclipse.linuxtools.tmf.core.exceptions.StateSystemDisposedException; |
200789b3 AM |
23 | import org.eclipse.linuxtools.tmf.core.exceptions.StateValueTypeException; |
24 | import org.eclipse.linuxtools.tmf.core.exceptions.TimeRangeException; | |
25 | import org.eclipse.linuxtools.tmf.core.exceptions.TmfTraceException; | |
26 | import org.eclipse.linuxtools.tmf.core.interval.ITmfStateInterval; | |
1c0de632 AM |
27 | import org.eclipse.linuxtools.tmf.core.signal.TmfSignal; |
28 | import org.eclipse.linuxtools.tmf.core.signal.TmfSignalManager; | |
29 | import org.eclipse.linuxtools.tmf.core.signal.TmfStatsUpdatedSignal; | |
0fe46f2a | 30 | import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateProvider; |
200789b3 | 31 | import org.eclipse.linuxtools.tmf.core.statesystem.ITmfStateSystem; |
3f436e7c | 32 | import org.eclipse.linuxtools.tmf.core.statesystem.TmfStateSystemFactory; |
200789b3 | 33 | import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace; |
e1385db9 | 34 | import org.eclipse.linuxtools.tmf.core.trace.TmfTraceManager; |
200789b3 AM |
35 | |
36 | /** | |
1c0de632 | 37 | * Implementation of ITmfStatistics which uses a state history for storing its |
802017fe AM |
38 | * information. In reality, it uses two state histories, one for "event totals" |
39 | * information (which should ideally use a fast backend), and another one for | |
40 | * the rest (per event type, per CPU, etc.). | |
1c0de632 | 41 | * |
802017fe AM |
42 | * Compared to the event-request-based statistics calculations, it adds the |
43 | * building the history first, but gives much faster response times once built : | |
44 | * Queries are O(log n) wrt the size of the trace, and O(1) wrt to the size of | |
45 | * the time interval selected. | |
200789b3 AM |
46 | * |
47 | * @author Alexandre Montplaisir | |
48 | * @since 2.0 | |
49 | */ | |
1c0de632 | 50 | public class TmfStateStatistics implements ITmfStatistics { |
200789b3 | 51 | |
802017fe AM |
52 | // ------------------------------------------------------------------------ |
53 | // Constants | |
54 | // ------------------------------------------------------------------------ | |
55 | ||
56 | /** | |
57 | * @deprecated Do not use, it's been replaced by {@link #TOTALS_STATE_ID} | |
58 | * and {@link #TYPES_STATE_ID} | |
59 | */ | |
60 | @Deprecated | |
a76f1067 AM |
61 | public static final String STATE_ID = "org.eclipse.linuxtools.tmf.statistics"; //$NON-NLS-1$ |
62 | ||
802017fe AM |
63 | /** ID for the "event totals" statistics state system |
64 | * @since 2.2 */ | |
65 | public static final String TOTALS_STATE_ID = "org.eclipse.linuxtools.tmf.statistics.totals"; //$NON-NLS-1$ | |
66 | ||
67 | /** ID for the "event types" statistics state system | |
68 | * @since 2.2 */ | |
69 | public static final String TYPES_STATE_ID = "org.eclipse.linuxtools.tmf.statistics.types"; //$NON-NLS-1$ | |
70 | ||
71 | /** Filename for the "event totals" state history file */ | |
72 | private static final String TOTALS_STATE_FILENAME = "statistics-totals.ht"; //$NON-NLS-1$ | |
73 | ||
74 | /** Filename for the "event types" state history file */ | |
75 | private static final String TYPES_STATE_FILENAME = "statistics-types.ht"; //$NON-NLS-1$ | |
76 | ||
77 | // ------------------------------------------------------------------------ | |
78 | // Fields | |
79 | // ------------------------------------------------------------------------ | |
200789b3 | 80 | |
1c0de632 AM |
81 | private final ITmfTrace trace; |
82 | ||
802017fe AM |
83 | /** The event totals state system */ |
84 | private final ITmfStateSystem totalsStats; | |
85 | ||
86 | /** The state system for event types */ | |
87 | private final ITmfStateSystem typesStats; | |
88 | ||
89 | // ------------------------------------------------------------------------ | |
90 | // Constructors | |
91 | // ------------------------------------------------------------------------ | |
200789b3 | 92 | |
36033ff0 AM |
93 | /** |
94 | * Empty constructor. The resulting TmfStatistics object will not be usable, | |
95 | * but it might be needed for sub-classes. | |
96 | */ | |
1c0de632 | 97 | public TmfStateStatistics() { |
802017fe AM |
98 | totalsStats = null; |
99 | typesStats = null; | |
1c0de632 | 100 | trace = null; |
89c06060 AM |
101 | } |
102 | ||
200789b3 AM |
103 | /** |
104 | * Constructor | |
105 | * | |
106 | * @param trace | |
107 | * The trace for which we build these statistics | |
108 | * @throws TmfTraceException | |
109 | * If something went wrong trying to initialize the statistics | |
110 | */ | |
1c0de632 | 111 | public TmfStateStatistics(ITmfTrace trace) throws TmfTraceException { |
1c0de632 | 112 | this.trace = trace; |
e1385db9 | 113 | String directory = TmfTraceManager.getSupplementaryFileDir(trace); |
200789b3 | 114 | |
802017fe AM |
115 | final File totalsFile = new File(directory + TOTALS_STATE_FILENAME); |
116 | final ITmfStateProvider totalsInput = new StatsProviderTotals(trace); | |
117 | this.totalsStats = TmfStateSystemFactory.newFullHistory(totalsFile, totalsInput, false); | |
118 | ||
119 | final File typesFile = new File(directory + TYPES_STATE_FILENAME); | |
120 | final ITmfStateProvider typesInput = new StatsProviderEventTypes(trace); | |
121 | this.typesStats = TmfStateSystemFactory.newFullHistory(typesFile, typesInput, false); | |
122 | ||
6c5e0863 | 123 | registerStateSystems(); |
200789b3 AM |
124 | } |
125 | ||
802017fe AM |
126 | /** |
127 | * Old manual constructor. | |
128 | * | |
129 | * @param trace Trace | |
130 | * @param historyFile Full history file | |
131 | * @deprecated Need to use {@link #TmfStateStatistics(ITmfTrace trace, | |
132 | * File fullHistoryFile, File partialHistoryFile)} now. | |
133 | */ | |
134 | @Deprecated | |
135 | public TmfStateStatistics(ITmfTrace trace, File historyFile) { | |
136 | this(); | |
137 | } | |
138 | ||
e1c43333 AM |
139 | /** |
140 | * Manual constructor. This should be used if the trace's Resource is null | |
141 | * (ie, for unit tests). It requires specifying the location of the history | |
802017fe | 142 | * files manually. |
e1c43333 AM |
143 | * |
144 | * @param trace | |
145 | * The trace for which we build these statistics | |
802017fe AM |
146 | * @param totalsHistoryFile |
147 | * The location of the totals state history file | |
148 | * @param typesHistoryFile | |
149 | * The location of the types state history file | |
e1c43333 AM |
150 | * @throws TmfTraceException |
151 | * If the file could not be written to | |
802017fe | 152 | * @since 2.2 |
e1c43333 | 153 | */ |
802017fe AM |
154 | public TmfStateStatistics(ITmfTrace trace, File totalsHistoryFile, |
155 | File typesHistoryFile) throws TmfTraceException { | |
e1c43333 | 156 | this.trace = trace; |
802017fe AM |
157 | final ITmfStateProvider totalsInput = new StatsProviderTotals(trace); |
158 | final ITmfStateProvider typesInput = new StatsProviderEventTypes(trace); | |
159 | this.totalsStats = TmfStateSystemFactory.newFullHistory(totalsHistoryFile, totalsInput, true); | |
160 | this.typesStats = TmfStateSystemFactory.newFullHistory(typesHistoryFile, typesInput, true); | |
6c5e0863 AM |
161 | registerStateSystems(); |
162 | } | |
163 | ||
164 | /** | |
802017fe | 165 | * Register the state systems used here into the trace's state system map. |
6c5e0863 AM |
166 | */ |
167 | private void registerStateSystems() { | |
802017fe AM |
168 | trace.registerStateSystem(TOTALS_STATE_ID, totalsStats); |
169 | trace.registerStateSystem(TYPES_STATE_ID, typesStats); | |
e1c43333 AM |
170 | } |
171 | ||
200789b3 | 172 | // ------------------------------------------------------------------------ |
1c0de632 | 173 | // ITmfStatistics |
200789b3 AM |
174 | // ------------------------------------------------------------------------ |
175 | ||
1a4205d9 AM |
176 | @Override |
177 | public void dispose() { | |
802017fe AM |
178 | totalsStats.dispose(); |
179 | typesStats.dispose(); | |
1a4205d9 AM |
180 | } |
181 | ||
1c0de632 | 182 | @Override |
8b260d9f AM |
183 | public void updateStats(final boolean isGlobal, final long start, |
184 | final long end) { | |
1c0de632 AM |
185 | /* |
186 | * Since we are currently in a signal handler (ie, in the UI thread), | |
187 | * and since state system queries can be arbitrarily long (O(log n) wrt | |
188 | * the size of the trace), we will run those queries in a separate | |
189 | * thread and update the statistics view out-of-band. | |
190 | */ | |
191 | Thread statsThread = new Thread("Statistics update") { //$NON-NLS-1$ | |
192 | @Override | |
193 | public void run() { | |
802017fe AM |
194 | /* Wait until the history building is completed */ |
195 | if (!waitUntilBuilt()) { | |
1a4205d9 AM |
196 | return; |
197 | } | |
1c0de632 AM |
198 | |
199 | /* Range should be valid for both global and time range queries */ | |
802017fe AM |
200 | long total = getEventsInRange(start, end); |
201 | Map<String, Long> map = getEventTypesInRange(start, end); | |
1c0de632 AM |
202 | |
203 | /* Send the signal to notify the stats viewer to update its display */ | |
204 | TmfSignal sig = new TmfStatsUpdatedSignal(this, trace, isGlobal, total, map); | |
205 | TmfSignalManager.dispatchSignal(sig); | |
206 | } | |
207 | }; | |
208 | statsThread.start(); | |
209 | return; | |
f3f93fa6 AM |
210 | } |
211 | ||
212 | @Override | |
213 | public List<Long> histogramQuery(final long start, final long end, final int nb) { | |
214 | final List<Long> list = new LinkedList<Long>(); | |
215 | final long increment = (end - start) / nb; | |
216 | ||
802017fe AM |
217 | if (!totalsStats.waitUntilBuilt()) { |
218 | return list; | |
f3f93fa6 AM |
219 | } |
220 | ||
221 | /* | |
222 | * We will do one state system query per "border", and save the | |
223 | * differences between each border. | |
224 | */ | |
802017fe | 225 | long prevTotal = (start == totalsStats.getStartTime()) ? 0 : getEventCountAt(start); |
f3f93fa6 AM |
226 | long curTime = start + increment; |
227 | ||
228 | long curTotal, count; | |
229 | for (int i = 0; i < nb - 1; i++) { | |
230 | curTotal = getEventCountAt(curTime); | |
231 | count = curTotal - prevTotal; | |
232 | list.add(count); | |
233 | ||
234 | curTime += increment; | |
235 | prevTotal = curTotal; | |
236 | } | |
237 | ||
238 | /* | |
239 | * For the last bucket, we'll stretch its end time to the end time of | |
240 | * the requested range, in case it got truncated down. | |
241 | */ | |
242 | curTotal = getEventCountAt(end); | |
243 | count = curTotal - prevTotal; | |
244 | list.add(count); | |
1c0de632 | 245 | |
f3f93fa6 | 246 | return list; |
1c0de632 AM |
247 | } |
248 | ||
200789b3 AM |
249 | @Override |
250 | public long getEventsTotal() { | |
e1c43333 | 251 | /* We need the complete state history to be built to answer this. */ |
802017fe | 252 | totalsStats.waitUntilBuilt(); |
e1c43333 | 253 | |
802017fe | 254 | long endTime = totalsStats.getCurrentEndTime(); |
e1c43333 | 255 | int count = 0; |
df310609 AM |
256 | |
257 | try { | |
802017fe AM |
258 | final int quark = totalsStats.getQuarkAbsolute(Attributes.TOTAL); |
259 | count= totalsStats.querySingleState(endTime, quark).getStateValue().unboxInt(); | |
1c0de632 AM |
260 | |
261 | } catch (TimeRangeException e) { | |
262 | /* Assume there is no events for that range */ | |
263 | return 0; | |
df310609 AM |
264 | } catch (AttributeNotFoundException e) { |
265 | e.printStackTrace(); | |
266 | } catch (StateValueTypeException e) { | |
267 | e.printStackTrace(); | |
96345c5a AM |
268 | } catch (StateSystemDisposedException e) { |
269 | e.printStackTrace(); | |
200789b3 | 270 | } |
df310609 | 271 | |
e1c43333 | 272 | return count; |
200789b3 AM |
273 | } |
274 | ||
275 | @Override | |
276 | public Map<String, Long> getEventTypesTotal() { | |
e1c43333 | 277 | /* We need the complete state history to be built to answer this. */ |
802017fe | 278 | typesStats.waitUntilBuilt(); |
e1c43333 | 279 | |
200789b3 | 280 | Map<String, Long> map = new HashMap<String, Long>(); |
802017fe | 281 | long endTime = typesStats.getCurrentEndTime(); |
200789b3 AM |
282 | |
283 | try { | |
284 | /* Get the list of quarks, one for each even type in the database */ | |
802017fe AM |
285 | int quark = typesStats.getQuarkAbsolute(Attributes.EVENT_TYPES); |
286 | List<Integer> quarks = typesStats.getSubAttributes(quark, false); | |
200789b3 AM |
287 | |
288 | /* Since we want the total we can look only at the end */ | |
802017fe | 289 | List<ITmfStateInterval> endState = typesStats.queryFullState(endTime); |
200789b3 AM |
290 | |
291 | String curEventName; | |
292 | long eventCount; | |
293 | for (int typeQuark : quarks) { | |
802017fe | 294 | curEventName = typesStats.getAttributeName(typeQuark); |
200789b3 AM |
295 | eventCount = endState.get(typeQuark).getStateValue().unboxInt(); |
296 | map.put(curEventName, eventCount); | |
297 | } | |
298 | ||
299 | } catch (TimeRangeException e) { | |
1c0de632 | 300 | /* Assume there is no events, nothing will be put in the map. */ |
200789b3 AM |
301 | } catch (AttributeNotFoundException e) { |
302 | e.printStackTrace(); | |
303 | } catch (StateValueTypeException e) { | |
304 | e.printStackTrace(); | |
96345c5a AM |
305 | } catch (StateSystemDisposedException e) { |
306 | e.printStackTrace(); | |
200789b3 AM |
307 | } |
308 | return map; | |
309 | } | |
310 | ||
311 | @Override | |
8b260d9f | 312 | public long getEventsInRange(long start, long end) { |
e1c43333 AM |
313 | // FIXME Instead of waiting until the end, we could check the current |
314 | // end time, and answer as soon as possible... | |
802017fe | 315 | totalsStats.waitUntilBuilt(); |
e1c43333 | 316 | |
f3f93fa6 | 317 | long startCount; |
802017fe | 318 | if (start == totalsStats.getStartTime()) { |
f3f93fa6 AM |
319 | startCount = 0; |
320 | } else { | |
321 | /* | |
322 | * We want the events happening at "start" to be included, so we'll | |
323 | * need to query one unit before that point. | |
324 | */ | |
325 | startCount = getEventCountAt(start - 1); | |
200789b3 | 326 | } |
f3f93fa6 | 327 | long endCount = getEventCountAt(end); |
df310609 | 328 | |
f3f93fa6 | 329 | return endCount - startCount; |
200789b3 AM |
330 | } |
331 | ||
332 | @Override | |
8b260d9f | 333 | public Map<String, Long> getEventTypesInRange(long start, long end) { |
e1c43333 AM |
334 | // FIXME Instead of waiting until the end, we could check the current |
335 | // end time, and answer as soon as possible... | |
802017fe | 336 | typesStats.waitUntilBuilt(); |
e1c43333 | 337 | |
200789b3 AM |
338 | Map<String, Long> map = new HashMap<String, Long>(); |
339 | ||
340 | /* Make sure the start/end times are within the state history, so we | |
341 | * don't get TimeRange exceptions. | |
342 | */ | |
1c0de632 AM |
343 | long startTime = checkStartTime(start); |
344 | long endTime = checkEndTime(end); | |
200789b3 AM |
345 | |
346 | try { | |
347 | /* Get the list of quarks, one for each even type in the database */ | |
802017fe AM |
348 | int quark = typesStats.getQuarkAbsolute(Attributes.EVENT_TYPES); |
349 | List<Integer> quarks = typesStats.getSubAttributes(quark, false); | |
200789b3 | 350 | |
802017fe | 351 | List<ITmfStateInterval> endState = typesStats.queryFullState(endTime); |
200789b3 | 352 | |
200789b3 AM |
353 | String curEventName; |
354 | long countAtStart, countAtEnd, eventCount; | |
200789b3 | 355 | |
802017fe | 356 | if (startTime == typesStats.getStartTime()) { |
e1c43333 AM |
357 | /* Only use the values picked up at the end time */ |
358 | for (int typeQuark : quarks) { | |
802017fe | 359 | curEventName = typesStats.getAttributeName(typeQuark); |
e1c43333 AM |
360 | eventCount = endState.get(typeQuark).getStateValue().unboxInt(); |
361 | if (eventCount == -1) { | |
362 | eventCount = 0; | |
363 | } | |
364 | map.put(curEventName, eventCount); | |
200789b3 | 365 | } |
e1c43333 | 366 | } else { |
200789b3 | 367 | /* |
e1c43333 AM |
368 | * Query the start time at -1, so the beginning of the interval |
369 | * is inclusive. | |
200789b3 | 370 | */ |
802017fe | 371 | List<ITmfStateInterval> startState = typesStats.queryFullState(startTime - 1); |
e1c43333 | 372 | for (int typeQuark : quarks) { |
802017fe | 373 | curEventName = typesStats.getAttributeName(typeQuark); |
e1c43333 AM |
374 | countAtStart = startState.get(typeQuark).getStateValue().unboxInt(); |
375 | countAtEnd = endState.get(typeQuark).getStateValue().unboxInt(); | |
376 | ||
377 | if (countAtStart == -1) { | |
378 | countAtStart = 0; | |
379 | } | |
380 | if (countAtEnd == -1) { | |
381 | countAtEnd = 0; | |
382 | } | |
383 | eventCount = countAtEnd - countAtStart; | |
384 | map.put(curEventName, eventCount); | |
200789b3 | 385 | } |
200789b3 | 386 | } |
e1c43333 | 387 | |
200789b3 | 388 | } catch (TimeRangeException e) { |
1c0de632 | 389 | /* Assume there is no events, nothing will be put in the map. */ |
200789b3 AM |
390 | } catch (AttributeNotFoundException e) { |
391 | /* | |
1c0de632 AM |
392 | * These other exception types would show a logic problem however, |
393 | * so they should not happen. | |
200789b3 AM |
394 | */ |
395 | e.printStackTrace(); | |
396 | } catch (StateValueTypeException e) { | |
397 | e.printStackTrace(); | |
96345c5a AM |
398 | } catch (StateSystemDisposedException e) { |
399 | e.printStackTrace(); | |
200789b3 AM |
400 | } |
401 | return map; | |
402 | } | |
403 | ||
802017fe AM |
404 | // ------------------------------------------------------------------------ |
405 | // Helper methods | |
406 | // ------------------------------------------------------------------------ | |
407 | ||
f3f93fa6 AM |
408 | private long getEventCountAt(long timestamp) { |
409 | /* Make sure the target time is within the range of the history */ | |
410 | long ts = checkStartTime(timestamp); | |
411 | ts = checkEndTime(ts); | |
412 | ||
413 | try { | |
802017fe AM |
414 | final int quark = totalsStats.getQuarkAbsolute(Attributes.TOTAL); |
415 | long count = totalsStats.querySingleState(ts, quark).getStateValue().unboxInt(); | |
f3f93fa6 AM |
416 | return count; |
417 | ||
418 | } catch (TimeRangeException e) { | |
419 | /* Assume there is no events for that range */ | |
420 | } catch (AttributeNotFoundException e) { | |
421 | e.printStackTrace(); | |
422 | } catch (StateValueTypeException e) { | |
423 | e.printStackTrace(); | |
424 | } catch (StateSystemDisposedException e) { | |
425 | e.printStackTrace(); | |
426 | } | |
427 | ||
428 | return 0; | |
429 | } | |
430 | ||
8b260d9f AM |
431 | private long checkStartTime(long initialStart) { |
432 | long start = initialStart; | |
802017fe AM |
433 | if (start < totalsStats.getStartTime()) { |
434 | return totalsStats.getStartTime(); | |
200789b3 AM |
435 | } |
436 | return start; | |
437 | } | |
438 | ||
8b260d9f AM |
439 | private long checkEndTime(long initialEnd) { |
440 | long end = initialEnd; | |
802017fe AM |
441 | if (end > totalsStats.getCurrentEndTime()) { |
442 | return totalsStats.getCurrentEndTime(); | |
200789b3 AM |
443 | } |
444 | return end; | |
445 | } | |
446 | ||
802017fe AM |
447 | /** |
448 | * Wait until both backing state systems are finished building. | |
449 | * | |
450 | * @return If both state systems were built successfully | |
451 | */ | |
452 | private boolean waitUntilBuilt() { | |
453 | boolean check1 = totalsStats.waitUntilBuilt(); | |
454 | boolean check2 = typesStats.waitUntilBuilt(); | |
455 | return (check1 && check2); | |
456 | } | |
457 | ||
200789b3 AM |
458 | |
459 | /** | |
460 | * The attribute names that are used in the state provider | |
461 | */ | |
462 | public static class Attributes { | |
463 | ||
df310609 AM |
464 | /** Total nb of events */ |
465 | public static final String TOTAL = "total"; //$NON-NLS-1$ | |
466 | ||
200789b3 AM |
467 | /** event_types */ |
468 | public static final String EVENT_TYPES = "event_types"; //$NON-NLS-1$< | |
469 | } | |
470 | } |