Source code for /engineering/autohit-2003/src/autohit/vm/VM.javaOriginal file VM.java
   1 /**
   2  * AUTOHIT 2003
   3  * Copyright Erich P Gatejen (c) 1989,1997,2003,2004
   4  * 
   5  * This program is free software; you can redistribute it and/or modify 
   6  * it under the terms of the GNU General Public License as published by 
   7  * the Free Software Foundation; either version 2 of the License, or (at
   8  * your option) any later version.
   9  * This program is distributed in the hope that it will be useful, but
  10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12  * more details.
  13  * 
  14  * You should have received a copy of the GNU General Public License along
  15  * with this program; if not, write to the Free Software Foundation, Inc.,
  16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17  *
  18  * Additional license information can be found in the documentation.
  19  * @author Erich P Gatejen
  20  */
  21 package autohit.vm;
  22 
  23 import java.util.Date;
  24 import java.util.Iterator;
  25 
  26 import org.omg.CORBA.Any;
  27 
  28 import autohit.common.AutohitLogInjectorWrapper;
  29 import autohit.common.Constants;
  30 import autohit.common.Utils;
  31 import autohit.common.channels.Injector;
  32 
  33 /**
  34  * The abstract base class for virtual machines.
  35  *
  36  * A derived class will implement the abstract execute()
  37  * method to actually run the VM.  Also, it would normally use the
  38  * pause() and resume() methods to control timing (rather than
  39  * overloading and re-implementing them).
  40  * <p> 
  41  * VM bring up sequence is as follows:  instantiation with default constructor,
  42  * call init(), call construct() of subclass, give VM to a Process, process
  43  * will call attach(), process will call start() to run first instruction,
  44  * start will call prepare() in the base class, start will call execute() for
  45  * the first time.  
  46  * <p>
  47  * The derived class MAY overload the method prepare() if it has 
  48  * anything it wants to do before the FIRST instruction (and only the
  49  * first) is executed.  For instance, it could add environment variables.
  50  * <p>
  51  * The pause() and resume() methods are not designed to be
  52  * called by external threads.  If you plan to wrap the 
  53  * derived vm in a threaded class, you may want to overload or 
  54  * just not use those methods outside of the vm.  Also, these methods
  55  * only manage state and timing; it is up the he execute() method
  56  * in the derived class to actually stop execution.
  57  * <p>
  58  * This is not threadsafe, since one one thread should ever own an instance.
  59  * <p>
  60  * NO VM is valid until the init() method is called!!!  Every VM needs a loader.
  61  * <p>
  62  * USE <b>THESE</b> SERVICES!  Do not make your own ip, for instance!  Methods of
  63  * this class depend upon it.
  64  *
  65  * @author Erich P. Gatejen
  66  * @version 1.0
  67  * <i>Version History</i>
  68  * <code>EPG - Rewrite - 8May03</code>
  69  * 
  70  */
  71 public abstract class VM {
  72 
  73 	/**
  74 	 *  Granulatiry for each tick of the VM's clock.  It
  75 	 *  is used to scale the system time to the vm clock time.
  76 	 *
  77 	 *  Currently, it is set to 1.  Given the current Java
  78 	 *  implementations, this should yield a 1 millisecond
  79 	 *  tick.  That is, each VM clock tick will take one
  80 	 *  millisecond.
  81 	 */
  82 	public static final int TIME_GRAN = 1;
  83 
  84 	/**
  85 	 *  State values for the VM.  Any value over STATE_ACTIVE_THRESHOLD
  86 	 * means the VM is active.
  87 	 */
  88 	public static final int STATE_INVALID = 0;
  89 	public static final int STATE_NO_VM = 1;
  90 	public static final int STATE_BUILDING = 50;
  91 	public static final int STATE_DONE = 99;
  92 	public static final int STATE_ACTIVE_THRESHOLD = 100;
  93 	public static final int STATE_NEW = 200;
  94 	public static final int STATE_RUNNING = 201;
  95 	public static final int STATE_PAUSED = 300;
  96 
  97 	/**
  98 	 *  VM start time.  System start time for this VM.
  99 	 *  This is a raw value that has not been scaled with the
 100 	 *  TIME_GRAN value.  We don't even want the derived class
 101 	 *  to get direct access to this, in case we have to
 102 	 *  compensate for a pause.
 103 	 */
 104 	private long starttime;
 105 
 106 	/**
 107 	 *  Used to compensate the time field after a resume().
 108 	 *  Since the system clock doesn't stop during a pause,
 109 	 *  we will have to change our perceived system time start
 110 	 *  to get the ticks for the VM.
 111 	 */
 112 	private long pauseCompensation;
 113 
 114 	/**
 115 	 *  VM state.
 116 	 */
 117 	protected int state;
 118 
 119 	/**
 120 	 *  Current instruction address/pointer.  A pointer into the insrtuction Vector.
 121 	 */
 122 	protected int ip;
 123 
 124 	/**
 125 	 *  Right value
 126 	 */
 127 	protected Object right;
 128 
 129 	/**
 130 	 *  Left value -- accumulator
 131 	 */
 132 	protected Object left;
 133 
 134 	/**
 135 	 *  VM Buffer
 136 	 */
 137 	protected StringBuffer buf;
 138 
 139 	/**
 140 	 * Controlling environment.  A loader, a process, and our core.
 141 	 */
 142 	public VMLoader loader;
 143 	public VMProcess process;
 144 	public VMCore core;
 145 
 146 	/**
 147 	 *  Name of the root program
 148 	 */
 149 	public String rootProgram;
 150 
 151 	/**
 152 	 *  Session name.
 153 	 */
 154 	public String sname;
 155 
 156 	/**
 157 	 *  Running error total.
 158 	 */
 159 	public int errors;
 160 
 161 	/**
 162 	 *  Running faults total.
 163 	 */
 164 	public int faults;
 165 
 166 	/**
 167 	 *  The root logging mechinism.  Used for controller logging.
 168 	 *
 169 	 *  @see autohit.common.AutohitLogInjectorWrapper
 170 	 */
 171 	public AutohitLogInjectorWrapper myLog;
 172 	
 173 	/**
 174 	 *  Injector for response channel.
 175 	 *
 176 	 *  @see autohit.common.channels.Injector
 177 	 */
 178 	protected Injector rinjector;
 179 
 180 	/**
 181 	 *  Might be used if attached
 182 	 */
 183 	private VMCore parentCore;
 184 
 185 	
 186 	/**
 187 	 * Default Constructor. 
 188 	 * <p>
 189 	 */
 190 	public VM() {
 191 		sname = Constants.VM_GENERIC_NAME;
 192 		state = STATE_BUILDING;
 193 		parentCore = null;
 194 		rootProgram =
 195 			"Software Detected Fault.  VM implementation did not set root program name during construction.";
 196 	}
 197 
 198 	/**
 199 	 * This will set a parent core.  The storage variables from the parent will be copied to 
 200 	 * this VM while attaching.
 201 	 * <p>
 202 	 */
 203 	public void setParentCore(VMCore pc) {	
 204 		parentCore = pc;
 205 	}
 206 	
 207 	/**
 208 	 *  Attach VM to it's owning process.  Typically this is called by 
 209 	 *  the VMProcess.  This will hook up the SystemContext attributes.
 210 	 *  This will effectively initialize the VM.
 211 	 *  @throws Any exceptions it encounters.
 212 	 * TODO fix the id KLUDGE
 213 	 */
 214 	public void attach(VMProcess vmp) throws Exception {
 215 
 216 		// Attach it
 217 		process = vmp;
 218 		loader = process.getSystemContext().getLoader();
 219 		int tpid = vmp.getPID();
 220 
 221 		// KLUDGE to hash the id
 222 		sname = Utils.norfIt(tpid);
 223 		
 224 		// Put loggin on the response channel
 225 		// Debug state is frozen here for the injector
 226 		myLog = new AutohitLogInjectorWrapper();
 227 		myLog.init(sname,rinjector);
 228 		myLog.debugFlag(process.getSystemContext().debuggingState());
 229 
 230 		// Now initialize it.
 231 		core = loader.create();
 232 		
 233 		// Parent?  If so, copy the storage
 234 		if (parentCore!=null) {
 235 			
 236 			try {
 237 				
 238 				// We need to copy this core into the new core to catch all
 239 				// properties
 240 				Iterator varList = parentCore.getStorageNameSet().iterator();
 241 				String ikey;
 242 				Object item;
 243 				while (varList.hasNext()) {
 244 					ikey = (String)varList.next();
 245 					item = parentCore.fetch(ikey);
 246 					core.store(ikey,item);
 247 				}
 248 				
 249 			} catch (Exception cce) {
 250 				throw new VMException(
 251 						"VM["
 252 							+ sname
 253 							+ "] FATAL EXCEPTION during startup.  Exception will be thrown to the Kernel.  exception="
 254 							+ cce.getMessage(),
 255 						VMException.CODE_VM_PREPARE_FAULT,
 256 						cce);
 257 			}
 258 		}
 259 
 260 		// TODO Maybe I shouldn't log to the root logger from VM attach
 261 		(loader.sc.getLogManager()).getRootLogger().info(
 262 			"VM attached to Process.  sname="
 263 				+ sname
 264 				+ ". pid="
 265 				+ tpid
 266 				+ ".  To run program="
 267 				+ rootProgram,
 268 			VMException.CODE_INFORMATIONAL_OK_VERBOSE);
 269 		
 270 		// State to NEW
 271 		state = STATE_NEW;
 272 	}
 273 
 274 	/**
 275 	 *  Start the VM.  It will set state and timing info, then
 276 	 *  call the abstract method execute() to execute the
 277 	 *  code.
 278 	 *  <p>
 279 	 *  Calling this method consecutively will effectively 
 280 	 *  reset the state and timing info.  It is probibly a 
 281 	 *  REAL BAD IDEA to call this from the execute method.
 282 	 *  <p>
 283 	 *  It throws any exceptions that are thrown out of execute().
 284 	 *
 285 	 *  @throws autohit.vm.VMException
 286 	 */
 287 	public void start() throws VMException {
 288 
 289 		state = STATE_RUNNING;
 290 		ip = 0; // Always start at home.  :-)
 291 		Date d = new Date();
 292 		starttime = d.getTime();
 293 		errors = 0;
 294 		faults = 0;
 295 		try {
 296 			prepare();
 297 		} catch (Exception e) {
 298 			throw new VMException(
 299 				"VM["
 300 					+ sname
 301 					+ "] FATAL EXCEPTION during startup.  Exception will be thrown to the Kernel.  exception="
 302 					+ e.getMessage(),
 303 				VMException.CODE_VM_PREPARE_FAULT,
 304 				e);
 305 		}
 306 		execute();
 307 	}
 308 
 309 	/**
 310 	 *  Get VM state.  Reports the state of the vm using the
 311 	 *  STATE_* values.
 312 	 *  <p>
 313 	 *  You may call this from another thread, but it isn't
 314 	 *  very reliable.
 315 	 *
 316 	 *  @return a STATE_* value
 317 	 */
 318 	public int getState() {
 319 		return state;
 320 	}
 321 
 322 	/**
 323 	 *  Pause execution in the VM.  This should NOT be called
 324 	 *  by another thread.
 325 	 *  <p>
 326 	 *  It will only pause if the VM is running.
 327 	 */
 328 	public void pause() {
 329 
 330 		if (state == STATE_RUNNING) {
 331 			state = STATE_PAUSED;
 332 			Date d = new Date();
 333 			pauseCompensation = - (d.getTime() - starttime);
 334 		}
 335 	}
 336 
 337 	/**
 338 	 *  Mark the VM for death.
 339 	 */
 340 	public void die() {
 341 
 342 		state = STATE_DONE;
 343 	}
 344 
 345 	/**
 346 	 *  Resume execution in the VM.  This should NOT be called
 347 	 *  by another thread.
 348 	 *  <p>
 349 	 *  It will only resume if the VM is paused.
 350 	 */
 351 	public void resume() {
 352 
 353 		if (state == STATE_PAUSED) {
 354 			state = STATE_RUNNING;
 355 			Date d = new Date();
 356 			starttime = d.getTime() - pauseCompensation;
 357 			pauseCompensation = d.getTime() - starttime;
 358 		}
 359 	}
 360 
 361 	/**
 362 	 *  Number of ticks the VM has been running.  It will
 363 	 *  be scaled according to the TIME_GRAN field.
 364 	 *  <p>
 365 	 *  Note that it returns an int rather than a long like 
 366 	 *  system time usually is.  This means that the VM timing.
 367 	 *  This technically could cause some overflow problems, but
 368 	 *  I doubt a VM would ever run that long.
 369 	 *
 370 	 *  @return number of ticks the VM has run.
 371 	 */
 372 	public int ticks() {
 373 
 374 		Date d = new Date();
 375 
 376 		long sticks = d.getTime() - starttime;
 377 
 378 		return (int) (sticks / TIME_GRAN);
 379 
 380 		// If the compiler has half of a brain, this division
 381 		// should be optimised out given the current
 382 		// granularity.
 383 	}
 384 
 385 	/**
 386 	 *  Absract method for VM execution.  The derived class
 387 	 *  must implement the actual execution.  This method will
 388 	 *  be automatically called by start().  Therefore, you
 389 	 *  probibly should not call start() from within this 
 390 	 *  method.
 391 	 *  <p>
 392 	 *  The implimentation of this method should only execute
 393 	 *  ONE INSTRUCTION.  Successive calls would then execute
 394 	 *  the entire program.  If you do not impliment it this way,
 395 	 *  you are likely to ghost the vm's.
 396 	 *  <p>
 397 	 *  NOTE!  An implementing method MUST throw a 
 398 	 *  VMException(VMException.DONE) when it reaches the
 399 	 *  end of execution.
 400 	 *  <p>
 401 	 *  If the derived-class VM encounters an instruction that
 402 	 *  it does now support, it should throw a 
 403 	 *  VMException.INVALID_INSTRUCTION.
 404 	 *
 405 	 *  @see autohit.vm.VMException
 406 	 */
 407 	public abstract void execute() throws VMException;
 408 
 409 	/**
 410 	 * Complete construction.  This will be called when the VM is
 411 	 * initialized.
 412 	 */
 413 	public abstract void construct() throws VMException;
 414 
 415 	/**
 416 	 * Destroy.  This will be called when the VM is
 417 	 * finalizing.
 418 	 */
 419 	public abstract void destruct() throws VMException;
 420 
 421 	/**
 422 	 * Complete initialization.  This must be called after the VM
 423 	 * is constructed, but before VM is attached to a process.
 424 	 * @param responseChannel the response channel
 425 	 * @param target target program in universe namespace
 426 	 */
 427 	final public void init(Injector responseChannel, String target) throws VMException {
 428 
 429 		rootProgram = target;
 430 		rinjector = responseChannel;
 431 		this.construct();
 432 	}
 433 
 434 	/**
 435 	 *  Prepare for execution of the first instruction.  The derived
 436 	 *  class may overload this if it has any stuff it wants to do
 437 	 *  before execute() is called the first time.
 438 	 *
 439 	 *  @throws Any exceptions it encounters.
 440 	 */
 441 	public void prepare() throws Exception {
 442 
 443 		// The base class doesn't wanna do anything...   
 444 	}
 445 
 446 	/**
 447 	 * Complete initialization.  This must be called after the VM
 448 	 * is constructed, but before VM is attached to a process.
 449 	 * @param responseChannel the response channel
 450 	 * @param target target program in universe namespace
 451 	 */
 452 	
 453 	/**
 454 	 * finalizer
 455 	 * It will kill the logs last.
 456 	 */
 457 	protected void finalize() throws Throwable {
 458 		if (finalizedvm == true) return;
 459 		super.finalize();
 460 		this.destruct();
 461 		
 462 		// kill the drain.  This is a horrible hack.
 463 		process.getSystemContext().getLogManager().discardDrainWriter(sname);
 464 		finalizedvm = true;
 465 	}
 466 	private boolean finalizedvm = false;
 467 
 468 }