This is the mail archive of the frysk@sources.redhat.com mailing list for the frysk project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Breakpoints


Hi,

This finally adds and merges the breakpoint support into frysk-core. I
get clean make check results on both x86 and x86_64. But other
architectures aren't supported at the moment.

There are two main differences with the first attempt. First it is a
little less abstract. The earlier version had a little bit too much
design for non-existing cases. I have stripped out anything not
explicitly needed at this time. The only architecture dependent code is
in the main BreakPoint class itself which just checks the Isa. No more
abstract classes.

The second main difference is is that it is now actually integrated in
the task and proc state machines. This means that there are no "timing
issues" anymore. And we can use the eventloop to serialize events. After
a couple of tries to integrate this with the syscall states and trying
to have another task hierarchy like the syscall states I ended up not
introducing new task states. It was really a nightmare to keep track of
the state explosion. So what I ended up doing was to add a little "extra
state" to the Running State. So the Running State is now 'magic' in that
it knows about any pending CodeObservers that need to be added or
removed and it knows about the current Breakpoint the Task is taking. I
find this setup clean and clear. But that is obviously because I spend
so much time thinking on it and not having much success with emulating
the traditional state machine design. I do think doing it this way can
also simplify some of the syscall state explosion, which should make it
easier to combine syscall tracing and breakpoints. Comments and/or
flames more than welcome.

2006-08-15  Mark Wielaard  <mark@klomp.org>

   * funit-breakpoints.c: New test prog.

2006-08-15  Mark Wielaard  <mark@klomp.org>

    * frysk/proc/Breakpoint.java: New file.
    * frysk/proc/BreakpointAddresses.java: Likewise.
    * frysk/proc/Proc.java (breakpoints): New final field.
    (Proc): Initialize breakpoints.
    (requestAddCodeObserver): New method.
    (requestDeleteCodeObserver): New method.
    * frysk/proc/Task.java (handleAddCodeObserver): New method.
    (handleDeleteCodeObserver): New method.
    (codeObservers): New field.
    (requestAddCodeObserver): New method.
    (requestDeleteCodeObserver): New method.
    (notifyCodeBreakpoint): Likewise.
    * frysk/proc/TaskCodeObservation.java: New file.
    * frysk/proc/TaskObserver.java (updateHit): Update documentation.
    * frysk/proc/TaskState.java (handleAddCodeObserver): New method.
    (handleDeleteCodeObserver): New method.
    (Running.handleStoppedEvent): Add and remove pending CodeObservers.
    (Running.steppingBreakpoint): New field.
    (Running.handleTrappedEvent): New method.
    (Running.pendingCodeObservers): New field.
    (Running.PendingCodeObserver): New struct.
    (Running.handleAddCodeObserver): New method.
    (Running.handleDeleteCodeObserver): New method.
    (BlockedSignal.handleAddCodeObserver): New method.
    (BlockedSignal.handleDeleteCodeObserver): New method.
    * frysk/proc/TestBreakpoints.java: new file.

Committed as attached.

Please let me know if I broke something horribly or if you think the
current setup is not clear.

Cheers,

Mark
? ForkTestLib.patch
? breakpoint.patch
? frysk/proc/.ChangeLog.swp
? frysk/proc/DummyProc.java.mark
Index: frysk/pkglibexecdir/ChangeLog
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/pkglibexecdir/ChangeLog,v
retrieving revision 1.28
diff -u -r1.28 ChangeLog
--- frysk/pkglibexecdir/ChangeLog	7 Aug 2006 20:22:15 -0000	1.28
+++ frysk/pkglibexecdir/ChangeLog	15 Aug 2006 16:21:30 -0000
@@ -1,3 +1,7 @@
+2006-08-15  Mark Wielaard  <mark@klomp.org>
+
+	* funit-breakpoints.c: New test prog.
+
 2006-08-07  Tim Moore  <timoore@redhat.com>
 
 	* funit-x8664-modify.S: New x8664 specific program.
