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; } } }