Package com.sun.faban.driver.engine

Source Code of com.sun.faban.driver.engine.DriverContext$TimingInfo

/* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://www.sun.com/cddl/cddl.html or
* install_dir/legal/LICENSE
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at install_dir/legal/LICENSE.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* $Id$
*
* Copyright 2005-2010 Sun Microsystems Inc. All Rights Reserved
*/
package com.sun.faban.driver.engine;

import com.sun.faban.common.FabanNamespaceContext;
import com.sun.faban.common.Utilities;
import com.sun.faban.driver.CustomMetrics;
import com.sun.faban.driver.CustomTableMetrics;
import com.sun.faban.driver.Timing;
import static com.sun.faban.driver.engine.AgentThread.TIME_NOT_SET;
import com.sun.faban.driver.util.Random;
import com.sun.faban.driver.util.Timer;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.logging.Logger;

/**
* DriverContext is the point of communication between the
* developer-provided driver and the Faban driver framework.
* Each thread has it's own context.<p>
* This class provides the actual implementation and is
* located in the engine package to get direct access to all
* the restricted/needed classes and members.
*
* @author Akara Sucharitakul
*/
public class DriverContext extends com.sun.faban.driver.DriverContext {

    /** Thread local used for obtaining the context. */
    private static ThreadLocal<DriverContext> localContext = new InheritableThreadLocal<DriverContext>();

    /** The thread associated with this context. */
    AgentThread agentThread;
   
    /** The timing structure for this thread/context. */
    TimingInfo timingInfo = new TimingInfo();

    /** The central timer. */
    Timer timer;

    /**
     * Flag whether pause is supported with the current protocol.
     * Default is true. Protocols that may not be request/response but
     * may have concurrent inbound and outbound traffic, AND wishes to
     * support auto timing should set this flag to false. The default is true.
     */
    boolean pauseSupported = true;

    /** Context-specific logger. */
    Logger logger;

    /** The properties hashmap. It is lazy initialized. */
    static HashMap<String, String[]> properties;

    /** Class name of this class. */
    private String className;

    /** The XPath instance used to evaluate the XPaths. */
    private XPath xPathInstance;

  /** Desired upload speed of this context */
  private int kbpsUpload = -1;

  /** Desired download speed of this context */
  private int kbpsDownload = -1;

    /**
     * Obtains the DriverContext associated with this thread.
     * @return the associated DriverContext
     */
    public static DriverContext getContext() {
        return localContext.get();
    }

    /**
     * Constructs a DriverContext. Called only from AgentThread.
     * @param thread The AgentThread used by this context
     * @param timer The timer used by this thread
     */
    DriverContext(AgentThread thread, Timer timer) {
        className = getClass().getName();
        agentThread = thread;
        this.timer = timer;
        localContext.set(this);
    }

    /**
     * Obtains the scale or scaling rate of the current run.
     *
     * @return the current run's scaling rate
     */
  public int getScale() {
        return agentThread.runInfo.scale;
    }

    /**
     * Obtains the number of client threads in this agent.
     *
     * @return the number of client threads
     */
    public int getClientsInAgent() {
        return agentThread.runInfo.agentInfo.threads;
    }

    /**
     * Obtains the total number of clients threads for this driver.
     *
     * @return the number of client threads for this driver
     */
    public int getClientsInDriver() {
        return agentThread.runInfo.driverConfig.numThreads;
    }

    /**
     * Obtains the global thread id for this context's thread. The thread id
     * is unique for each driver type.
     * @return the global agentImpl thread id
     */
  public int getThreadId() {
        return agentThread.id;
    }

    /**
     * Obtains the agent id for this agentImpl.
     * @return the current agentImpl's id
     */
  public int getAgentId() {
        return agentThread.runInfo.agentInfo.agentNumber;
    }

    /**
     * Obtains the driver's name as annotated in the driver class.
     * @return the driver name
     */
  public String getDriverName() {
        return agentThread.driverConfig.name;
    }