Index: frysk/pkglibexecdir/funit-breakpoints.c
===================================================================
RCS file: frysk/pkglibexecdir/funit-breakpoints.c
diff -N frysk/pkglibexecdir/funit-breakpoints.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ frysk/pkglibexecdir/funit-breakpoints.c	15 Aug 2006 16:21:30 -0000
@@ -0,0 +1,110 @@
+// This file is part of the program FRYSK.
+//
+// Copyright 2006, Red Hat Inc.
+//
+// FRYSK is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// FRYSK is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with FRYSK; if not, write to the Free Software Foundation,
+// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+// 
+// In addition, as a special exception, Red Hat, Inc. gives You the
+// additional right to link the code of FRYSK with code not covered
+// under the GNU General Public License ("Non-GPL Code") and to
+// distribute linked combinations including the two, subject to the
+// limitations in this paragraph. Non-GPL Code permitted under this
+// exception must only link to the code of FRYSK through those well
+// defined interfaces identified in the file named EXCEPTION found in
+// the source code files (the "Approved Interfaces"). The files of
+// Non-GPL Code may instantiate templates or use macros or inline
+// functions from the Approved Interfaces without causing the
+// resulting work to be covered by the GNU General Public
+// License. Only Red Hat, Inc. may make changes or additions to the
+// list of Approved Interfaces. You must obey the GNU General Public
+// License in all respects for all of the FRYSK code and other code
+// used in conjunction with FRYSK except the Non-GPL Code covered by
+// this exception. If you modify this file, you may extend this
+// exception to your version of the file, but you are not obligated to
+// do so. If you do not wish to provide this exception without
+// modification, you must delete this exception statement from your
+// version and license this file solely under the GPL without
+// exception.
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+
+// Counters for how many times the breakpoint functions have been called.
+// Used as sanity check to tell the tester the functions have actually ran.
+static int bp1;
+static int bp2;
+
+static void dummy() { /* nop */ }
+
+static void
+first_breakpoint_function ()
+{
+  bp1++;
+  dummy();
+}
+
+static void
+second_breakpoint_function ()
+{
+  bp2++;
+  dummy();
+}
+
+int
+main ()
+{
+  // The number of runs the tester wants us to do.
+  // Zero when we should terminate.
+  // Minus one when something went wrong.
+  int runs;
+
+  // Function call counters.
+  bp1 = 0;
+  bp2 = 0;
+
+  // Tell the tester the addresses of the functions to put breakpoints on.
+  // XXX add ppc case
+  printf("%p\n", &first_breakpoint_function);
+  printf("%p\n", &second_breakpoint_function);
+  fflush(stdout);
+
+  // Go round and round.
+  while (1)
+    {
+      // Wait till we are OK to go!
+      runs = getchar();
+      if (runs == 0)
+	break;
+      if (runs < 0) 
+        {
+	  fprintf(stderr, "Couldn't read runs\n");
+	  break;
+        }
+
+      while(runs--)
+	{
+	  first_breakpoint_function ();
+	  second_breakpoint_function ();
+	}
+
+      printf("%d\n", bp1);
+      printf("%d\n", bp2);
+      fflush(stdout);
+    }
+
+  return runs;
+}
Index: frysk/proc/Breakpoint.java
===================================================================
RCS file: frysk/proc/Breakpoint.java
diff -N frysk/proc/Breakpoint.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ frysk/proc/Breakpoint.java	15 Aug 2006 16:21:30 -0000
@@ -0,0 +1,221 @@
+// This file is part of the program FRYSK.
+//
+// Copyright 2006, Red Hat Inc.
+//
+// FRYSK is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// FRYSK is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with FRYSK; if not, write to the Free Software Foundation,
+// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+// 
+// In addition, as a special exception, Red Hat, Inc. gives You the
+// additional right to link the code of FRYSK with code not covered
+// under the GNU General Public License ("Non-GPL Code") and to
+// distribute linked combinations including the two, subject to the
+// limitations in this paragraph. Non-GPL Code permitted under this
+// exception must only link to the code of FRYSK through those well
+// defined interfaces identified in the file named EXCEPTION found in
+// the source code files (the "Approved Interfaces"). The files of
+// Non-GPL Code may instantiate templates or use macros or inline
+// functions from the Approved Interfaces without causing the
+// resulting work to be covered by the GNU General Public
+// License. Only Red Hat, Inc. may make changes or additions to the
+// list of Approved Interfaces. You must obey the GNU General Public
+// License in all respects for all of the FRYSK code and other code
+// used in conjunction with FRYSK except the Non-GPL Code covered by
+// this exception. If you modify this file, you may extend this
+// exception to your version of the file, but you are not obligated to
+// do so. If you do not wish to provide this exception without
+// modification, you must delete this exception statement from your
+// version and license this file solely under the GPL without
+// exception.
+
+
+package frysk.proc;
+
+import inua.eio.ByteBuffer;
+
+import java.util.HashMap;
+
+/**
+ * Internal proc class that represents a Breakpoint at a certain
+ * address in a Proc.
+ */
+public class Breakpoint
+{
+  // These two fields define a Breakpoint
+  private final long address;
+  private final Proc proc;
+
+  // Whether the breakpoint is setup for a step instruction.
+  private boolean stepping;
+
+  // Static cache of installed break points.
+  private static HashMap installed = new HashMap();
+
+  // The instruction that will generate a trap signal.  Happens to be
+  // the same for x86 and x86_64, but will depend on the Isa in use
+  // for other architectures.
+  private static final byte TRAP = (byte) 0xcc;
+
+  // The original instruction at the location we replaced with TRAP.
+  private byte orig;
+
+  /**
+   * Private constructor called by create to record address and
+   * proc.
+   */
+  private Breakpoint(long address, Proc proc)
+  {
+    if (proc == null)
+      throw new NullPointerException("proc");
+
+    this.address = address;
+    this.proc = proc;
+  }
+
+  /**
+   * Creates a Breakpoint for the Proc at the given Address but does
+   * not set it yet.  Returns the appropriate Breakpoint depending on
+   * host type. If a Breakpoint for this address and proc is already
+   * installed that Breakpoint will be returned.
+   */
+  static Breakpoint create(long address, Proc proc)
+  {
+    Breakpoint breakpoint = new Breakpoint(address, proc);
+
+    // If possible return an existing installed breakpoint.
+    synchronized (installed)
+      {
+	Breakpoint existing = (Breakpoint) installed.get(breakpoint);
+	if (existing != null)
+	  return existing;
+      }
+    return breakpoint;
+  }
+
+  /**
+   * Installs breakpoint. Caller must make sure there is no breakpoint set
+   * at that address yet and that install() is not called again till remove()
+   * is called on it.
+   */
+  public void install(Task task)
+  {
+    synchronized (installed)
+      {
+	Breakpoint existing = (Breakpoint) installed.get(this);
+	if (existing != null)
+	  throw new IllegalStateException("Already installed: " + this);
+
+	installed.put(this, this);
+	set(task);
+      }
+  }
+
+  /**
+   * Actually sets the breakpoint.
+   */
+  private void set(Task task)
+  {
+    ByteBuffer buffer = task.memory;
+    buffer.position(address);
+    orig = buffer.getByte();
+    buffer.position(address);
+    buffer.putByte(TRAP);
+  }
+
+  /**
+   * Removes the breakpoint. Caller must make sure it is called only
+   * when it is installed.
+   */
+  public void remove(Task task)
+  {
+    synchronized (installed)
+      {
+	if (! this.equals(installed.remove(this)))
+	  throw new IllegalStateException("Not installed: " + this);
+
+	reset(task);
+      }
+  }
+
+  /**
+   * Actually removes the breakpoint.
+   */
+  private void reset(Task task)
+  {
+    ByteBuffer buffer = task.memory;
+    buffer.position(address);
+    buffer.putByte(orig);
+  }
+
+  // XXX Prepare step and step done are not multi-task safe.
+
+  /**
+   * Prepares the given Task for a step over the breakpoint.
+   * This sets up the program counter and makes sure the next
+   * instruction is the one on which the breakpoint was placed.
+   * Should not be called again until <code>stepDone</code> is
+   * called. The given Task should be stopped.
+   */
+  public void prepareStep(Task task) throws TaskException
+  {
+    if (stepping)
+      throw new IllegalStateException("Already stepping");
+
+    Isa isa = task.getIsa();
+    Register pc;
+    if (isa instanceof LinuxIa32)
+      pc = isa.getRegisterByName("eip");
+    else if (isa instanceof LinuxEMT64)
+      pc = isa.getRegisterByName("rip");
+    else
+      throw new RuntimeException("unsupported architecture: " + isa);
+    pc.put(task, address);
+    reset(task);
+    stepping = true;
+  }
+
+  /**
+   * Notifies the breakpoint that a step has just taken place
+   * and that the task should be put into a state ready to continue
+   * (keeping the breakpoint in place).
+   */
+  public void stepDone(Task task)
+  {
+    if (! stepping)
+      throw new IllegalStateException("Not stepping");
+
+    set(task);
+    stepping = false;
+  }
+
+  // Utility methods for keeping the map of breakpoints.
+
+  public int hashCode()
+  {
+    return (int) (address ^ (address >>> 32));
+  }
+
+  public boolean equals(Object o)
+  {
+    if (o == null || o.getClass() != this.getClass())
+      return false;
+
+    Breakpoint other = (Breakpoint) o;
+    return other.proc.equals(proc) && other.address == address;
+  }
+
+  public String toString()
+  {
+    return this.getClass().getName() + "[proc=" + proc
+      + ", address=" + Long.toHexString(address) + "]";
+  }
+}
Index: frysk/proc/BreakpointAddresses.java
===================================================================
RCS file: frysk/proc/BreakpointAddresses.java
diff -N frysk/proc/BreakpointAddresses.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ frysk/proc/BreakpointAddresses.java	15 Aug 2006 16:21:30 -0000
@@ -0,0 +1,157 @@
+// This file is part of the program FRYSK.
+//
+// Copyright 2006, Red Hat Inc.
+//
+// FRYSK is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// FRYSK is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with FRYSK; if not, write to the Free Software Foundation,
+// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+// 
+// In addition, as a special exception, Red Hat, Inc. gives You the
+// additional right to link the code of FRYSK with code not covered
+// under the GNU General Public License ("Non-GPL Code") and to
+// distribute linked combinations including the two, subject to the
+// limitations in this paragraph. Non-GPL Code permitted under this
+// exception must only link to the code of FRYSK through those well
+// defined interfaces identified in the file named EXCEPTION found in
+// the source code files (the "Approved Interfaces"). The files of
+// Non-GPL Code may instantiate templates or use macros or inline
+// functions from the Approved Interfaces without causing the
+// resulting work to be covered by the GNU General Public
+// License. Only Red Hat, Inc. may make changes or additions to the
+// list of Approved Interfaces. You must obey the GNU General Public
+// License in all respects for all of the FRYSK code and other code
+// used in conjunction with FRYSK except the Non-GPL Code covered by
+// this exception. If you modify this file, you may extend this
+// exception to your version of the file, but you are not obligated to
+// do so. If you do not wish to provide this exception without
+// modification, you must delete this exception statement from your
+// version and license this file solely under the GPL without
+// exception.
+
+
+package frysk.proc;
+
+import java.util.*;
+
+/**
+ * Keeps track of address breakpoints for a Proc (all Tasks of a Proc
+ * share the same breakpoints).  Address breakpoints are absolute
+ * addresses in the Proc text area.  This class is used to construct
+ * higher level breakpoint observers.  The class keeps track of the
+ * number of observers interested in an address for the Proc. It adds
+ * or deletes the actual breakpoint depending on the number of active
+ * observers.  But it does not handle mapping to and from the language
+ * model to the actual addresses.  It also doesn't handle tracking of
+ * future breakpoints and code module loading.
+ * <p>
+ * This datastructure isn't multithread safe, it should only be called
+ * from the eventloop in response to requests pending for the Proc.
+ */
+public class BreakpointAddresses
+{
+  /**
+   * Proc used to set breakpoints and which sents us notifications
+   * when breakpoints are hit.
+   */
+  private final Proc proc;
+
+  /**
+   * Maps breakpoint addresses to a list of observers.  We assume the
+   * number of observers for each address is small, so an ArrayList
+   * will do.
+   */
+  private final HashMap map;
+  
+  /**
+   * Package private constructor used by the Proc when created.
+   */
+  BreakpointAddresses(Proc proc)
+  {
+    this.proc = proc;
+    map = new HashMap();
+  }
+
+  /**
+   * Adds a breakpoint observer to an address. If there is not yet a
+   * breakpoint at the given address the given Task is asked to add
+   * one (the method will return true). Otherwise the observer is
+   * added to the list of objects to notify when the breakpoint is
+   * hit (and the method returns false).
+   */
+  public boolean addBreakpoint(TaskObserver.Code observer)
+  {
+    long address = observer.getAddress();
+    Breakpoint breakpoint = Breakpoint.create(address, proc);
+
+    ArrayList list = (ArrayList) map.get(breakpoint);
+    if (list == null)
+      {
+	list = new ArrayList();
+	map.put(breakpoint, list);
+	list.add(observer);
+	return true;
+      }
+    else
+      {
+	list.add(observer);
+	return false;
+      }
+  }
+
+  /**
+   * Removes an observer from a breakpoint. If this is the last
+   * observer interested in this particular address then the
+   * breakpoint is really removed by requestion the given task to do
+   * so (the method will return true). Otherwise just this observer
+   * will be removed from the list of observers for the breakpoint
+   * address (and the method will return false).
+   *
+   * @throws IllegalArgumentException if the observer was never added.
+   */
+  public boolean removeBreakpoint(TaskObserver.Code observer)
+  {
+    long address = observer.getAddress();
+    Breakpoint breakpoint = Breakpoint.create(address, proc);
+    ArrayList list = (ArrayList) map.get(breakpoint);
+    if (list == null || ! list.remove(observer))
+      throw new IllegalArgumentException("No breakpoint installed: "
+					 + breakpoint);
+    
+    if (list.isEmpty())
+      {
+	map.remove(breakpoint);
+	return true;
+      }
+    else
+      return false;
+  }
+
+  /**
+   * Called by the Proc when it has trapped a breakpoint.  Returns an
+   * Iterator of TaskObserver.Code observers interested in the given
+   * address.
+   */
+  Iterator getCodeObservers(long address)
+  {
+    ArrayList observers;
+    Breakpoint breakpoint = Breakpoint.create(address, proc);
+    ArrayList list = (ArrayList) map.get(breakpoint);
+    if (list == null)
+      throw new IllegalArgumentException();
+    
+    // Return the cloned list of observers in case the Code observer
+    // wants to add or remove itself or a new observer to that same
+    // breakpoint.
+    observers = (ArrayList) list.clone();
+    return observers.iterator();
+  }
+}
Index: frysk/proc/ChangeLog
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/ChangeLog,v
retrieving revision 1.392
diff -u -r1.392 ChangeLog
--- frysk/proc/ChangeLog	15 Aug 2006 13:12:44 -0000	1.392
+++ frysk/proc/ChangeLog	15 Aug 2006 16:21:31 -0000
@@ -1,5 +1,9 @@
 2006-08-15  Mark Wielaard  <mark@klomp.org>
 
