/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.scheduler; import java.lang.reflect.Method; import java.util.ArrayList; import org.jikesrvm.VM; import org.jikesrvm.objectmodel.VM_ObjectModel; import org.jikesrvm.runtime.VM_Magic; import org.jikesrvm.scheduler.greenthreads.VM_FileSystem; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.LocalAddress; /** * An interactive debugger that runs inside the virtual machine. * This thread is normally dormant and only scheduled for execution * by VM_Thread.threadSwitch() following receipt of a debug request * signal (SIGQUIT). */ public class VM_DebuggerThread extends VM_Scheduler.ThreadModel { public VM_DebuggerThread() { super("Debugger"); makeDaemon(true); } /** * Is this the debugger thread? * @return true */ @Uninterruptible @Override public boolean isDebuggerThread() { return true; } @Override public void run() { while (true) { try { VM.sysWrite("debug> "); String[] tokens = readTokens(); eval(tokens); } catch (Exception e) { VM.sysWrite("oops: " + e + "\n"); e.printStackTrace(); } } } private static final char EOF = 0xFFFF; private static String[] previousTokens; // Evaluate an expression. // private static void eval(String[] tokens) throws Exception { char command = tokens == null ? EOF : tokens.length == 0 ? ' ' : tokens[0].charAt(0); VM.sysWriteln("COMMAND IS '" + command + "'"); switch (command) { case' ': // repeat previous command once if (previousTokens != null) { eval(previousTokens); } return; case'*': // repeat previous command once per second, until SIGQUIT is received if (previousTokens != null) { for (VM_Scheduler.debugRequested = false; !VM_Scheduler.debugRequested;) { VM.sysWrite("\033[H\033[2J"); eval(previousTokens); VM_Thread.sleep(1000,0); } } return; } previousTokens = tokens; VM.sysWriteln("COMMAND IS '" + command + "'"); switch (command) { case't': // display thread(s) if (tokens.length == 1) { // for (VM_Scheduler.ThreadModel thread : VM_Scheduler.threads) { if (thread == null) continue; VM.sysWrite(rightJustify(thread.getIndex() + " ", 4) + leftJustify(thread.toString(), 40) + thread.getThreadState() + "\n"); } } else if (tokens.length == 2) { // display specified thread int threadIndex = Integer.valueOf(tokens[1]); // !!TODO: danger here - how do we know if thread's context registers are // currently valid (ie. thread is not currently running and // won't start running while we're looking at it) ? VM_Thread thread = VM_Scheduler.threads[threadIndex]; VM.sysWriteln(thread.getIndex() + " " + thread + " " + thread.getThreadState()); LocalAddress fp = (thread == VM_Scheduler.getCurrentThread()) ? VM_Magic.getFramePointer() : thread.contextRegisters.getInnermostFramePointer(); VM_Processor.getCurrentProcessor().disableThreadSwitching("disabled for debugger to dump stacks"); VM_Scheduler.dumpStack(fp); VM_Processor.getCurrentProcessor().enableThreadSwitching(); } else { VM.sysWrite("please specify a thread id\n"); } return; case'p': // print object if (tokens.length == 2) { Address addr = (VM.BuildFor64Addr) ? Address.fromLong(Long.parseLong(tokens[1], 16)) : Address.fromIntZeroExtend(Integer.parseInt(tokens[1],16)); VM.sysWrite("Object at addr 0x"); VM.sysWriteHex(addr); VM.sysWrite(": "); VM_ObjectModel.describeObject(addr.toObjectReference()); VM.sysWriteln(); } else { VM.sysWriteln("Please specify an address\n"); } return; case'd': // dump virtual machine state VM_Scheduler.dumpVirtualMachine(); return; case'c': // continue execution of virtual machine (make debugger dormant until next debug request) VM_Scheduler.suspendDebuggerThread(); return; case'q': // terminate execution of virtual machine VM.sysWrite("terminating execution\n"); VM.sysExit(VM.EXIT_STATUS_MISC_TROUBLE); return; case EOF: // got a signal without a stdin; dump VM and continue VM_Scheduler.dumpVirtualMachine(); VM_Scheduler.suspendDebuggerThread(); return; default: if (tokens.length == 1) { // offer help VM.sysWrite("Try one of:\n"); VM.sysWrite(" t - display all threads\n"); VM.sysWrite(" t <threadIndex> - display specified thread\n"); VM.sysWrite(" p <hex addr> - print (describe) object at given address\n"); VM.sysWrite(" d - dump virtual machine state\n"); VM.sysWrite(" c - continue execution\n"); VM.sysWrite(" q - terminate execution\n"); VM.sysWrite(" <class>.<method> - call a method\n"); VM.sysWrite("Or:\n"); VM.sysWrite(" <enter> - repeat previous command once\n"); VM.sysWrite(" * - repeat previous command once per second until SIGQUIT is received\n"); return; } // call a method // For the moment we only evaluate calls to static methods that take no parameters // !!TODO: more elaborate parsing to accept arbitrary number of classname-qualifiers, // parameter lists, static variables, arrays, etc. if (tokens.length != 3 || !tokens[1].equals(".")) { VM.sysWrite("please specify <class>.<method>\n"); } else { Class<?> cls = Class.forName(tokens[0]); Class<?>[] signature = new Class[0]; Method method = cls.getMethod(tokens[2], signature); Object[] args = new Object[0]; method.invoke(null, args); } } } private static final int STDIN = 0; // Read and tokenize one line of input. // Taken: nothing // Returned: null -> no input (end of file encountered) // String[0] -> no input (end of line encountered) // String[N] -> N tokens of input // private static String[] readTokens() { StringBuilder line = new StringBuilder(); int bb = VM_FileSystem.readByte(STDIN); if (bb < 0) { return null; } for (; bb >= 0 && bb != '\n'; bb = VM_FileSystem.readByte(STDIN)) { line.append((char) bb); } ArrayList<String> tokens = new ArrayList<String>(); for (int i = 0, n = line.length(); i < n; ++i) { char ch = line.charAt(i); if (Character.isLetter(ch) || Character.isDigit(ch)) { StringBuilder alphaNumericToken = new StringBuilder(); while (Character.isLetter(ch) || Character.isDigit(ch)) { alphaNumericToken.append(ch); if (++i == n) break; ch = line.charAt(i); } --i; tokens.add(alphaNumericToken.toString()); continue; } if (ch != ' ' && ch != '\r' && ch != '\t') { tokens.add(Character.toString(ch)); } } String[] result = new String[tokens.size()]; tokens.toArray(result); return result; } private static String leftJustify(String s, int width) { while (s.length() < width) { s = s + " "; } return s; } private static String rightJustify(String s, int width) { while (s.length() < width) { s = " " + s; } return s; } }