[1/3] GDBserver and vfork's child/parent shared address spaces

Message ID 20201127221958.18585-2-pedro@palves.net
State New
Headers show
Series
  • Fix "set breakpoint always-inserted" + gdbserver
Related show

Commit Message

Pedro Alves Nov. 27, 2020, 10:19 p.m.
Running the gdb.base/foll-vfork.exp testcase against gdbserver with
"set breakpoint always-inserted on", like this:

$ make check \
   RUNTESTFLAGS="--target_board=native-gdbserver GDBFLAGS='-ex set\ breakpoint\ always-inserted\ on'" \
   TESTS="gdb.*/foll-vfork.exp"

shows this regression compared to a normal "set breakpoint
always-inserted off" run:

 Running src/gdb/testsuite/gdb.base/foll-vfork.exp ...
 ERROR: internal buffer is full.
 WARNING: Timed out waiting for EOF in server after monitor exit
 ERROR: internal buffer is full.
 WARNING: Timed out waiting for EOF in server after monitor exit

gdb.log shows:

 (gdb) PASS: gdb.base/foll-vfork.exp: exec: vfork child follow, finish after tcatch vfork: continue to vfork
 finish
 Run till exit from #0  vfork () at ../sysdeps/unix/sysv/linux/x86_64/vfork.S:62
 [Attaching after Thread 524814.524814 vfork to child Thread 524815.524815]
 [New inferior 2 (process 524815)]
 warning: Error removing breakpoint 0
 warning: Error removing breakpoint 0
 warning: Error removing breakpoint 0
 warning: Error removing breakpoint 0
 warning: Error removing breakpoint 0
 ... repeat many times ...
 ERROR: internal buffer is full.
 UNRESOLVED: gdb.base/foll-vfork.exp: exec: vfork child follow, finish after tcatch vfork: finish
 ...

This testcase is about doing a "finish" across a vfork with "set
follow-fork-mode child".

Turning on "set debug remote" reveals the problem:

 Temporary catchpoint 3 (vforked process 21097), vfork () at ../sysdeps/unix/sysv/linux/x86_64/vfork.S:62
 62      ../sysdeps/unix/sysv/linux/x86_64/vfork.S: No such file or directory.
 (gdb) finish
 Sending packet: $g#67...Packet received: daffffffffffffff....[1120 bytes omitted]
 Sending packet: $m7fffffffdc40,40#29...Packet received: 00000000000000....
 [infrun] clear_proceed_status_thread: Thread 21049.21049
 Run till exit from #0  vfork () at ../sysdeps/unix/sysv/linux/x86_64/vfork.S:62
 Sending packet: $m55555555527f,1#76...Packet received: f3
 Sending packet: $Z0,55555555527f,1#bf...Packet received: OK                     <<<<< Line #1
 [Attaching after Thread 21049.21049 vfork to child Thread 21097.21097]
 [New inferior 2 (process 21097)]
 Sending packet: $Hgp5269.5269#f9...Packet received: OK                          <<<<< Line #2
 ...
 [infrun] proceed: addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT
 [infrun] resume_1: step=0, signal=GDB_SIGNAL_0, trap_expected=0, current thread [Thread 21097.21097] at 0x7ffff7e9d0cc
 Sending packet: $vCont;c:p5269.-1#b4...[infrun] infrun_async: enable=1
 [infrun] prepare_to_wait: prepare_to_wait
 [infrun] do_target_wait: Found 2 inferiors, starting at #1
 [infrun] do_target_wait: Found 2 inferiors, starting at #0
 Packet received: T05swbreak:;06:60dcffffff7f0000;07:40ccffffff7f0000;10:7f52555555550000;thread:p5269.5269;core:e;
 [infrun] target_wait (-1.0.0, status) =
 [infrun]   21097.21097.0 [Thread 21097.21097],
 [infrun]   status->kind = stopped, signal = GDB_SIGNAL_TRAP
 [infrun] handle_inferior_event: status->kind = stopped, signal = GDB_SIGNAL_TRAP
 [infrun] handle_signal_stop: stop_pc=0x55555555527f
 Sending packet: $m7fffffffdc40,40#29...Packet received: 0000000000000000605155555555000050ddffffff7f000000000000020000000000000000000000b3e0ddf7ff7f0000710000000000000058ddffffff7f0000
 [infrun] process_event_stop_test: BPSTAT_WHAT_SINGLE
 [infrun] switch_back_to_stepped_thread: thread [Thread 21097.21097] still needs step-over
 [infrun] should_be_inserted: skipping breakpoint: stepping past insn at: 0x55555555527f
 Sending packet: $z0,55555555527f,1#df...Packet received: E01                    <<<<< Line #3