    /**
     * Obtains the logger to be used by the calling driver.
     * @return the appropriate logger
     */
  public Logger getLogger() {
        if (logger == null) {
            logger = Logger.getLogger(agentThread.driverConfig.
                    className + '.' + agentThread.id);
            if (agentThread.runInfo.logHandler != null) {
        logger.addHandler(agentThread.runInfo.logHandler);
      }
        }
        return logger;
    }

    /**
     * Obtains the name of the operation currently executing.
     * @return the current operation's name,
     *         or null if called from outside an operation.
     */
  public String getCurrentOperation() {
        if (agentThread.currentOperation == -1)
            return null;
        return agentThread.driverConfig.operations[
                agentThread.currentOperation].name;
    }

    /**
     * Obtains the unique id assigned to the current operation type.
     * This id is commonly used to index into array structures containing
     * operation-specific information such as stats. The id ranges from 0 to
     * n where n is the number of operations in the driver less one.
     *
     * @return The unique id assigned to this operation type,
     *         or -1 if called from outside an operation.
     */
  public int getOperationId() {
        return agentThread.currentOperation;
    }

    /**
     * Obtains the number of operations active in this driver.
     *
     * @return The number of active operations
     */
  public int getOperationCount() {
        return agentThread.driverConfig.operations.length;
    }

    /**
     * Obtains the per-thread random value generator. Drivers
     * should use this random value generator and not instantiate
     * their own.
     * @return The random value generator
     */
  public Random getRandom() {
        return agentThread.random;
    }

    /**
     * Resets the state of the current mix to start off at the beginning
     * of the mix. For stateless mixes such as FlatMix, this operation
     * does nothing.
     */
  public void resetMix() {
        agentThread.selector[agentThread.mixId].reset();
    }

    /**
     * Attaches a custom metrics object to the primary metrics.
     * This should be done by the driver at initialization time.
     * Only one custom metrics can be attached. Subsequent calls
     * to this method replaces the previously attached metrics.
     * @param metrics The custom metrics to be replaced
     */
  public void attachMetrics(CustomMetrics metrics) {
        attachMetrics("Miscellaneous Statistics", metrics);
    }

    /**
     * Attaches a custom metrics object to the primary metrics,
     * given a name or description. The name/description must be unique.
     * This should be done by the driver at initialization time.
     * Only one custom metrics can be attached. Subsequent calls
     * to this method replaces the previously attached metrics.
     * @param name    The name or description of this metrics
     * @param metrics The custom metrics to be replaced
     */
    public void attachMetrics(String name, CustomMetrics metrics) {
        if (agentThread.metrics.metricAttachments == null)
            agentThread.metrics.metricAttachments =
                    new LinkedHashMap<String, CustomMetrics>();
        agentThread.metrics.metricAttachments.put(name, metrics);
    }

    /**
     * Attaches a custom table metrics object to the primary metrics,
     * given a name or description. The name/description must be unique.
     * This should be done by the driver at initialization time.
     * Only one custom metrics can be attached. Subsequent calls
     * to this method replaces the previously attached metrics.
     * @param name    The name or description of this metrics
     * @param metrics The custom metrics to be replaced
     */
    public void attachMetrics(String name, CustomTableMetrics metrics) {
        if (agentThread.metrics.tableAttachments == null)
            agentThread.metrics.tableAttachments =
                    new LinkedHashMap<String, CustomTableMetrics>();
        agentThread.metrics.tableAttachments.put(name, metrics);
    }

