Milán Major

netscape-revival

1 branch
Code

sun-java/classsrc/sun/tools/debug/Agent.java

/*
 * @(#)Agent.java	1.62 96/02/16
 *
 * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

package sun.tools.debug;

import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.SecurityManager;
import sun.tools.java.Package;
import sun.tools.java.ClassPath;
import sun.tools.java.ClassFile;
import sun.tools.java.Identifier;

class MainThread extends Thread {
    Agent agent;
    Class clazz;
    String argv[];
    
    public MainThread(Agent agent, ThreadGroup group,
		      Class clazz, String argv[]) throws IOException {
	super(group, "main");
	setPriority(NORM_PRIORITY);
	setDaemon(false);

	this.agent = agent;
	this.clazz = clazz;
	this.argv = argv;

	start();
    }

    public void run() {
	try {
	    agent.runMain(clazz, argv);
	} catch (IllegalAccessError e) {
	    agent.error("\"public static void main(String argv[])\" " +
			"method not found in " + clazz.getName());
	}
    }
}

class Agent implements Runnable, AgentConstants {
//class Agent extends Thread implements AgentConstants {

    /* The agent socket */
    ServerSocket socket;

    /* The output stream back to the remote debugger */
    DataOutputStream asyncOutputStream;

    /* The hashtable of objects */
    Hashtable objects;

    /* The breakpoint handler process */
    private BreakpointHandler bkptHandler;

    /* The EmptyApp thread, if any. */
    static Thread emptyAppThread = null;

    private DataOutputStream out = null;
    private ResponseStream outBuffer = null;
    private ClassPath sourcePath = null;
    static boolean verbose = false;

    static String toHex(int n) {
	char str[] = new char[10];
	for (int i = 0 ; i < 10 ; i++) {
	    str[i] = '0';
	}
	str[1] = 'x';

	for (int i = 9 ; n != 0 ; n >>>= 4, i--) {
	    int d = n & 0xF;
	    str[i] = (char)((d < 10) ? ('0' + d) : ('a' + d - 10));
	}
	return new String(str);
    }

    /* Send all currently known classes to the RemoteAgent. */
    private void dumpClasses() throws IOException {
	message("dumpClasses()");

	/* Create a vector of classes. */
	Enumeration enum = objects.elements();
	Vector v = new Vector();
	int nClasses = 0;
	while (enum.hasMoreElements()) {
	    try {
		Object o = enum.nextElement();
		if (o != null && o instanceof Class) {
		    v.addElement(o);
		    nClasses++;
		}
	    } catch (Exception e) {
		error("dumpClasses() failed: " + e.toString());
	    }
	}

	/* Pass the vector's contents to the RemoteAgent. */
	out.writeInt(nClasses);
	enum = v.elements();
	while (enum.hasMoreElements()) {
	    writeObject(enum.nextElement());
	}
    }

    private String cookieToString(int cookie) {
        int radix = PSWDCHARS.length();
	int digitCount = 0;
	boolean minval = false;

	StringBuffer buf = new StringBuffer(32);
	while (cookie > 0) {
	    if (cookie < radix) {
		buf.append(PSWDCHARS.charAt(cookie));
		cookie = 0;
	    } else {
		int j = cookie % radix;
		cookie /= radix;
		buf.append(PSWDCHARS.charAt(j));
	    }
	    digitCount++;
	}
	if (digitCount > 0) {
	    int j = buf.length();
	    char tmp[] = new char[j];
	    int i = 0;
	    while (j-- > 0) {
		tmp[j] = buf.charAt(i++);
	    }
	    return String.valueOf(tmp);
	} else
	    return "0";
    }

    private String makePassword(int port) {
	if (port > 0xffff) {
	    System.err.println("Invalid port number (" + port + ")???");
	    System.exit(1);
	}
	int mask = (int)(Math.round(Math.random() * 0x800) % 0x800);
        int cookie = 0;
        int slice = 3;
        for (int i = 0; i < 8; i++) {
            int portbits = ((port & slice) << (i + 1));
            int maskbits = ((mask & (1 << i)) << (i * 2));
            cookie |= portbits | maskbits;
            slice <<= 2;
        }
        cookie |= (mask & 0x700) << 16;
        return cookieToString(cookie);
    }

    /* Create an Agent. */
    Agent(int port) {
	for (int i = 0; ; i++) { 
	    try {
		socket = new ServerSocket(port, 10);
		System.out.println("Agent password=" +
				   makePassword(socket.getLocalPort()));
		return;
	    } catch(Exception e) {
		message("[Waiting to create port]\n");
		try {
		    Thread.sleep(5000);
		} catch (InterruptedException ex) {
		    // re-interrupt and drop through.
		    Thread.currentThread().interrupt();
	 	}
	    }
	    if (i >= 10) {
		error("**Failed to create port\n");
		return;
	    }
	}
    }

    /* Report the empty app thread, so it can be resumed after a real app is
     * run. */
    static void setEmptyAppThread(Thread t) {
	emptyAppThread = t;
    }

    /* Main agent event loop */
    public void run() {
	PrintStream stdout = null;
	Socket cmdSocket = null;
	Socket asyncSocket = null;
	
	SecurityManager.setScopePermission();
	String sourcePathString = System.getProperty("java.class.path");
	SecurityManager.resetScopePermission();
	if (sourcePathString == null) {
	    sourcePathString = ".";
	}
	sourcePath = new ClassPath(sourcePathString);

	try {
	    SecurityManager.setScopePermission();
	    // Really should be MAX_PRIORITY + 1
	    Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

	    objects = new Hashtable();
	    bkptHandler = new BreakpointHandler(this);
	    bkptHandler.setPriority(Thread.MAX_PRIORITY - 1);
	    SecurityManager.resetScopePermission();
	    bkptHandler.start();
            initAgent();

	    while (true) {
		cmdSocket = socket.accept();
		message("cmd socket: " + cmdSocket.toString());
		DataInputStream in = new DataInputStream(
		    new BufferedInputStream(cmdSocket.getInputStream()));
		outBuffer = new ResponseStream(
		    cmdSocket.getOutputStream(), 8192);
		out = new DataOutputStream(outBuffer);

		/* Redirect agent stdout to client via an AgentOutputStream. */
		asyncSocket = socket.accept();
		stdout = System.out;
		asyncOutputStream = new DataOutputStream(
		    new BufferedOutputStream(asyncSocket.getOutputStream()));
                setSystemIO(System.in, 
		            new PrintStream(
		             new BufferedOutputStream(
			      new AgentOutputStream(asyncOutputStream), 128), true),
                           System.err);
		message("connection accepted");

		out.writeInt(AGENT_VERSION);

		try {
		    writeObject(Class.forName("java.lang.Object"));
		    writeObject(Class.forName("java.lang.Class"));
		    writeObject(Class.forName("java.lang.String"));
		} catch (ClassNotFoundException e) {
		    // If *these* classes can't be found, just bail.
		    System.exit(1);
		}
		dumpClasses();
		writeObject(Thread.currentThread().getThreadGroup());
		out.flush();

		try {
		    for (int cmd = in.read() ; cmd != -1 ; cmd = in.read()) {
			try {
			    handle(cmd, in, out);
			} catch (Throwable t) {
			    error(exceptionStackTrace(t));
			    outBuffer.reset();  // discard normal output.
			    out.writeInt(CMD_EXCEPTION);
			    out.writeUTF(t.toString());
			}
			out.flush();
		    }
		} catch (Exception e) {
		    setSystemIO(System.in, stdout, System.err);
		    error(exceptionStackTrace(e));
		}
		setSystemIO(System.in, stdout, System.err);
		message("connection closed");
	    }
	} catch (ThreadDeath td) {
	    setSystemIO(System.in, stdout, System.err);
	    message("ThreadDeath caught.");
	} catch (IOException ex) {
	    setSystemIO(System.in, stdout, System.err);
	    message("IOException caught.");
	}
        if (asyncSocket != null) {
	    try {
		asyncSocket.close();
	    } catch (Exception ex) {
		// Ignore exceptions here
	    }
	}
	if (cmdSocket != null) {
	    try {
		cmdSocket.close();
	    } catch (Exception ex) {
		// Ignore exceptions here
	    }
	}
    }

    /* hack: Unless we have "mutable" in/out/err in System.java,
	we need to force the setting into "final static" members.
	For now, we use a native method.  Me *might* some day
	switch to fancy mutable streams that can be properly
	retargeted with Java accessCheck*() calls as protection.
	This native method forwards to a native in system.c, 
        bypassing all our inter-class-security. */
    private native static void setSystemIO(InputStream in, 
					   PrintStream out, 
					   PrintStream err);


    /*
     * Return true if this is a special system thread that shouldn\'t
     * be suspended.
     */
    public static boolean systemThread(Thread t) {
	String	tname = String.valueOf(t.getName());

	if (tname.equals("Idle thread") ||
	    tname.equals("clock handler") ||
	    tname.equals("Debugger agent") ||
	    tname.equals("Agent output") ||
	    tname.equals("Breakpoint handler")) {
	    return true;
	}
	return false;
    }

    private void suspendThread(Thread t) {
	if (!systemThread(t)) {
	    message("suspending " + t.getName());
	    try {
		SecurityManager.setScopePermission();
		t.suspend();
	    } catch (IllegalThreadStateException e) {
		error("suspend failed: " + e.getMessage());
	    }
	}
    }

    private void stepThread(Thread t, boolean stepLine) {
	if (!systemThread(t)) {
	    message("stepping " + t.getName());
	    try {
		if (stepLine) {
		    StackFrame sf = getStackFrame(t, 0);
		    LineNumber ln = lineno2pc(sf.clazz, sf.lineno);
		    if (ln != null) { /* If line info available. */
			ln.thread = t;
			bkptHandler.addSkipLine(ln);
		    }
		}
		setSingleStep(t, true);
                resumeAllThreads();
	    } catch (IllegalThreadStateException e) {
		error("step failed: " + e.getMessage());
	    }
	}
    }

    private void stepNextThread(Thread t) {
	if (!systemThread(t)) {
	    message("next stepping " + t.getName());
	    try {
                StackFrame sf = getStackFrame(t, 0);
                LineNumber ln = lineno2pc(sf.clazz, sf.lineno);
                if (ln == null) {
                    message("no line information available, single-stepping.");
                    stepThread(t, false);
                    return;
                }
                try {
                    bkptHandler.addBreakpoint(ln.clazz, ln.endPC + 1, 
                                              BKPT_ONESHOT, null);
                } catch (Exception e) {
                    message("next-step failed: " + e.toString() + 
                            ", single-stepping.");
                    stepThread(t, false);
                    return;
                }
                resumeAllThreads();
	    } catch (IllegalThreadStateException e) {
		error("next step failed: " + e.getMessage());
	    }
	}
    }

    /* Used by the BreakpointHandler. */
    void suspendAllThreads() {
	message("suspendAllThreads()");
	Thread list[] = new Thread[100];
	int n = Thread.enumerate(list);
	for (int i = 0 ; i < n ; i++) {
	    suspendThread(list[i]);
	}
    }

    void resumeAllThreads() {
	message("resumeAllThreads()");
	Thread list[] = new Thread[100];
	int n = Thread.enumerate(list);
	for (int i = 0 ; i < n ; i++) {
	    resumeThread(list[i]);
	}
    }

    private void resumeThread(Thread t) {
        if (!systemThread(t)) {
	    message("resuming " + t.getName());
	    try {
	        SecurityManager.setScopePermission();
	        t.resume();
	    } catch (IllegalThreadStateException e) {
	        error("resume failed: " + e.getMessage());
	    }
	}
    }

    native int getThreadStatus(Thread t);
    native StackFrame getStackFrame(Thread t, int frameNum);
    native Field getMethods(Class c)[];
    native Field getFields(Class c)[];
    native Class getClasses()[];

    native Object getSlotObject(Object o, int slot);
    native int getSlotSignature(Class c, int slot)[];
    native boolean getSlotBoolean(Object o, int slot);
    native int getSlotInt(Object o, int slot);
    native long getSlotLong(Object o, int slot);
    native float getSlotFloat(Object o, int slot);
    native Object getSlotArray(Object o, int slot);
    native int getSlotArraySize(Object o, int slot);

    native Object getStackObject(Thread t, int frameNum, int slot);
    native boolean getStackBoolean(Thread t, int frameNum, int slot);
    native int getStackInt(Thread t, int frameNum, int slot);
    native long getStackLong(Thread t, int frameNum, int slot);
    native float getStackFloat(Thread t, int frameNum, int slot);

    native LineNumber lineno2pc(Class c, int lineno);
    native int pc2lineno(Class c, int pc);
    native int method2pc(Class c, int method_slot);
    native String exceptionStackTrace(Throwable exc);
    native void setSingleStep(Thread t, boolean step);
    native void initAgent();
    native String getClassSourceName(Class c);

    native void setSlotBoolean(Object o, int slot, boolean v);
    native void setSlotInt(Object o, int slot, int v);
    native void setSlotLong(Object o, int slot, long v);
    native void setSlotDouble(Object o, int slot, double v);
    native void setStackBoolean(Thread t, int frameNum, int slot, boolean v);
    native void setStackInt(Thread t, int frameNum, int slot, int v);
    native void setStackLong(Thread t, int frameNum, int slot, long v);
    native void setStackDouble(Thread t, int frameNum, int slot, double v);

    /* handle command */
    synchronized void handle(int cmd, DataInputStream in, DataOutputStream out) throws IOException {
	out.writeInt(cmd);

	switch (cmd) {

	  case CMD_LIST_CLASSES: {
	      Class list[] = getClasses();
	      out.writeInt(list.length);
	      for (int i = 0 ; i < list.length ; i++) {
		  writeObject(list[i]);
	      }
	      return;
	  }
	    
	  case CMD_GET_CLASS_INFO: {
	      Class c = (Class)objects.get(new Integer(in.readInt()));
	      out.writeUTF(c.getName());
              out.writeUTF(getClassSourceName(c));
	      out.writeInt(c.isInterface() ? 1 : 0);
	      writeObject(c.getSuperclass());
	      writeObject(c.getClassLoader());
	      Class interfaces[] = c.getInterfaces();
	      out.writeInt(interfaces.length);
	      for (int i = 0 ; i < interfaces.length ; i++) {
		  writeObject(interfaces[i]);
	      }
	      return;
	  }

 	  case CMD_GET_THREAD_NAME:
 	    out.writeUTF(((Thread)objects.get(new Integer(in.readInt()))).getName());
 	    return;

	  case CMD_GET_CLASS_BY_NAME: {
	      String name = in.readUTF();
	      try {
		  writeObject(Class.forName(name));
	      } catch (ClassNotFoundException e) {
		  message("no such class: " + name);
		  writeObject(null);
	      } catch (NoClassDefFoundError e) {
		  message("no such class: " + name);
		  writeObject(null);
	      }
	      return;
	  }

	  case CMD_GET_THREAD_STATUS:
	    out.writeInt(getThreadStatus(
		    (Thread)objects.get(new Integer(in.readInt()))));
	    return;

	  case CMD_GET_STACK_TRACE: {
	      Thread t = (Thread)objects.get(new Integer(in.readInt()));
	      int nFrames = t.countStackFrames();
	      out.writeInt(nFrames);
	      for (int i = 0; i < nFrames; i++) {
		  StackFrame sf = getStackFrame(t, i);
		  writeObject(sf);
		  out.writeUTF(sf.className);
		  out.writeUTF(sf.methodName);
		  out.writeInt(sf.lineno);
		  out.writeInt(sf.pc);
		  writeObject(sf.clazz);
		  out.writeInt(sf.localVariables.length);
		  for (int j = 0; j < sf.localVariables.length; j++) {
		      message("lvar " + j + ": slot=" +
			      sf.localVariables[j].slot +
			      ", name=" + sf.localVariables[j].name +
			      ", sig=" + sf.localVariables[j].signature + 
                              ", argument=" + sf.localVariables[j].methodArgument);
		      out.writeInt(sf.localVariables[j].slot);
		      out.writeUTF(sf.localVariables[j].name);
		      out.writeUTF(sf.localVariables[j].signature);
		      out.writeBoolean(sf.localVariables[j].methodArgument);
		  }
	      }
	      return;
	  }

	  case CMD_GET_STACK_VALUE: {
	      Thread t = (Thread)objects.get(new Integer(in.readInt()));
	      int frameNum = in.readInt();
	      int slot = in.readInt();
	      char type = in.readChar();
	      switch (type) {
		case 'Z':
		  write(getStackBoolean(t, frameNum, slot));
		  return;
		case 'B':
		  write((byte)getStackInt(t, frameNum, slot));
		  return;
		case 'C':
		  write((char)getStackInt(t, frameNum, slot));
		  return;
		case 'S':
		  write((short)getStackInt(t, frameNum, slot));
		  return;
		case 'I':
		  write(getStackInt(t, frameNum, slot));
		  return;
		case 'J':
		  write(getStackLong(t, frameNum, slot));
		  return;
		case 'F':
		  write(getStackFloat(t, frameNum, slot));
		  return;
		case 'D':
		  write((double)getStackFloat(t, frameNum, slot));
		  return;
		case '[':
		case 'L': 
		  writeObject(getStackObject(t, frameNum, slot));
		  return;
		case 'V':
		  writeObject(null);
		  return;
		default:
		  String strError = new String("bogus signature(" +
					       type + ")");
		  error(strError);
		  writeObject(strError);
	      }
	      return;
	  }
	  
	  case CMD_SET_STACK_VALUE: {
	      Thread t = (Thread)objects.get(new Integer(in.readInt()));
	      int frameNum = in.readInt();
	      int slot = in.readInt();
	      int type = in.readInt();
	      switch (type) {
		case TC_BOOLEAN:
                  setStackBoolean(t, frameNum, slot, in.readBoolean());
                  break;
		case TC_INT:
                  setStackInt(t, frameNum, slot, in.readInt());
                  break;
		case TC_LONG:
                  setStackLong(t, frameNum, slot, in.readLong());
                  break;
		case TC_DOUBLE:
                  setStackDouble(t, frameNum, slot, in.readDouble());
                  break;
		default:
                  error("bogus type(" + type + ")");
                  break;
	      }
	      return;
	  }
	  
          case CMD_MARK_OBJECTS: {
              int nMarked = in.readInt();
              Hashtable marked_objects = new Hashtable();
              for (int i = 0; i < nMarked; i++) {
                  Object obj = objects.get(new Integer(in.readInt()));
                  if (obj != null) {
                      marked_objects.put(new Integer(objectId(obj)), obj);
                  }
              }

              out.flush();
              cmd = in.read();
              out.writeInt(cmd);
              switch (cmd) {
                case CMD_FREE_OBJECTS :
                    try {
                        // Clone the existing objects hashtable.
                        Hashtable saved_objects = new Hashtable();
                        Enumeration enum = objects.elements();
                        while (enum.hasMoreElements()) {
                            Object obj = enum.nextElement();
                            saved_objects.put(new Integer(objectId(obj)), obj);
                        }

                        // Now remove unmarked objects, but not classes.
                        enum = objects.elements();
                        while (enum.hasMoreElements()) {
                            Object obj = enum.nextElement();
                            if (!(obj instanceof Class)) {
                                Integer id = new Integer(objectId(obj));
                                if (marked_objects.get(id) == null) {
                                    message("gc: freeing " + obj);
                                    saved_objects.remove(id);
                                }
                            }
                        }
                        objects = saved_objects;
                        System.gc();

                        // Return saved object ids.
                        enum = objects.elements();
                        while (enum.hasMoreElements()) {
                            Object obj = enum.nextElement();
                            if (obj != null) {
                                writeObject(obj);
                            }
                        }
                        writeObject(null);
                    } catch (Exception e) {
                        error("CMD_MARK_OBJECTS failed: " + e.toString());
                    }
                    break;
                default:
                    error("mark objects command failed");
                    break;
              }
              out.flush();
              return;
          }

	  case CMD_LIST_THREADGROUPS: {
	      SecurityManager.setScopePermission();
	      ThreadGroup list[] = new ThreadGroup[20];
	      SecurityManager.resetScopePermission();
	      ThreadGroup tg = (ThreadGroup)objects.get(
		  new Integer(in.readInt()));
	      int n = 0;
	      if (tg == null) {
		  /* List all threadgroups, including "system". */
		  message("Getting all threadgroups");
		  tg = Thread.currentThread().getThreadGroup();
		  n = tg.enumerate(list);
		  out.writeInt(n + 1);
		  writeObject(tg);
	      } else {
		  message("Getting threadgroups for " + tg.getName());
		  n = tg.enumerate(list);
		  out.writeInt(n);
	      }
	      for (int i = 0; i < n; i++) {
		  writeObject(list[i]);
	      }
	      return;
	  }

	  case CMD_GET_THREADGROUP_INFO: {
	      ThreadGroup tg = (ThreadGroup)objects.get(
		  new Integer(in.readInt()));
	      writeObject(tg.getParent());
	      out.writeUTF(tg.getName());
	      out.writeInt(tg.getMaxPriority());
	      out.writeBoolean(tg.isDaemon());
	      return;
	  }

	  case CMD_LIST_THREADS: {
	      Thread list[] = new Thread[40];
	      ThreadGroup tg = (ThreadGroup)objects.get(
		  new Integer(in.readInt()));
	      boolean recurse = in.readBoolean();
	      message("Getting threads for " + tg.getName());
	      int n = tg.enumerate(list, recurse);
	      out.writeInt(n);
	      for (int i = 0; i < n; i++) {
		  writeObject(list[i]);
	      }
	      return;
	  }

	  case CMD_RUN: {
	      String argv[] = new String[in.read() - 1];
	      String nm = in.readUTF();
	      String errorStr = new String("");
	      for (int i = 0 ; i < argv.length ; i++) {
		  argv[i] = in.readUTF();
	      }
	      try {
		  Class c = Class.forName(nm);
		  SecurityManager.setScopePermission();
		  ThreadGroup newGroup =
		      new ThreadGroup(c.getName() + ".main");
		  MainThread t = new MainThread(this, newGroup, c, argv);
		  SecurityManager.resetScopePermission();
		  writeObject(t.getThreadGroup());

		  /* kick the empty app, so it can wake up and exit */
		  if (emptyAppThread != null) {
		      SecurityManager.setScopePermission();
		      emptyAppThread.resume();
		      SecurityManager.resetScopePermission();
		      emptyAppThread = null;
		  }
	      } catch (Exception e) {
		  message(exceptionStackTrace(e));
		  writeObject(null);
	      }
	      return;
	  }

	  case CMD_SUSPEND_THREAD:
	      suspendThread((Thread)objects.get(new Integer(in.readInt())));
	      return;

	  case CMD_RESUME_THREAD:
	      resumeThread((Thread)objects.get(new Integer(in.readInt())));
	      return;

	  case CMD_GET_FIELDS: {
	      Class c = (Class)objects.get(new Integer(in.readInt()));
	      Field fields[] = getFields(c);
	      out.writeInt(fields.length);
	      for (int i = 0; i < fields.length ; i++) {
		  out.writeInt(fields[i].slot);
		  out.writeUTF(fields[i].name);
		  out.writeUTF(fields[i].signature);
		  out.writeShort(fields[i].access);
		  writeObject(fields[i].clazz);
	      }
	      return;
	  }

	  case CMD_GET_METHODS: {
	      Class c = (Class)objects.get(new Integer(in.readInt()));
	      Field methods[] = getMethods(c);
	      out.writeInt(methods.length);
	      for (int i = 0; i < methods.length ; i++) {
		  out.writeInt(methods[i].slot);
		  out.writeUTF(methods[i].name);
		  out.writeUTF(methods[i].signature);
		  out.writeShort(methods[i].access);
		  writeObject(methods[i].clazz);
	      }
	      return;
	  }

	  case CMD_GET_ELEMENTS: {
	      Object obj = (Object)objects.get(new Integer(in.readInt()));
	      int type = in.readInt();
	      int beginIndex = in.readInt();
	      int endIndex = in.readInt();
	      out.writeInt(endIndex - beginIndex + 1);
	      switch(type) {
		case TC_CHAR: {
		    char array[] = (char[])obj;
		    for (int i = beginIndex; i <= endIndex ; i++) {
			write(array[i]);
		    }
		    return;
		}
		case TC_BYTE: {
		    byte array[] = (byte[])obj;
		    for (int i = beginIndex; i <= endIndex ; i++) {
			write(array[i]);
		    }
		    return;
		}
	        case TC_CLASS:
		case TP_OBJECT: {
		    Object array[] = (Object[])obj;
		    for (int i = beginIndex; i <= endIndex ; i++) {
			writeObject(array[i]);
		    }
		    return;
		}
		case TC_FLOAT: {
		    float array[] = (float[])obj;
		    for (int i = beginIndex; i <= endIndex ; i++) {
			write(array[i]);
		    }
		    return;
		}
		case TC_DOUBLE: {
		    double array[] = (double[])obj;
		    for (int i = beginIndex; i <= endIndex ; i++) {
			write(array[i]);
		    }
		    return;
		}
		case TC_INT: {
		    int array[] = (int[])obj;
		    for (int i = beginIndex; i <= endIndex ; i++) {
			write(array[i]);
		    }
		    return;
		}
		case TC_LONG: {
		    long array[] = (long[])obj;
		    for (int i = beginIndex; i <= endIndex ; i++) {
			write(array[i]);
		    }
		    return;
		}
		case TC_SHORT: {
		    short array[] = (short[])obj;
		    for (int i = beginIndex; i <= endIndex ; i++) {
			write(array[i]);
		    }
		    return;
		}
		case TC_BOOLEAN: {
		    boolean array[] = (boolean[])obj;
		    for (int i = beginIndex; i <= endIndex ; i++) {
			write(array[i]);
		    }
		    return;
		}
		default:
		case TC_VOID: {
		    for (int i = beginIndex; i <= endIndex ; i++) {
			writeObject(null);
		    }
		    return;
		}
	      }
	  }

	  case CMD_GET_SLOT_SIGNATURE: {
	      Class c = (Class)objects.get(new Integer(in.readInt()));
	      int slot = in.readInt();
	      int sig[] = getSlotSignature(c, slot);
              if (sig == null) {
                  out.writeInt(0);
                  return;
              }
	      out.writeInt(sig.length);
	      for (int i = 0; i < sig.length; i++) {
		  out.writeInt(sig[i]);
	      }
	      return;
	  }

	  case CMD_GET_SLOT_VALUE: {
	      Object obj = (Object)objects.get(new Integer(in.readInt()));
	      int slot = in.readInt();
	      int sig[] = getSlotSignature(
		  (obj instanceof Class) ? (Class)obj : obj.getClass(), slot);
              if (sig == null) {
                  writeObject(new ArrayIndexOutOfBoundsException(
                      "invalid slot index " + slot));
                  return;
              }
	      switch (sig[0]) {
		case TC_BOOLEAN:
		  write(getSlotBoolean(obj, slot));
		  return;
		case TC_BYTE:
		  write((byte)getSlotInt(obj, slot));
		  return;
		case TC_CHAR:
		  write((char)getSlotInt(obj, slot));
		  return;
		case TC_SHORT:
		  write((short)getSlotInt(obj, slot));
		  return;
		case TC_INT:
		  write(getSlotInt(obj, slot));
		  return;
		case TC_LONG:
		  write(getSlotLong(obj, slot));
		  return;
		case TC_FLOAT:
		  write(getSlotFloat(obj, slot));
		  return;
		case TC_DOUBLE:
		  write((double)getSlotFloat(obj, slot));
		  return;
		case TC_ARRAY: {
		    Object array = getSlotArray(obj, slot);
		    int size = getSlotArraySize(obj, slot);
		    writeArray(array, size);
		    return;
		}
		case TP_OBJECT: 
		case TP_CLASS:
		  Object objectRet = getSlotObject(obj, slot);
		  writeObject(objectRet);
		  return;
		case TC_VOID:
		  writeObject(null);
		  return;
		default:
		  String strError = new String("bogus signature(");
		  for (int i = 0; i < sig.length; i++) {
		      strError = strError.concat(" " +
			  new Integer(sig[i]).toString());
		  }
		  strError = strError.concat(" )");
		  error(strError);
		  writeObject(strError);
	      }
	  }

	  case CMD_SET_SLOT_VALUE: {
	      Object obj = (Object)objects.get(new Integer(in.readInt()));
	      int slot = in.readInt();
	      int sig = in.readInt();
	      switch (sig) {
		case TC_BOOLEAN:
		  setSlotBoolean(obj, slot, in.readBoolean());
		  break;
		case TC_INT:
		  setSlotInt(obj, slot, in.readInt());
		  break;
		case TC_LONG:
		  setSlotLong(obj, slot, in.readLong());
		  break;
		case TC_DOUBLE:
		  setSlotDouble(obj, slot, in.readDouble());
		  break;
		default:
                  error("bogus type(" + sig + ")");
                  break;
	      }
              return;
	  }

	  case CMD_SET_BRKPT_LINE: {
	      Class c = (Class)objects.get(new Integer(in.readInt()));
	      int lineno = in.readInt();
	      LineNumber ln = lineno2pc(c, lineno);
	      String errorStr = "";
	      if (ln == null) {
		  errorStr = "No code at line " + lineno +
		      ", or class is optimized.";
	      } else {
		  try {
		      bkptHandler.addBreakpoint(c, ln.startPC,
						BKPT_NORMAL, null);
		  } catch (Exception e) {
		      message(exceptionStackTrace(e));
		      errorStr = e.toString();
		  }
	      }
	      out.writeUTF(errorStr);
	      return;
	  }

	  case CMD_SET_BRKPT_METHOD: {
	      Class c = (Class)objects.get(new Integer(in.readInt()));
	      int method_slot = in.readInt();
	      int pc = method2pc(c, method_slot);
	      message("method2pc(" + c.getName() + "," + method_slot +
		      ") = " + pc);
	      String errorStr = "";
	      if (pc == -1) {
		  errorStr = "Not a Java method";
	      } else {
		  try {
		      bkptHandler.addBreakpoint(c, pc, BKPT_NORMAL, null);
		  } catch (Exception e) {
		      message(exceptionStackTrace(e));
		      errorStr = e.toString();
		  }
	      }
	      out.writeUTF(errorStr);
	      return;
	  }

	  case CMD_LIST_BREAKPOINTS: {
	      BreakpointSet bkpts[] = bkptHandler.listBreakpoints();
	      out.writeInt(bkpts.length);
	      for (int i = 0; i < bkpts.length; i++) {
		  out.writeUTF(bkpts[i].clazz.getName() + ":" + 
			       pc2lineno(bkpts[i].clazz, bkpts[i].pc));
	      }
	      return;
	  }

	  case CMD_CLEAR_BRKPT: {
	      Class c = (Class)objects.get(new Integer(in.readInt()));
	      int pc = in.readInt();
	      message("clearing bkpt at " + c.getName() + ":" + pc);
	      String errorStr = "";
	      if (pc == -1) {
		  errorStr = "No code at pc " + pc;
	      } else {
		  try {
		      bkptHandler.deleteBreakpoint(c, pc);
		  } catch (Exception e) {
		      message(exceptionStackTrace(e));
		      errorStr = e.toString();
		  }
	      }
	      out.writeUTF(errorStr);
	      return;
	  }

	  case CMD_CLEAR_BRKPT_LINE: {
	      Class c = (Class)objects.get(new Integer(in.readInt()));
	      int lineno = in.readInt();
	      message("clearing bkpt at " + c.getName() + ":" + lineno);
	      LineNumber ln = lineno2pc(c, lineno);
	      String errorStr = "";
	      if (ln == null) {
		  errorStr = "No code at line " + lineno +
		      ", or class is optimized.";
	      } else {
		  try {
		      bkptHandler.deleteBreakpoint(c, ln.startPC);
		  } catch (Exception e) {
		      message(exceptionStackTrace(e));
		      errorStr = e.toString();
		  }
	      }
	      out.writeUTF(errorStr);
	      return;
	  }

	  case CMD_CLEAR_BRKPT_METHOD: {
	      Class c = (Class)objects.get(new Integer(in.readInt()));
	      int slotno = in.readInt();
	      message("clearing bkpt at " + c.getName());
	      int pc = method2pc(c, slotno);
	      String errorStr = "";
	      if (pc == -1) {
		  errorStr = "Not a Java method";
	      } else {
		  try {
		      bkptHandler.deleteBreakpoint(c, pc);
		  } catch (Exception e) {
		      message(exceptionStackTrace(e));
		      errorStr = e.toString();
		  }
	      }
	      out.writeUTF(errorStr);
	      return;
	  }

	  case CMD_CATCH_EXCEPTION_CLS: {
	      String errorStr = "";
	      try {
		  Class c = (Class)objects.get(new Integer(in.readInt()));
		  bkptHandler.catchExceptionClass(c);
	      } catch (Exception e) {
		  error("catchExceptionClass failed: " + e);
	      }
	      return;
	  }

	  case CMD_IGNORE_EXCEPTION_CLS: {
	      String errorStr = "";
	      try {
		  Class c = (Class)objects.get(new Integer(in.readInt()));
		  bkptHandler.ignoreExceptionClass(c);
	      } catch (Exception e) {
		  error("ignoreExceptionClass failed: " + e);
	      }
	      return;
	  }

	  case CMD_GET_CATCH_LIST: {
	      Class list[] = bkptHandler.getCatchList();
	      out.writeInt(list.length);
	      for (int i = 0; i < list.length; i++) {
		  writeObject(list[i]);
	      }
	      return;
	  }

	  case CMD_STOP_THREAD: {
	      Thread t = (Thread)objects.get(new Integer(in.readInt()));
	      SecurityManager.setScopePermission();
	      t.stop();
              message(t.getName() + " stopped.");
	      return;
	  }

	  case CMD_STOP_THREAD_GROUP: {
	      ThreadGroup tg = (ThreadGroup)objects.get(
		  new Integer(in.readInt()));
	      SecurityManager.setScopePermission();
	      tg.stop();
              message(tg.getName() + " stopped.");
	      return;
	  }

	  case CMD_SET_VERBOSE:
	      verbose = in.readBoolean();
	      return;

	  case CMD_SYSTEM: {
	      switch (in.readInt()) {
		case SYS_MEMORY_FREE:
		  // REMIND: use writeLong instead
		  out.writeInt((int)Runtime.getRuntime().freeMemory());
		  break;

		case SYS_MEMORY_TOTAL:
		  // REMIND: use writeLong instead
		  out.writeInt((int)Runtime.getRuntime().totalMemory());
		  break;

		case SYS_TRACE_METHOD_CALLS:
		  Runtime.getRuntime().traceMethodCalls(in.readInt() != 0);
		  break;

		case SYS_TRACE_INSTRUCTIONS:
		  Runtime.getRuntime().traceInstructions(in.readInt() != 0);
		  break;
	      }
	      return;
	  }

	  case CMD_GET_SOURCE_FILE: {
	      String sourceName = in.readUTF();
	      String className = in.readUTF();

	      // Extract the package name from the class (if any).
	      int iDot = className.lastIndexOf('.');
	      String pkgName = (iDot >= 0) ?
		  className.substring(0, iDot) : "";
	      Package pkg = null;
	      try {
		  pkg = new Package(sourcePath, Identifier.lookup(pkgName));
	      } catch (Exception e) {
		  error("cannot create a Package for " + pkgName);
		  out.writeInt(-1);
		  return;
	      }	      
	      
	      ClassFile sourceFile = null;
	      InputStream input = null;
	      try {
		  sourceFile = pkg.getSourceFile(sourceName);
		  input = sourceFile.getInputStream();
	      } catch (Exception e) {
		  error("cannot find " + sourceName);
		  out.writeInt(-1);
		  return;
	      }
	      int length = (int)sourceFile.length();
	      out.writeInt(length);
	      byte buffer[] = new byte[length];
	      try {
		  int off = 0, len = buffer.length;
		  while (len > 0) {
		      int n = input.read(buffer, off, len);
		      if (n == -1) {
			  throw new IOException();
		      }
		      off += n;
		      len -= n;
		  }
		  out.write(buffer);
	      } catch (IOException e) {
		  error("unable to read " + sourceName);
		  return;
	      }
	      input.close();
	      return;
	  }

	  case CMD_OBJECT_TO_STRING: {
	      Object o = objects.get(new Integer(in.readInt()));
	      String retStr = "";
	      try {
		  retStr = o.toString();
	      } catch (Exception e) {}
	      out.writeUTF(retStr);
	      return;
	  }

	  case CMD_GET_SOURCEPATH: {
	      out.writeUTF(sourcePath.toString());
	      return;
	  }

	  case CMD_SET_SOURCEPATH: {
	      String sourcePathString = in.readUTF();
	      if (sourcePathString == null) {
		  sourcePathString = ".";
	      }
	      sourcePath = new ClassPath(sourcePathString);
	      return;
	  }

	  case CMD_STEP_THREAD:
	      stepThread((Thread)objects.get(new Integer(in.readInt())),
		     in.readBoolean());
	      return;

	  case CMD_NEXT_STEP_THREAD:
	      stepNextThread((Thread)objects.get(new Integer(in.readInt())));
	      return;

	  case CMD_QUIT: {
	      System.exit(0);
	  }
	}
	error("command not understood: " + cmd);
    }

    native int objectId(Object obj);
    native void runMain(Class c, String argv[]);

    void writeObject(Object obj, DataOutputStream out) throws IOException {
	int h = 0;

	if (obj == null) {
	    out.writeInt(TP_OBJECT);
	} else if (obj instanceof Class) {
	    out.writeInt(TP_CLASS);
	    objects.put(new Integer(h = objectId(obj)), obj);
	} else if (obj instanceof String) {
	    out.writeInt(TP_STRING);
	    objects.put(new Integer(h = objectId(obj)), obj);
	} else {
	    if (obj instanceof Thread) {
		out.writeInt(TP_THREAD);
	    } else if (obj instanceof ThreadGroup) {
		out.writeInt(TP_THREADGROUP);
	    } else {
		out.writeInt(TP_OBJECT);
	    }
	    Class c = obj.getClass();
	    objects.put(new Integer(h = objectId(c)), c);
	    out.writeInt(h);
	    objects.put(new Integer(h = objectId(obj)), obj);
	}
	out.writeInt(h);
    }

    /* Write an object */
    void writeObject(Object obj) throws IOException{
	writeObject(obj, out);
    }

    void writeArray(Object obj, int size) throws IOException {
	int h = 0;

	out.writeInt(TC_ARRAY);
	if (obj == null) {
	    out.writeInt(0);
	} else {
	    Class c = obj.getClass();
	    objects.put(new Integer(h = objectId(c)), c);
	    out.writeInt(h);
	    objects.put(new Integer(h = objectId(obj)), obj);
	    out.writeInt(h);
	    out.writeInt(size);
	}
    }

    void write(boolean b) throws IOException {
	out.writeInt(TC_BOOLEAN);
	out.writeBoolean(b);
    }

    void write(byte b) throws IOException {
	out.writeInt(TC_BYTE);
	out.writeByte(b);
    }
    
    void write(char c) throws IOException {
	out.writeInt(TC_CHAR);
	out.writeChar(c);
    }
    
    void write(short s) throws IOException {
	out.writeInt(TC_SHORT);
	out.writeShort(s);
    }
    
    void write(int i) throws IOException {
	out.writeInt(TC_INT);
	out.writeInt(i);
    }
    
    void write(long l) throws IOException {
	out.writeInt(TC_LONG);
	out.writeLong(l);
    }
    
    void write(float f) throws IOException {
	out.writeInt(TC_FLOAT);
	out.writeFloat(f);
    }
    
    void write(double d) throws IOException {
	out.writeInt(TC_DOUBLE);
	out.writeDouble(d);
    }

    void reportAppExit() throws IOException {
        /* The application has either called System.exit, or its
         * main has returned.
         */
        synchronized (asyncOutputStream) {
            asyncOutputStream.write(CMD_QUIT_NOTIFY);
            asyncOutputStream.flush();
        }
    }

    /* Error, status messages */
    static void error(String msg) {
	System.out.println("[AGENT: " + msg + "]");
    }
    static void message(String msg) {
	if (verbose) {
	    System.out.println("[agent: " + msg + "]");
	}
    }

    /* Bootstrap the agent */
    public static void boot(int port) {
	SecurityManager.setScopePermission();
	Agent agent = new Agent(port);
	Thread t = new Thread(agent);
	t.setDaemon(true);
	t.setName("Debugger agent");
	SecurityManager.resetScopePermission();
	t.start();
    }

    static {
	SecurityManager.setScopePermission();
	System.loadLibrary("agent");
    }
}