/******************************************************************************* * Copyright (c) 2012, 2015 Ericsson * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Alexandre Montplaisir - Initial API and implementation * Patrick Tasse - Fix TimeRangeException ******************************************************************************/ package org.eclipse.tracecompass.tmf.core.statistics; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; /** * Implementation of ITmfStatistics which uses a state history for storing its * information. In reality, it uses two state histories, one for "event totals" * information (which should ideally use a fast backend), and another one for * the rest (per event type, per CPU, etc.). * * Compared to the event-request-based statistics calculations, it adds the * building the history first, but gives much faster response times once built : * Queries are O(log n) wrt the size of the trace, and O(1) wrt to the size of * the time interval selected. * * @author Alexandre Montplaisir */ public class TmfStateStatistics implements ITmfStatistics { // ------------------------------------------------------------------------ // Fields // ------------------------------------------------------------------------ /** The event totals state system */ private final ITmfStateSystem totalsStats; /** The state system for event types */ private final ITmfStateSystem typesStats; // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ /** * Constructor * * @param totals * The state system containing the "totals" information * @param eventTypes * The state system containing the "event types" information */ public TmfStateStatistics(@NonNull ITmfStateSystem totals, @NonNull ITmfStateSystem eventTypes) { this.totalsStats = totals; this.typesStats = eventTypes; } /** * Return the state system containing the "totals" values * * @return The "totals" state system */ public ITmfStateSystem getTotalsSS() { return totalsStats; } /** * Return the state system containing the "event types" values * * @return The "event types" state system */ public ITmfStateSystem getEventTypesSS() { return typesStats; } // ------------------------------------------------------------------------ // ITmfStatistics // ------------------------------------------------------------------------ @Override public void dispose() { totalsStats.dispose(); typesStats.dispose(); } @Override public List<Long> histogramQuery(final long start, final long end, final int nb) { final List<Long> list = new LinkedList<>(); final long increment = (end - start) / nb; if (totalsStats.isCancelled()) { return list; } /* * We will do one state system query per "border", and save the * differences between each border. */ long prevTotal = (start == totalsStats.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 public long getEventsTotal() { long endTime = totalsStats.getCurrentEndTime(); int count = 0; try { final int quark = totalsStats.getQuarkAbsolute(Attributes.TOTAL); count= totalsStats.querySingleState(endTime, quark).getStateValue().unboxInt(); } catch (StateSystemDisposedException e) { /* Assume there is no events for that range */ return 0; } catch (AttributeNotFoundException e) { e.printStackTrace(); } return count; } @Override public Map<@NonNull String, @NonNull Long> getEventTypesTotal() { final Map<@NonNull String, @NonNull Long> map = new HashMap<>(); long endTime = typesStats.getCurrentEndTime(); try { /* Get the list of quarks, one for each even type in the database */ int quark = typesStats.getQuarkAbsolute(Attributes.EVENT_TYPES); List<Integer> quarks = typesStats.getSubAttributes(quark, false); /* Since we want the total we can look only at the end */ List<ITmfStateInterval> endState = typesStats.queryFullState(endTime); String curEventName; long eventCount; for (int typeQuark : quarks) { curEventName = typesStats.getAttributeName(typeQuark); eventCount = endState.get(typeQuark).getStateValue().unboxInt(); map.put(curEventName, eventCount); } } catch (StateSystemDisposedException e) { /* Assume there is no events, nothing will be put in the map. */ } catch (AttributeNotFoundException e) { e.printStackTrace(); } return map; } @Override public long getEventsInRange(long start, long end) { long startCount; if (start == totalsStats.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); return endCount - startCount; } @Override public Map<String, Long> getEventTypesInRange(long start, long end) { final Map<String, Long> map = new HashMap<>(); List<Integer> quarks; /* Make sure the start/end times are within the state history, so we * don't get TimeRange exceptions. */ long startTime = checkStartTime(start, typesStats); long endTime = checkEndTime(end, typesStats); if (endTime < startTime) { /* The start/end times do not intersect this state system range. * Return the empty map. */ return map; } try { /* Get the list of quarks, one for each even type in the database */ int quark = typesStats.getQuarkAbsolute(Attributes.EVENT_TYPES); quarks = typesStats.getSubAttributes(quark, false); } catch (AttributeNotFoundException e) { /* * The state system does not (yet?) have the needed attributes, it * probably means there are no events counted yet. Return the empty * map. */ return map; } try { List<ITmfStateInterval> endState = typesStats.queryFullState(endTime); if (startTime == typesStats.getStartTime()) { /* Only use the values picked up at the end time */ for (int typeQuark : quarks) { String curEventName = typesStats.getAttributeName(typeQuark); long eventCount = endState.get(typeQuark).getStateValue().unboxInt(); if (eventCount == -1) { eventCount = 0; } map.put(curEventName, eventCount); } } else { /* * Query the start time at -1, so the beginning of the interval * is inclusive. */ List<ITmfStateInterval> startState = typesStats.queryFullState(startTime - 1); for (int typeQuark : quarks) { String curEventName = typesStats.getAttributeName(typeQuark); long countAtStart = startState.get(typeQuark).getStateValue().unboxInt(); long countAtEnd = endState.get(typeQuark).getStateValue().unboxInt(); if (countAtStart == -1) { countAtStart = 0; } if (countAtEnd == -1) { countAtEnd = 0; } long eventCount = countAtEnd - countAtStart; map.put(curEventName, eventCount); } } } catch (StateSystemDisposedException e) { /* Assume there is no (more) events, nothing will be put in the map. */ } return map; } // ------------------------------------------------------------------------ // Helper methods // ------------------------------------------------------------------------ private long getEventCountAt(long timestamp) { /* Make sure the target time is within the range of the history */ long ts = checkStartTime(timestamp, totalsStats); ts = checkEndTime(ts, totalsStats); try { final int quark = totalsStats.getQuarkAbsolute(Attributes.TOTAL); long count = totalsStats.querySingleState(ts, quark).getStateValue().unboxInt(); return count; } catch (StateSystemDisposedException e) { /* Assume there is no (more) events, nothing will be put in the map. */ } catch (AttributeNotFoundException e) { e.printStackTrace(); } return 0; } private static long checkStartTime(long initialStart, ITmfStateSystem ss) { long start = initialStart; if (start < ss.getStartTime()) { return ss.getStartTime(); } return start; } private static long checkEndTime(long initialEnd, ITmfStateSystem ss) { long end = initialEnd; if (end > ss.getCurrentEndTime()) { return ss.getCurrentEndTime(); } return end; } /** * The attribute names that are used in the state provider */ public static class Attributes { /** Total nb of events */ public static final String TOTAL = "total"; //$NON-NLS-1$ /** event_types */ public static final String EVENT_TYPES = "event_types"; //$NON-NLS-1$ /** lost_events * @since 2.0*/ public static final String LOST_EVENTS = "lost_events"; //$NON-NLS-1$ } }