    /**
     * Parses the properties DOM tree in puts the output into a HashMap.
     * Returns properties so that we do not have the effect of double-checks.
     * @param propertiesElement The DOM element containing the properties
     * @return The resulting map
     */
    private static synchronized HashMap<String, String[]> parseProperties(
            Element propertiesElement) {
        if (properties == null) {
            NodeList list = propertiesElement.getElementsByTagNameNS(
                                            RunInfo.DRIVERURI, "property");
            int length = list.getLength();
            HashMap<String, String[]> props =
                    new HashMap<String, String[]>(length);
            for (int i = 0; i < length; i++) {
                Element propertyElement = (Element) list.item(i);
                Attr attr = propertyElement.getAttributeNodeNS(null, "name");
                if (attr != null) {
          props.put(attr.getValue(), getValue(propertyElement));
        }
                NodeList nameList =
                        propertyElement.getElementsByTagNameNS(
                                                RunInfo.DRIVERURI, "name");
                if (nameList.getLength() != 1) {
          continue;
        }
                Element nameElement = (Element) nameList.item(0);
                String name = nameElement.getFirstChild().getNodeValue();
                if (name != null) {
          props.put(name, getValue(propertyElement));
        }
            }
            properties = props;
        }
        return properties;
    }

    /**
     * Gets the value of a property DOM element.
     * @param propertyElement The DOM element
     * @return The list of associated values
     */
    private static String[] getValue(Element propertyElement) {
        NodeList valueList = propertyElement.getElementsByTagNameNS(
                                                RunInfo.DRIVERURI, "value");
        String[] values;
        int length = valueList.getLength();
        if (length >= 1) {
            values = new String[length];
            for (int i = 0; i < length; i++) {
                Node valueNode = valueList.item(i).getFirstChild();
                values[i] = valueNode == null ? "" : valueNode.getNodeValue();
            }
        } else {
            values = new String[1];
            Node valueNode = propertyElement.getFirstChild();
            values[0] = valueNode == null ? "" : valueNode.getNodeValue();
        }
        return values;
    }

    /**
     * Obtains a single-value property from the configuration. If the name
     * of a multi-value property is given, only one value is returned.
     * It is undefined as to which value in the list is returned.
     *
     * @param name The property name
     * @return The property value, or null if there is no such property
     */
  public String getProperty(String name) {
        if (properties == null) {
      properties = parseProperties(getPropertiesNode());
    }
        String[] value = properties.get(name);
        if (value == null) {
      return null;
    }
        return value[0];
    }

    /**
     * Obtains a multiple-value property from the configuration. A
     * single-value property will be returned as an array of dimension 1.
     *
     * @param name The property name
     * @return The property values
     */
  public String[] getPropertyValues(String name) {
        if (properties == null) {
      properties = parseProperties(getPropertiesNode());
    }
        return properties.get(name);
    }

    /**
     * Obtains the reference to the whole properties element as configured
     * in the driverConfig element of this driver in the config file. This
     * method allows custom free-form structures but the driver will need
     * to spend the effort walking the DOM tree.
     *
     * @return The DOM tree representing the properties node
     */
  public Element getPropertiesNode() {
        return agentThread.driverConfig.properties;
    }

    /**
     * Checks whether the driver is currently in steady state or not.
     * This method needs to be called after the critical section of the
     * operation. The transaction times must have been recorded in order
     * to establish whether or not the transaction is in steady state.
     * @return True if in steady state, false if not.
     */
  public boolean isTxSteadyState() {
        return agentThread.isSteadyState();
    }

    /**
     * Reads the element or attribute by it's XPath. The XPath is evaluated
     * from the root of the configuration file.
     *
     * @param xPath The XPath to evaluate.
     * @return The element or attribute value defined by the XPath
     * @exception XPathExpressionException If the given XPath has an error
     */
  public String getXPathValue(String xPath) throws XPathExpressionException {
        if (xPathInstance == null) {
            XPathFactory xf = XPathFactory.newInstance();
            FabanNamespaceContext nsCtx = new FabanNamespaceContext();           
            xPathInstance = xf.newXPath();
            xPathInstance.setNamespaceContext(nsCtx);
        }
        return xPathInstance.evaluate(xPath,
                agentThread.driverConfig.rootElement);
    }

