/* 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-2009 Sun Microsystems Inc. All Rights Reserved
*/
package com.sun.faban.driver.engine;
import com.sun.faban.driver.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Implements the basic benchmark, driver, and operation definitions.
*
* @author Akara Sucharitakul
*/
public class BenchmarkDefinition implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
String name;
String version;
Driver[] drivers;
RunControl runControl;
String metric;
String scaleName;
String scaleUnit;
boolean configPrecedence;
static BenchmarkDefinition read(String defClassName)
throws DefinitionException, ConfigurationException {
BenchmarkDefinition def = new BenchmarkDefinition();
String className = def.getClass().getName();
Logger logger = Logger.getLogger(className);
Class<?> defClass;
try {
defClass = Class.forName(defClassName);
} catch (ClassNotFoundException e) {
//Did not find the class in the default classloader,
//look first in the faban.tmpdir then in java.io.tmpdir
//for the generated class
String tempDir = System.getProperty("faban.tmpdir");
if(tempDir==null){
tempDir = System.getProperty("java.io.tmpdir");
}
File classFile = new File(tempDir);
URL url[]= new URL[1];
try {
url[0] = classFile.toURI().toURL();
} catch (MalformedURLException ex) {
logger.log(Level.SEVERE, "Bad file URL for generated java class!");
throw new ConfigurationException(ex);
}
URLClassLoader loader = new URLClassLoader(url, BenchmarkDefinition.class.getClassLoader());
try {
defClass=loader.loadClass(defClassName);
} catch(ClassNotFoundException cnfex) {
ConfigurationException ce = new ConfigurationException(e);
logger.log(Level.SEVERE, e.getMessage(), ce);
throw ce;
}
}
if (!defClass.isAnnotationPresent(
com.sun.faban.driver.BenchmarkDefinition.class)) {
String msg = "Class " + defClassName +
" is not a benchmark definition.";
logger.severe(msg);
throw new ConfigurationException(msg);
}
com.sun.faban.driver.BenchmarkDefinition benchDefAnnotation = defClass.
getAnnotation(com.sun.faban.driver.BenchmarkDefinition.class);
def.name = benchDefAnnotation.name();
def.version = benchDefAnnotation.version();
def.runControl = benchDefAnnotation.runControl();
def.metric = benchDefAnnotation.metric();
def.scaleName = benchDefAnnotation.scaleName();
def.scaleUnit = benchDefAnnotation.scaleUnit();
def.configPrecedence = benchDefAnnotation.configPrecedence();
ArrayList<Class<?>> driverClassList = new ArrayList<Class<?>>();
// Get all the driver classes
for (Class<?> driverClass : benchDefAnnotation.drivers()) {
if (driverClass != Object.class && driverClass.isAnnotationPresent(
BenchmarkDriver.class)) {
driverClassList.add(driverClass);
}
}
// If defClass is not in list and is a driver, prepend
if (driverClassList.indexOf(defClass) < 0 &&
defClass.isAnnotationPresent(BenchmarkDriver.class)) {
driverClassList.add(0, defClass);
}
// Check that we have at least one driver
if (driverClassList.size() <= 0) {
String msg = "No driver classes found";
logger.severe(msg);
throw new DefinitionException(msg);
}
// Transfer the classes to an array
Class<?>[] driverClasses = new Class<?>[driverClassList.size()];
driverClasses = driverClassList.toArray(driverClasses);
def.drivers = new Driver[driverClasses.length];
// Obtain all driver and driver class names
for (int i = 0; i < driverClasses.length; i++) {
BenchmarkDriver benchDriver = driverClasses[i].getAnnotation(
BenchmarkDriver.class);
def.drivers[i] = new Driver();
def.drivers[i].name = benchDriver.name();
def.drivers[i].metric = benchDriver.metric();
def.drivers[i].opsUnit = benchDriver.opsUnit();
def.drivers[i].threadPerScale = benchDriver.threadPerScale();
String[] percentiles = benchDriver.percentiles();
def.drivers[i].percentiles = new double[percentiles.length];
def.drivers[i].pctString = new String[percentiles.length];
def.drivers[i].pctSuffix = new String[percentiles.length];
// Parse the percentiles.
for (int j = 0; j < percentiles.length; j++) {
String percentile = percentiles[j];
int length = percentile.length();
if (percentile.endsWith("%"))
--length;
if (!Character.isDigit(percentile.charAt(length - 1))) {
String suffix = percentile.substring(length - 2, length);
if (!suffix.equals("th") && !suffix.equals("st") &&
!suffix.equals("nd") && !suffix.equals("rd"))
throw new DefinitionException(
"Invalid percentile suffix " + suffix);
def.drivers[i].pctSuffix[j] = suffix;
length -= 2;
}
percentile = percentile.substring(0, length);
def.drivers[i].pctString[j] = percentile;
try {
def.drivers[i].percentiles[j] =
Double.parseDouble(percentile);
} catch (NumberFormatException e) {
throw new DefinitionException(percentile + " not a number.",
e);
}
if (def.drivers[i].percentiles[j] <= 0d ||
def.drivers[i].percentiles[j] >= 100d) {
throw new DefinitionException("Percentile " + percentile +
" must be greater than 0 and less than 100.");
}
}
def.drivers[i].responseTimeUnit = benchDriver.responseTimeUnit();
if (def.drivers[i].responseTimeUnit.equals(TimeUnit.NANOSECONDS))
throw new DefinitionException("@BenchmarkDriver " +
"responseTimeUnit must not be NANOSECONDS");
def.drivers[i].className = driverClasses[i].getName();
populatePrePost(driverClasses[i], def.drivers[i]);
getBackground(driverClasses[i], def.drivers[i]);
def.drivers[i].mix[0] = Mix.getMix(driverClasses[i]);
def.drivers[i].initialDelay[0] = getInitialDelay(driverClasses[i]);
int totalOps = def.drivers[i].mix[0].operations.length;
if (def.drivers[i].mix[1] != null) {
totalOps += def.drivers[i].mix[1].operations.length;
}
// Copy operation references into a flat array.
def.drivers[i].operations =
new BenchmarkDefinition.Operation[totalOps];
System.arraycopy(def.drivers[i].mix[0].operations, 0,
def.drivers[i].operations, 0,
def.drivers[i].mix[0].operations.length);
if (def.drivers[i].mix[1] != null) {
System.arraycopy(def.drivers[i].mix[1].operations, 0,
def.drivers[i].operations,
def.drivers[i].mix[0].operations.length,
def.drivers[i].mix[1].operations.length);
}
// Check the percentile limit on each operation
double maxPctLimit = Double.MIN_VALUE;
for (Operation op : def.drivers[i].operations) {
if (op.percentileLimits.length !=
def.drivers[i].percentiles.length) {
throw new DefinitionException("@BenchmarkOperation " +
op.name + " percentileLimits array must be the " +
"same length as @BenchmarkDriver percentiles");
}
for (double limit : op.percentileLimits) {
if (limit > 0d && limit > maxPctLimit) {
maxPctLimit = limit;
}
}
}
if (def.drivers[i].percentiles.length > 0) {
if (maxPctLimit <= 0d)
throw new DefinitionException("At least one percentile " +
"limit must be specified.");
} else { // Old style...
for (Operation op : def.drivers[i].operations)
if (op.max90th > 0d && op.max90th > maxPctLimit)
maxPctLimit = op.max90th;
if (maxPctLimit <= 0d)
throw new DefinitionException("At least one max90th " +
"must be specified.");
}
def.drivers[i].maxPercentile = maxPctLimit;
def.drivers[i].driverClass = driverClasses[i];
}
return def;
}
/**
* Reads the Faban definition annotations and prints a DD to file.
* @param defClassName The defining class name
* @throws ConfigurationException Error in the benchmark configuration
* @throws DefinitionException Error in the benchmark definition
*/
public static void printFabanDD(String defClassName)
throws ConfigurationException, DefinitionException {
Logger logger = Logger.getLogger(BenchmarkDefinition.class.getName());
logger.fine("Generating Faban DD.");
Class<?> defClass;
try {
defClass = Class.forName(defClassName);
logger.fine("Found benchmark definition class " + defClassName);
} catch (ClassNotFoundException e) {
File runXml = new File(System.getProperty("benchmark.config"));
throw new ConfigurationException("Defining class " + defClassName +
" in " + runXml.getName() +
" not found in deployment.", e);
}
if (!defClass.isAnnotationPresent(
com.sun.faban.driver.BenchmarkDefinition.class)) {
String msg = "Defining class " + defClassName +
" is not a benchmark definition.";
logger.severe(msg);
throw new DefinitionException(msg);
}
com.sun.faban.driver.BenchmarkDefinition benchDefAnnotation = defClass.
getAnnotation(com.sun.faban.driver.BenchmarkDefinition.class);
logger.fine("Start building XML.");
StringBuilder b = new StringBuilder(2048);
b.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
b.append("<!-- Generated Faban Driver Framework DD, please do not " +
"modify -->\n");
b.append("<fabanDriver>\n");
b.append(" <name>").append(benchDefAnnotation.name()).
append("</name>\n");
b.append(" <version>").append(benchDefAnnotation.version()).
append("</version>\n");
b.append(" <runControl>").append(benchDefAnnotation.runControl()).
append("</runControl>\n");
b.append(" <metric>").append(benchDefAnnotation.metric()).
append("</metric>\n");
b.append(" <scaleName>").append(benchDefAnnotation.scaleName()).
append("</scaleName>\n");
b.append(" <scaleUnit>").append(benchDefAnnotation.scaleUnit()).
append("</scaleUnit>\n");
b.append(" <configPrecedence>").append(benchDefAnnotation.
configPrecedence()).append("</configPrecedence>\n");
b.append("</fabanDriver>\n");
String outputFile = System.getProperty("benchmark.ddfile");
try {
logger.fine("Writing DD file " + outputFile);
FileOutputStream out = new FileOutputStream(outputFile);
out.write(b.toString().getBytes());
out.flush();
out.close();
logger.fine("Done writing " + outputFile);
} catch (IOException e) {
logger.severe(e.getMessage());
}
}
private static void getBackground(Class<?> driverClass, Driver driver)
throws DefinitionException {
if (!driverClass.isAnnotationPresent(Background.class)) {
driver.mix[1] = null;
driver.initialDelay[1] = null;
return;
}
Background background = driverClass.getAnnotation(Background.class);
String[] ops = background.operations();
FixedSequence mix = new FixedSequence();
mix.deviation = 2d;
mix.operations = getOperationsNoCycles(driverClass, ops);
com.sun.faban.driver.FixedTime[] timings = background.timings();
if (timings.length == 0) { // No bg timing, use driver or method timing
Cycle.setCycles(mix.operations, driverClass);
} else if (timings.length == 1) { // Apply to all the background ops
FixedTime fixedTime = new FixedTime();
fixedTime.init(timings[0]);
for (Operation op : mix.operations) {
op.cycle = fixedTime;
}
} else if (timings.length > 1) { // Appy timing to each background op
if (timings.length != mix.operations.length) {
throw new DefinitionException("No of background ops must " +
"match the no of timings, currently " +
mix.operations.length + " vs " + timings.length);
}
for (int i = 0; i < timings.length; i++) {
if (mix.operations[i].cycle != null) {
throw new DefinitionException("Duplicate operations " +
"entry in @Background");
}
mix.operations[i].cycle = new FixedTime();
mix.operations[i].cycle.init(timings[i]);
}
}
driver.mix[1] = mix;
driver.initialDelay[1] =
getInitialDelay(background.initialDelay().max());
}
private static Cycle getInitialDelay(Class<?> driverClass) {
InitialDelay initDelay = driverClass.getAnnotation(
InitialDelay.class);
int max;
if (initDelay == null) {
max = 0;
} else {
max = initDelay.max();
}
return getInitialDelay(max);
}
private static Cycle getInitialDelay(int max) {
Cycle delay;
if (max <= 0) {
FixedTime fixed = new FixedTime();
fixed.cycleTime = 0;
delay = fixed;
} else {
Uniform uniform = new Uniform();
uniform.cycleMin = 0;
uniform.cycleMax = max;
delay = uniform;
}
delay.cycleType = CycleType.CYCLETIME;
delay.cycleDeviation = 0d;
return delay;
}
private BenchmarkDefinition() {
super();
}
static Operation[] getOperations(Class<?> driverClass, String[] names)
throws DefinitionException {
Operation[] ops = getOperationsNoCycles(driverClass, names);
Cycle.setCycles(ops, driverClass);
return ops;
}
private static Operation[] getOperationsNoCycles(Class<?> driverClass,
String[] names)
throws DefinitionException {
HashMap<String, Operation> operationMap =
new HashMap<String, Operation>();
// First we read all the operations that have the annotation
Method[] methods = driverClass.getMethods();
for (Method m : methods) {
if (m.isAnnotationPresent(BenchmarkOperation.class)) {
BenchmarkOperation benchOp = m.getAnnotation(
BenchmarkOperation.class);
Operation op = new Operation();
op.name = benchOp.name();
op.percentileLimits = benchOp.percentileLimits();
op.max90th = benchOp.max90th();
op.timing = benchOp.timing();
op.countToMetric = benchOp.countToMetric();
op.m = m;
operationMap.put(op.name, op);
}
}
Operation[] ops = new Operation[names.length];
// Then we list them according to the name
for (int i = 0; i < names.length; i++) {
ops[i] = operationMap.get(names[i]);
if (ops[i] == null) {
throw new DefinitionException("Operation \"" + names[i] +
"\" listed in mix not found");
}
}
return ops;
}
static Operation[] getOperations(Class<?> driverClass)
throws DefinitionException {
ArrayList<Operation> operationList = new ArrayList<Operation>();
Method[] methods = driverClass.getMethods();
for (Method m : methods) {
if (m.isAnnotationPresent(BenchmarkOperation.class)) {
BenchmarkOperation benchOp = m.getAnnotation(
BenchmarkOperation.class);
Operation op = new Operation();
op.name = benchOp.name();
op.percentileLimits = benchOp.percentileLimits();
op.max90th = benchOp.max90th();
op.timing = benchOp.timing();
op.countToMetric = benchOp.countToMetric();
op.m = m;
operationList.add(op);
}
}
Operation[] ops = new Operation[operationList.size()];
ops = operationList.toArray(ops);
Cycle.setCycles(ops, driverClass);
return ops;
}
static void populatePrePost(Class<?> driverClass, Driver driver)
throws DefinitionException {
Method[] methods = driverClass.getMethods();
for (Method m : methods) {
if (m.isAnnotationPresent(OnceBefore.class)) {
if (driver.preRun == null) {
driver.preRun = new DriverMethod();
driver.preRun.m = m;
driver.preRun.genericName = m.toGenericString();
} else {
throw new DefinitionException("Found more than one " +
"@OnceBefore method, " + driver.preRun.genericName +
" and " + m.toGenericString() + ".");
}
} else if (m.isAnnotationPresent(OnceAfter.class)) {
if (driver.postRun == null) {
driver.postRun = new DriverMethod();
driver.postRun.m = m;
driver.postRun.genericName = m.toGenericString();
} else {
throw new DefinitionException("Found more than one " +
"@OnceAfter method, " + driver.postRun.genericName +
" and " + m.toGenericString() + ".");
}
}
}
}
/**
* The refillOperations method re-establishes the non-serializable
* parts of the operations array.
* @param driverClass The driver class
* @param operations The operation array
*/
static void refillOperations(Class<?> driverClass, Operation[] operations) {
{ // Use a separate code block as the vars in here are not used later
int i;
// Find a method in the list that is null
for (i = 0; i < operations.length; i++ ) {
if (operations[i].m == null) {
break;
}
}
// If none found, we do not need to do anything else.
if (i == operations.length) {
return;
}
}
HashMap<String, Method> methodMap =
new HashMap<String, Method>();
// First we read all the operations that have the annotation
Method[] methods = driverClass.getMethods();
for (Method m : methods) {
if (m.isAnnotationPresent(BenchmarkOperation.class)) {
BenchmarkOperation benchOp = m.getAnnotation(
BenchmarkOperation.class);
methodMap.put(benchOp.name(), m);
}
}
// Then we check each operation and get the method from the map.
for (Operation o : operations) {
if (o.m == null) {
o.m = methodMap.get(o.name);
}
}
}
/**
* The refillMethod method re-establishes the non-serializable parts of
* a DriverMethod object.
* @param driverClass The driver class
* @param method The DriverMethod instance
*/
static void refillMethod(Class<?> driverClass, DriverMethod method) {
if (method != null && method.m == null) {
Method[] methods = driverClass.getMethods();
for (Method m : methods) {
if (method.genericName.equals(m.toGenericString())) {
method.m = m;
break;
}
}
}
}
/**
* Creates and returns a copy of this object. The precise meaning
* of "copy" may depend on the class of the object. The general
* intent is that, for any object <tt>x</tt>, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression:
* <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be <tt>true</tt>, but these are not absolute requirements.
* While it is typically the case that:
* <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be <tt>true</tt>, this is not an absolute requirement.
* <p/>
* By convention, the returned object should be obtained by calling
* <tt>super.clone</tt>. If a class and all of its superclasses (except
* <tt>Object</tt>) obey this convention, it will be the case that
* <tt>x.clone().getClass() == x.getClass()</tt>.
* <p/>
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by <tt>super.clone</tt> before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by <tt>super.clone</tt>
* need to be modified.
* <p/>
* The method <tt>clone</tt> for class <tt>Object</tt> performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface <tt>Cloneable</tt>, then a
* <tt>CloneNotSupportedException</tt> is thrown. Note that all arrays
* are considered to implement the interface <tt>Cloneable</tt>.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p/>
* The class <tt>Object</tt> does not itself implement the interface
* <tt>Cloneable</tt>, so calling the <tt>clone</tt> method on an object
* whose class is <tt>Object</tt> will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the <code>Cloneable</code> interface. Subclasses
* that override the <code>clone</code> method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see Cloneable
*/
@Override
public Object clone() throws CloneNotSupportedException {
// Shallow copy for primitives and immutables
BenchmarkDefinition clone = (BenchmarkDefinition) super.clone();
// Then deep copy for the arrays and mutables.
clone.drivers = new Driver[drivers.length];
for (int i = 0; i < drivers.length; i++) {
clone.drivers[i] = (Driver) drivers[i].clone();
}
return clone;
}
static class Driver implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
String name;
String metric;
String opsUnit;
float threadPerScale;
double[] percentiles;
String[] pctString;
String[] pctSuffix;
TimeUnit responseTimeUnit;
Mix[] mix = new Mix[2]; // Foreground (0) and background (1) mix.
Cycle[] initialDelay = new Cycle[2]; // Foreground and background
Operation[] operations;
DriverMethod preRun;
DriverMethod postRun;
String className;
double maxPercentile;
// We try to send the whole driver class over to the agents so that all
// agents will run consistent drivers. In case this is slow or does not
// work due to dependencies, we will need to set this field transient.
// The receiving end must check this value for null and re-load the
// class in such cases.
Class<?> driverClass;
/**
* Creates and returns a copy of this object. The precise meaning
* of "copy" may depend on the class of the object. The general
* intent is that, for any object <tt>x</tt>, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression:
* <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be <tt>true</tt>, but these are not absolute requirements.
* While it is typically the case that:
* <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be <tt>true</tt>, this is not an absolute requirement.
* <p/>
* By convention, the returned object should be obtained by calling
* <tt>super.clone</tt>. If a class and all of its superclasses (except
* <tt>Object</tt>) obey this convention, it will be the case that
* <tt>x.clone().getClass() == x.getClass()</tt>.
* <p/>
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by <tt>super.clone</tt> before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by <tt>super.clone</tt>
* need to be modified.
* <p/>
* The method <tt>clone</tt> for class <tt>Object</tt> performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface <tt>Cloneable</tt>, then a
* <tt>CloneNotSupportedException</tt> is thrown. Note that all arrays
* are considered to implement the interface <tt>Cloneable</tt>.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p/>
* The class <tt>Object</tt> does not itself implement the interface
* <tt>Cloneable</tt>, so calling the <tt>clone</tt> method on an object
* whose class is <tt>Object</tt> will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the <code>Cloneable</code> interface. Subclasses
* that override the <code>clone</code> method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see Cloneable
*/
@Override
public Object clone() throws CloneNotSupportedException {
Driver clone = (Driver) super.clone();
if (preRun != null) {
clone.preRun = (DriverMethod) preRun.clone();
}
if (postRun != null) {
clone.postRun = (DriverMethod) postRun.clone();
}
clone.mix[0] = (Mix) mix[0].clone();
if (mix[1] != null) {
clone.mix[1] = (Mix) mix[1].clone();
}
clone.percentiles = percentiles.clone();
clone.pctString = pctString.clone();
clone.pctSuffix = pctSuffix.clone();
clone.initialDelay[0] = (Uniform) initialDelay[0].clone();
if (initialDelay[1] != null) {
clone.initialDelay[1] = (Uniform) initialDelay[1].clone();
}
// Copy operation references into a flat array.
int totalOps = operations.length;
clone.operations = new BenchmarkDefinition.Operation[totalOps];
System.arraycopy(clone.mix[0].operations, 0, clone.operations, 0,
mix[0].operations.length);
System.arraycopy(clone.mix[1].operations, 0, clone.operations,
mix[0].operations.length, mix[1].operations.length);
return clone;
}
/**
* Obtains the global operation index based on the mix identifier and
* operation identifier inside the mix.
*
* @param mixId 0 for foreground and 1 for background mix
* @param opId the id of the operation within the mix
* @return The global index into the operation
*/
public int getOperationIdx(int mixId, int opId) {
int idx = 0;
for (int i = 0; i < mixId; i++) {
idx += mix[i].operations.length;
}
idx += opId;
return idx;
}
}
static class DriverMethod implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
String genericName;
transient Method m;
/**
* Creates s shallow clone of this object.
* @return a clone of this instance.
* @see Cloneable
*/
@Override
public final Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
Logger logger = Logger.getLogger(this.getClass().getName());
logger.log(Level.SEVERE, "Unexpected exception!", e);
}
return clone;
}
}
static class Operation implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
String name;
double[] percentileLimits;
double max90th;
Timing timing;
boolean countToMetric;
Cycle cycle;
transient Method m;
/**
* Creates an exact deep clone of this object.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the <code>Cloneable</code> interface. Subclasses
* that override the <code>clone</code> method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see Cloneable
*/
@Override
public Object clone() throws CloneNotSupportedException {
Operation clone = (Operation) super.clone();
clone.cycle = (Cycle) cycle.clone();
return clone;
}
}
}