+	* frysk/proc/Breakpoint.java: New file.
+
+2006-08-15  Mark Wielaard  <mark@klomp.org>
+
 	* ForkTestLib.java: New file.
 	* cni/ForkTestLib.cxx: Likewise.
 
Index: frysk/proc/Proc.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/Proc.java,v
retrieving revision 1.77
diff -u -r1.77 Proc.java
--- frysk/proc/Proc.java	5 Jul 2006 18:07:50 -0000	1.77
+++ frysk/proc/Proc.java	15 Aug 2006 16:21:31 -0000
@@ -151,6 +151,8 @@
     protected abstract String[] sendrecCmdLine ();
     private String[] argv;
 
+    final BreakpointAddresses breakpoints;
+
     /**
      * Create a new Proc skeleton.  Since PARENT could be NULL,
      * explicitly specify the HOST.
@@ -161,6 +163,7 @@
 	this.id = id;
 	this.parent = parent;
 	this.creator = creator;
+	this.breakpoints = new BreakpointAddresses(this);
 	// Keep parent informed.
 	if (parent != null)
 	    parent.add (this);
@@ -410,6 +413,48 @@
     }
 
     /**
+     * (Internal) Tell the process to add the specified Code Observation,
+     * attaching to the process if necessary.
+     * Adds a TaskCodeObservation to the eventloop which instructs the
+     * task to install the breakpoint if necessary.
+     */
+    void requestAddCodeObserver (Task task,
+				 TaskObservable observable,
+				 TaskObserver.Code observer)
+    {
+	logger.log (Level.FINE, "{0} requestAddCodeObserver\n", this); 
+	TaskCodeObservation tco;
+	tco = new TaskCodeObservation(task, observable, observer)
+	  {
+	    public void execute ()
+	    {
+	      handleAddObservation (this);
+	    }
+	  };
+	Manager.eventLoop.add(tco);
+    }
+    
+    /**
+     * (Internal) Tell the process to delete the specified
+     * Code Observation, detaching from the process if necessary.
+     */
+    void requestDeleteCodeObserver (Task task,
+				    TaskObservable observable,
+				    TaskObserver.Code observer)
+    {
+      TaskCodeObservation tco;
+      tco = new TaskCodeObservation(task, observable, observer)
+	{
+	  public void execute()
+	  {
+	    newState = oldState().handleDeleteObservation(Proc.this, this);
+	  }
+	};
+
+      Manager.eventLoop.add(tco);
+    }
+
+    /**
      * Table of this processes child processes.
      */
     private Set childPool = new HashSet ();
