From f3f93fa65c52603a43e5844648d375fb04ee273c Mon Sep 17 00:00:00 2001 From: Alexandre Montplaisir Date: Thu, 8 Nov 2012 11:15:35 -0500 Subject: [PATCH] tmf: Add the Histogram query API to ITmfStatistics See the added Javadoc for the complete description. This will allow the Histogram view to easily plug into the trace's ITmfStatistics object to get its information (so it can be optimized for each trace type, etc.) Change-Id: Ib94aeaa369a8bf40da5246825152e26326a45e92 Signed-off-by: Alexandre Montplaisir Reviewed-on: https://git.eclipse.org/r/8675 Tested-by: Hudson CI Reviewed-by: Matthew Khouzam IP-Clean: Matthew Khouzam Tested-by: Matthew Khouzam --- .../tests/statistics/TmfStatisticsTest.java | 63 ++++++++++++ .../tmf/core/statistics/ITmfStatistics.java | 24 +++++ .../core/statistics/TmfEventsStatistics.java | 82 ++++++++++++++++ .../core/statistics/TmfStateStatistics.java | 98 ++++++++++++++----- 4 files changed, 241 insertions(+), 26 deletions(-) diff --git a/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/statistics/TmfStatisticsTest.java b/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/statistics/TmfStatisticsTest.java index bc571de161..3c2d28aefb 100644 --- a/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/statistics/TmfStatisticsTest.java +++ b/org.eclipse.linuxtools.tmf.core.tests/src/org/eclipse/linuxtools/tmf/core/tests/statistics/TmfStatisticsTest.java @@ -14,6 +14,7 @@ package org.eclipse.linuxtools.tmf.core.tests.statistics; import static org.junit.Assert.assertEquals; +import java.util.List; import java.util.Map; import org.eclipse.linuxtools.tmf.core.statistics.ITmfStatistics; @@ -46,6 +47,68 @@ public abstract class TmfStatisticsTest { private static final String eventType = "lttng_statedump_process_state"; //$NON-NLS-1$ + // ------------------------------------------------------------------------ + // Tests for histogramQuery() + // ------------------------------------------------------------------------ + + /** + * Test the {@link ITmfStatistics#histogramQuery} method for the small known + * interval. + */ + @Test + public void testHistogramQuerySmall() { + final int NB_REQ = 10; + List results = backend.histogramQuery(t1, t6, NB_REQ); + + /* Make sure the returned array has the right size */ + assertEquals(NB_REQ, results.size()); + + /* Check the contents of each "bucket" */ + assertEquals(0, results.get(0).longValue()); + assertEquals(0, results.get(1).longValue()); + assertEquals(0, results.get(2).longValue()); + assertEquals(0, results.get(3).longValue()); + assertEquals(1, results.get(4).longValue()); + assertEquals(0, results.get(5).longValue()); + assertEquals(0, results.get(6).longValue()); + assertEquals(0, results.get(7).longValue()); + assertEquals(0, results.get(8).longValue()); + assertEquals(1, results.get(9).longValue()); + + } + + /** + * Test the {@link ITmfStatistics#histogramQuery} method over the whole + * trace. + */ + @Test + public void testHistogramQueryFull() { + final int NB_REQ = 10; + List results = backend.histogramQuery(tStart, tEnd, NB_REQ); + + /* Make sure the returned array has the right size */ + assertEquals(NB_REQ, results.size()); + + /* Check the total number of events */ + long count = 0; + for (Long val : results) { + count += val; + } + assertEquals(totalNbEvents, count); + + /* Check the contents of each "bucket" */ + assertEquals(94161, results.get(0).longValue()); + assertEquals(87348, results.get(1).longValue()); + assertEquals(58941, results.get(2).longValue()); + assertEquals(59879, results.get(3).longValue()); + assertEquals(66941, results.get(4).longValue()); + assertEquals(68939, results.get(5).longValue()); + assertEquals(72746, results.get(6).longValue()); + assertEquals(60749, results.get(7).longValue()); + assertEquals(61208, results.get(8).longValue()); + assertEquals(64407, results.get(9).longValue()); + } + // ------------------------------------------------------------------------ // Test for getEventsTotal() // ------------------------------------------------------------------------ diff --git a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/ITmfStatistics.java b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/ITmfStatistics.java index 46747496dd..4d6900fedc 100644 --- a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/ITmfStatistics.java +++ b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/ITmfStatistics.java @@ -12,6 +12,7 @@ package org.eclipse.linuxtools.tmf.core.statistics; +import java.util.List; import java.util.Map; import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp; @@ -52,6 +53,29 @@ public interface ITmfStatistics { */ public void updateStats(boolean isGlobal, long start, long end); + /** + * Run a histogram query on the statistics back-end. This means, return the + * total number of events in a series of 'nb' equal-sized ranges between + * 'start' and 'end'. As its name implies, this is typically used to fill + * the histogram data (where each range represents one pixel on the + * histogram). + * + * Unlike {@link #updateStats}, this method will block the caller until the + * results are returned, so it should not be called from a signal handler or + * from the UI thread. + * + * @param start + * Start time of the query + * @param end + * End time of the query + * @param nb + * The number of ranges to separate the complete time range into. + * It will be the size() of the returned array. + * @return The array representing the number of events found in each + * sub-range. + */ + public List histogramQuery(long start, long end, int nb); + /** * Return the total number of events in the trace. * diff --git a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/TmfEventsStatistics.java b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/TmfEventsStatistics.java index 0f8c18c935..4bd9e4dc3d 100644 --- a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/TmfEventsStatistics.java +++ b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/TmfEventsStatistics.java @@ -12,8 +12,12 @@ package org.eclipse.linuxtools.tmf.core.statistics; +import java.util.Collection; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; +import java.util.TreeMap; import org.eclipse.linuxtools.tmf.core.event.ITmfEvent; import org.eclipse.linuxtools.tmf.core.event.ITmfTimestamp; @@ -128,6 +132,25 @@ public class TmfEventsStatistics implements ITmfStatistics { return; } + @Override + public List histogramQuery(long start, long end, int nb) { + final long[] borders = new long[nb]; + final long increment = (end - start) / nb; + + long curTime = start; + for (int i = 0; i < nb; i++) { + borders[i] = curTime; + curTime += increment; + } + + HistogramQueryRequest req = new HistogramQueryRequest(borders, end); + sendAndWait(req); + + List results = new LinkedList(req.getResults()); + return results; + + } + private synchronized void cancelOngoingRequests() { if (totalRequest != null && totalRequest.isRunning()) { totalRequest.cancel(); @@ -260,4 +283,63 @@ public class TmfEventsStatistics implements ITmfStatistics { } } + /** + * Event request for histogram queries. It is much faster to do one event + * request then set the results accordingly than doing thousands of them one + * by one. + */ + private class HistogramQueryRequest extends TmfEventRequest { + + /** Map of */ + private final TreeMap results; + + /** + * New histogram request + * + * @param borders + * The array of borders (not including the end time). The + * first element should be the start time of the queries. + * @param endTime + * The end time of the query. Not used in the results map, + * but we need to know when to stop the event request. + */ + public HistogramQueryRequest(long[] borders, long endTime) { + super(trace.getEventType(), + new TmfTimeRange( + new TmfTimestamp(borders[0], SCALE), + new TmfTimestamp(endTime, SCALE)), + TmfDataRequest.ALL_DATA, + trace.getCacheSize(), + ITmfDataRequest.ExecutionType.BACKGROUND); + + /* Prepare the results map, with all counts at 0 */ + results = new TreeMap(); + for (long border : borders) { + results.put(border, 0L); + } + } + + public Collection getResults() { + return results.values(); + } + + @Override + public void handleData(ITmfEvent event) { + super.handleData(event); + if ((event != null) && (event.getTrace() == trace)) { + long ts = event.getTimestamp().normalize(0, SCALE).getValue(); + Long key = results.floorKey(ts); + if (key != null) { + incrementValue(key); + } + } + } + + private void incrementValue(Long key) { + long value = results.get(key); + value++; + results.put(key, value); + } + } + } diff --git a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/TmfStateStatistics.java b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/TmfStateStatistics.java index 024529f22e..c33a53d419 100644 --- a/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/TmfStateStatistics.java +++ b/org.eclipse.linuxtools.tmf.core/src/org/eclipse/linuxtools/tmf/core/statistics/TmfStateStatistics.java @@ -14,6 +14,7 @@ package org.eclipse.linuxtools.tmf.core.statistics; import java.io.File; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -45,7 +46,6 @@ import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace; * @author Alexandre Montplaisir * @since 2.0 */ - public class TmfStateStatistics implements ITmfStatistics { /** ID for the statistics state system */ @@ -157,7 +157,44 @@ public class TmfStateStatistics implements ITmfStatistics { }; statsThread.start(); return; + } + + @Override + public List histogramQuery(final long start, final long end, final int nb) { + final List list = new LinkedList(); + final long increment = (end - start) / nb; + + /* Wait until the history building completed */ + if (!stats.waitUntilBuilt()) { + return null; + } + + /* + * We will do one state system query per "border", and save the + * differences between each border. + */ + long prevTotal = (start == stats.getStartTime()) ? 0 : getEventCountAt(start); + long curTime = start + increment; + + long curTotal, count; + for (int i = 0; i < nb - 1; i++) { + curTotal = getEventCountAt(curTime); + count = curTotal - prevTotal; + list.add(count); + + curTime += increment; + prevTotal = curTotal; + } + + /* + * For the last bucket, we'll stretch its end time to the end time of + * the requested range, in case it got truncated down. + */ + curTotal = getEventCountAt(end); + count = curTotal - prevTotal; + list.add(count); + return list; } @Override @@ -228,33 +265,19 @@ public class TmfStateStatistics implements ITmfStatistics { // end time, and answer as soon as possible... stats.waitUntilBuilt(); - int countAtStart = 0, countAtEnd = 0; - long startTime = checkStartTime(start); - long endTime = checkEndTime(end); - - try { - final int quark = stats.getQuarkAbsolute(Attributes.TOTAL); - if (startTime == stats.getStartTime()) { - countAtStart = 0; - } else { - /* State system works that way... */ - countAtStart = stats.querySingleState(startTime - 1, quark).getStateValue().unboxInt(); - } - countAtEnd = stats.querySingleState(endTime, quark).getStateValue().unboxInt(); - - } catch (TimeRangeException e) { - /* Assume there is no events for that range */ - return 0; - } catch (AttributeNotFoundException e) { - e.printStackTrace(); - } catch (StateValueTypeException e) { - e.printStackTrace(); - } catch (StateSystemDisposedException e) { - e.printStackTrace(); + long startCount; + if (start == stats.getStartTime()) { + startCount = 0; + } else { + /* + * We want the events happening at "start" to be included, so we'll + * need to query one unit before that point. + */ + startCount = getEventCountAt(start - 1); } + long endCount = getEventCountAt(end); - long total = countAtEnd - countAtStart; - return total; + return endCount - startCount; } @Override @@ -329,6 +352,29 @@ public class TmfStateStatistics implements ITmfStatistics { return map; } + private long getEventCountAt(long timestamp) { + /* Make sure the target time is within the range of the history */ + long ts = checkStartTime(timestamp); + ts = checkEndTime(ts); + + try { + final int quark = stats.getQuarkAbsolute(Attributes.TOTAL); + long count = stats.querySingleState(ts, quark).getStateValue().unboxInt(); + return count; + + } catch (TimeRangeException e) { + /* Assume there is no events for that range */ + } catch (AttributeNotFoundException e) { + e.printStackTrace(); + } catch (StateValueTypeException e) { + e.printStackTrace(); + } catch (StateSystemDisposedException e) { + e.printStackTrace(); + } + + return 0; + } + private long checkStartTime(long initialStart) { long start = initialStart; if (start < stats.getStartTime()) { -- 2.34.1