Notice the lines marked "Line #N".  Trimming everything down to bare
essentials, we have:

 Sending packet: $Z0,55555555527f,1#bf...Packet received: OK                     <<<<< Line #1
 [Attaching after Thread 21049.21049 vfork to child Thread 21097.21097]
 [New inferior 2 (process 21097)]
 Sending packet: $Hgp5269.5269#f9...Packet received: OK                          <<<<< Line #2
 Sending packet: $z0,55555555527f,1#df...Packet received: E01                    <<<<< Line #3

What is going on here is that GDB inserts the bp_finish breakpoint at
0x55555555527f, via the parent.  On a vfork, both child and parent
share the same address space, so the bp_finish breakpoint instruction
is visible in both processes.  Later, when GDB decides to remove the
breakpoint, GDB looks up an inferior for the address space associated
with the breakpoint, and uses that inferior/process as current context
to remove the breakpoint from.  The child is a good inferior for that,
and so GDB tells GDBserver to delete the breakpoint from the child.
And that fails.

It fails because GDBserver's follow-fork support is not aware that the
parent and child share the same address space.  In gdbserver, the
breakpoint list is per-process.  When a fork or a vfork is
intercepted, gdbserver deep-copies the parent's breakpoint list to the
child.  This makes sense for fork, since in that case the address
space of the child is a copy of the address space of the parent.  But
it doesn't make sense for vfork.

This patch fixes it, by introducing a new refcounted address_space
class, and moving the breakpoint lists there.  A process now holds a
reference to an address space.  After a vfork, both parent and child
will point to the same address space object, so deleting a breakpoint
via either the parent or the child will delete the breakpoint from
both.

gdb/testsuite/ChangeLog:

	* gdb.base/foll-vfork.exp (tcatch_vfork_then_child_follow_exec)
	(tcatch_vfork_then_child_follow_exit): Add 'always_inserted'
	parameter and handle it.
	(do_vfork_and_follow_child_tests_exec): Run
	tcatch_vfork_then_child_follow_exec with both breakpoint
	always-inserted on and off.
	(do_vfork_and_follow_child_tests_exit): Run
	tcatch_vfork_then_child_follow_exit with both breakpoint
	always-inserted on and off.

gdbserver/ChangeLog:

	* inferiors.cc (add_process): Add address_space_ref parameter.
	Pass it down.
	(remove_process): Only free all breakpoints the process's address
	space has refcount of 1.
	* inferiors.h: Include "gdbsupport/refcounted-object.h" and
	"gdbsupport/gdb_ref_ptr.h".
	(address_space, address_space_ref, address_space_ref_policy): New.
	(process_info::process_info): Add address_space_ref parameter and
	handle it.
	(process_info): Field 'breakpoints', 'raw_breakpoints' and
	'fast_tracepoint_jumps' moved to address_space.  Add 'aspace'
	field.
	(add_process): Add address_space_ref parameter.
	* linux-low.cc (linux_process_target::add_linux_process): Add
	address_space_ref parameter and pass it down.
	(linux_process_target::handle_extended_wait): If handling a
	PTRACE_EVENT_VFORK, make the child and the parent share the same
	address space.
	* linux-low.h (linux_process_target::add_linux_process): Add
	address_space_ref parameter.
	* mem-break.cc (any_persistent_commands)
	(find_enabled_raw_code_breakpoint_at, find_raw_breakpoint_at)
	(set_raw_breakpoint_at, find_fast_tracepoint_jump_at)
	(delete_fast_tracepoint_jump, set_fast_tracepoint_jump)
	(set_breakpoint, delete_raw_breakpoint, delete_breakpoint_1)
	(find_gdb_breakpoint, delete_single_step_breakpoints)
	(uninsert_breakpoints_at, uninsert_all_breakpoints)
	(uninsert_single_step_breakpoints, reinsert_breakpoints_at)
	(has_single_step_breakpoints, reinsert_all_breakpoints)
	(reinsert_single_step_breakpoints, check_breakpoints)
	(breakpoint_here, breakpoint_inserted_here)
	(software_breakpoint_inserted_here)
	(hardware_breakpoint_inserted_here)
	(single_step_breakpoint_inserted_here)
	(delete_disabled_breakpoints, validate_breakpoints)
	(check_mem_write, delete_all_breakpoints, mark_breakpoints_out)
	(free_all_breakpoints, clone_all_breakpoints): Adjust.