Index: frysk/proc/Task.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/Task.java,v
retrieving revision 1.92
diff -u -r1.92 Task.java
--- frysk/proc/Task.java	7 Aug 2006 20:22:15 -0000	1.92
+++ frysk/proc/Task.java	15 Aug 2006 16:21:31 -0000
@@ -299,6 +299,26 @@
   }
 
   /**
+   * Called by <code>TaskCodeObservation</code> when added through the
+   * event loop.
+   */
+  void handleAddCodeObserver (Observable observable,
+			      TaskObserver.Code observer)
+  {
+    newState = oldState().handleAddCodeObserver(this, observable, observer);
+  }
+
+  /**
+   * Called by <code>TaskCodeObservation</code> when deleted through the
+   * event loop.
+   */
+  void handleDeleteCodeObserver (Observable observable,
+				 TaskObserver.Code observer)
+  {
+    newState = oldState().handleDeleteCodeObserver(this, observable, observer);
+  }
+
+  /**
    * (Internal) Tell the task to remove itself (it is no longer listed in the
    * system process table and, presumably, has exited).
    */
@@ -870,4 +890,46 @@
    * Turns off systemcall entry and exit tracing 
    */
   protected abstract void stopTracingSyscalls ();
+
+  /**
+   * Set of Code observers.
+   */
+  private TaskObservable codeObservers = new TaskObservable(this);
+  
+  /**
+   * Add TaskObserver.Code to the TaskObserver pool.
+   */
+  public void requestAddCodeObserver (TaskObserver.Code o)
+  {
+    logger.log(Level.FINE, "{0} requestAddCodeObserver\n", this);
+    proc.requestAddCodeObserver(this, codeObservers, o);
+  }
+
+  /**
+   * Delete TaskObserver.Code for the TaskObserver pool.
+   */
+  public void requestDeleteCodeObserver (TaskObserver.Code o)
+  {
+    logger.log(Level.FINE, "{0} requestDeleteCodeObserver\n", this);
+    proc.requestDeleteCodeObserver(this, codeObservers, o);
+  }
+  
+  /**
+   * Notify all Code observers of the breakpoint. Return the number of
+   * blocking observers.
+   */
+  int notifyCodeBreakpoint (long address)
+  {
+    logger.log(Level.FINE, "{0} notifyCodeBreakpoint({1})\n",
+	       new Object[] { this, Long.valueOf(address) });
+    
+    Iterator i = proc.breakpoints.getCodeObservers(address);
+    while (i.hasNext())
+      {
+	TaskObserver.Code observer = (TaskObserver.Code) i.next();
+	if (observer.updateHit(this) == Action.BLOCK)
+	  blockers.add(observer);
+      }
+    return blockers.size();
+  }
 }
Index: frysk/proc/TaskCodeObservation.java
===================================================================
RCS file: frysk/proc/TaskCodeObservation.java
diff -N frysk/proc/TaskCodeObservation.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ frysk/proc/TaskCodeObservation.java	15 Aug 2006 16:21:31 -0000
@@ -0,0 +1,76 @@
+// This file is part of the program FRYSK.
+//
+// Copyright 2006, Red Hat Inc.
+//
+// FRYSK is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// FRYSK is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with FRYSK; if not, write to the Free Software Foundation,
+// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+// 
+// In addition, as a special exception, Red Hat, Inc. gives You the
+// additional right to link the code of FRYSK with code not covered
+// under the GNU General Public License ("Non-GPL Code") and to
+// distribute linked combinations including the two, subject to the
+// limitations in this paragraph. Non-GPL Code permitted under this
+// exception must only link to the code of FRYSK through those well
+// defined interfaces identified in the file named EXCEPTION found in
+// the source code files (the "Approved Interfaces"). The files of
+// Non-GPL Code may instantiate templates or use macros or inline
+// functions from the Approved Interfaces without causing the
+// resulting work to be covered by the GNU General Public
+// License. Only Red Hat, Inc. may make changes or additions to the
+// list of Approved Interfaces. You must obey the GNU General Public
+// License in all respects for all of the FRYSK code and other code
+// used in conjunction with FRYSK except the Non-GPL Code covered by
+// this exception. If you modify this file, you may extend this
+// exception to your version of the file, but you are not obligated to
+// do so. If you do not wish to provide this exception without
+// modification, you must delete this exception statement from your
+// version and license this file solely under the GPL without
+// exception.
+
+package frysk.proc;
+
+/**
+ * Observation on tasks that will call
+ * <code>handleAddCodeObserver</code> on the given Task from
+ * <code>handleAdd</code> and <code>handleDeleteCodeObserver</code> on
+ * <code>handleDelete</code>.
+ */
+abstract class TaskCodeObservation
+  extends TaskObservation
+{
+  /**
+   * Create a new CodeObserver binding.
+   */
+  public TaskCodeObservation(Task task,
+			     Observable observable,
+			     TaskObserver.Code observer)
+  {
+    super(task, observable, observer);
+  }
+
+  /**
+   * Handle adding the SyscallObserver to the Observable.
+   */
+  public void handleAdd()
+  {
+    task.handleAddCodeObserver(observable, (TaskObserver.Code) observer);
+  }
+
+  /**
+   * Handle deleting the SyscallObserver from the Observable.
+   */
+  public void handleDelete ()
+  {
+    task.handleDeleteCodeObserver(observable, (TaskObserver.Code) observer);
+  }
+}
Index: frysk/proc/TaskObserver.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/TaskObserver.java,v
retrieving revision 1.15
diff -u -r1.15 TaskObserver.java
--- frysk/proc/TaskObserver.java	30 May 2006 05:10:08 -0000	1.15
+++ frysk/proc/TaskObserver.java	15 Aug 2006 16:21:31 -0000
@@ -202,7 +202,9 @@
 	long getAddress ();
 	/**
 	 * The task has hit the breakpoint.  Return Action.BLOCK to
-	 * block the task's further execution.
+	 * block the task's further execution.  Note that all Tasks of
+	 * a Proc share their breakpoints, so this method needs to
+	 * check the actual Task that got hit.
 	 */
 	Action updateHit (Task task);
     }
