// // Copyright (C) 2010 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.perturb; import java.util.Random; import java.util.Vector; import gov.nasa.jpf.Config; import gov.nasa.jpf.vm.ChoiceGenerator; import gov.nasa.jpf.vm.IntChoiceGenerator; import gov.nasa.jpf.vm.MethodInfo; import gov.nasa.jpf.vm.StackFrame; import gov.nasa.jpf.vm.Types; import gov.nasa.jpf.vm.choice.IntChoiceFromSet; /** * This file implements a generic data abstraction module that can be used * with the Perturbator to execute a method with a choice of values for the * method parameters. * * This module handles all basic types and selects values for them as follows: * int, short: random negative integer | 0 | random positive integer * float: random negative floating point | 0 | random positive floating point * char: two random values in [0, 255] and an explicit 0 * boolean: true | false * * An instance of this class is expected to be specialized for each method that we * want perturbed. The cstor creates a vector of valuations for the parameters * using all choices for each basic type such that each vector can be written * directly into the method stack frame for parameters * */ public class GenericDataAbstractor implements OperandPerturbator { // A valuations helper class public class Valuation { protected int valuation[] = null; public Valuation(int size) { valuation = new int[size]; } // create an object from an existing valuation public Valuation(Valuation seedValuation) { valuation = (int[])seedValuation.valuation.clone(); } public Valuation(Valuation val, int size) { valuation = new int[size]; int[] valuationArray = val.getValuation(); for (int i = 0; i < valuationArray.length; i++) valuation[i] = valuationArray[i]; } public int[] getValuation() { return valuation; } public void add(int index, int element) { valuation[index] = element; } } static long seed = 5; protected MethodInfo mi; protected StackFrame stackFrame; protected int nParams; protected byte[] paramTypes = null; protected String[] paramTypeNames = null; protected String[] paramNames = null; protected Vector<Valuation> valuations = new Vector<Valuation>(); protected int choices; protected int operandSize; protected Valuation valuation = null; protected boolean isStatic; protected Random randomizer = new Random(seed); public GenericDataAbstractor (Config conf, String keyPrefix){ // this will expand to read parameters from the configuration // to specialize the behavior of the abstractor mi = null; choices = 0; } // At the time of instance creation we don't have the MethodInfo object // for the method that needs its parameters perturbed. Therefore, we // should set MethodInfo using a call to the following method before // we can use this instance to perturb a method invocation public void setMethodInfo(MethodInfo m, StackFrame frame) { if (mi != null) return; mi = m; stackFrame = frame; // Need to figure out the right number of parameters. paramTypes = mi.getArgumentTypes(); paramTypeNames = mi.getArgumentTypeNames(); nParams = paramTypes.length; isStatic = mi.isStatic(); // now compute the operand size in 32-bit words operandSize = 0; for (byte type : paramTypes) { if (type == Types.T_LONG || type == Types.T_DOUBLE) operandSize += 2; else operandSize++; } // useful when we call the various populate methods that populate // choice vectors. The parameter name can be used to specialize // population to parameter names paramNames = new String[nParams]; if (nParams != 0) { String[] localVars = mi.getLocalVariableNames(); for (int i = 0; i < nParams; i++) { paramNames[i] = isStatic? localVars[i] : localVars[i + 1]; } } // We build an array of choices, with each choice being an index into // an array of integers representing the operand stack values. // We then use an IntChoiceGenerator to give us an index that is then // used to access the values we want to replace for the operands on the // stack valuation = new Valuation(operandSize); valuations.add(valuation); populateValuations(frame, 0, 0); // we now know how many choices there are and hence set choices choices = valuations.size() - 1; } // Method to populate boolean values public int[] populateBoolean(MethodInfo mi, String name) { int[] bVec = {0 /* false */, 1 /* true */}; return bVec; } // Method to populate character values public int[] populateChar(MethodInfo mi, String name) { int[] iVec = {Math.abs(randomizer.nextInt() % 255), 0, Math.abs(randomizer.nextInt() % 255)}; return iVec; } // Method to populate byte values public int[] populateByte(MethodInfo mi, String name) { int[] iVec = {Math.abs(randomizer.nextInt() % 128), 0, -1 * Math.abs(randomizer.nextInt() % 127)}; return iVec; } // Method to populate integer values public int[] populateInt(MethodInfo mi, String name) { int[] iVec = {Math.abs(randomizer.nextInt() % 100), 0, -1 * Math.abs(randomizer.nextInt() % 100)}; return iVec; } // Method to populate integer values public int[] populateShort(MethodInfo mi, String name) { return populateInt(mi, name); } // Method to populate long values public int[] populateLong(MethodInfo mi, String name) { long[] lVec = {Math.abs(randomizer.nextLong() % 100), 0, -1 * Math.abs(randomizer.nextLong() % 100)}; int[] iVec = new int[lVec.length * 2]; int i = 0; for (long l : lVec) { iVec[i++] = Types.hiLong(l); iVec[i++] = Types.loLong(l); } return iVec; } // Method to populate integer values public int[] populateFloat(MethodInfo mi, String name) { int[] fVec = {Float.floatToIntBits(randomizer.nextFloat()), 0, -1 * Float.floatToIntBits(randomizer.nextFloat())}; return fVec; } // Method to populate long values public int[] populateDouble(MethodInfo mi, String name) { double[] dVec = {-1.414, 0.0, 3.141}; int[] iVec = new int[dVec.length * 2]; int i = 0; for (double d : dVec) { iVec[i++] = Types.hiDouble(d); iVec[i++] = Types.loDouble(d); } return iVec; } public void populateValuations(StackFrame frame, int paramIndex, int dataIndex) { if (paramIndex == nParams) { // copy the contents of the previous vector as a // suffix of it will be over-written, retaining // the valuations for all parameters ahead of the // suffix valuation = new Valuation(valuation); valuations.add(valuation); } else { switch (paramTypes[paramIndex]) { case Types.T_ARRAY: populateValuations(frame, paramIndex + 1, dataIndex + 1); break; case Types.T_BOOLEAN: int[] bVec = populateBoolean(mi, paramNames[paramIndex]); for (int i = 0; i < bVec.length; i++) { valuation.add(dataIndex, bVec[i]); populateValuations(frame, paramIndex + 1, dataIndex + 1); } break; case Types.T_FLOAT: int[] fVec = populateFloat(mi, paramNames[paramIndex]); for (int i = 0; i < fVec.length; i++) { valuation.add(dataIndex, fVec[i]); populateValuations(frame, paramIndex + 1, dataIndex + 1); } break; case Types.T_CHAR: int[] iVec = populateChar(mi, paramNames[paramIndex]); for (int i = 0; i < iVec.length; i++) { valuation.add(dataIndex, iVec[i]); populateValuations(frame, paramIndex + 1, dataIndex + 1); } break; case Types.T_BYTE: iVec = populateByte(mi, paramNames[paramIndex]); for (int i = 0; i < iVec.length; i++) { valuation.add(dataIndex, iVec[i]); populateValuations(frame, paramIndex + 1, dataIndex + 1); } break; case Types.T_INT: iVec = populateInt(mi, paramNames[paramIndex]); for (int i = 0; i < iVec.length; i++) { valuation.add(dataIndex, iVec[i]); populateValuations(frame, paramIndex + 1, dataIndex + 1); } break; case Types.T_SHORT: iVec = populateShort(mi, paramNames[paramIndex]); for (int i = 0; i < iVec.length; i++) { valuation.add(dataIndex, iVec[i]); populateValuations(frame, paramIndex + 1, dataIndex + 1); } break; case Types.T_LONG: int[] lVec = populateLong(mi, paramNames[paramIndex]); int i = 0; while (i < lVec.length) { valuation.add(dataIndex, lVec[i++]); valuation.add(dataIndex + 1, lVec[i++]); populateValuations(frame, paramIndex + 1, dataIndex + 2); } break; case Types.T_DOUBLE: int[] dVec = populateDouble(mi, paramNames[paramIndex]); i = 0; while (i < dVec.length) { valuation.add(dataIndex, dVec[i++]); valuation.add(dataIndex + 1, dVec[i++]); populateValuations(frame, paramIndex + 1, dataIndex + 2); } break; } } } public ChoiceGenerator<?> createChoiceGenerator (String id, StackFrame frame, Object refObject) { // We expect that the refObject in this case will be a MethodInfo object // Set it so that we can create valuation vectors assert refObject instanceof MethodInfo : "wrong refObject type for GenericDataAbstractor: " + refObject.getClass().getName(); setMethodInfo((MethodInfo)refObject, frame); if (choices > 0) { // now create a choices vector which will be used to iterate over the number of // parameter valuations we want to use. We set each element of the vector simply // to an index into the valuations vector int[] indices = new int[choices]; for (int i = 0; i < choices; i++) { indices[i] = i; } return new IntChoiceFromSet(id, indices); } else return null; } public boolean perturb(ChoiceGenerator<?>cg, StackFrame frame) { assert cg instanceof IntChoiceGenerator : "wrong choice generator type for GenericDataAbstractor: " + cg.getClass().getName(); int choice = ((IntChoiceGenerator)cg).getNextChoice(); Valuation valuation = valuations.get(choice); // iterate over the number of operands and set the operand array to the values // we have in the valuation vector int val = 0; int top = frame.getTopPos(); int stackIdx = frame.getLocalVariableCount() + ((isStatic)? 0 : 1); int argSize = paramTypes.length; for (int j = 0; j < argSize; j++) { // j ranges over actual arguments if (!frame.isOperandRef(top - stackIdx)) { frame.setOperand(top - stackIdx++, valuation.getValuation()[val++], false); if (paramTypes[j] == Types.T_LONG || paramTypes[j] == Types.T_DOUBLE) { frame.setOperand(top - stackIdx++, valuation.getValuation()[val++], false); } } } return cg.hasMoreChoices(); } public Class<? extends ChoiceGenerator<?>> getChoiceGeneratorType(){ return IntChoiceFromSet.class; } }