/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.mmtk.harness.options;
import java.util.TreeSet;
import org.mmtk.harness.Main;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.Word;
import org.vmmagic.unboxed.harness.MemoryConstants;
import org.vmmagic.unboxed.harness.WordComparator;
import org.vmutil.options.AddressOption;
import org.vmutil.options.BooleanOption;
import org.vmutil.options.EnumOption;
import org.vmutil.options.FloatOption;
import org.vmutil.options.IntOption;
import org.vmutil.options.MicrosecondsOption;
import org.vmutil.options.Option;
import org.vmutil.options.PagesOption;
import org.vmutil.options.StringOption;
/**
* Class to handle command-line arguments and options for GC.
*/
public final class HarnessOptionSet extends org.vmutil.options.OptionSet {
/*
* The following option types are used only by the MMTk Harness,
* since they are hard to implement without allocating.
*/
/** A Set<Enum> valued option */
public static final int ENUM_SET_OPTION = 1001;
/** A multi-valued integer valued option */
public static final int INT_SET_OPTION = 1002;
/** A multi-valued integer valued option */
public static final int WORD_SET_OPTION = 1003;
/**
* Take a string (most likely a command-line argument) and try to proccess it
* as an option command. Return true if the string was understood, false
* otherwise.
*
* @param arg a String to try to process as an option command
* @return true if successful, false otherwise
*/
public boolean process(String arg) {
// First handle the "option commands"
if (arg.equals("help")) {
printHelp();
return true;
}
if (arg.equals("printOptions")) {
printOptions();
return true;
}
if (arg.length() == 0) {
printHelp();
return true;
}
// Required format of arg is 'name=value'
// Split into 'name' and 'value' strings
int split = arg.indexOf('=');
if (split == -1) {
System.err.println(" Illegal option specification!\n \""+arg+
"\" must be specified as a name-value pair in the form of option=value");
return false;
}
String name = arg.substring(0,split);
String value = arg.substring(split+1);
Option o = getOption(name);
if (o == null) return false;
switch (o.getType()) {
case Option.BOOLEAN_OPTION:
if (value.equals("true")) {
((BooleanOption)o).setValue(true);
return true;
} else if (value.equals("false")) {
((BooleanOption)o).setValue(false);
return true;
}
return false;
case Option.INT_OPTION:
try {
int ival = Integer.parseInt(value);
((IntOption)o).setValue(ival);
return true;
} catch (NumberFormatException nfe) {}
return false;
case Option.ADDRESS_OPTION:
try {
int ival = Integer.parseInt(value,16);
((AddressOption)o).setValue(ival);
return true;
} catch (NumberFormatException nfe) {}
return false;
case Option.FLOAT_OPTION:
try {
float fval = Float.parseFloat(value);
((FloatOption)o).setValue(fval);
return true;
} catch (NumberFormatException nfe) {}
return false;
case Option.STRING_OPTION:
((StringOption)o).setValue(value);
return true;
case Option.ENUM_OPTION:
((EnumOption)o).setValue(value);
return true;
case Option.PAGES_OPTION:
try {
char last = value.charAt(value.length() - 1);
int factor = 1;
switch (last) {
case 'g': case 'G': factor *= 1024;
case 'm': case 'M': factor *= 1024;
case 'k': case 'K': factor *= 1024;
value = value.substring(0, value.length() - 1);
}
int ival = Integer.parseInt(value);
((PagesOption)o).setBytes(Extent.fromIntZeroExtend(ival * factor));
return true;
} catch (NumberFormatException nfe) {
} catch (IndexOutOfBoundsException nfe) {}
return false;
case Option.MICROSECONDS_OPTION:
try {
int ival = Integer.parseInt(value);
((MicrosecondsOption)o).setMicroseconds(ival);
return true;
} catch (NumberFormatException nfe) {}
return false;
case ENUM_SET_OPTION:
((EnumSetOption)o).setValue(value);
return true;
case INT_SET_OPTION:
try {
((IntSetOption)o).setValue(parseIntSet(value));
} catch (NumberFormatException nfe) {
return false;
}
return true;
case WORD_SET_OPTION:
((WordSetOption)o).setValue(parseWordSet(value));
return true;
}
// None of the above tests matched, so this wasn't an option
return false;
}
private int[] parseIntSet(String str) {
TreeSet<Integer> values = new TreeSet<Integer>();
for (String element : str.split(",")) {
values.add(Integer.valueOf(element));
}
int[] result = new int[values.size()];
for (int i=0; i < result.length; i++) {
result[i] = values.pollFirst();
}
return result;
}
private Word[] parseWordSet(String str) {
TreeSet<Word> values = new TreeSet<Word>(new WordComparator());
for (String element : str.split(",")) {
Long value;
if (element.startsWith("0x")) {
value = Long.valueOf(element.substring(2),16);
} else {
value = Long.valueOf(element);
}
values.add(Word.fromLong(value));
}
Word[] result = new Word[values.size()];
for (int i=0; i < result.length; i++) {
result[i] = values.pollFirst();
}
return result;
}
/**
* Print a short description of every option
*/
public void printHelp() {
System.err.println("Commands");
System.err.println("help\t\t\tPrint brief description of arguments");
System.err.println("printOptions\t\tPrint the current values of options");
System.err.println();
//Begin generated help messages
System.err.print("Boolean Options (");
System.err.print("<option>=true or ");
System.err.println("<option>=false)");
System.err.println("Option Description");
Option o = getFirst();
while (o != null) {
if (o.getType() == Option.BOOLEAN_OPTION) {
String key = o.getKey();
System.err.print(key);
for (int c = key.length(); c<39;c++) {
System.err.print(" ");
}
System.err.println(o.getDescription());
}
o = o.getNext();
}
System.err.print("\nValue Options (");System.err.println("<option>=<value>)");
System.err.println("Option Type Description");
o = getFirst();
while (o != null) {
if (o.getType() != Option.BOOLEAN_OPTION &&
o.getType() != Option.ENUM_OPTION) {
String key = o.getKey();
System.err.print(key);
for (int c = key.length(); c<31;c++) {
System.err.print(" ");
}
switch (o.getType()) {
case Option.INT_OPTION: System.err.print("int "); break;
case Option.ADDRESS_OPTION: System.err.print("address "); break;
case Option.FLOAT_OPTION: System.err.print("float "); break;
case Option.MICROSECONDS_OPTION: System.err.print("usec "); break;
case Option.PAGES_OPTION: System.err.print("bytes "); break;
case Option.STRING_OPTION: System.err.print("string "); break;
}
System.err.println(o.getDescription());
}
o = o.getNext();
}
System.err.println("\nSelection Options (set option to one of an enumeration of possible values)");
o = getFirst();
while (o != null) {
if (o.getType() == Option.ENUM_OPTION) {
String key = o.getKey();
System.err.print(key);
for (int c = key.length(); c<31;c++) {
System.err.print(" ");
}
System.err.println(o.getDescription());
System.err.print(" { ");
boolean first = true;
for (String val : ((EnumOption)o).getValues()) {
System.err.print(first ? "" : ", ");
System.err.print(val);
first = false;
}
System.err.println(" }");
}
o = o.getNext();
}
Main.exitWithFailure();
}
/**
* Print out the option values
*/
public void printOptions() {
System.err.println("Current value of GC options");
Option o = getFirst();
while (o != null) {
if (o.getType() == Option.BOOLEAN_OPTION) {
String key = o.getKey();
System.err.print("\t");
System.err.print(key);
for (int c = key.length(); c<31;c++) {
System.err.print(" ");
}
System.err.print(" = ");
logValue(o, false);
System.err.println();
}
o = o.getNext();
}
o = getFirst();
while (o != null) {
if (o.getType() != Option.BOOLEAN_OPTION &&
o.getType() != Option.ENUM_OPTION) {
String key = o.getKey();
System.err.print("\t");
System.err.print(key);
for (int c = key.length(); c<31;c++) {
System.err.print(" ");
}
System.err.print(" = ");
logValue(o, false);
System.err.println();
}
o = o.getNext();
}
o = getFirst();
while (o != null) {
if (o.getType() == Option.ENUM_OPTION) {
String key = o.getKey();
System.err.print("\t");
System.err.print(key);
for (int c = key.length(); c<31;c++) {
System.err.print(" ");
}
System.err.print(" = ");
logValue(o, false);
System.err.println();
}
o = o.getNext();
}
}
/**
* Format and log an option value.
*
* @param o The option.
* @param forXml Is this part of xml output?
*/
@Override
protected void logValue(Option o, boolean forXml) {
switch (o.getType()) {
case Option.BOOLEAN_OPTION:
System.err.print(((BooleanOption) o).getValue() ? "true" : "false");
break;
case Option.INT_OPTION:
System.err.print(((IntOption) o).getValue());
break;
case Option.ADDRESS_OPTION:
System.err.print(((AddressOption) o).getValue());
break;
case Option.FLOAT_OPTION:
System.err.print(((FloatOption) o).getValue());
break;
case Option.MICROSECONDS_OPTION:
System.err.print(((MicrosecondsOption) o).getMicroseconds());
System.err.print(" usec");
break;
case Option.PAGES_OPTION:
System.err.print(((PagesOption) o).getBytes());
System.err.print(" bytes");
break;
case Option.STRING_OPTION:
System.err.print(((StringOption) o).getValue());
break;
case Option.ENUM_OPTION:
System.err.print(((EnumOption) o).getValueString());
break;
}
}
/**
* Log a string.
*/
@Override
protected void logString(String s) {
System.err.print(s);
}
/**
* Print a new line.
*/
@Override
protected void logNewLine() {
System.err.println();
}
/**
* Determine the VM specific key for a given option name. Option names are
* space delimited with capitalised words (e.g. "GC Verbosity Level").
*
* @param name The option name.
* @return The VM specific key.
*/
@Override
protected String computeKey(String name) {
int space = name.indexOf(' ');
if (space < 0) return name.toLowerCase();
String word = name.substring(0, space);
String key = word.toLowerCase();
do {
int old = space+1;
space = name.indexOf(' ', old);
if (space < 0) {
key += name.substring(old);
return key;
}
key += name.substring(old, space);
} while (true);
}
/**
* A non-fatal error occurred during the setting of an option. This method
* calls into the VM and shall not cause the system to stop.
*
* @param o The responsible option.
* @param message The message associated with the warning.
*/
@Override
protected void warn(Option o, String message) {
System.err.println("WARNING: Option '" + o.getKey() + "' : " + message);
}
/**
* A fatal error occurred during the setting of an option. This method
* calls into the VM and is required to cause the system to stop.
*
* @param o The responsible option.
* @param message The error message associated with the failure.
*/
@Override
protected void fail(Option o, String message) {
throw new RuntimeException("Option '" + o.getKey() + "' : " + message);
}
/**
* Convert bytes into pages, rounding up if necessary.
*
* @param bytes The number of bytes.
* @return The corresponding number of pages.
*/
@Override
@Uninterruptible
protected int bytesToPages(Extent bytes) {
return bytes.plus(MemoryConstants.BYTES_IN_PAGE-1).toWord().rshl(MemoryConstants.LOG_BYTES_IN_PAGE).toInt();
}
/**
* Convert from pages into bytes.
* @param pages the number of pages.
* @return The corresponding number of bytes.
*/
@Override
@Uninterruptible
protected Extent pagesToBytes(int pages) {
return Word.fromIntZeroExtend(pages).lsh(MemoryConstants.LOG_BYTES_IN_PAGE).toExtent();
}
}