---
 gdb/testsuite/gdb.base/foll-vfork.exp | 16 ++++--
 gdbserver/inferiors.cc                |  7 +--
 gdbserver/inferiors.h                 | 62 +++++++++++++++++-----
 gdbserver/linux-low.cc                | 30 +++++++----
 gdbserver/linux-low.h                 |  6 ++-
 gdbserver/mem-break.cc                | 98 +++++++++++++++++------------------
 6 files changed, 139 insertions(+), 80 deletions(-)

-- 
2.14.5

Patch

diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp b/gdb/testsuite/gdb.base/foll-vfork.exp
index 710f3c7dcea..665d214e6ed 100644
--- a/gdb/testsuite/gdb.base/foll-vfork.exp
+++ b/gdb/testsuite/gdb.base/foll-vfork.exp
@@ -263,7 +263,7 @@  proc tcatch_vfork_then_parent_follow {} {
    exec sleep 1
 }}
 
-proc tcatch_vfork_then_child_follow_exec {} {
+proc tcatch_vfork_then_child_follow_exec {always_inserted} {
   with_test_prefix "vfork child follow, finish after tcatch vfork" {
    global gdb_prompt
    global srcfile
@@ -271,6 +271,8 @@  proc tcatch_vfork_then_child_follow_exec {} {
 
    setup_gdb
 
+   gdb_test_no_output "set breakpoint always-inserted $always_inserted"
+
    gdb_test_no_output "set follow-fork child"
 
    gdb_test "tcatch vfork" "Catchpoint .*(vfork).*"
@@ -300,13 +302,15 @@  proc tcatch_vfork_then_child_follow_exec {} {
    exec sleep 1
 }}
 
-proc tcatch_vfork_then_child_follow_exit {} {
+proc tcatch_vfork_then_child_follow_exit {always_inserted} {
   with_test_prefix "vfork child follow, finish after tcatch vfork" {
    global gdb_prompt
    global srcfile
 
    setup_gdb
 
+   gdb_test_no_output "set breakpoint always-inserted $always_inserted"
+
    gdb_test_no_output "set follow-fork child"
 
    gdb_test "tcatch vfork" "Catchpoint .*(vfork).*"
@@ -421,7 +425,9 @@  proc do_vfork_and_follow_child_tests_exec {} {
 
    # Try catching a vfork, and stepping out to the child.
    #
-   tcatch_vfork_then_child_follow_exec
+   foreach_with_prefix always-inserted {"off" "on"} {
+       tcatch_vfork_then_child_follow_exec ${always-inserted}
+   }
 
    # Test the ability to follow both child and parent of a vfork.  Do
    # this without catchpoints.
@@ -450,7 +456,9 @@  proc do_vfork_and_follow_child_tests_exit {} {
 
    # Try catching a vfork, and stepping out to the child.
    #
-   tcatch_vfork_then_child_follow_exit
+   foreach_with_prefix always-inserted {"off" "on"} {
+       tcatch_vfork_then_child_follow_exit ${always-inserted}
+   }
 
    # Step over a vfork in the child, do "info inferiors" and check the
    # parent/child relation is displayed.  Run the child to completion,
diff --git a/gdbserver/inferiors.cc b/gdbserver/inferiors.cc
index 9a1280d039b..ed57ea3dc8f 100644
--- a/gdbserver/inferiors.cc
+++ b/gdbserver/inferiors.cc
@@ -138,9 +138,9 @@  clear_inferiors (void)
 }
 
 struct process_info *
-add_process (int pid, int attached)
+add_process (int pid, int attached, address_space_ref aspace)
 {
-  process_info *process = new process_info (pid, attached);
+  process_info *process = new process_info (pid, attached, aspace);
 
   all_processes.push_back (process);
 
@@ -155,7 +155,8 @@  void
 remove_process (struct process_info *process)
 {
   clear_symbol_cache (&process->symbol_cache);
-  free_all_breakpoints (process);
+  if (process->aspace->refcount () == 1)
+    free_all_breakpoints (process);
   gdb_assert (find_thread_process (process) == NULL);
   all_processes.remove (process);
   delete process;
diff --git a/gdbserver/inferiors.h b/gdbserver/inferiors.h
index 70c09d2c231..b1c12fd6d88 100644
--- a/gdbserver/inferiors.h
+++ b/gdbserver/inferiors.h
@@ -20,6 +20,8 @@ 
 #define GDBSERVER_INFERIORS_H
 
 #include "gdbsupport/gdb_vecs.h"
+#include "gdbsupport/refcounted-object.h"
+#include "gdbsupport/gdb_ref_ptr.h"
 #include <list>
 
 struct thread_info;
@@ -31,11 +33,48 @@  struct raw_breakpoint;
 struct fast_tracepoint_jump;
 struct process_info_private;
 
+/* This is refcounted because after a vfork, both parent and child
+   share the address space.  */
+struct address_space : refcounted_object
+{
+  /* The list of memory breakpoints.  */
+  breakpoint *breakpoints = nullptr;
+
+  /* The list of raw memory breakpoints.  */
+  raw_breakpoint *raw_breakpoints = nullptr;
+
+  /* The list of installed fast tracepoints.  */
+  fast_tracepoint_jump *fast_tracepoint_jumps = nullptr;
+};
+
+struct address_space_ref_policy
+{
+  static void incref (address_space *t)
+  {
+    t->incref ();
+  }
+
+  static void decref (address_space *t)
+  {
+    t->decref ();
+    if (t->refcount () == 0)
+      delete t;
+  }
+};
+
+using address_space_ref
+  = gdb::ref_ptr<address_space, address_space_ref_policy>;
+
 struct process_info
 {
-  process_info (int pid_, int attached_)
-  : pid (pid_), attached (attached_)
-  {}
+  process_info (int pid_, int attached_, address_space_ref aspace_)
+    : pid (pid_), attached (attached_)
+  {
+    if (aspace_ == nullptr)
+      aspace = address_space_ref::new_reference (new address_space ());
+    else
+      aspace = aspace_;
+  }
 
   /* This process' pid.  */
   int pid;
@@ -51,14 +90,8 @@  struct process_info
   /* The symbol cache.  */
   struct sym_cache *symbol_cache = NULL;
 
-  /* The list of memory breakpoints.  */
-  struct breakpoint *breakpoints = NULL;
-
-  /* The list of raw memory breakpoints.  */
-  struct raw_breakpoint *raw_breakpoints = NULL;
-
-  /* The list of installed fast tracepoints.  */
-  struct fast_tracepoint_jump *fast_tracepoint_jumps = NULL;
+  /* The address space.  */
+  address_space_ref aspace;
 
   /* The list of syscalls to report, or just a single element, ANY_SYSCALL,
      for unfiltered syscall reporting.  */
@@ -132,7 +165,12 @@  extern struct thread_info *current_thread;
 /* Return the first process in the processes list.  */
 struct process_info *get_first_process (void);
 
-struct process_info *add_process (int pid, int attached);
+/* Add a process to the common process list.  If ASPACE is non-NULL,
+   it is reused (e.g., this is a vfork child, and it does shares the
+   address space with the parent).  Otherwise a new address space is
+   created.  */
+struct process_info *add_process (int pid, int attached,
+				  address_space_ref aspace = nullptr);
 void remove_process (struct process_info *process);
 struct process_info *find_process_pid (int pid);
 int have_started_inferiors_p (void);
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index db3620e0f6e..132962470f3 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -402,11 +402,12 @@  linux_process_target::low_delete_thread (arch_lwp_info *info)
 }
 
 process_info *
-linux_process_target::add_linux_process (int pid, int attached)
+linux_process_target::add_linux_process (int pid, int attached,
+					 address_space_ref aspace)
 {
   struct process_info *proc;
 
-  proc = add_process (pid, attached);
+  proc = add_process (pid, attached, aspace);
   proc->priv = XCNEW (struct process_info_private);
 
   proc->priv->arch_private = low_new_process ();
@@ -508,12 +509,21 @@  linux_process_target::handle_extended_wait (lwp_info **orig_event_lwp,
 			    ptid.pid ());
 	    }
 
-	  /* Add the new process to the tables and clone the breakpoint
-	     lists of the parent.  We need to do this even if the new process
-	     will be detached, since we will need the process object and the
-	     breakpoints to remove any breakpoints from memory when we
-	     detach, and the client side will access registers.  */
-	  child_proc = add_linux_process (new_pid, 0);
+	  parent_proc = get_thread_process (event_thr);
+
+	  /* Add the new process to the tables.  If this was a vfork,
+	     make the parent and child share the same address space
+	     and breakpoints.  If this was a fork, the child gets a
+	     new address space and a deep copy of the parent's
+	     breakpoint list.  We need to do this even if the new
+	     process will be detached, since we will need the process
+	     object and the breakpoints to remove any breakpoints from
+	     memory when we detach, and the client side will access
+	     registers.  */
+	  if (event == PTRACE_EVENT_VFORK)
+	    child_proc = add_linux_process (new_pid, 0, parent_proc->aspace);
+	  else
+	    child_proc = add_linux_process (new_pid, 0);
 	  gdb_assert (child_proc != NULL);
 	  child_lwp = add_lwp (ptid);
 	  gdb_assert (child_lwp != NULL);
@@ -536,7 +546,6 @@  linux_process_target::handle_extended_wait (lwp_info **orig_event_lwp,
 	      child_lwp->suspended = 1;
 	    }
 
-	  parent_proc = get_thread_process (event_thr);
 	  child_proc->attached = parent_proc->attached;
 
 	  if (event_lwp->bp_reinsert != 0
@@ -550,7 +559,8 @@  linux_process_target::handle_extended_wait (lwp_info **orig_event_lwp,
 	      uninsert_single_step_breakpoints (event_thr);
 	    }
 
-	  clone_all_breakpoints (child_thr, event_thr);
+	  if (event == PTRACE_EVENT_FORK)
+	    clone_all_breakpoints (child_thr, event_thr);
 
 	  target_desc_up tdesc = allocate_target_description ();
 	  copy_target_description (tdesc.get (), parent_proc->tdesc);
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index 56c353319c2..fa29180491e 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -538,8 +538,10 @@  class linux_process_target : public process_stratum_target
 		      int direction);
 
   /* Add a process to the common process list, and set its private
-     data.  */
-  process_info *add_linux_process (int pid, int attached);
+     data.  If ASPACE is non-NULL, reuses it.  Otherwise a new address
+     space is created.  */
+  process_info *add_linux_process (int pid, int attached,
+				   address_space_ref aspace = nullptr);
 
   /* Add a new thread.  */
   lwp_info *add_lwp (ptid_t ptid);
diff --git a/gdbserver/mem-break.cc b/gdbserver/mem-break.cc
index 6b7af3a7d33..ba57b105500 100644
--- a/gdbserver/mem-break.cc
+++ b/gdbserver/mem-break.cc
@@ -308,7 +308,7 @@  any_persistent_commands (process_info *proc)
   struct breakpoint *bp;
   struct point_command_list *cl;
 
-  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->breakpoints; bp != NULL; bp = bp->next)
     {
       if (is_gdb_breakpoint (bp->type))
 	{
@@ -332,7 +332,7 @@  find_enabled_raw_code_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type)
   struct process_info *proc = current_process ();
   struct raw_breakpoint *bp;
 
-  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->raw_breakpoints; bp != NULL; bp = bp->next)
     if (bp->pc == addr
 	&& bp->raw_type == type
 	&& bp->inserted >= 0)
@@ -350,7 +350,7 @@  find_raw_breakpoint_at (CORE_ADDR addr, enum raw_bkpt_type type, int kind)
   struct process_info *proc = current_process ();
   struct raw_breakpoint *bp;
 
-  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->raw_breakpoints; bp != NULL; bp = bp->next)
     if (bp->pc == addr && bp->raw_type == type && bp->kind == kind)
       return bp;
 
@@ -480,8 +480,8 @@  set_raw_breakpoint_at (enum raw_bkpt_type type, CORE_ADDR where, int kind,
   /* Link the breakpoint in, if this is the first reference.  */
   if (++bp->refcount == 1)
     {
-      bp->next = proc->raw_breakpoints;
-      proc->raw_breakpoints = bp;
+      bp->next = proc->aspace->raw_breakpoints;
+      proc->aspace->raw_breakpoints = bp;
     }
   return bp;
 }
@@ -540,7 +540,7 @@  find_fast_tracepoint_jump_at (CORE_ADDR where)
   struct process_info *proc = current_process ();
   struct fast_tracepoint_jump *jp;
 
-  for (jp = proc->fast_tracepoint_jumps; jp != NULL; jp = jp->next)
+  for (jp = proc->aspace->fast_tracepoint_jumps; jp != NULL; jp = jp->next)
     if (jp->pc == where)
       return jp;
 
@@ -562,8 +562,8 @@  delete_fast_tracepoint_jump (struct fast_tracepoint_jump *todel)
   int ret;
   struct process_info *proc = current_process ();
 
-  bp = proc->fast_tracepoint_jumps;
-  bp_link = &proc->fast_tracepoint_jumps;
+  bp = proc->aspace->fast_tracepoint_jumps;
+  bp_link = &proc->aspace->fast_tracepoint_jumps;
 
   while (bp)
     {
@@ -668,8 +668,8 @@  set_fast_tracepoint_jump (CORE_ADDR where,
 
   /* Link the jump in.  */
   jp->inserted = 1;
-  jp->next = proc->fast_tracepoint_jumps;
-  proc->fast_tracepoint_jumps = jp;
+  jp->next = proc->aspace->fast_tracepoint_jumps;
+  proc->aspace->fast_tracepoint_jumps = jp;
 
   /* Since there can be trap breakpoints inserted in the same address
      range, we use use `target_write_memory', which takes care of
@@ -687,7 +687,7 @@  set_fast_tracepoint_jump (CORE_ADDR where,
 		      paddress (where), safe_strerror (err));
 
       /* Unlink it.  */
-      proc->fast_tracepoint_jumps = jp->next;
+      proc->aspace->fast_tracepoint_jumps = jp->next;
       free (jp);
 
       return NULL;
@@ -841,8 +841,8 @@  set_breakpoint (enum bkpt_type type, enum raw_bkpt_type raw_type,
   bp->type = type;
   bp->raw = raw;
 
-  bp->next = proc->breakpoints;
-  proc->breakpoints = bp;
+  bp->next = proc->aspace->breakpoints;
+  proc->aspace->breakpoints = bp;
 
   return bp;
 }
@@ -877,8 +877,8 @@  delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel)
   struct raw_breakpoint *bp, **bp_link;
   int ret;
 
-  bp = proc->raw_breakpoints;
-  bp_link = &proc->raw_breakpoints;
+  bp = proc->aspace->raw_breakpoints;
+  bp_link = &proc->aspace->raw_breakpoints;
 
   while (bp)
     {
@@ -948,8 +948,8 @@  delete_breakpoint_1 (struct process_info *proc, struct breakpoint *todel)
   struct breakpoint *bp, **bp_link;
   int err;
 
-  bp = proc->breakpoints;
-  bp_link = &proc->breakpoints;
+  bp = proc->aspace->breakpoints;
+  bp_link = &proc->aspace->breakpoints;
 
   while (bp)
     {
@@ -993,7 +993,7 @@  find_gdb_breakpoint (char z_type, CORE_ADDR addr, int kind)
   struct breakpoint *bp;
   enum bkpt_type type = Z_packet_to_bkpt_type (z_type);
 
-  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->breakpoints; bp != NULL; bp = bp->next)
     if (bp->type == type && bp->raw->pc == addr
 	&& (kind == -1 || bp->raw->kind == kind))
       return (struct gdb_breakpoint *) bp;
@@ -1493,8 +1493,8 @@  delete_single_step_breakpoints (struct thread_info *thread)
   struct process_info *proc = get_thread_process (thread);
   struct breakpoint *bp, **bp_link;
 
-  bp = proc->breakpoints;
-  bp_link = &proc->breakpoints;
+  bp = proc->aspace->breakpoints;
+  bp_link = &proc->aspace->breakpoints;
 
   while (bp)
     {
@@ -1551,7 +1551,7 @@  uninsert_breakpoints_at (CORE_ADDR pc)
   struct raw_breakpoint *bp;
   int found = 0;
 
-  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->raw_breakpoints; bp != NULL; bp = bp->next)
     if ((bp->raw_type == raw_bkpt_type_sw
 	 || bp->raw_type == raw_bkpt_type_hw)
 	&& bp->pc == pc)
@@ -1579,7 +1579,7 @@  uninsert_all_breakpoints (void)
   struct process_info *proc = current_process ();
   struct raw_breakpoint *bp;
 
-  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->raw_breakpoints; bp != NULL; bp = bp->next)
     if ((bp->raw_type == raw_bkpt_type_sw
 	 || bp->raw_type == raw_bkpt_type_hw)
 	&& bp->inserted)
@@ -1592,7 +1592,7 @@  uninsert_single_step_breakpoints (struct thread_info *thread)
   struct process_info *proc = get_thread_process (thread);
   struct breakpoint *bp;
 
-  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->breakpoints; bp != NULL; bp = bp->next)
     {
     if (bp->type == single_step_breakpoint
 	&& ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread))
@@ -1636,7 +1636,7 @@  reinsert_breakpoints_at (CORE_ADDR pc)
   struct raw_breakpoint *bp;
   int found = 0;
 
-  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->raw_breakpoints; bp != NULL; bp = bp->next)
     if ((bp->raw_type == raw_bkpt_type_sw
 	 || bp->raw_type == raw_bkpt_type_hw)
 	&& bp->pc == pc)
@@ -1663,8 +1663,8 @@  has_single_step_breakpoints (struct thread_info *thread)
   struct process_info *proc = get_thread_process (thread);
   struct breakpoint *bp, **bp_link;
 
-  bp = proc->breakpoints;
-  bp_link = &proc->breakpoints;
+  bp = proc->aspace->breakpoints;
+  bp_link = &proc->aspace->breakpoints;
 
   while (bp)
     {
@@ -1687,7 +1687,7 @@  reinsert_all_breakpoints (void)
   struct process_info *proc = current_process ();
   struct raw_breakpoint *bp;
 
-  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->raw_breakpoints; bp != NULL; bp = bp->next)
     if ((bp->raw_type == raw_bkpt_type_sw
 	 || bp->raw_type == raw_bkpt_type_hw)
 	&& !bp->inserted)
@@ -1700,7 +1700,7 @@  reinsert_single_step_breakpoints (struct thread_info *thread)
   struct process_info *proc = get_thread_process (thread);
   struct breakpoint *bp;
 
-  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->breakpoints; bp != NULL; bp = bp->next)
     {
       if (bp->type == single_step_breakpoint
 	  && ((struct single_step_breakpoint *) bp)->ptid == ptid_of (thread))
@@ -1725,8 +1725,8 @@  check_breakpoints (CORE_ADDR stop_pc)
   struct process_info *proc = current_process ();
   struct breakpoint *bp, **bp_link;
 
-  bp = proc->breakpoints;
-  bp_link = &proc->breakpoints;
+  bp = proc->aspace->breakpoints;
+  bp_link = &proc->aspace->breakpoints;
 
   while (bp)
     {
@@ -1770,7 +1770,7 @@  breakpoint_here (CORE_ADDR addr)
   struct process_info *proc = current_process ();
   struct raw_breakpoint *bp;
 
-  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->raw_breakpoints; bp != NULL; bp = bp->next)
     if ((bp->raw_type == raw_bkpt_type_sw
 	 || bp->raw_type == raw_bkpt_type_hw)
 	&& bp->pc == addr)
@@ -1785,7 +1785,7 @@  breakpoint_inserted_here (CORE_ADDR addr)
   struct process_info *proc = current_process ();
   struct raw_breakpoint *bp;
 
-  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->raw_breakpoints; bp != NULL; bp = bp->next)
     if ((bp->raw_type == raw_bkpt_type_sw
 	 || bp->raw_type == raw_bkpt_type_hw)
 	&& bp->pc == addr
@@ -1803,7 +1803,7 @@  software_breakpoint_inserted_here (CORE_ADDR addr)
   struct process_info *proc = current_process ();
   struct raw_breakpoint *bp;
 
-  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->raw_breakpoints; bp != NULL; bp = bp->next)
     if (bp->raw_type == raw_bkpt_type_sw
 	&& bp->pc == addr
 	&& bp->inserted)
@@ -1820,7 +1820,7 @@  hardware_breakpoint_inserted_here (CORE_ADDR addr)
   struct process_info *proc = current_process ();
   struct raw_breakpoint *bp;
 
-  for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->raw_breakpoints; bp != NULL; bp = bp->next)
     if (bp->raw_type == raw_bkpt_type_hw
 	&& bp->pc == addr
 	&& bp->inserted)
@@ -1837,7 +1837,7 @@  single_step_breakpoint_inserted_here (CORE_ADDR addr)
   struct process_info *proc = current_process ();
   struct breakpoint *bp;
 
-  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->breakpoints; bp != NULL; bp = bp->next)
     if (bp->type == single_step_breakpoint
 	&& bp->raw->pc == addr
 	&& bp->raw->inserted)
@@ -1873,7 +1873,7 @@  delete_disabled_breakpoints (void)
   struct process_info *proc = current_process ();
   struct breakpoint *bp, *next;
 
-  for (bp = proc->breakpoints; bp != NULL; bp = next)
+  for (bp = proc->aspace->breakpoints; bp != NULL; bp = next)
     {
       next = bp->next;
       if (bp->raw->inserted < 0)
@@ -1899,7 +1899,7 @@  validate_breakpoints (void)
   struct process_info *proc = current_process ();
   struct breakpoint *bp;
 
-  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+  for (bp = proc->aspace->breakpoints; bp != NULL; bp = bp->next)
     {
       struct raw_breakpoint *raw = bp->raw;
 
@@ -1914,8 +1914,8 @@  void
 check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
 {
   struct process_info *proc = current_process ();
-  struct raw_breakpoint *bp = proc->raw_breakpoints;
-  struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps;
+  struct raw_breakpoint *bp = proc->aspace->raw_breakpoints;
+  struct fast_tracepoint_jump *jp = proc->aspace->fast_tracepoint_jumps;
   CORE_ADDR mem_end = mem_addr + mem_len;
   int disabled_one = 0;
 
@@ -1998,8 +1998,8 @@  check_mem_write (CORE_ADDR mem_addr, unsigned char *buf,
 		 const unsigned char *myaddr, int mem_len)
 {
   struct process_info *proc = current_process ();
-  struct raw_breakpoint *bp = proc->raw_breakpoints;
-  struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps;
+  struct raw_breakpoint *bp = proc->aspace->raw_breakpoints;
+  struct fast_tracepoint_jump *jp = proc->aspace->fast_tracepoint_jumps;
   CORE_ADDR mem_end = mem_addr + mem_len;
   int disabled_one = 0;
 
@@ -2090,8 +2090,8 @@  delete_all_breakpoints (void)
 {
   struct process_info *proc = current_process ();
 
-  while (proc->breakpoints)
-    delete_breakpoint_1 (proc, proc->breakpoints);
+  while (proc->aspace->breakpoints)
+    delete_breakpoint_1 (proc, proc->aspace->breakpoints);
 }
 
 /* Clear the "inserted" flag in all breakpoints.  */
@@ -2101,7 +2101,7 @@  mark_breakpoints_out (struct process_info *proc)
 {
   struct raw_breakpoint *raw_bp;
 
-  for (raw_bp = proc->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next)
+  for (raw_bp = proc->aspace->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next)
     raw_bp->inserted = 0;
 }
 
@@ -2117,8 +2117,8 @@  free_all_breakpoints (struct process_info *proc)
      delete_all_breakpoints --- CURRENT_INFERIOR may already have been
      released when we get here.  There should be no call to
      current_process from here on.  */
-  while (proc->breakpoints)
-    delete_breakpoint_1 (proc, proc->breakpoints);
+  while (proc->aspace->breakpoints)
+    delete_breakpoint_1 (proc, proc->aspace->breakpoints);
 }
 
 /* Clone an agent expression.  */
@@ -2224,10 +2224,10 @@  clone_all_breakpoints (struct thread_info *child_thread,
   struct raw_breakpoint *raw_bkpt_tail = NULL;
   struct process_info *child_proc = get_thread_process (child_thread);
   struct process_info *parent_proc = get_thread_process (parent_thread);
-  struct breakpoint **new_list = &child_proc->breakpoints;
-  struct raw_breakpoint **new_raw_list = &child_proc->raw_breakpoints;
+  struct breakpoint **new_list = &child_proc->aspace->breakpoints;
+  struct raw_breakpoint **new_raw_list = &child_proc->aspace->raw_breakpoints;
 
-  for (bp = parent_proc->breakpoints; bp != NULL; bp = bp->next)
+  for (bp = parent_proc->aspace->breakpoints; bp != NULL; bp = bp->next)
     {
       new_bkpt = clone_one_breakpoint (bp, ptid_of (child_thread));
       APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);