Milán Major

netscape-revival

1 branch
Code

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

/*
 * @(#)BreakpointHandler.java	1.17 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.util.*;
import java.io.IOException;

class BreakpointHandler extends Thread implements AgentConstants {
    static BreakpointQueue	the_bkptQ;
    static Hashtable		the_bkptHash;

    private Agent		agent;
    private Hashtable		catchHash;
    private Vector		skipLines;

    static final boolean	debug = false;

    native int setBreakpoint(int pc) throws sun.tools.debug.InvalidPCException;
    native void clrBreakpoint(int pc, int oldOpcode) 
        throws sun.tools.debug.InvalidPCException;

    synchronized public static int getOpcode(int pc) {
        BreakpointSet bkpt = (BreakpointSet)the_bkptHash.get(new Integer(pc));
        return (bkpt == null) ? -1 : bkpt.opcode;
    }

    synchronized void addBreakpoint(Class classObj, int pc,
				    int type, byte cond[]) throws IOException {
	BreakpointSet	bkpt;
	int		opcode;

	bkpt = (BreakpointSet)the_bkptHash.get(new Integer(pc));
	if (bkpt == null) {
            if (debug) {
                Agent.message("Adding breakpoint for pc " + pc);
            }
	    if (classObj == null) {
                if (debug) {
                    Agent.message("Invalid classObj in addBreakpoint");
                }
		return;
	    }
	    try {
	        opcode = setBreakpoint(pc);
	    } catch (InvalidPCException ex) {
		throw new IOException("invalid PC");
	    }
	    bkpt = new BreakpointSet(pc, opcode, classObj, type, cond);
	    the_bkptHash.put(new Integer(pc), bkpt);
            if (cond != null && cond.length > 0) {
                bkpt.addCondition(cond);
            }
	}
    }

    synchronized void deleteBreakpoint(Class classObj, int pc) throws IOException {
	BreakpointSet	bkpt;

	bkpt = (BreakpointSet)the_bkptHash.get(new Integer(pc));
	if (bkpt == null) {
	    return;
	}
	try {
	    clrBreakpoint(pc, bkpt.opcode);
	} catch (InvalidPCException ex) {
	    throw new IOException("invalid PC");
	}
	the_bkptHash.remove(new Integer(pc));
    }

    synchronized BreakpointSet listBreakpoints()[] {
	BreakpointSet ret[] = new BreakpointSet[the_bkptHash.size()];
	Enumeration e = the_bkptHash.elements();
	for (int i = 0; i < ret.length; i++) {
	    try {
		ret[i] = (BreakpointSet)e.nextElement();
	    } catch (NoSuchElementException nse) {
		// Should never happen!
		Agent.error("Internal error" + agent.exceptionStackTrace(nse));
		return null;
	    }
	}
	return ret;
    }

    synchronized void catchExceptionClass(Class c) {
	if (catchHash.get(c) == null) {
	    catchHash.put(c, c);
	}
    }

    synchronized void ignoreExceptionClass(Class c) {
	catchHash.remove(c);
    }

    Class getCatchList()[] {
	Class list[] = new Class[catchHash.size()];
	Enumeration enum = catchHash.elements();
	for (int i = 0; i < list.length; i++) {
	    list[i] = (Class)enum.nextElement();
	}
	return list;
    }

    void addSkipLine(LineNumber line) {
        if (debug) {
            Agent.message("addSkipLine: thread=" + line.thread.getName() +
                          ", startPC=" + line.startPC + 
                          ", endPC = " + line.endPC);
        }
	skipLines.addElement(line);
    }

    void removeSkipLine(LineNumber line) {
	skipLines.removeElement(line);
    }

    boolean ignoreSingleStep(Thread thread, int pc) {
        if (debug) {
            Agent.message("ignoreSingleStep(" + thread.getName() + 
                          ", " + pc + ")");
        }
	Enumeration e = skipLines.elements();
	while (e.hasMoreElements()) {
	    LineNumber ln = (LineNumber)e.nextElement();
            if (debug) {
                Agent.message("   ln: thread=" + ln.thread.getName() +
                              ", startPC=" + ln.startPC + 
                              ", endPC = " + ln.endPC);
            }
	    if (ln.thread == thread) {
		if (pc >= ln.startPC && pc <= ln.endPC) {
		    return true;
		} else {
		    // Only one skip line per thread.
		    removeSkipLine(ln);
		}
	    }
	}
        if (debug) {
            Agent.message("   no match.");
        }
	return false;
    }

    BreakpointHandler(Agent ag) {
	super("Breakpoint handler");
	the_bkptQ = new BreakpointQueue();
	the_bkptHash = new Hashtable();
	catchHash = new Hashtable();
	skipLines = new Vector();
	agent = ag;
    }

    public void run() {
	try {
	    BreakpointSet bkpt;

	    while (the_bkptQ.nextEvent()) {

		if (agent.asyncOutputStream == null) {
		    continue;
		}

		if (the_bkptQ.exception != null) {
		    if (the_bkptQ.exception instanceof ThreadDeath) {
                        if (debug) {
                            Agent.message("Received ThreadDeath event" +
                                          ", pc=" + the_bkptQ.pc);
                        }
                        synchronized (agent.asyncOutputStream) {
                            agent.asyncOutputStream.write(CMD_THREADDEATH_NOTIFY);
                            agent.writeObject(the_bkptQ.thread,
                                              agent.asyncOutputStream);
                            agent.asyncOutputStream.flush();
                        }
		    } else {
                        if (debug) {
                            Agent.message("Received exception " +
                                          the_bkptQ.exception.toString()
                                          + ", pc=" + the_bkptQ.pc);
                        }
			Class excClass = the_bkptQ.exception.getClass();
			if (catchHash.containsKey(excClass) ||
			    the_bkptQ.catch_pc == 0) {

			    agent.suspendAllThreads();
			    String errorText = (the_bkptQ.catch_pc == 0) ?
				"Uncaught exception: " : "Exception: ";
			    String excText = agent.exceptionStackTrace(
				the_bkptQ.exception);
			    errorText = errorText.concat(excText);

                            synchronized (agent.asyncOutputStream) {
                                agent.asyncOutputStream.write(CMD_EXCEPTION_NOTIFY);
                                agent.writeObject(the_bkptQ.thread,
                                                  agent.asyncOutputStream);
                                agent.asyncOutputStream.writeUTF(errorText);
                                agent.asyncOutputStream.flush();
                            }
			}
		    }
		} else if (the_bkptQ.opcode != -1) {
		    addBreakpoint(/*the_bkptQ.clazz*/ null,
				  the_bkptQ.pc, BKPT_NORMAL, null);
		} else {
		    bkpt = (BreakpointSet)the_bkptHash.get(
			new Integer(the_bkptQ.pc));
		    boolean ignoreBreakpoint = false;
		    if (bkpt == null) {
                        if (debug) {
                            Agent.message("Single step event: pc=" +
                                          the_bkptQ.pc);
                        }
			if (ignoreSingleStep(the_bkptQ.thread,
					     the_bkptQ.pc)) {
			    continue;
			}
			agent.setSingleStep(the_bkptQ.thread, false);
			the_bkptQ.opcode = -1;
		    } else {
                        if (debug) {
                            Agent.message("Received a breakpoint event: pc=" +
                                          the_bkptQ.pc);
                        }
			the_bkptQ.opcode = bkpt.opcode;
			the_bkptQ.updated = true;
		    }
		    agent.suspendAllThreads();

		    if (bkpt != null && bkpt.type == BKPT_ONESHOT) {
			deleteBreakpoint(bkpt.clazz, bkpt.pc);
		    }
                    synchronized (agent.asyncOutputStream) {
                        agent.asyncOutputStream.write(CMD_BRKPT_NOTIFY);
                        agent.writeObject(the_bkptQ.thread,
                                          agent.asyncOutputStream);
                        agent.asyncOutputStream.flush();
		    }
		}
	    }
	} catch (ThreadDeath td) {
            if (debug) {
                Agent.message("BreakpointHandler: ThreadDeath received.");
            }
	    /* Clear all breakpoints before exiting. */
	    Enumeration enum = the_bkptHash.elements();
	    while (enum.hasMoreElements()) {
		BreakpointSet bkpt = (BreakpointSet)enum.nextElement();
	        try {
		    clrBreakpoint(bkpt.pc, bkpt.opcode);
		} catch (Exception x) {
		    // Ignore exceptions here
		}
		the_bkptHash.remove(new Integer(bkpt.pc));
	    }
	    throw td;
	} catch (Exception ex) {
	    Agent.error(agent.exceptionStackTrace(ex));
	    Enumeration enum = the_bkptHash.elements();
	    while (enum.hasMoreElements()) {
		BreakpointSet bkpt = (BreakpointSet)enum.nextElement();
	        try {
		    clrBreakpoint(bkpt.pc, bkpt.opcode);
		} catch (Exception x) {
		    // Ignore exceptions here
		}
		the_bkptHash.remove(new Integer(bkpt.pc));
	    }
	    return;
	}
    }
}