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]

Adding breakpoints


Hi,

I was playing with adding (low-level) breakpoint support to Tasks and
came up with the following setup. We add a BreakpointAddresses class
which keeps track of the actual breakpoints needed (one address can have
multiple observers). The Task uses this BreakpointAddresses class to
notify the observers when it traps a breakpoint. The BreakpointAddresses
class interacts with the Task to actually set and remove the breakpoints
when needed, and tells the Task when it needs to start or when it can
stop tracing breakpoints (because none are left). When a breakpoint is
added or removed in a state that doesn't immediately allow
setting/removing a breakpoint (normally that can only be done when the
Task is in an suspended state) then the BreakpointAddresses class lets
the Task know it has updates of the breakpoints list and gets called by
the Task as soon as it enters a TaskState that allows addition/removal
of breakpoints. An draft of the BreakpointAddresses class and the
methods it uses is attached.

Some open questions on which I hope to get answers when implementing the
rest (or maybe someone already has ideas on these):

- What is the precise thread model used? The data-structures of this
class are thread safe, but that might be overkill if observers are
always added from the same thread.
- Breakpoints should be removed from a cloned task, or shouldn't they?
Or should that be a property per observer?
- Is this setup powerful enough to add higher level language breakpoints
on top of it?

Comments welcome.

Cheers,

Mark
// 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 Task. Address breakpoints
 * are absolute addresses in the Task 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
 * Task. 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.
 */
public class BreakpointAddresses
{
  /**
   * Task used to set breakpoints and which sents us notifications
   * when breakpoints are hit.
   */
  private final Task task;

  /**
   * Maps breakpoint addresses to a list of observers.  We assume the
   * number of observers is small, so an ArrayList will do.
   */
  private final HashMap map;
  
  /**
   * List of breakpoint addresses that still need to be added.
   */
  private final ArrayList pendingAdditions;

  /**
   * List of breakpoint addresses that need to be removed.
   */
  private final ArrayList pendingRemovals;

  /**
   * Package private constructor used by the Task when created.
   */
  BreakpointAddresses(Task task)
  {
    this.task = task;
    map = new HashMap();
    pendingAdditions = new ArrayList();
    pendingRemovals = new ArrayList();
  }

  /**
   * Adds a breakpoint observer to an address. If there is not yet a
   * breakpoint at the given address the Task is asked to add
   * one. Otherwise the observer is added to the list of objects to
   * notify when the breakpoint is hit.  breakpoint.
   */
  public void addBreakpoint(TaskObserver.Code observer)
  {
    long address = observer.getAddress();
    Long breakpoint = Long.valueOf(address);
    synchronized(map)
      {
	// First breakpoint ever set?
	if (map.isEmpty())
	  task.startTracingBreakpoints();

	ArrayList list = (ArrayList) map.get(breakpoint);
	if (list == null)
	  {
	    list = new ArrayList();
	    map.put(breakpoint, list);

	    // We might be adding a breakpoint that was just on the way
	    // out. If not, then ask the Task to call us for an update.
	    if (! pendingRemovals.remove(breakpoint))
	      {
		pendingAdditions.add(breakpoint);
		task.requestUpdateBreakpoints();
	      }
	  }
	list.add(observer);
      }
  }

  /**
   * Removes an observer from a breakpoint. If this is the last observer
   * interested in this particular address then the breakpoint is really
   * removed.
   *
   * @throws IllegalArgumentException if the observer was never added.
   */
  public void removeBreakpoint(TaskObserver.Code observer)
  {
    long address = observer.getAddress();
    Long breakpoint = Long.valueOf(address);
    synchronized(map)
      {
	ArrayList list = (ArrayList) map.get(breakpoint);
        if (list == null || ! list.remove(observer))
	  throw new IllegalArgumentException();

	if (list.isEmpty())
          {
            map.remove(breakpoint);

	    // If we are not just removing a pending breakpoint ask
	    // task to call us for an breakpoint update.
	    if (! pendingAdditions.remove(breakpoint))
	      {
		pendingRemovals.add(breakpoint);
		task.requestUpdateBreakpoints();
	      }
          }

	if (map.isEmpty())
	  task.stopTracingBreakpoints();
      }
  }

  /**
   * Called by the Task when it has trapped a breakpoint in the task.
   * Returns an Iterator of TaskObserver.Code observers interested in
   * the given address.
   */
  Iterator handleBreakpointEvent(long address)
  {
    ArrayList observers;
    Long breakpoint = Long.valueOf(address);
    synchronized(map)
      {
	ArrayList list = (ArrayList) map.get(breakpoint);
        if (list == null)
          throw new IllegalArgumentException();

	observers = (ArrayList) list.clone();
      }

    // 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.
    return observers.iterator();
  }

  /**
   * Called as soon as possible after Task.requestBreakpointUpdate()
   * is called by the Task. Flushes any pending breakpoint additions or
   * removals.
   */
  void updateBreakpoints()
  {
    // synchronize on map to lock pending lists.
    synchronized(map)
      {
	Iterator it = pendingRemovals.iterator();
	while (it.hasNext())
	  {
	    long address = ((Integer) it.next()).longValue();
	    task.removeBreakpoint(address);
	    it.remove();
	  }

	it = pendingAdditions.iterator();
	while (it.hasNext())
	  {
	    long address = ((Integer) it.next()).longValue();
	    task.addBreakpoint(address);
	    it.remove();
	  }
      }
  }
}

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