    /**
     * Records the start and end time of the critical section of an operation.
     * This operation may block until the appropriate start time for the
     * operation has arrived. There is no blocking for the end time.
     * This method is for use in the driver code to demarcate critical
     * sections.
     * @throws IllegalStateException if the operation uses auto timing
     */
  public void recordTime() {
        if (agentThread.currentOperation == -1)
            throw new IllegalStateException("DriverContext.recordTime called " +
                                            "outside an operation");
        if (agentThread.driverConfig.operations[agentThread.currentOperation].
                timing != Timing.MANUAL) {
            String msg = "Driver: " + getDriverName() + ", Operation: " +
                    getCurrentOperation() + ", timing: MANUAL illegal call " +
                    "to recordTime() in driver code.";
            logger.severe(msg);
            IllegalStateException e = new IllegalStateException(msg);
            logger.throwing(className, "recordTime", e);
            throw e;
        }
        if (timingInfo != null) {
      if (timingInfo.invokeTime == TIME_NOT_SET) {
                timer.wakeupAt(timingInfo.intendedInvokeTime);
                // But since sleep may not be exact, we get the time again here.
                timingInfo.invokeTime = System.nanoTime();
            } else if (timingInfo.lastRespondTime != TIME_NOT_SET) {
                // The critical section was paused.
                timingInfo.pauseTime +=
                        System.nanoTime() - timingInfo.lastRespondTime;
                timingInfo.lastRespondTime = TIME_NOT_SET;
            } else {
                timingInfo.respondTime = System.nanoTime();
            }
    }
    }

    /**
     * Pauses the critical section so that operations made during the pause
     * do not count into the response time. If Timing.AUTO is used, the pause
     * ends automatically when the next request is sent to the server. For
     * manual timing, the next call to recordTime ends the pause. Calls
     * pauseTime when the critical section is already paused are simply ignored.
     */
  public void pauseTime() {
        if (agentThread.currentOperation == -1)
            throw new IllegalStateException("DriverContext.pauseTime called " +
                                            "outside an operation");
        if (agentThread.driverConfig.operations[agentThread.currentOperation].
                timing != Timing.MANUAL) {
            String msg = "Driver: " + getDriverName() + ", Operation: " +
                    getCurrentOperation() + ", timing: MANUAL illegal call " +
                    "to pauseTime() in driver code.";
            logger.severe(msg);
            IllegalStateException e = new IllegalStateException(msg);
            logger.throwing(className, "recordTime", e);
            throw e;
        }
        if (timingInfo.lastRespondTime == TIME_NOT_SET) {
            timingInfo.lastRespondTime = System.nanoTime();
    }
    }

    /**
     * Obtains a relative time, in milliseconds. This time is relative to
     * a certain time at the beginning of the benchmark run and does not
     * represent a wall clock time. All agents will have the same reference
     * time. Use this time to check time durations during the benchmark run.
     *
     * @return The relative time of the benchmark run
     */
  public int getTime() {
        return timer.getTime();
    }

    /**
     * Wakes up closest to a system nanosec time.
     * @param time The time to wake up
     */
    public void wakeupAt(long time) {
        timer.wakeupAt(time);
    }

    /**
     * Obtains the relative time - in milliseconds - that steady state starts,
     * if set. The if the time is not yet set, it will return 0.
     *
     * @return The relative time steady state starts
     */
  public int getSteadyStateStart() {
        return (int) (timer.toRelTime(agentThread.endRampUp) / Utilities.TO_NANOS);
    }

    /**
     * Obtains a relative time, in nanosecs. This time is relative to
     * a certain time at the beginning of the benchmark run and does not
     * represent a wall clock time. All agents will have the same reference
     * time. Use this time to check time durations during the benchmark run.
     *
     * @return The relative time of the benchmark run
     */
    public long getNanoTime() {
       return timer.toRelTime(System.nanoTime());
    }