Index: frysk/proc/TaskState.java
===================================================================
RCS file: /cvs/frysk/frysk-core/frysk/proc/TaskState.java,v
retrieving revision 1.101
diff -u -r1.101 TaskState.java
--- frysk/proc/TaskState.java	27 Jul 2006 18:29:50 -0000	1.101
+++ frysk/proc/TaskState.java	15 Aug 2006 16:21:31 -0000
@@ -41,6 +41,9 @@
 
 import java.util.logging.Level;
 
+import java.util.Iterator;
+import java.util.LinkedList;
+
 /**
  * The task state machine.
  */
@@ -158,6 +161,17 @@
     {
 	throw unhandled (task, "handleDeleteSyscallObserver");
     }
+    TaskState handleAddCodeObserver(Task task, Observable observable,
+				    TaskObserver.Code observer)
+    {
+      throw unhandled (task, "handleAddCodeObserver");
+    } 
+    TaskState handleDeleteCodeObserver(Task task, Observable observable,
+				       TaskObserver.Code observer)
+    {
+      throw unhandled (task, "handleDeleteCodeObserver");
+    } 
+
 
     
     /**
@@ -868,13 +882,52 @@
 	}
 	TaskState handleStoppedEvent (Task task)
 	{
-	    // From time to time bogus stop events appear, for
-	    // instance when the kernel simultaneously receives both
-	    // an attach and signal for an identical process.  Just
-	    // discard them.
-	    logger.log (Level.FINE, "{0} handleStoppedEvent\n", task); 
-	    return this;
+	  if (pendingCodeObservers.isEmpty())
+	    {
+	      // From time to time bogus stop events appear, for
+	      // instance when the kernel simultaneously receives both
+	      // an attach and signal for an identical process.  Just
+	      // discard them.
+	      logger.log (Level.FINE,
+			  "{0} spurious handleStoppedEvent\n", task); 
+	      return this;
+	    }
+
+	  logger.log (Level.FINE, "{0} handleStoppedEvent\n", task); 
+	  Iterator it = pendingCodeObservers.iterator();
+	  while (it.hasNext())
+	    {
+	      PendingCodeObserver pco = (PendingCodeObserver) it.next();
+	      if (pco.addition)
+		{
+		  if (task.proc.breakpoints.addBreakpoint(pco.observer))
+		    {
+		      long address = pco.observer.getAddress();
+		      Breakpoint breakpoint;
+		      breakpoint = Breakpoint.create(address, task.proc);
+		      breakpoint.install(task);
+		    }
+		  pco.observable.add(pco.observer);
+		}
+	      else
+		{
+		  if (task.proc.breakpoints.removeBreakpoint(pco.observer))
+		    {
+		      long address = pco.observer.getAddress();
+		      Breakpoint breakpoint;
+		      breakpoint = Breakpoint.create(address, task.proc);
+		      breakpoint.remove(task);
+		    }
+		  pco.observable.delete(pco.observer);
+		}
+	      it.remove();
+	    }
+
+	  // And pretend nothing happend, happily continue running.
+	  task.sendContinue(0);
+	  return this;
 	}
+
 	TaskState handleTerminatingEvent (Task task, boolean signal,
 					  int value)
 	{
@@ -949,6 +1002,117 @@
 	    task.sendContinue (0);
 	    return running;
 	}
+
+      // Whether we are currently stepping over a breakpoint.
+      private Breakpoint steppingBreakpoint;
+
+      /**
+       * Handles traps caused by breakpoints. If there are any Code
+       * observers at the address of the trap they get notified. If
+       * none of the Code observers blocks we continue over the
+       * breakpoint (breakpoint stepping state), otherwise we block
+       * till all blocking observers are happy (breakpoint stopped
+       * state).
+       */
+      TaskState handleTrappedEvent (Task task)
+      {
+	logger.log (Level.FINE, "{0} handleTrappedEvent\n", task);
+
+	if (steppingBreakpoint != null)
+	  {
+	    steppingBreakpoint.stepDone(task);
+	    steppingBreakpoint = null;
+	    task.sendContinue(0);
+	    return this;
+	  }
+
+	// FIXME This might not hold for all architectures.
+	// Should probably be handled by the Isa.
+	long address;
+	try
+	  {
+	    address = task.getIsa().pc(task) - 1;
+	  }
+	catch (TaskException tte)
+	  {
+	    // XXX - Now what - did the process die suddenly?
+	    throw new RuntimeException(tte);
+	  }
+
+	int blockers = task.notifyCodeBreakpoint(address);
+	if (blockers == 0)
+	  {
+	    try
+	      {
+		Breakpoint bp = Breakpoint.create(address, task.getProc());
+		bp.prepareStep(task);
+		task.sendStepInstruction(0);
+		steppingBreakpoint = bp;
+		return running;
+	      }
+	    catch (TaskException te)
+	      {
+		// Argh, major trouble! No way to recover from this one...
+		throw new RuntimeException(te);
+	      }
+	  }
+	else
+	  return blockedContinue;
+      }
+
+      // List containing the CodeObservers that are pending addition
+      // or deletion (in order that they were requested).
+      LinkedList pendingCodeObservers = new LinkedList();
+
+      // Small struct to put in pendingCodeObservers
+      static final class PendingCodeObserver
+      {
+	// True if this observer needs to be added,
+	// false if it needs to be deleted deletion.
+	boolean addition;
+
+	// The Code observer to add or delete.
+	TaskObserver.Code observer;
+
+	// The observable - XXX isn't this always the task?
+	Observable observable;
+
+	public String toString()
+	{
+	  return ("PendingCodeObserver[observer=" +observer
+		  + ", addition=" + addition
+		  + ", observable=" + observable + "]");
+	}
+      }
+
+      TaskState handleAddCodeObserver(Task task, Observable observable,
+				      TaskObserver.Code observer)
+      {
+	// We cannot add or delete when running, push it on the queue
+	// and stop the task.
+	PendingCodeObserver pco = new PendingCodeObserver();
+	pco.addition = true;
+	pco.observer = observer;
+	pco.observable = observable;
+	pendingCodeObservers.add(pco);
+	task.sendStop();
+	return this;
+      }
+
+      TaskState handleDeleteCodeObserver(Task task, Observable observable,
+					 TaskObserver.Code observer)
+      {
+	// We cannot add or delete when running, push it on the queue
+	// and stop the task.
+	PendingCodeObserver pco = new PendingCodeObserver();
+	pco.addition = false;
+	pco.observer = observer;
+	pco.observable = observable;
+	pendingCodeObservers.add(pco);
+	task.sendStop();
+	return this;
+      }
+
 	TaskState handleAddObserver (Task task, Observable observable,
 				     Observer observer)
 	{
@@ -1440,8 +1604,33 @@
 	      return new SyscallBlockedSignal(sig);
 	    }
   	  }
