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