    /**
     * Obtains the relative time - in nanosecs - that steady state starts,
     * if set. The if the time is not yet set, it will return 0.
     *
     * @return The relative time steady state starts
     */
    public long getSteadyStateStartNanos() {
        return timer.toRelTime(agentThread.endRampUp);
    }

    /**
     * Obtains the configured ramp up time.
     *
     * @return The configured ramp up time, in seconds
     */
  public int getRampUp() {
        return agentThread.runInfo.rampUp;
    }

    /**
     * Obtains the configured steady state time.
     *
     * @return The configured steady state time, in seconds
     */
  public int getSteadyState() {
        return agentThread.runInfo.stdyState;
    }

    /**
     * Obtains the configured ramp down time.
     *
     * @return The configured ramp down time, in seconds
     */
  public int getRampDown() {
        return agentThread.runInfo.rampDown;
    }

    /**
     * Property whether pause is supported with the current protocol.
     * Default is true. Protocols that may not be request/response but
     * may have concurrent inbound and outbound traffic, AND wishes to
     * support auto timing should set this flag to false. The default is true.
     *
     * @return The current setting of the pauseSupported property.
     */
    public boolean isPauseSupported() {
        return pauseSupported;
    }

    /**
     * Property whether pause is supported with the current protocol.
     * Default is true. Protocols that may not be request/response but
     * may have concurrent inbound and outbound traffic, AND wishes to
     * support auto timing should set this flag to false. The default is true.
     *
     * @param pause The new setting of the pauseSupported property.
     */
    public void setPauseSupported(boolean pause) {
        pauseSupported = pause;
    }

    /**
     * Records the start time of an operation. This method is not
     * exposed through the interface and is only used by the transport
     * facilities.
     * @return The recorded time - system nanotime, or TIME_NOT_SET if not set
     */
    public long recordStartTime() {
        // Not in an operation, don't record time.
        if (agentThread.currentOperation == -1)
            return TIME_NOT_SET;
        if (timingInfo != null && agentThread.driverConfig.operations[
                agentThread.currentOperation].timing == Timing.AUTO) {
            if (timingInfo.invokeTime == TIME_NOT_SET) {
                if (timingInfo.respondTime != TIME_NOT_SET)
                    logger.warning("Respond time already set before " +
                                   "sleeping. Please report a bug.");
                timer.wakeupAt(timingInfo.intendedInvokeTime);
                // But since sleep may not be exact, we get the time again here.
                timingInfo.invokeTime = System.nanoTime();
                return timingInfo.invokeTime;
            } else if (pauseSupported && timingInfo.respondTime != TIME_NOT_SET) {
                if (timingInfo.respondTime < timingInfo.invokeTime)
                    logger.warning("Respond time (" + timingInfo.respondTime +
                            ") less than invoke time (" +
                            timingInfo.invokeTime + "). Please report a bug.");

                // Some response already read, then transmit again.
                // In this case the time from last receive to this transmit
                // is the pause time ...
                timingInfo.lastRespondTime = timingInfo.respondTime;

                // We set the pause time only on the first byte transmitted.
                timingInfo.respondTime = TIME_NOT_SET;

                long time = System.nanoTime();
                timingInfo.pauseTime += time - timingInfo.lastRespondTime;
                return time;
            }
            // Otherwise this can be a subsequent write.
            // Invoke time already set and respond time not set.
        }
        return TIME_NOT_SET;
    }