-	 }
-    
+
+      TaskState handleAddCodeObserver(Task task, Observable observable,
+				      TaskObserver.Code observer)
+      {
+	if (task.proc.breakpoints.addBreakpoint(observer))
+	  {
+	    long address = observer.getAddress();
+	    Breakpoint breakpoint = Breakpoint.create(address, task.proc);
+	    breakpoint.install(task);
+	  }
+	observable.add(observer);
+	return this;
+      }
+
+      TaskState handleDeleteCodeObserver(Task task, Observable observable,
+					 TaskObserver.Code observer)
+      {
+	if (task.proc.breakpoints.removeBreakpoint(observer))
+	  {
+	    long address = observer.getAddress();
+	    Breakpoint breakpoint = Breakpoint.create(address, task.proc);
+	    breakpoint.remove(task);
+	  }
+	observable.delete(observer);
+	return this;
+      }
+    }
     
     /**
      * The task is in the blocked state with no pending signal.
Index: frysk/proc/TestBreakpoints.java
===================================================================
RCS file: frysk/proc/TestBreakpoints.java
diff -N frysk/proc/TestBreakpoints.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ frysk/proc/TestBreakpoints.java	15 Aug 2006 16:21:31 -0000
@@ -0,0 +1,702 @@
+// This file is part of the program FRYSK.
+//
+// Copyright 2006, Red Hat Inc.
+//
+// FRYSK is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation; version 2 of the License.
+//
+// FRYSK is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+// 
+// You should have received a copy of the GNU General Public License
+// along with FRYSK; if not, write to the Free Software Foundation,
+// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+// 
+// In addition, as a special exception, Red Hat, Inc. gives You the
+// additional right to link the code of FRYSK with code not covered
+// under the GNU General Public License ("Non-GPL Code") and to
+// distribute linked combinations including the two, subject to the
+// limitations in this paragraph. Non-GPL Code permitted under this
+// exception must only link to the code of FRYSK through those well
+// defined interfaces identified in the file named EXCEPTION found in
+// the source code files (the "Approved Interfaces"). The files of
+// Non-GPL Code may instantiate templates or use macros or inline
+// functions from the Approved Interfaces without causing the
+// resulting work to be covered by the GNU General Public
+// License. Only Red Hat, Inc. may make changes or additions to the
+// list of Approved Interfaces. You must obey the GNU General Public
+// License in all respects for all of the FRYSK code and other code
+// used in conjunction with FRYSK except the Non-GPL Code covered by
+// this exception. If you modify this file, you may extend this
+// exception to your version of the file, but you are not obligated to
+// do so. If you do not wish to provide this exception without
+// modification, you must delete this exception statement from your
+// version and license this file solely under the GPL without
+// exception.
+
+package frysk.proc;
+
+import java.io.*;
+
+public class TestBreakpoints
+  extends TestLib
+{
+  // Process id and Proc representation of our test program.
+  int pid;
+  Proc proc;
+
+  // How we communicate with the test program.
+  BufferedReader in;
+  DataOutputStream out;
+
+  // The thread that handles the event loop.
+  EventLoopRunner eventloop;
+
+  // Whether we are attached (the AttachedObserver has triggered).
+  boolean attached;
+
+  // How the process exits (set by the TerminatingObserver).
+  boolean terminating;
+  boolean exitSignal;
+  int exitValue;
+
+  // Monitor to notify and wait on for state of event changes..
+  static Object monitor = new Object();
+
+  /**
+   * Launch our test program and setup clean environment with a runner
+   * eventloop.
+   */
+  public void setUp()
+  {
+    // Make sure everything is setup so spawned processes are recognized
+    // and destroyed in tearDown().
+    super.setUp();
+
+    // Create a process that we will communicate with through stdin/out.
+    String command = TestLib.getExecPrefix() + "funit-breakpoints";
+    ForkTestLib.ForkedProcess process;
+    process = ForkTestLib.fork(new String[] { command });
+    pid = process.pid;
+    in = new BufferedReader(new InputStreamReader(process.in));
+    out = new DataOutputStream(process.out);
+
+    // Make sure the core knows about it.
+    Manager.host.requestRefreshXXX(true);
+    Manager.eventLoop.runPending();
+    proc = Manager.host.getProc(new ProcId(pid));
+
+    attached = false;
+    terminating = false;
+
+    // Start an EventLoop so we don't have to poll for events all the time.
+    eventloop = new EventLoopRunner();
+    eventloop.start();
+  }
+
+  /**
+   * Make sure the test program is really gone and the event loop is
+   * stopped.  Individual tests are responsible for nice termination
+   * if the want to.
+   */
+  public void tearDown()
+  {
+    // Make sure event loop is gone.
+    eventloop.requestStop();
+    synchronized (monitor)
+      {
+	while (!eventloop.isStopped())
+	  {
+	    try
+	      {
+		monitor.wait();
+	      }
+	    catch (InterruptedException ie)
+	      {
+		// Ignored
+	      }
+	  }
+      }
+
+    // And kill off any remaining processes we spawned
+    super.tearDown();
+  }
+
+  public void testHitAndRun() throws IOException
+  {
+    String line;
+    
+    // Request addresses to put breakpoints on.
+    line = in.readLine();
+    long breakpoint1 = Long.decode(line).longValue();
+
+    // Never used.
+    line = in.readLine();
+    long breakpoint2 = Long.decode(line).longValue();
+
+    // Make sure we are attached - XXX - do we need this?
+    // Can't the requestAddCodeObserver() do that for us?
+    // The observer will block the task so we can put in the breakpoints.
+    Task task = proc.getMainTask();
+
+    AttachedObserver ao = new AttachedObserver();
+    task.requestAddAttachedObserver(ao);
+
+    TerminatingObserver to = new TerminatingObserver();
+    task.requestAddTerminatingObserver(to);
+
+    // Wait till we are attached.
+    synchronized (monitor)
+      {
+	while (! attached)
+	  {
+	    try
+	      {
+		monitor.wait();
+	      }
+	    catch (InterruptedException ie)
+	      {
+		// Ignored
+	      }
+	  }
+      }
+
+    // Put in breakpoint observers
+    CodeObserver code1 = new CodeObserver(breakpoint1);
+    task.requestAddCodeObserver(code1);
+    CodeObserver code2 = new CodeObserver(breakpoint2);
+    task.requestAddCodeObserver(code2);
+
+    // Make sure the observers are properly installed.
+    synchronized (monitor)
+      {
+	while (! code1.isAdded() || ! code2.isAdded())
+	  {
+	    try
+	      {
+		monitor.wait();
+	      }
+	    catch (InterruptedException ie)
+	      {
+		// ignored
+	      }
+	  }
+      }
+
+    // Unblock and tell the process to go some rounds!
+    task.requestUnblock(ao);
+
+    out.writeByte(42);
+    out.flush();
+    
+    // Sanity check that the functions have actually been run.
+    line = in.readLine();
+    int bp1 = Integer.decode(line).intValue();
+    line = in.readLine();
+    int bp2 = Integer.decode(line).intValue();
+
+    assertEquals(42, bp1);
+    assertEquals(42, bp2);
+
+    assertEquals(42, code1.getTriggered());
+    assertEquals(42, code2.getTriggered());
+
+    // We are all done, you can go now.
+    out.writeByte(0);
+    out.flush();
+
+    // So how did it all go, did it exit properly?
+    synchronized (monitor)
+      {
+	while (! terminating)
+	  {
+	    try
+	      {
+		monitor.wait();
+	      }
+	    catch (InterruptedException ie)
+	      {
+		// Ignored
+	      }
+	  }
+      }
+
+    assertEquals(0, exitValue);
+    assertFalse(exitSignal);
+  }
+
+  public void testInsertRemove() throws IOException
+  {
+    String line;
+
+    // Request addresses to put breakpoints on.
+    line = in.readLine();
+    long breakpoint1 = Long.decode(line).longValue();
+    line = in.readLine();
+    long breakpoint2 = Long.decode(line).longValue();
+
+    // Make sure we are attached and look for termination.
+    Task task = proc.getMainTask();
+
+    AttachedObserver ao = new AttachedObserver();
+    task.requestAddAttachedObserver(ao);
+    TerminatingObserver to = new TerminatingObserver();
+    task.requestAddTerminatingObserver(to);
+
+    // Wait till we are attached.
+    synchronized (monitor)
+      {
+	while (! attached)
+	  {
+	    try
+	      {
+		monitor.wait();
+	      }
+	    catch (InterruptedException ie)
+	      {
+		// Ignored
+	      }
+	  }
+      }
+
+    // Put in breakpoint observers
+    CodeObserver code1 = new CodeObserver(breakpoint1);
+    CodeObserver code2 = new CodeObserver(breakpoint2);
+    task.requestAddCodeObserver(code1);
+    task.requestAddCodeObserver(code2);
+
+    // Make sure the observers are properly installed.
+    synchronized (monitor)
+      {
+	while (! code1.isAdded() || ! code2.isAdded())
+	  {
+	    try
+	      {
+		monitor.wait();
+	      }
+	    catch (InterruptedException ie)
+	      {
+		// ignored
+	      }
+	  }
+      }
+
+    // Unblock and tell the process to go!
+    task.requestUnblock(ao);
+
+    // Run a couple of times.
+    out.writeByte(3);
+    out.flush();
+
+    // Sanity check that the functions have actually been run.
+    line = in.readLine();
+    int bp1 = Integer.decode(line).intValue();
+    line = in.readLine();
+    int bp2 = Integer.decode(line).intValue();
+
+    assertEquals(3, bp1);
+    assertEquals(3, bp2);
+    assertEquals(3, code1.getTriggered());
+    assertEquals(3, code2.getTriggered());
+
+    // Remove one breakpoint.
+    task.requestDeleteCodeObserver(code2);
+
+    // Wait for removal
+    synchronized (monitor)
+      {
+        while (! code2.isRemoved())
+          {
+            try
+              {
+                monitor.wait();
+              }
+            catch (InterruptedException ie)
+              {
+                // Ignored
+              }
+          }
+      }
+
+    // And go again for 5 times.
+    out.writeByte(5);
+    out.flush();
+
+    line = in.readLine();
+    bp1 = Integer.decode(line).intValue();
+    line = in.readLine();
+    bp2 = Integer.decode(line).intValue();
+
+    assertEquals(8, bp1);
+    assertEquals(8, bp2);
+    assertEquals(8, code1.getTriggered());
+    assertEquals(3, code2.getTriggered());
+
+    // Remove the other breakpoint.
+    task.requestDeleteCodeObserver(code1);
+
+    // Wait for removal
+    synchronized (monitor)
+      {
+        while (! code1.isRemoved())
+          {
+            try
+              {
+                monitor.wait();
+              }
+            catch (InterruptedException ie)
+              {
+                // Ignored
+              }
+          }
+      }
+
+    // And go again for 5 times.
+    out.writeByte(5);
+    out.flush();
+
+    line = in.readLine();
+    bp1 = Integer.decode(line).intValue();
+    line = in.readLine();
+    bp2 = Integer.decode(line).intValue();
+
+    // And we are done.
+    out.writeByte(0);
+    out.flush();
+
+    // Wait for termination. How did it all go, did it exit properly?
+    synchronized (monitor)
+      {
+	while (! terminating)
+	  {
+	    try
+	      {
+		monitor.wait();
+	      }
+	    catch (InterruptedException ie)
+	      {
+		// Ignored
+	      }
+	  }
+      }
+
+    assertEquals(0, exitValue);
+    assertFalse(exitSignal);
+
+    assertEquals(13, bp1);
+    assertEquals(13, bp2);
+    assertEquals(8, code1.getTriggered());
+    assertEquals(3, code2.getTriggered());
+  }
+
+  public void testAddLots() throws IOException
+  {
+    String line;
+
+    // Request addresses to put breakpoints on.
+    line = in.readLine();
+    long breakpoint1 = Long.decode(line).longValue();
+    line = in.readLine();
+    long breakpoint2 = Long.decode(line).longValue();
+
+    // Make sure we are attached and look for termination.
+    Task task = proc.getMainTask();
+
+    AttachedObserver ao = new AttachedObserver();
+    task.requestAddAttachedObserver(ao);
+    TerminatingObserver to = new TerminatingObserver();
+    task.requestAddTerminatingObserver(to);
+
+    // Wait till we are attached.
+    synchronized (monitor)
+      {
+	while (! attached)
+	  {
+	    try
+	      {
+		monitor.wait();
+	      }
+	    catch (InterruptedException ie)
+	      {
+		// Ignored
+	      }
+	  }
+      }
+
+    // Put in breakpoint observers
+    CodeObserver[] codes1 = new CodeObserver[1512];
+    for (int i = 0; i < 1512; i++)
+      {
+	CodeObserver code = new CodeObserver(breakpoint1);
+	task.requestAddCodeObserver(code);
+	codes1[i] = code;
+      }
+    CodeObserver[] codes2 = new CodeObserver[1512];
+    for (int i = 0; i < 1512; i++)
+      {
+	CodeObserver code = new CodeObserver(breakpoint2);
+	task.requestAddCodeObserver(code);
+	codes2[i] = code;
+      }
+
+    // Make sure the observers are properly installed.
+    synchronized (monitor)
+      {
+	boolean allAdded = true;
+	for (int i = 0; i < 1512 && allAdded; i++)
+	  allAdded = codes1[i].isAdded() && codes2[i].isAdded();
+	while (! allAdded)
+	  {
+	    try
+	      {
+		monitor.wait();
+	      }
+	    catch (InterruptedException ie)
+	      {
+		// ignored
+	      }
+	    
+	    allAdded = true;
+	    for (int i = 0; i < 1512 && allAdded; i++)
+	      allAdded = codes1[i].isAdded() && codes2[i].isAdded();
+	  }
+      }
+
+    // Unblock and tell the process to go!
+    task.requestUnblock(ao);
+
+    // Run a couple of times.
+    out.writeByte(42);
+    out.flush();
+
+    // Sanity check that the functions have actually been run.
+    line = in.readLine();
+    int bp1 = Integer.decode(line).intValue();
+    line = in.readLine();
+    int bp2 = Integer.decode(line).intValue();
+
+    assertEquals(42, bp1);
+    assertEquals(42, bp2);
+
+    // And we are done.
+    out.writeByte(0);
+    out.flush();
+
+    // Wait for termination. How did it all go, did it exit properly?
+    synchronized (monitor)
+      {
+	while (! terminating)
+	  {
+	    try
+	      {
+		monitor.wait();
+	      }
+	    catch (InterruptedException ie)
+	      {
+		// Ignored
+	      }
+	  }
+      }
+
+    assertEquals(0, exitValue);
+    assertFalse(exitSignal);
+
+    for (int i = 0; i < 1512; i++)
+      {
+	assertEquals(42, codes1[i].getTriggered());
+	assertEquals(42, codes2[i].getTriggered());
+      }
+  }
+  
+
+  class AttachedObserver implements TaskObserver.Attached
+  {
+    private boolean added;
+
+    public Action updateAttached(Task task)
+    {
+      synchronized (monitor)
+	{
+	  attached = true;
+	  monitor.notifyAll();
+	}
+      return Action.BLOCK;
+    }
+
+    public void addFailed(Object observable, Throwable w)
+    {
+      w.printStackTrace();
+      fail(w.getMessage());
+    }
+    
+    public void addedTo(Object observable)
+    {
+      // Hurray! Lets notify everybody.
+      synchronized (monitor)
+	{
+	  added = true;
+	  monitor.notifyAll();
+	}
+    }
+
+    public boolean isAdded()
+    {
+      return added;
+    }
+    
+    public void deletedFrom(Object observable)
+    {
+      // Ignored
+    }
+  }
+
+  class TerminatingObserver implements TaskObserver.Terminating
+  {
+    private boolean added;
+    
+    public Action updateTerminating (Task task, boolean signal, int value)
+    {
+      synchronized (monitor)
+	{
+	  terminating = true;
+	  exitValue = value;
+	  exitSignal = signal;
+	  monitor.notifyAll();
+	}
+      return Action.CONTINUE;
+    }
+
+    public void addFailed(Object observable, Throwable w)
+    {
+      w.printStackTrace();
+      fail(w.getMessage());
+    }
+    
+    public void addedTo(Object observable)
+    {
+      // Hurray! Lets notify everybody.
+      synchronized (monitor)
+	{
+	  added = true;
+	  monitor.notifyAll();
+	}
+    }
+
+    public boolean isAdded()
+    {
+      return added;
+    }
+    
+    public void deletedFrom(Object observable)
+    {
+      // Ignored
+    }
+  }
+
+  class CodeObserver implements TaskObserver.Code
+  {
+    private long address;
+    private int triggered;
+
+    private boolean added;
+    private boolean removed;
+
+    CodeObserver(long address)
+    {
+      this.address = address;
+    }
+
+    public Action updateHit (Task task)
+    {
+      triggered++;
+      return Action.CONTINUE;
+    }
+
+    public long getAddress()
+    {
+      return address;
+    }
+
+    int getTriggered()
+    {
+      return triggered;
+    }
+
+    public void addFailed(Object observable, Throwable w)
+    {
+      w.printStackTrace();
+      fail(w.getMessage());
+    }
+    
+    public void addedTo(Object observable)
+    {
+      // Hurray! Lets notify everybody.
+      synchronized (monitor)
+	{
+	  added = true;
+	  removed = false;
+	  monitor.notifyAll();
+	}
+    }
+
+    public boolean isAdded()
+    {
+      return added;
+    }
+    
+    public void deletedFrom(Object observable)
+    {
+      synchronized (monitor)
+	{
+	  removed = true;
+	  added = true;
+	  monitor.notifyAll();
+	}
+    }
+
+    public boolean isRemoved()
+    {
+      return removed;
+    }
+  }
+
+  static class EventLoopRunner extends Thread
+  {
+    private boolean stopped;
+
+    public void run()
+    {
+      stopped = false;
+      try
+	{
+	  Manager.eventLoop.run();
+	}
+      finally
+	{
+	  synchronized (monitor)
+	    {
+	      stopped = true;
+	      monitor.notifyAll();
+	    }
+	}
+    }
+
+    public void requestStop()
+    {
+      Manager.eventLoop.requestStop();
+    }
+
+    public boolean isStopped()
+    {
+      return stopped;
+    }
+
+    public String toString()
+    {
+      return "EventLoop-" + super.toString();
+    }
+  }
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]