package lejos.nxt.debug;
import lejos.nxt.*;
/**
* Simple debug monitor that can be run alongside and nxj program. This class
* catches unhandled excpetions and user interrupts (accept + escape key), it
* displays information about the event (stack trace etc.). The user is then
* abble to either perform a soft reset (Escape), a hard reset (Escape + Accept),
* or continue running the program (any other key). All output is directed via
* System.err.
* @author andy
*/
public class DebugMonitor
{
/**
* Display information about the uncaught excpetion on System.err
* @param info the current VM event
*/
static void displayException(DebugInterface info)
{
VM vm = VM.getVM();
int base = vm.getImage().getImageBase();
System.err.println("Java Exception");
System.err.println("Class: " + vm.getVMClass(info.exception).getClassNo());
String msg = info.exception.getMessage();
if (msg != null && msg.length() > 0)
System.err.println("Msg: " + msg);
System.err.println(" at: " + info.method + "(" + info.pc + ")");
int cnt = 0;
VM.VMStackFrames stack = vm.getVMThread(info.thread).getStackFrames(info.frame-1);
for(VM.VMStackFrame sf : stack)
{
System.err.println(" at: " + sf.getVMMethod().getMethodNumber() + "(" + (sf.pc-base) + ")");
if (cnt++ > 5) break;
}
}
static String[] states = {"N", "D", "I", "R", "E", "W", "S"};
/**
* Dump information about all of the active threads to System.err. The
* threads are dumped in reverse order (low priority first), so that if
* there are more then eight threads the most important 8 will still be
* on the LCD display!
* @param info
*/
static void displayThreads(DebugInterface info)
{
VM vm = VM.getVM();
VM.VMThreads threads = vm.getVMThreads();
for(VM.VMThread thread : threads)
{
String out = "";
out += thread.threadId;
out += (thread.getJavaThread() == info.thread ? "*" : states[thread.state & 0x7f]);
int cnt = 0;
VM.VMStackFrames stack = thread.getStackFrames();
for(VM.VMStackFrame frame : stack)
{
out += " " + frame.getVMMethod().getMethodNumber();
if (++cnt >= 3) break;
}
System.err.println(out);
}
}
public static void main(String[] args) throws Exception
{
// Setup the monitoring thread.
DebugInterface monitor = DebugInterface.get();
DebugInterface.eventOptions(DebugInterface.DBG_EXCEPTION, DebugInterface.DBG_EVENT_ENABLE);
DebugInterface.eventOptions(DebugInterface.DBG_USER_INTERRUPT, DebugInterface.DBG_EVENT_ENABLE);
// Start the real program in a new thread.
Thread prog = new Thread()
{
public void run()
{
VM.executeProgram(1);
// This point will never be reached
}
};
// Make sure we keep running when we start the program
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
// Enable stricter run time type checking
VM.setVMOptions(VM.getVMOptions() | VM.VM_TYPECHECKS);
prog.start();
while (true)
{
// Allow exit if the program finishes normally.
Thread.currentThread().setDaemon(true);
// Wait for a debug event
int event = monitor.waitEvent(0);
Thread.currentThread().setDaemon(false);
// Display the information
LCD.clear();
switch (event)
{
case DebugInterface.DBG_EXCEPTION:
displayException(monitor);
break;
case DebugInterface.DBG_USER_INTERRUPT:
displayThreads(monitor);
break;
}
LCD.refresh();
Sound.playTone(73, 150);
Sound.pause(300);
Sound.playTone(62, 500);
// Wait for any buttons to be released
while (Button.readButtons() != 0)
Thread.yield();
// Enable user interrupts again
DebugInterface.eventOptions(DebugInterface.DBG_USER_INTERRUPT, DebugInterface.DBG_EVENT_ENABLE);
// and wait to see what the user wants to do
int pressed = Button.waitForPress();
// If escape do soft-reboot
if ((Button.ESCAPE.getId() & pressed) != 0)
System.exit(1);
// Otherwise try and continue gulp!
LCD.clear();
// Clear the event
monitor.clear();
VM.resumeThread(null);
}
}
}