gdb/gdb-6.6-multifork-debugreg....

1332 lines
41 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

http://sourceware.org/ml/gdb-patches/2008-01/msg00042.html
[ Backported for GDB-6.6 (only removed the new file inclusion). ]
+
2007-09-16 Daniel Jacobowitz <dan@codesourcery.com>
Jeff Johnston <jjohnstn@redhat.com>
* gdb.texinfo (Setting Watchpoints): Adjust warning text about
multi-threaded watchpoints.
2007-12-15 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.texinfo (Setting Watchpoints): New paragraph on the software
watchpoints safety wrt `set scheduler-locking'.
2008-03-01 Jan Kratochvil <jan.kratochvil@redhat.com>
Port to GDB-6.8pre.
2008-03-31 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.threads/watchpoint-fork-forkoff.c (forkoff): New delay after the
parent/child messages to fix a race.
2008-05-28 Jan Kratochvil <jan.kratochvil@redhat.com>
* s390-nat.c (s390_fix_watch_points): Fix its compilation failure
- rename it to S390_FIX_WATCH_POINTS_LIST.
Index: gdb-6.8.50.20090803/gdb/amd64-linux-nat.c
===================================================================
--- gdb-6.8.50.20090803.orig/gdb/amd64-linux-nat.c 2009-08-04 07:17:19.000000000 +0200
+++ gdb-6.8.50.20090803/gdb/amd64-linux-nat.c 2009-08-04 07:17:58.000000000 +0200
@@ -411,25 +411,43 @@ amd64_linux_dr_set (ptid_t ptid, int reg
static void
amd64_linux_dr_set_control (unsigned long control)
{
- struct lwp_info *lp;
- ptid_t ptid;
-
amd64_linux_dr[DR_CONTROL] = control;
- ALL_LWPS (lp, ptid)
- amd64_linux_dr_set (ptid, DR_CONTROL, control);
+
+ /* I386_DETACH_BREAKPOINTS may need to reset the registers on single process
+ not listed for ALL_LWPS. */
+
+ if (ptid_get_lwp (inferior_ptid) == 0)
+ amd64_linux_dr_set (inferior_ptid, DR_CONTROL, control);
+ else
+ {
+ struct lwp_info *lp;
+ ptid_t ptid;
+
+ ALL_LWPS (lp, ptid)
+ amd64_linux_dr_set (ptid, DR_CONTROL, control);
+ }
}
static void
amd64_linux_dr_set_addr (int regnum, CORE_ADDR addr)
{
- struct lwp_info *lp;
- ptid_t ptid;
-
gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
amd64_linux_dr[DR_FIRSTADDR + regnum] = addr;
- ALL_LWPS (lp, ptid)
- amd64_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr);
+
+ /* I386_DETACH_BREAKPOINTS may need to reset the registers on single process
+ not listed for ALL_LWPS. */
+
+ if (ptid_get_lwp (inferior_ptid) == 0)
+ amd64_linux_dr_set (inferior_ptid, DR_FIRSTADDR + regnum, addr);
+ else
+ {
+ struct lwp_info *lp;
+ ptid_t ptid;
+
+ ALL_LWPS (lp, ptid)
+ amd64_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr);
+ }
}
static void
@@ -454,6 +472,42 @@ amd64_linux_new_thread (ptid_t ptid)
amd64_linux_dr_set (ptid, DR_CONTROL, amd64_linux_dr[DR_CONTROL]);
}
+
+/* TO_FOLLOW_FORK stores here the PID under DETACH_BREAKPOINTS for the child
+ process of traced FORK. We must clear such watchpoints only once during
+ DETACH_BREAKPOINTS. */
+
+static int amd64_linux_detach_breakpoints_pid;
+
+/* Remove a watchpoint that watched the memory region which starts at
+ address ADDR, whose length is LEN bytes, and for accesses of the
+ type TYPE. Return 0 on success, -1 on failure. */
+int
+amd64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type)
+{
+ if (ptid_get_pid (inferior_ptid) == amd64_linux_detach_breakpoints_pid)
+ return 0;
+ /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions. */
+ amd64_linux_detach_breakpoints_pid = 0;
+
+ return target_remove_watchpoint (addr, len, type);
+}
+
+static void
+amd64_linux_detach_breakpoints (int detached_pid)
+{
+ struct cleanup *old_chain = save_inferior_ptid ();
+ int i;
+ extern void i386_detach_breakpoints (int detached_pid);
+
+ amd64_linux_detach_breakpoints_pid = detached_pid;
+ /* Depend on `!is_lwp (inferior_ptid)' for the I386_* macros. */
+ inferior_ptid = pid_to_ptid (detached_pid);
+
+ i386_detach_breakpoints (detached_pid);
+
+ do_cleanups (old_chain);
+}
/* This function is called by libthread_db as part of its handling of
@@ -767,6 +821,42 @@ amd64_linux_siginfo_fixup (struct siginf
return 0;
}
+static int (*amd64_linux_super_follow_fork) (struct target_ops *ops,
+ int follow_child);
+
+/* We need to duplicate the LINUX_CHILD_FOLLOW_FORK behavior here and catch its
+ called DETACH_BREAKPOINTS to avoid corrupting our local registers mirror. */
+
+static int
+amd64_linux_follow_fork (struct target_ops *ops, int follow_child)
+{
+ ptid_t last_ptid;
+ struct target_waitstatus last_status;
+ int has_vforked;
+ int parent_pid, child_pid;
+
+ get_last_target_status (&last_ptid, &last_status);
+ has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+ parent_pid = ptid_get_lwp (last_ptid);
+ if (parent_pid == 0)
+ parent_pid = ptid_get_pid (last_ptid);
+ child_pid = ptid_get_lwp (last_status.value.related_pid);
+ if (child_pid == 0)
+ child_pid = ptid_get_pid (last_status.value.related_pid);
+
+ if (! follow_child)
+ {
+ amd64_linux_detach_breakpoints (child_pid);
+ }
+ else
+ {
+ if (! has_vforked)
+ amd64_linux_detach_breakpoints (child_pid);
+ }
+
+ return (*amd64_linux_super_follow_fork) (ops, follow_child);
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */
void _initialize_amd64_linux_nat (void);
@@ -809,6 +899,9 @@ _initialize_amd64_linux_nat (void)
linux_elfcore_write_prstatus = amd64_linux_elfcore_write_prstatus;
linux_elfcore_write_prfpreg = amd64_linux_elfcore_write_prfpreg;
+ amd64_linux_super_follow_fork = t->to_follow_fork;
+ t->to_follow_fork = amd64_linux_follow_fork;
+
/* Register the target. */
linux_nat_add_target (t);
linux_nat_set_new_thread (t, amd64_linux_new_thread);
Index: gdb-6.8.50.20090803/gdb/i386-linux-nat.c
===================================================================
--- gdb-6.8.50.20090803.orig/gdb/i386-linux-nat.c 2009-08-04 07:17:19.000000000 +0200
+++ gdb-6.8.50.20090803/gdb/i386-linux-nat.c 2009-08-04 07:26:43.000000000 +0200
@@ -637,21 +637,42 @@ i386_linux_dr_set_control (unsigned long
ptid_t ptid;
i386_linux_dr[DR_CONTROL] = control;
- ALL_LWPS (lp, ptid)
- i386_linux_dr_set (ptid, DR_CONTROL, control);
+
+ /* I386_DETACH_BREAKPOINTS may need to reset the registers on single process
+ not listed for ALL_LWPS. */
+
+ if (ptid_get_lwp (inferior_ptid) == 0)
+ i386_linux_dr_set (inferior_ptid, DR_CONTROL, control);
+ else
+ {
+ struct lwp_info *lp;
+ ptid_t ptid;
+
+ ALL_LWPS (lp, ptid)
+ i386_linux_dr_set (ptid, DR_CONTROL, control);
+ }
}
static void
i386_linux_dr_set_addr (int regnum, CORE_ADDR addr)
{
- struct lwp_info *lp;
- ptid_t ptid;
-
gdb_assert (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR);
i386_linux_dr[DR_FIRSTADDR + regnum] = addr;
- ALL_LWPS (lp, ptid)
- i386_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr);
+
+ /* I386_DETACH_BREAKPOINTS may need to reset the registers on single process
+ not listed for ALL_LWPS. */
+
+ if (ptid_get_lwp (inferior_ptid) == 0)
+ i386_linux_dr_set (inferior_ptid, DR_FIRSTADDR + regnum, addr);
+ else
+ {
+ struct lwp_info *lp;
+ ptid_t ptid;
+
+ ALL_LWPS (lp, ptid)
+ i386_linux_dr_set (ptid, DR_FIRSTADDR + regnum, addr);
+ }
}
static void
@@ -676,6 +697,42 @@ i386_linux_new_thread (ptid_t ptid)
i386_linux_dr_set (ptid, DR_CONTROL, i386_linux_dr[DR_CONTROL]);
}
+
+/* TO_FOLLOW_FORK stores here the PID under DETACH_BREAKPOINTS for the child
+ process of traced FORK. We must clear such watchpoints only once during
+ DETACH_BREAKPOINTS. */
+
+static int i386_linux_detach_breakpoints_pid;
+
+/* Remove a watchpoint that watched the memory region which starts at
+ address ADDR, whose length is LEN bytes, and for accesses of the
+ type TYPE. Return 0 on success, -1 on failure. */
+int
+i386_linux_remove_watchpoint (CORE_ADDR addr, int len, int type)
+{
+ if (ptid_get_pid (inferior_ptid) == i386_linux_detach_breakpoints_pid)
+ return 0;
+ /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions. */
+ i386_linux_detach_breakpoints_pid = 0;
+
+ return target_remove_watchpoint (addr, len, type);
+}
+
+static void
+i386_linux_detach_breakpoints (int detached_pid)
+{
+ struct cleanup *old_chain = save_inferior_ptid ();
+ int i;
+ extern void i386_detach_breakpoints (int detached_pid);
+
+ i386_linux_detach_breakpoints_pid = detached_pid;
+ /* Depend on `!is_lwp (inferior_ptid)' for the I386_* macros. */
+ inferior_ptid = pid_to_ptid (detached_pid);
+
+ i386_detach_breakpoints (detached_pid);
+
+ do_cleanups (old_chain);
+}
/* Called by libthread_db. Returns a pointer to the thread local
@@ -818,6 +875,40 @@ i386_linux_child_post_startup_inferior (
super_post_startup_inferior (ptid);
}
+static int (*i386_linux_super_follow_fork) (struct target_ops *ops,
+ int follow_child);
+
+/* We need to duplicate the LINUX_CHILD_FOLLOW_FORK behavior here and catch its
+ called DETACH_BREAKPOINTS to avoid corrupting our local registers mirror. */
+
+static int
+i386_linux_follow_fork (struct target_ops *ops, int follow_child)
+{
+ ptid_t last_ptid;
+ struct target_waitstatus last_status;
+ int has_vforked;
+ int parent_pid, child_pid;
+
+ get_last_target_status (&last_ptid, &last_status);
+ has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+ parent_pid = ptid_get_lwp (last_ptid);
+ if (parent_pid == 0)
+ parent_pid = ptid_get_pid (last_ptid);
+ child_pid = ptid_get_pid (last_status.value.related_pid);
+
+ if (! follow_child)
+ {
+ i386_linux_detach_breakpoints (child_pid);
+ }
+ else
+ {
+ if (! has_vforked)
+ i386_linux_detach_breakpoints (child_pid);
+ }
+
+ return (*i386_linux_super_follow_fork) (ops, follow_child);
+}
+
void
_initialize_i386_linux_nat (void)
{
@@ -845,6 +936,9 @@ _initialize_i386_linux_nat (void)
t->to_fetch_registers = i386_linux_fetch_inferior_registers;
t->to_store_registers = i386_linux_store_inferior_registers;
+ i386_linux_super_follow_fork = t->to_follow_fork;
+ t->to_follow_fork = i386_linux_follow_fork;
+
/* Register the target. */
linux_nat_add_target (t);
linux_nat_set_new_thread (t, i386_linux_new_thread);
Index: gdb-6.8.50.20090803/gdb/i386-nat.c
===================================================================
--- gdb-6.8.50.20090803.orig/gdb/i386-nat.c 2009-08-04 07:17:19.000000000 +0200
+++ gdb-6.8.50.20090803/gdb/i386-nat.c 2009-08-04 07:17:58.000000000 +0200
@@ -520,6 +520,18 @@ i386_remove_watchpoint (CORE_ADDR addr,
return retval;
}
+void
+i386_detach_breakpoints (int detached_pid)
+{
+ int i;
+
+ /* Do not touch any DR_MIRROR or DR_CONTROL_MIRROR mirrors here. */
+ i386_dr_low.set_control (0);
+ if (i386_dr_low.reset_addr)
+ ALL_DEBUG_REGISTERS(i)
+ i386_dr_low.reset_addr (i);
+}
+
/* Return non-zero if we can watch a memory region that starts at
address ADDR and whose length is LEN bytes. */
Index: gdb-6.8.50.20090803/gdb/ia64-linux-nat.c
===================================================================
--- gdb-6.8.50.20090803.orig/gdb/ia64-linux-nat.c 2009-08-04 07:17:19.000000000 +0200
+++ gdb-6.8.50.20090803/gdb/ia64-linux-nat.c 2009-08-04 07:17:58.000000000 +0200
@@ -583,6 +583,12 @@ ia64_linux_insert_watchpoint (CORE_ADDR
return 0;
}
+/* TO_FOLLOW_FORK stores here the PID under DETACH_BREAKPOINTS for the child
+ process of traced FORK. We must clear such watchpoints only once during
+ DETACH_BREAKPOINTS. */
+
+static int ia64_linux_detach_breakpoints_pid;
+
static int
ia64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type)
{
@@ -590,6 +596,11 @@ ia64_linux_remove_watchpoint (CORE_ADDR
long dbr_addr, dbr_mask;
int max_watchpoints = 4;
+ if (ptid_get_pid (inferior_ptid) == ia64_linux_detach_breakpoints_pid)
+ return 0;
+ /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions. */
+ ia64_linux_detach_breakpoints_pid = 0;
+
if (len <= 0 || !is_power_of_2 (len))
return -1;
@@ -617,6 +628,22 @@ ia64_linux_remove_watchpoint (CORE_ADDR
}
static void
+ia64_linux_detach_breakpoints (int detached_pid)
+{
+ int idx, i;
+ long dbr_addr, dbr_mask;
+ int max_watchpoints = 4;
+
+ ia64_linux_detach_breakpoints_pid = detached_pid;
+
+ /* Do not touch any DEBUG_REGISTERS mirrors here. */
+ dbr_addr = 0;
+ dbr_mask = 0;
+ for (idx = 0; idx < max_watchpoints; idx++)
+ store_debug_register_pair (ptid_build (detached_pid, 0, 0), idx, &dbr_addr, &dbr_mask);
+}
+
+static void
ia64_linux_new_thread (ptid_t ptid)
{
int i, any;
@@ -807,6 +834,40 @@ ia64_linux_xfer_partial (struct target_o
offset, len);
}
+static int (*ia64_linux_super_follow_fork) (struct target_ops *ops,
+ int follow_child);
+
+/* We need to duplicate the LINUX_CHILD_FOLLOW_FORK behavior here and catch its
+ called DETACH_BREAKPOINTS to avoid corrupting our local registers mirror. */
+
+int
+ia64_linux_follow_fork (struct target_ops *ops, int follow_child)
+{
+ ptid_t last_ptid;
+ struct target_waitstatus last_status;
+ int has_vforked;
+ int parent_pid, child_pid;
+
+ get_last_target_status (&last_ptid, &last_status);
+ has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+ parent_pid = ptid_get_lwp (last_ptid);
+ if (parent_pid == 0)
+ parent_pid = ptid_get_pid (last_ptid);
+ child_pid = ptid_get_pid (last_status.value.related_pid);
+
+ if (! follow_child)
+ {
+ ia64_linux_detach_breakpoints (child_pid);
+ }
+ else
+ {
+ if (! has_vforked)
+ ia64_linux_detach_breakpoints (child_pid);
+ }
+
+ return (*ia64_linux_super_follow_fork) (ops, follow_child);
+}
+
void _initialize_ia64_linux_nat (void);
/*
@@ -901,6 +962,9 @@ _initialize_ia64_linux_nat (void)
t->to_insert_watchpoint = ia64_linux_insert_watchpoint;
t->to_remove_watchpoint = ia64_linux_remove_watchpoint;
+ ia64_linux_super_follow_fork = t->to_follow_fork;
+ t->to_follow_fork = ia64_linux_follow_fork;
+
/* Register the target. */
linux_nat_add_target (t);
linux_nat_set_new_thread (t, ia64_linux_new_thread);
Index: gdb-6.8.50.20090803/gdb/ppc-linux-nat.c
===================================================================
--- gdb-6.8.50.20090803.orig/gdb/ppc-linux-nat.c 2009-08-04 07:17:19.000000000 +0200
+++ gdb-6.8.50.20090803/gdb/ppc-linux-nat.c 2009-08-04 07:17:58.000000000 +0200
@@ -1377,6 +1377,12 @@ ppc_linux_insert_watchpoint (CORE_ADDR a
return 0;
}
+/* TO_FOLLOW_FORK stores here the PID under DETACH_BREAKPOINTS for the child
+ process of traced FORK. We must clear such watchpoints only once during
+ DETACH_BREAKPOINTS. */
+
+static int ppc_linux_detach_breakpoints_pid;
+
static int
ppc_linux_remove_watchpoint (CORE_ADDR addr, int len, int rw)
{
@@ -1384,6 +1390,11 @@ ppc_linux_remove_watchpoint (CORE_ADDR a
ptid_t ptid;
long dabr_value = 0;
+ if (ptid_get_pid (inferior_ptid) == ppc_linux_detach_breakpoints_pid)
+ return 0;
+ /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions. */
+ ppc_linux_detach_breakpoints_pid = 0;
+
saved_dabr_value = 0;
ALL_LWPS (lp, ptid)
if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value) < 0)
@@ -1397,6 +1408,15 @@ ppc_linux_new_thread (ptid_t ptid)
ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0, saved_dabr_value);
}
+static void
+ppc_linux_detach_breakpoints (int detached_pid)
+{
+ ppc_linux_detach_breakpoints_pid = detached_pid;
+
+ /* Do not touch the SAVED_DABR_VALUE mirror here. */
+ ptrace (PTRACE_SET_DEBUGREG, detached_pid, 0, 0);
+}
+
static int
ppc_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p)
{
@@ -1629,6 +1649,40 @@ ppc_linux_read_description (struct targe
return isa205? tdesc_powerpc_isa205_32l : tdesc_powerpc_32l;
}
+static int (*ppc_linux_super_follow_fork) (struct target_ops *ops,
+ int follow_child);
+
+/* We need to duplicate the LINUX_CHILD_FOLLOW_FORK behavior here and catch its
+ called DETACH_BREAKPOINTS to avoid corrupting our local registers mirror. */
+
+int
+ppc_linux_follow_fork (struct target_ops *ops, int follow_child)
+{
+ ptid_t last_ptid;
+ struct target_waitstatus last_status;
+ int has_vforked;
+ int parent_pid, child_pid;
+
+ get_last_target_status (&last_ptid, &last_status);
+ has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+ parent_pid = ptid_get_lwp (last_ptid);
+ if (parent_pid == 0)
+ parent_pid = ptid_get_pid (last_ptid);
+ child_pid = ptid_get_pid (last_status.value.related_pid);
+
+ if (! follow_child)
+ {
+ ppc_linux_detach_breakpoints (child_pid);
+ }
+ else
+ {
+ if (! has_vforked)
+ ppc_linux_detach_breakpoints (child_pid);
+ }
+
+ return (*ppc_linux_super_follow_fork) (ops, follow_child);
+}
+
void _initialize_ppc_linux_nat (void);
void
@@ -1655,6 +1709,9 @@ _initialize_ppc_linux_nat (void)
t->to_read_description = ppc_linux_read_description;
t->to_auxv_parse = ppc_linux_auxv_parse;
+ ppc_linux_super_follow_fork = t->to_follow_fork;
+ t->to_follow_fork = ppc_linux_follow_fork;
+
/* Register the target. */
linux_nat_add_target (t);
linux_nat_set_new_thread (t, ppc_linux_new_thread);
Index: gdb-6.8.50.20090803/gdb/s390-nat.c
===================================================================
--- gdb-6.8.50.20090803.orig/gdb/s390-nat.c 2009-08-04 07:17:19.000000000 +0200
+++ gdb-6.8.50.20090803/gdb/s390-nat.c 2009-08-04 07:17:58.000000000 +0200
@@ -285,21 +285,15 @@ s390_stopped_by_watchpoint (void)
}
static void
-s390_fix_watch_points (ptid_t ptid)
+s390_fix_watch_points_list (int tid, struct watch_area *area_list)
{
- int tid;
-
per_struct per_info;
ptrace_area parea;
CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0;
struct watch_area *area;
- tid = TIDGET (ptid);
- if (tid == 0)
- tid = PIDGET (ptid);
-
- for (area = watch_base; area; area = area->next)
+ for (area = area_list; area; area = area->next)
{
watch_lo_addr = min (watch_lo_addr, area->lo_addr);
watch_hi_addr = max (watch_hi_addr, area->hi_addr);
@@ -311,7 +305,7 @@ s390_fix_watch_points (ptid_t ptid)
if (ptrace (PTRACE_PEEKUSR_AREA, tid, &parea) < 0)
perror_with_name (_("Couldn't retrieve watchpoint status"));
- if (watch_base)
+ if (area_list)
{
per_info.control_regs.bits.em_storage_alteration = 1;
per_info.control_regs.bits.storage_alt_space_ctl = 1;
@@ -328,6 +322,18 @@ s390_fix_watch_points (ptid_t ptid)
perror_with_name (_("Couldn't modify watchpoint status"));
}
+static void
+s390_fix_watch_points (ptid_t ptid)
+{
+ int tid;
+
+ tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
+
+ s390_fix_watch_points_list (tid, watch_base);
+}
+
static int
s390_insert_watchpoint (CORE_ADDR addr, int len, int type)
{
@@ -349,6 +355,12 @@ s390_insert_watchpoint (CORE_ADDR addr,
return 0;
}
+/* TO_FOLLOW_FORK stores here the PID under DETACH_BREAKPOINTS for the child
+ process of traced FORK. We must clear such watchpoints only once during
+ DETACH_BREAKPOINTS. */
+
+static int s390_detach_breakpoints_pid;
+
static int
s390_remove_watchpoint (CORE_ADDR addr, int len, int type)
{
@@ -356,6 +368,11 @@ s390_remove_watchpoint (CORE_ADDR addr,
ptid_t ptid;
struct watch_area *area, **parea;
+ if (ptid_get_pid (inferior_ptid) == s390_detach_breakpoints_pid)
+ return 0;
+ /* FOLLOW-FORK-MODE CHILD runs later the CHILD with no restrictions. */
+ s390_detach_breakpoints_pid = 0;
+
for (parea = &watch_base; *parea; parea = &(*parea)->next)
if ((*parea)->lo_addr == addr
&& (*parea)->hi_addr == addr + len - 1)
@@ -363,8 +380,10 @@ s390_remove_watchpoint (CORE_ADDR addr,
if (!*parea)
{
+#if 0 /* Red Hat fork/threads watchpoints changes may trigger it. */
fprintf_unfiltered (gdb_stderr,
"Attempt to remove nonexistent watchpoint.\n");
+#endif
return -1;
}
@@ -377,6 +396,15 @@ s390_remove_watchpoint (CORE_ADDR addr,
return 0;
}
+static void
+s390_detach_breakpoints (int detached_pid)
+{
+ s390_detach_breakpoints_pid = detached_pid;
+
+ /* Do not touch the WATCH_BASE here. */
+ s390_fix_watch_points_list (detached_pid, NULL);
+}
+
static int
s390_can_use_hw_breakpoint (int type, int cnt, int othertype)
{
@@ -389,6 +417,39 @@ s390_region_ok_for_hw_watchpoint (CORE_A
return 1;
}
+static int (*s390_super_follow_fork) (struct target_ops *ops, int follow_child);
+
+/* We need to duplicate the LINUX_CHILD_FOLLOW_FORK behavior here and catch its
+ called DETACH_BREAKPOINTS to avoid corrupting our local registers mirror. */
+
+int
+s390_follow_fork (struct target_ops *ops, int follow_child)
+{
+ ptid_t last_ptid;
+ struct target_waitstatus last_status;
+ int has_vforked;
+ int parent_pid, child_pid;
+
+ get_last_target_status (&last_ptid, &last_status);
+ has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
+ parent_pid = ptid_get_lwp (last_ptid);
+ if (parent_pid == 0)
+ parent_pid = ptid_get_pid (last_ptid);
+ child_pid = ptid_get_pid (last_status.value.related_pid);
+
+ if (! follow_child)
+ {
+ s390_detach_breakpoints (child_pid);
+ }
+ else
+ {
+ if (! has_vforked)
+ s390_detach_breakpoints (child_pid);
+ }
+
+ return (*s390_super_follow_fork) (ops, follow_child);
+}
+
void _initialize_s390_nat (void);
@@ -412,6 +473,9 @@ _initialize_s390_nat (void)
t->to_insert_watchpoint = s390_insert_watchpoint;
t->to_remove_watchpoint = s390_remove_watchpoint;
+ s390_super_follow_fork = t->to_follow_fork;
+ t->to_follow_fork = s390_follow_fork;
+
/* Register the target. */
linux_nat_add_target (t);
linux_nat_set_new_thread (t, s390_fix_watch_points);
Index: gdb-6.8.50.20090803/gdb/testsuite/gdb.threads/watchpoint-fork-forkoff.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-6.8.50.20090803/gdb/testsuite/gdb.threads/watchpoint-fork-forkoff.c 2009-08-04 07:17:58.000000000 +0200
@@ -0,0 +1,172 @@
+/* Test case for forgotten hw-watchpoints after fork()-off of a process.
+
+ Copyright 2008
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <string.h>
+#include <errno.h>
+
+static void delay (void)
+{
+ int i = usleep (1000000 / 100);
+ assert (i == 0 || errno == EINTR);
+}
+
+#if defined FOLLOW_PARENT
+
+static void forkoff (int nr)
+{
+ pid_t child, pid_got;
+ int exit_code = 42 + nr;
+ int status, i;
+
+ child = fork ();
+ switch (child)
+ {
+ case -1:
+ assert (0);
+ case 0:
+ printf ("child%d: %d\n", nr, (int) getpid ());
+ /* Delay to get both the "child%d" and "parent%d" message printed without
+ a race breaking expect by its endless wait on `$gdb_prompt$':
+ Breakpoint 3, breakpoint () at ../../../gdb/testsuite/gdb.threads/watchpoint-fork.c:33
+ 33 }
+ (gdb) parent2: 14223 */
+ i = sleep (1);
+ assert (i == 0);
+
+ /* We must not get caught here (against a forgotten breakpoint). */
+ var++;
+ breakpoint ();
+
+ _exit (exit_code);
+ default:
+ printf ("parent%d: %d\n", nr, (int) child);
+ /* Delay to get both the "child%d" and "parent%d" message printed, see
+ above. */
+ i = sleep (1);
+ assert (i == 0);
+
+ pid_got = wait (&status);
+ assert (pid_got == child);
+ assert (WIFEXITED (status));
+ assert (WEXITSTATUS (status) == exit_code);
+
+ /* We must get caught here (against a false watchpoint removal). */
+ breakpoint ();
+ }
+}
+
+#elif defined FOLLOW_CHILD
+
+static volatile int usr1_got;
+
+static void handler_usr1 (int signo)
+{
+ usr1_got++;
+}
+
+static void forkoff (int nr)
+{
+ pid_t child;
+ int i, loop;
+ struct sigaction act, oldact;
+#ifdef THREAD
+ void *thread_result;
+#endif
+
+ memset (&act, 0, sizeof act);
+ act.sa_flags = SA_RESTART;
+ act.sa_handler = handler_usr1;
+ sigemptyset (&act.sa_mask);
+ i = sigaction (SIGUSR1, &act, &oldact);
+ assert (i == 0);
+
+ child = fork ();
+ switch (child)
+ {
+ case -1:
+ assert (0);
+ default:
+ printf ("parent%d: %d\n", nr, (int) child);
+
+ /* Sleep for a while to possibly get incorrectly ATTACH_THREADed by GDB
+ tracing the child fork with no longer valid thread/lwp entries of the
+ parent. */
+
+ i = sleep (2);
+ assert (i == 0);
+
+ /* We must not get caught here (against a forgotten breakpoint). */
+
+ var++;
+ breakpoint ();
+
+#ifdef THREAD
+ /* And neither got caught our thread. */
+
+ step = 99;
+ i = pthread_join (thread, &thread_result);
+ assert (i == 0);
+ assert (thread_result == (void *) 99UL);
+#endif
+
+ /* Be sure our child knows we did not get caught above. */
+
+ i = kill (child, SIGUSR1);
+ assert (i == 0);
+
+ /* Sleep for a while to check GDB's `info threads' no longer tracks us in
+ the child fork. */
+
+ i = sleep (2);
+ assert (i == 0);
+
+ _exit (0);
+ case 0:
+ printf ("child%d: %d\n", nr, (int) getpid ());
+
+ /* Let the parent signal us about its success. Be careful of races. */
+
+ for (loop = 0; loop < 1000; loop++)
+ {
+ /* Parent either died (and USR1_GOT is zero) or it succeeded. */
+ if (kill (getppid (), 0) != 0)
+ break;
+ /* Parent succeeded? */
+ if (usr1_got)
+ break;
+
+ delay ();
+ }
+ assert (usr1_got);
+
+ /* We must get caught here (against a false watchpoint removal). */
+
+ breakpoint ();
+ }
+
+ i = sigaction (SIGUSR1, &oldact, NULL);
+ assert (i == 0);
+}
+
+#else
+# error "!FOLLOW_PARENT && !FOLLOW_CHILD"
+#endif
Index: gdb-6.8.50.20090803/gdb/testsuite/gdb.threads/watchpoint-fork-mt.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-6.8.50.20090803/gdb/testsuite/gdb.threads/watchpoint-fork-mt.c 2009-08-04 07:17:58.000000000 +0200
@@ -0,0 +1,154 @@
+/* Test case for forgotten hw-watchpoints after fork()-off of a process.
+
+ Copyright 2008
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <assert.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#include <asm/unistd.h>
+#include <unistd.h>
+#define gettid() syscall (__NR_gettid)
+
+/* Non-atomic `var++' should not hurt as we synchronize the threads by the STEP
+ variable. Hit-comments need to be duplicite there to catch both at-stops
+ and behind-stops, depending on the target. */
+
+static volatile int var;
+
+static void dummy (void)
+{
+}
+
+static void breakpoint (void)
+{
+}
+
+/* Include here the functions:
+ static void forkoff (int nr);
+ static void delay (void); */
+
+static pthread_t thread;
+static volatile int step;
+#define THREAD
+
+#include "watchpoint-fork-forkoff.c"
+
+static void *start (void *arg)
+{
+ if (step >= 3)
+ goto step_3;
+
+ while (step != 1)
+ delay ();
+
+ var++; /* validity-thread-B */
+ dummy (); /* validity-thread-B */
+ step = 2;
+ while (step != 3)
+ {
+ if (step == 99)
+ goto step_99;
+ delay ();
+ }
+
+step_3:
+ if (step >= 5)
+ goto step_5;
+
+ var++; /* after-fork1-B */
+ dummy (); /* after-fork1-B */
+ step = 4;
+ while (step != 5)
+ {
+ if (step == 99)
+ goto step_99;
+ delay ();
+ }
+
+step_5:
+ var++; /* after-fork2-B */
+ dummy (); /* after-fork2-B */
+ return (void *) 5UL;
+
+step_99:
+ /* We must not get caught here (against a forgotten breakpoint). */
+ var++;
+ breakpoint ();
+ return (void *) 99UL;
+}
+
+int main (void)
+{
+ int i;
+ void *thread_result;
+
+ setbuf (stdout, NULL);
+ printf ("main: %d\n", (int) gettid ());
+
+ /* General watchpoints validity. */
+ var++; /* validity-first */
+ dummy (); /* validity-first */
+
+ i = pthread_create (&thread, NULL, start, NULL);
+ assert (i == 0);
+
+ var++; /* validity-thread-A */
+ dummy (); /* validity-thread-A */
+ step = 1;
+ while (step != 2)
+ delay ();
+
+ /* Hardware watchpoints got disarmed here. */
+ forkoff (1);
+
+ var++; /* after-fork1-A */
+ dummy (); /* after-fork1-A */
+ step = 3;
+#ifdef FOLLOW_CHILD
+ /* Spawn new thread as it was deleted in the child of FORK. */
+ i = pthread_create (&thread, NULL, start, NULL);
+ assert (i == 0);
+#endif
+ while (step != 4)
+ delay ();
+
+ /* A sanity check for double hardware watchpoints removal. */
+ forkoff (2);
+
+ var++; /* after-fork2-A */
+ dummy (); /* after-fork2-A */
+ step = 5;
+#ifdef FOLLOW_CHILD
+ /* Spawn new thread as it was deleted in the child of FORK. */
+ i = pthread_create (&thread, NULL, start, NULL);
+ assert (i == 0);
+#endif
+
+ i = pthread_join (thread, &thread_result);
+ assert (i == 0);
+ assert (thread_result == (void *) 5UL);
+
+ return 0;
+}
Index: gdb-6.8.50.20090803/gdb/testsuite/gdb.threads/watchpoint-fork.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-6.8.50.20090803/gdb/testsuite/gdb.threads/watchpoint-fork.c 2009-08-04 07:17:58.000000000 +0200
@@ -0,0 +1,56 @@
+/* Test case for forgotten hw-watchpoints after fork()-off of a process.
+
+ Copyright 2008
+ Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program 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; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include <assert.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static volatile int var;
+
+static void breakpoint (void)
+{
+}
+
+/* Include here the function:
+ static void forkoff (int nr); */
+
+#include "watchpoint-fork-forkoff.c"
+
+int main (void)
+{
+ setbuf (stdout, NULL);
+ printf ("main: %d\n", (int) getpid ());
+
+ /* General watchpoints validity. */
+ var++;
+ /* Hardware watchpoints got disarmed here. */
+ forkoff (1);
+ /* This watchpoint got lost before. */
+ var++;
+ /* A sanity check for double hardware watchpoints removal. */
+ forkoff (2);
+ var++;
+
+ return 0;
+}
Index: gdb-6.8.50.20090803/gdb/testsuite/gdb.threads/watchpoint-fork.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-6.8.50.20090803/gdb/testsuite/gdb.threads/watchpoint-fork.exp 2009-08-04 07:17:58.000000000 +0200
@@ -0,0 +1,140 @@
+# Copyright 2008 Free Software Foundation, Inc.
+
+# This program 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; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test case for forgotten hw-watchpoints after fork()-off of a process.
+
+proc test {type symbol} {
+ global objdir subdir srcdir
+
+ global pf_prefix
+ set prefix_test $pf_prefix
+ lappend pf_prefix "$type:"
+ set prefix_mt $pf_prefix
+
+ # no threads
+
+ set pf_prefix $prefix_mt
+ lappend pf_prefix "singlethreaded:"
+
+ set testfile watchpoint-fork
+ set srcfile ${testfile}.c
+ set binfile ${objdir}/${subdir}/${testfile}
+
+ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "debug additional_flags=-D$symbol"] != "" } {
+ untested "Couldn't compile test program"
+ return -1
+ }
+
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+ gdb_load ${binfile}
+
+ gdb_test "set follow-fork-mode $type"
+ # Testcase uses it for the `follow-fork-mode child' type.
+ gdb_test "handle SIGUSR1 nostop noprint pass"
+
+ if { ![runto_main] } then {
+ gdb_suppress_tests
+ return
+ }
+
+ # Install the watchpoint only after getting into MAIN - workaround some PPC
+ # problem.
+ gdb_test "watch var" "atchpoint 2: var" "Set the watchpoint"
+
+ # It is never hit but it should not be left over in the fork()ed-off child.
+ gdb_breakpoint "breakpoint"
+
+ gdb_test "continue" \
+ "atchpoint 2: var.*Old value = 0.*New value = 1.*forkoff *\\(1\\).*" "watchpoints work"
+ gdb_test "continue" \
+ "reakpoint 3, breakpoint.*" "breakpoint after the first fork"
+ gdb_test "continue" \
+ "atchpoint 2: var.*Old value = 1.*New value = 2.*forkoff *\\(2\\).*" "watchpoint after the first fork"
+ gdb_test "continue" \
+ "reakpoint 3, breakpoint.*" "breakpoint after the second fork"
+ gdb_test "continue" \
+ "atchpoint 2: var.*Old value = 2.*New value = 3.*return *0;" "watchpoint after the second fork"
+ gdb_test "continue" "Continuing..*Program exited normally." "finish"
+
+
+ # threads
+
+ set pf_prefix $prefix_mt
+ lappend pf_prefix "multithreaded:"
+
+ set testfile watchpoint-fork-mt
+ set srcfile ${testfile}.c
+ set binfile ${objdir}/${subdir}/${testfile}
+
+ if { [gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "debug additional_flags=-D$symbol"] != "" } {
+ untested "Couldn't compile test program"
+ return -1
+ }
+
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+ gdb_load ${binfile}
+
+ gdb_test "set follow-fork-mode $type"
+ # Testcase uses it for the `follow-fork-mode child' type.
+ gdb_test "handle SIGUSR1 nostop noprint pass"
+
+ if { ![runto_main] } then {
+ gdb_suppress_tests
+ return
+ }
+
+ # Install the watchpoint only after getting into MAIN - workaround some PPC
+ # problem.
+ gdb_test "watch var" "atchpoint 2: var" "Set the watchpoint"
+
+ # It is never hit but it should not be left over in the fork()ed-off child.
+ gdb_breakpoint "breakpoint"
+
+ gdb_test "continue" \
+ "atchpoint 2: var.*Old value = 0.*New value = 1.*validity-first.*" "singlethread watchpoints work"
+ gdb_test "continue" \
+ "atchpoint 2: var.*Old value = 1.*New value = 2.*validity-thread-A.*" "multithreaded watchpoints work at A"
+ gdb_test "continue" \
+ "atchpoint 2: var.*Old value = 2.*New value = 3.*validity-thread-B.*" "multithreaded watchpoints work at B"
+ gdb_test "continue" \
+ "reakpoint 3, breakpoint.*" "breakpoint (A) after the first fork"
+ gdb_test "continue" \
+ "atchpoint 2: var.*Old value = 3.*New value = 4.*after-fork1-A.*" "watchpoint A after the first fork"
+ gdb_test "continue" \
+ "atchpoint 2: var.*Old value = 4.*New value = 5.*after-fork1-B.*" "watchpoint B after the first fork"
+ gdb_test "continue" \
+ "reakpoint 3, breakpoint.*" "breakpoint (A) after the second fork"
+ gdb_test "continue" \
+ "atchpoint 2: var.*Old value = 5.*New value = 6.*after-fork2-A.*" "watchpoint A after the second fork"
+ gdb_test "continue" \
+ "atchpoint 2: var.*Old value = 6.*New value = 7.*after-fork2-B.*" "watchpoint B after the second fork"
+ gdb_test "continue" "Continuing..*Program exited normally." "finish"
+
+
+ # cleanup
+
+ set pf_prefix $prefix_test
+}
+
+test parent FOLLOW_PARENT
+
+# Only GNU/Linux is known to support `set follow-fork-mode child'.
+if {[istarget "*-*-linux*"]} {
+ test child FOLLOW_CHILD
+}
Index: gdb-6.8.50.20090803/gdb/doc/gdb.texinfo
===================================================================
--- gdb-6.8.50.20090803.orig/gdb/doc/gdb.texinfo 2009-08-04 07:17:19.000000000 +0200
+++ gdb-6.8.50.20090803/gdb/doc/gdb.texinfo 2009-08-04 07:17:58.000000000 +0200
@@ -3625,6 +3625,14 @@ confident that no other thread can becom
software watchpoints as usual. However, @value{GDBN} may not notice
when a non-current thread's activity changes the expression. (Hardware
watchpoints, in contrast, watch an expression in all threads.)
+
+Software watchpoints single-step the current thread to track the changes.
+Other threads are left freely running on @code{continue}; therefore, their
+changes cannot be caught. To get more reliable software watchpoints, please
+use @code{set scheduler-locking on}. The default for Red Hat/Fedora
+@value{GDBN} is @code{set scheduler-locking step}, which makes the software
+watchpoints safe for the @code{step} command, but not for the @code{continue}
+command. @xref{Thread Stops}.
@end quotation
@xref{set remote hardware-watchpoint-limit}.
Index: gdb-6.8.50.20090803/gdb/config/i386/nm-linux.h
===================================================================
--- gdb-6.8.50.20090803.orig/gdb/config/i386/nm-linux.h 2009-08-04 07:17:19.000000000 +0200
+++ gdb-6.8.50.20090803/gdb/config/i386/nm-linux.h 2009-08-04 07:17:58.000000000 +0200
@@ -29,4 +29,14 @@
#define FILL_FPXREGSET
#endif
+/* Remove a watchpoint that watched the memory region which starts at
+ * address ADDR, whose length is LEN bytes, and for accesses of the
+ * type TYPE. Return 0 on success, -1 on failure. */
+extern int i386_linux_remove_watchpoint (CORE_ADDR addr, int len, int type);
+
+/* Override basic i386 macros for watchpoint and hardware breakpoint
+ insertion/removal to support threads. */
+#define target_remove_watchpoint(addr, len, type) \
+ i386_linux_remove_watchpoint (addr, len, type)
+
#endif /* nm-linux.h */
Index: gdb-6.8.50.20090803/gdb/config/i386/nm-linux64.h
===================================================================
--- gdb-6.8.50.20090803.orig/gdb/config/i386/nm-linux64.h 2009-08-04 07:17:19.000000000 +0200
+++ gdb-6.8.50.20090803/gdb/config/i386/nm-linux64.h 2009-08-04 07:17:58.000000000 +0200
@@ -51,4 +51,14 @@ extern unsigned long amd64_linux_dr_get_
#define I386_DR_LOW_GET_STATUS() \
amd64_linux_dr_get_status ()
+/* Remove a watchpoint that watched the memory region which starts at
+ * address ADDR, whose length is LEN bytes, and for accesses of the
+ * type TYPE. Return 0 on success, -1 on failure. */
+extern int amd64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type);
+
+/* Override basic amd64 macros for watchpoint and hardware breakpoint
+ insertion/removal to support threads. */
+#define target_remove_watchpoint(addr, len, type) \
+ amd64_linux_remove_watchpoint (addr, len, type)
+
#endif /* nm-linux64.h */
Index: gdb-6.8.50.20090803/gdb/target.h
===================================================================
--- gdb-6.8.50.20090803.orig/gdb/target.h 2009-08-04 07:17:19.000000000 +0200
+++ gdb-6.8.50.20090803/gdb/target.h 2009-08-04 07:35:13.000000000 +0200
@@ -1114,11 +1114,15 @@ extern char *normal_pid_to_str (ptid_t p
for write, 1 for read, and 2 for read/write accesses. Returns 0 for
success, non-zero for failure. */
+#ifndef target_insert_watchpoint
#define target_insert_watchpoint(addr, len, type) \
(*current_target.to_insert_watchpoint) (addr, len, type)
+#endif
+#ifndef target_remove_watchpoint
#define target_remove_watchpoint(addr, len, type) \
(*current_target.to_remove_watchpoint) (addr, len, type)
+#endif
#define target_insert_hw_breakpoint(gdbarch, bp_tgt) \
(*current_target.to_insert_hw_breakpoint) (gdbarch, bp_tgt)