edu.brown.profilers.ProfileMeasurement.java Source code

Java tutorial

Introduction

Here is the source code for edu.brown.profilers.ProfileMeasurement.java

Source

/***************************************************************************
 *  Copyright (C) 2012 by H-Store Project                                  *
 *  Brown University                                                       *
 *  Massachusetts Institute of Technology                                  *
 *  Yale University                                                        *
 *                                                                         *
 *  http://hstore.cs.brown.edu/                                            *
 *                                                                         *
 *  Permission is hereby granted, free of charge, to any person obtaining  *
 *  a copy of this software and associated documentation files (the        *
 *  "Software"), to deal in the Software without restriction, including    *
 *  without limitation the rights to use, copy, modify, merge, publish,    *
 *  distribute, sublicense, and/or sell copies of the Software, and to     *
 *  permit persons to whom the Software is furnished to do so, subject to  *
 *  the following conditions:                                              *
 *                                                                         *
 *  The above copyright notice and this permission notice shall be         *
 *  included in all copies or substantial portions of the Software.        *
 *                                                                         *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        *
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     *
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR      *
 *  OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,  *
 *  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  *
 *  OTHER DEALINGS IN THE SOFTWARE.                                        *
 ***************************************************************************/
package edu.brown.profilers;

import java.io.File;
import java.io.IOException;
import java.util.Collection;

import org.apache.commons.collections15.Buffer;
import org.apache.commons.collections15.buffer.CircularFifoBuffer;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;
import org.voltdb.catalog.Database;

import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.utils.EventObservable;
import edu.brown.utils.EventObserver;
import edu.brown.utils.JSONSerializable;
import edu.brown.utils.JSONUtil;

/**
 * @author pavlo
 */
public class ProfileMeasurement implements JSONSerializable {
    private static final Logger LOG = Logger.getLogger(ProfileMeasurement.class);
    private static final LoggerBoolean debug = new LoggerBoolean();
    private static final LoggerBoolean trace = new LoggerBoolean();
    static {
        LoggerUtil.attachObserver(LOG, debug, trace);
    }

    private static final long NULL_MARKER = -1l;

    /** The profile type */
    private String name;
    /** Total amount of time spent processing the profiled section (in ms) */
    private long total_time;
    /** The number of times that this ProfileMeasurement has been started */
    private int invocations = 0;

    private Buffer<Long> history = null;

    /**
     * This marker is used to set when the boundary area of the code we are
     * trying to profile starts and stops. When it is zero, the system is
     * outside of the profiled area.
     */
    private transient long marker = NULL_MARKER;

    private transient boolean reset = false;

    private transient EventObservable<ProfileMeasurement> start_observable;
    private transient EventObservable<ProfileMeasurement> stop_observable;

    // ----------------------------------------------------------------------------
    // CONSTRUCTORS
    // ----------------------------------------------------------------------------

    public ProfileMeasurement() {
        // For serialization
    }

    /**
     * Constructor
     * @param pmtype
     */
    public ProfileMeasurement(String pmtype) {
        this(pmtype, false);
    }

    /**
     * Constructor
     * @param pmtype
     * @param history Enable history tracking
     */
    public ProfileMeasurement(String pmtype, boolean history) {
        this.name = pmtype;
        if (history)
            this.enableHistoryTracking();
        this.reset();
    }

    /**
     * Copy constructor
     * 
     * @param orig
     */
    public ProfileMeasurement(ProfileMeasurement orig) {
        this(orig.name, (orig.history != null));
        if (this.history != null) {
            this.history.addAll(orig.history);
        }
        this.appendTime(orig);
    }

    // ----------------------------------------------------------------------------
    // UTILITY METHODS
    // ----------------------------------------------------------------------------

    public void enableHistoryTracking() {
        if (this.history == null) {
            synchronized (this) {
                if (this.history == null) {
                    this.history = new CircularFifoBuffer<Long>(10000);
                }
            } // SYNCH
            if (debug.val)
                LOG.debug("Enabled history tracking in " + this);
        }
    }

    public void reset() {
        if (this.marker != NULL_MARKER) {
            this.reset = true;
        }
        this.total_time = 0;
        this.invocations = 0;
        if (this.history != null)
            this.history.clear();
    }

    public void clear() {
        this.marker = NULL_MARKER;
        this.invocations = 0;
        this.total_time = 0;
        if (this.history != null)
            this.history.clear();
    }