    /**
     * Records the end time of an operation. This method is not
     * exposed through the interface and is only used by the transport
     * facilities.
     * @return The recorded time - system nanotime, or TIME_NOT_SET if not set
     */
    public long recordEndTime() {
        long tstamp = TIME_NOT_SET;
        // Not in an operation, don't record time.
        if (agentThread.currentOperation != -1) {
            if (timingInfo != null && agentThread.driverConfig.operations[
                    agentThread.currentOperation].timing == Timing.AUTO ) {
                // Some stacks clear the connection by doing a read before a
                // write in a request, normally a read of 0 bytes. We need to
                // make sure such reads are not part of the response time.
                if (timingInfo.invokeTime == TIME_NOT_SET) {
                    int[] previousOps = agentThread.previousOperation;
                    String name = agentThread.driverConfig.mix[0].
                            operations[previousOps[0]].name;
                    if (previousOps.length > 1)
                        name += ',' + agentThread.driverConfig.mix[1].
                                operations[previousOps[1]].name;
                    logger.warning("Read before write! Some input may still " +
                            "be in the buffer from previous operation " +
                            name + ". Ignoring such input.");
                } else {
                    timingInfo.respondTime = tstamp = System.nanoTime();
                }
            }
        }
        return tstamp;
    }

    /**
     * Sets the intended invocation time for the next invocation
     * on this thread. This is called from AgentThread only.
     * @param time The time to invoke
     */
    void setInvokeTime(long time) {

        // Then set the intended start time.
        timingInfo.intendedInvokeTime = time;
        // And set the other times to invalid.
        timingInfo.invokeTime = TIME_NOT_SET;
        timingInfo.respondTime = TIME_NOT_SET;
        timingInfo.lastRespondTime = TIME_NOT_SET;
        timingInfo.pauseTime = 0l;
    }

    /**
     * TimingInfo is a value object that contains individual
     * timing records for each operation.
     */
    public static class TimingInfo {

      /** Intended Invoke Time. */
        public long intendedInvokeTime = TIME_NOT_SET;

        /** Actual Invoke Time. */
        public long invokeTime = TIME_NOT_SET;

        /** Respond Time. */
        public long respondTime = TIME_NOT_SET;

        /** Last respond time, if any. */
        public long lastRespondTime = TIME_NOT_SET;

        /** Pause Time. */
        public long pauseTime = 0l;
    }

    /**
     * Obtains the base directory where the benchmark currently being run
     * is installed.
     *
     * @return The benchmark's base directory
     */
    public String getBaseDir() {
        return agentThread.agent.driverBase;
    }

    /**
     * Obtains the resource directory used for this benchmark, if exists.
     * @return The resource directory for this benchmark
     */
    public String getResourceDir() {
        return agentThread.agent.driverBase + File.separator + "resources";
    }

    /**
     * Set the desired upload speed for the thread using this context.
     * This method is intended for use only by transport classes; drivers
     * should call an apporpriate method on the transport to set this
     * value. Note that not all transports support bandwidth throttling
     * (so drivers that do call this method will have no idea if the value
     * is used or not).
     *
     * @param kbps desired speed in kilobytes per second. If kbps is < 0,
     * speed will be unlimited.
     */
    public void setUploadSpeed(int kbps) {
        this.kbpsUpload = kbps;
    }

    /**
     * Return the desired upload speed for the thread using this context.
     *
     * @return desired speed in kilobytes per second
     */
    public int getUploadSpeed() {
        return kbpsUpload;
    }

    /**
     * Set the desired downoad speed for the thread using this context.
     * This method is intended for use only by transport classes; drivers
     * should call an apporpriate method on the transport to set this
     * value. Note that not all transports support bandwidth throttling
     * (so drivers that do call this method will have no idea if the value
     * is used or not).
     *
     * @param kbps desired speed in kilobytes per second. If kbps is < 0,
     * speed will be unlimited.
     */
    public void setDownloadSpeed(int kbps) {
        this.kbpsDownload = kbps;
    }

    /**
     * Return the desired download speed for the thread using this context.
     *
     * @return desired speed in kilobytes per second
     */
    public int getDownloadSpeed() {
        return kbpsDownload;
    }
}
TOP

Related Classes of com.sun.faban.driver.engine.DriverContext$TimingInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.