package nachos.ag;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import nachos.machine.Lib;
import nachos.machine.Machine;
import nachos.machine.Processor;
import nachos.machine.StandardConsole;
import nachos.security.Privilege;
import nachos.threads.Semaphore;
/**
* @author Kang Zhang
*
*/
public class CoffGrader extends BasicTestGrader {
private static final int ActionDone = 0;
private static final int ActionFail = 1;
private static final int ActionP = 2;
private static final int ActionV = 3;
private static final int ActionRead = 4;
private static final int ActionStore = 5;
private static final int ActionRandom = 6;
private static final int ActionReadParameter = 7;
private static final int NumSemaphore = 20;
private static final int NumStoreValues = 20;
private static final int Num_Parameter = 4;
private static final String ParameterTag = "coffPar";
private static final String InputText = "input";
private static final String OutputText = "output";
private static final String QuietMode = "quiet";
private static final String MetricMode = "metric";
private static final String TestDirectory = "testRoot";
protected Semaphore[] semaphores;
protected int[] storeValues = new int[NumStoreValues];
protected ArrayList<Integer> params = new ArrayList<Integer>();
protected EmbededConsole embededConsole;
protected String testDirectory = null;
protected File testRoot = null;
protected boolean quietMode = false;
protected boolean metricMode = false;
@Override
protected void init() {
super.init();
Lib.debug('g', "Coffgrader initialize");
Arrays.fill(storeValues, 0);
embededConsole = new EmbededConsole(super.privilege);
super.privilege.machine.setConsole(embededConsole);
for( int i = 0 ; i < Num_Parameter ; i++){
if( hasArgument(ParameterTag+i) )
params.add(getIntegerArgument(ParameterTag + i));
else
break;
}
if( hasArgument(TestDirectory) )
testDirectory = getStringArgument(TestDirectory);
super.privilege.doPrivileged(new Runnable(){
public void run() {
if(testDirectory == null)
testRoot = new File(new File("").getAbsoluteFile().getParentFile(),"test");
else
testRoot = new File(testDirectory);
}
});
if(hasArgument(InputText))
embededConsole.in.append(loadFromFile(getStringArgument(InputText)));
if(hasArgument(OutputText))
embededConsole.out.append(loadFromFile(getStringArgument(OutputText)));
if(hasArgument(QuietMode))
quietMode = getBooleanArgument(QuietMode);
if(hasArgument(MetricMode))
metricMode = getBooleanArgument(MetricMode);
}
private FileReader fileReader = null;
/* load a file's content from your disk */
private String loadFromFile(final String fileName) {
super.privilege.doPrivileged(new Runnable(){
public void run() {
try {
fileReader = new FileReader(new File(testRoot,fileName));
} catch (FileNotFoundException e) {
fileReader = null;
}
}
});
Lib.assertTrue(fileReader != null,"Load file:"+fileName+" failed");
StringBuffer sb = new StringBuffer();
int b = -1;
try {
while((b = fileReader.read()) != -1){
sb.append((char)b);
}
fileReader.close();
} catch (IOException e) {
Lib.assertNotReached("File read/close error");
}
return sb.toString();
}
/* Hook on exception handler */
@Override
public boolean exceptionHandler(Privilege privilege) {
super.exceptionHandler(privilege);
Processor processor = Machine.processor();
int cause = processor.readRegister(Processor.regCause);
if (cause != Processor.exceptionSyscall
|| processor.readRegister(Processor.regV0) != -1)
return true;
int result = handleTestSystemCall(processor
.readRegister(Processor.regA0), processor
.readRegister(Processor.regA1), processor
.readRegister(Processor.regA2), processor
.readRegister(Processor.regA3));
processor.writeRegister(Processor.regV0, result);
processor.advancePC();
return false;
}
/* Handle the test framework system call*/
protected int handleTestSystemCall(int type, int a0, int a1, int a2) {
switch (type) {
case ActionDone:
Lib.assertTrue(embededConsole.outputMatched,"Test failed, mismatched the output");
done();
Lib.assertNotReached(" Test has been ended");
break;
case ActionFail:
System.out.println("Test failed");
Machine.halt();
break;
case ActionP:
checkSemIndex(a0);
semaphores[a0].P();
break;
case ActionV:
checkSemIndex(a0);
semaphores[a0].V();
break;
case ActionRead:
checkStoreIndex(a0);
return storeValues[a0];
case ActionStore:
checkStoreIndex(a0);
storeValues[a0] = a1;
break;
case ActionRandom:
Lib.assertTrue(a0 > 0, "Invalid random range");
return Lib.random(a0);
case ActionReadParameter:
Lib.assertTrue(a0 >= 0 && a0 < params.size(),
"Invalid parameter index"+a0+" .Maybe exceed "+params.size()+" ?");
return params.get(a0);
default:
Lib.assertNotReached("Unknow system call. ("+type+")");
break;
}
return 0;
}
protected void checkSemIndex(int a0) {
Lib.assertTrue(a0 >= 0 && a0 < NumSemaphore,
"Invalid semaphone index:(" + a0 + ")");
}
protected void checkStoreIndex(int a0) {
Lib.assertTrue(a0 >= 0 && a0 < NumStoreValues, "Invalid store index:("
+ a0 + ")");
}
@Override
protected void run() {
semaphores = new Semaphore[NumSemaphore];
for (int i = 0; i < NumSemaphore; i++)
semaphores[i] = new Semaphore(0);
super.run();
}
/* EmbededConsole, a modified console used to support standard console */
protected class EmbededConsole extends StandardConsole {
public StringBuffer in = new StringBuffer();
public StringBuffer out = new StringBuffer();
public boolean outputMatched = true;
private int inOffset = 0;
private int outOffset = 0;
public EmbededConsole(Privilege privilege) {
super(privilege);
}
protected int in() {
if (inOffset >= in.length())
return -1;
else
return in.charAt(inOffset++);
}
protected void out(int value) {
if( !quietMode )
super.out(value);
if(outOffset >= out.length() || out.charAt(outOffset++) != value)
outputMatched = false;
}
}
}