    /**
     * Reset this ProfileMeasurements internal data when an update arrives
     * for the given EventObservable
     * @param e
     */
    public <T> void resetOnEventObservable(EventObservable<T> e) {
        e.addObserver(new EventObserver<T>() {
            @Override
            public void update(EventObservable<T> o, T arg) {
                ProfileMeasurement.this.reset();
            }
        });
    }

    /**
     * Get the profile type name
     * @return
     */
    public String getName() {
        return (this.name);
    }

    /**
     * Get the total amount of time spent in the profiled area in nanoseconds
     * @return
     */
    public long getTotalThinkTime() {
        return (this.total_time);
    }

    /**
     * Get the total amount of time spent in the profiled area in milliseconds
     * @return
     */
    public double getTotalThinkTimeMS() {
        return (this.total_time / 1000000d);
    }

    /**
     * Get the total amount of time spent in the profiled area in seconds
     * @return
     */
    public double getTotalThinkTimeSeconds() {
        return (this.total_time / 1000000d / 1000d);
    }

    /**
     * Get the average think time per invocation in nanoseconds
     * @return
     */
    public double getAverageThinkTime() {
        return (this.invocations > 0 ? this.total_time / (double) this.invocations : 0d);
    }

    /**
     * Get the average think time per invocation in milliseconds
     * @return
     */
    public double getAverageThinkTimeMS() {
        return (this.getAverageThinkTime() / 1000000d);
    }

    /**
     * Get the total number of times this object was started
     * @return
     */
    public int getInvocations() {
        return (this.invocations);
    }

    protected long getMarker() {
        return (this.marker);
    }

    public Collection<Long> getHistory(Collection<Long> to_fill) {
        to_fill.addAll(this.history);
        return (to_fill);
    }

    // ----------------------------------------------------------------------------
    // START METHODS
    // ----------------------------------------------------------------------------

    /**
     * Main method for stop this ProfileMeasurement from recording time
     * 
     * @return this
     */

    public ProfileMeasurement start(long timestamp) {
        assert (this.marker == NULL_MARKER) : String.format("Trying to start %s before it was stopped!", this.name);
        if (debug.val)
            LOG.debug(String.format("START %s", this));
        this.marker = timestamp;
        this.invocations++;
        if (this.start_observable != null)
            this.start_observable.notifyObservers(this);
        return (this);
    }

    public ProfileMeasurement start() {
        return (this.start(getTime()));
    }

    public boolean isStarted() {
        return (this.marker != NULL_MARKER);
    }

    public void addStartObserver(EventObserver<ProfileMeasurement> observer) {
        if (this.start_observable == null) {
            synchronized (this) {
                if (this.start_observable == null) {
                    this.start_observable = new EventObservable<ProfileMeasurement>();
                }
            } // SYNCH
        }
        this.start_observable.addObserver(observer);
    }

    // ----------------------------------------------------------------------------
    // STOP METHODS
    // ----------------------------------------------------------------------------

    /**
     * Main method for stop this ProfileMeasurement from recording time We will
     * check to make sure that this handle was started first
     * 
     * @return this
     */
    public ProfileMeasurement stop(long timestamp) {
        if (this.reset) {
            this.reset = false;
            this.marker = NULL_MARKER;
            return (this);
        }
        if (debug.val)
            LOG.debug(String.format("STOP %s", this));
        assert (this.marker != NULL_MARKER) : String.format("Trying to stop %s before it was started!", this.name);
        long added = (timestamp - this.marker);
        if (added < 0) {
            LOG.warn(String.format("Invalid stop timestamp for %s [timestamp=%d, marker=%d, added=%d]", this.name,
                    timestamp, this.marker, added));
        } else {
            this.total_time += added;
            if (this.history != null)
                this.history.add(added);
        }
        this.marker = NULL_MARKER;
        if (this.stop_observable != null)
            this.stop_observable.notifyObservers(this);
        // if (type == Type.JAVA)
        // LOG.info(String.format("STOP %s [time=%d, id=%d]", this.type, added,
        // this.hashCode()));
        return (this);
    }

    public ProfileMeasurement stop() {
        return (this.stop(getTime()));
    }

    public ProfileMeasurement stopIfStarted() {
        return this.stopIfStarted(getTime());
    }

    public ProfileMeasurement stopIfStarted(long timestamp) {
        if (this.isStarted())
            this.stop(timestamp);
        return (this);
    }

    public void addStopObserver(EventObserver<ProfileMeasurement> observer) {
        if (this.stop_observable == null) {
            synchronized (this) {
                if (this.stop_observable == null) {
                    this.stop_observable = new EventObservable<ProfileMeasurement>();
                }
            } // SYNCH
        }
        this.stop_observable.addObserver(observer);
    }

    // ----------------------------------------------------------------------------
    // UTILITY METHODS
    // ----------------------------------------------------------------------------

    public ProfileMeasurement restart() {
        long timestamp = ProfileMeasurement.getTime();
        this.stopIfStarted(timestamp).start(timestamp);
        return (this);
    }

    public ProfileMeasurement decrementTime(ProfileMeasurement other, boolean checkType) {
        assert (other != null);
        if (checkType)
            assert (this.name == other.name);
        this.total_time = Math.max(0, this.total_time - other.total_time);
        this.invocations = Math.max(0, this.invocations - other.invocations);
        return (this);
    }

    public ProfileMeasurement decrementTime(ProfileMeasurement other) {
        return (this.decrementTime(other, false));
    }

    public ProfileMeasurement appendTime(ProfileMeasurement other) {
        assert (other != null);
        this.total_time += other.total_time;
        this.marker = other.marker;
        this.invocations += other.invocations;
        return (this);
    }

    /**
     * Append the think time without locking the marker. This is the preferred
     * way to update the ProfileMeasurement when multiple threads are using it.
     * @param start
     * @param stop
     * @param invocations
     */
    public void appendTime(long start, long stop, int invocations) {
        assert (start >= 0);
        assert (stop >= 0);
        this.total_time += (stop - start);
        this.invocations += invocations;
    }

    /**
     * Append the think time without locking the marker. This is the preferred
     * way to update the ProfileMeasurement when multiple threads are using it.
     * @param start
     * @param stop
     */
    public void appendTime(long start, long stop) {
        this.appendTime(start, stop, 1);
    }

    /**
     * Append the think time without locking the marker. This is the preferred
     * way to update the ProfileMeasurement when multiple threads are using it.
     * @param start
     */
    public void appendTime(long start) {
        this.appendTime(start, getTime(), 1);
    }

    /**
     * Return the current time in nano-seconds
     * 
     * @return
     */
    public static long getTime() {
        // return System.currentTimeMillis();
        return System.nanoTime();
    }

    // --------------------------------------------------------------------------------------------
    // DEBUG METHODS
    // --------------------------------------------------------------------------------------------

    @Override
    public String toString() {
        return (this.debug(false));
    }

    public String debug() {
        return this.debug(true);
    }

    private String debug(boolean verbose) {
        String prefix = this.name + "/" + this.hashCode();
        if (verbose) {
            return (String.format("%s[total=%d, marker=%s, invocations=%d, avg=%.2f ms]", prefix, this.total_time,
                    this.marker, this.invocations, this.getAverageThinkTimeMS()));
        } else {
            return (prefix);
        }
    }

    // --------------------------------------------------------------------------------------------
    // SERIALIZATION METHODS
    // --------------------------------------------------------------------------------------------

    @Override
    public void load(File input_path, Database catalog_db) throws IOException {
        JSONUtil.load(this, catalog_db, input_path);
    }

    @Override
    public void save(File output_path) throws IOException {
        JSONUtil.save(this, output_path);
    }

    @Override
    public String toJSONString() {
        return (JSONUtil.toJSONString(this));
    }

    @Override
    public void toJSON(JSONStringer stringer) throws JSONException {
        stringer.key("NAME").value(this.name);
        stringer.key("TIME").value(this.total_time);
        stringer.key("INVOCATIONS").value(this.invocations);
        if (this.history != null) {
            stringer.key("HISTORY").array();
            for (long val : this.history) {
                stringer.value(val);
            } // FOR
            stringer.endArray();
        }
    }

    @Override
    public void fromJSON(JSONObject json_object, Database catalog_db) throws JSONException {
        this.name = json_object.getString("NAME");
        this.total_time = json_object.getLong("TIME");
        this.invocations = json_object.getInt("INVOCATIONS");
        if (json_object.has("HISTORY")) {
            this.history = null;
            this.enableHistoryTracking();
            JSONArray json_arr = json_object.getJSONArray("HISTORY");
            for (int i = 0, cnt = json_arr.length(); i < cnt; i++) {
                this.history.add(json_arr.getLong(i));
            } // FOR
        }
    }
}