From patchwork Tue Mar 17 20:56:26 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Don Breazeal X-Patchwork-Id: 5657 Received: (qmail 31530 invoked by alias); 17 Mar 2015 20:57:47 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 31510 invoked by uid 89); 17 Mar 2015 20:57:46 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.1 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 X-HELO: relay1.mentorg.com Received: from relay1.mentorg.com (HELO relay1.mentorg.com) (192.94.38.131) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Tue, 17 Mar 2015 20:57:44 +0000 Received: from svr-orw-fem-05.mgc.mentorg.com ([147.34.97.43]) by relay1.mentorg.com with esmtp id 1YXyYX-0000V9-JE from Don_Breazeal@mentor.com ; Tue, 17 Mar 2015 13:57:41 -0700 Received: from build4-lucid-cs (147.34.91.1) by svr-orw-fem-05.mgc.mentorg.com (147.34.97.43) with Microsoft SMTP Server id 14.3.224.2; Tue, 17 Mar 2015 13:57:41 -0700 Received: by build4-lucid-cs (Postfix, from userid 1905) id 0A33E40F0F; Tue, 17 Mar 2015 13:57:40 -0700 (PDT) From: Don Breazeal To: , Subject: [PATCH v6 4/6] Arch-specific remote follow fork Date: Tue, 17 Mar 2015 13:56:26 -0700 Message-ID: <1426625788-4469-5-git-send-email-donb@codesourcery.com> In-Reply-To: <1426625788-4469-1-git-send-email-donb@codesourcery.com> References: <1424997977-13316-1-git-send-email-donb@codesourcery.com> <1426625788-4469-1-git-send-email-donb@codesourcery.com> MIME-Version: 1.0 X-IsSubscribed: yes Hi Pedro, This version of this patch is unchanged except possibly for merges from the mainline. It was previously approved here: https://sourceware.org/ml/gdb-patches/2015-02/msg00262.html Thanks, --Don This patch implements the architecture-specific pieces of follow-fork for remote and extended-remote Linux targets, which in the current implementation copyies the parent's debug register state into the new child's data structures. This is required for x86, arm, aarch64, and mips. This follows the native implementation as closely as possible by implementing a new linux_target_ops function 'new_fork', which is analogous to 'linux_nat_new_fork' in linux-nat.c. In gdbserver, the debug registers are stored in the process list, instead of an architecture-specific list, so the function arguments are process_info pointers instead of an lwp_info and a pid as in the native implementation. In the MIPS implementation the debug register mirror is stored differently from x86, ARM, and aarch64, so instead of doing a simple structure assignment I had to clone the list of watchpoint structures. Tested using gdb.threads/watchpoint-fork.exp on x86, and ran manual tests on a MIPS board and an ARM board. I don't currently have access to an aarch64 board, so if someone is able to test this easily, please do. gdb/ 2015-03-17 Don Breazeal * NEWS: Remove hardware breakpoint caveat from announcement of remote follow fork support. gdb/gdbserver/ 2015-03-17 Don Breazeal * linux-aarch64-low.c (aarch64_linux_new_fork): New function. (the_low_target) : Initialize new member. * linux-arm-low.c (arm_new_fork): New function. (the_low_target) : Initialize new member. * linux-low.c (handle_extended_wait): Call new target function new_fork. * linux-low.h (struct linux_target_ops) : New member. * linux-mips-low.c (mips_add_watchpoint): New function extracted from mips_insert_point. (the_low_target) : Initialize new member. (mips_linux_new_fork): New function. (mips_insert_point): Call mips_add_watchpoint. * linux-x86-low.c (x86_linux_new_fork): New function. (the_low_target) : Initialize new member. --- gdb/NEWS | 3 +- gdb/gdbserver/linux-aarch64-low.c | 28 +++++++++++++++ gdb/gdbserver/linux-arm-low.c | 42 ++++++++++++++++++++++ gdb/gdbserver/linux-low.c | 4 +++ gdb/gdbserver/linux-low.h | 3 ++ gdb/gdbserver/linux-mips-low.c | 76 ++++++++++++++++++++++++++++++++------- gdb/gdbserver/linux-x86-low.c | 29 +++++++++++++++ 7 files changed, 171 insertions(+), 14 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 5aedda5..ce1bb03 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -103,8 +103,7 @@ Itanium running HP-UX ia64-*-hpux* GDBserver extended-remote Linux targets now provides basic support for fork events. This enables follow-fork-mode and detach-on-fork - for those targets with Linux kernels 2.5.60 and later. Hardware - watchpoints are not inherited across a fork in this implementation. + for those targets with Linux kernels 2.5.60 and later. *** Changes in GDB 7.9 diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c index 7934e78..e8fb201 100644 --- a/gdb/gdbserver/linux-aarch64-low.c +++ b/gdb/gdbserver/linux-aarch64-low.c @@ -1135,6 +1135,33 @@ aarch64_linux_new_thread (void) return info; } +static void +aarch64_linux_new_fork (struct process_info *parent, + struct process_info *child) +{ + /* These are allocated by linux_add_process. */ + gdb_assert (parent->private != NULL + && parent->private->arch_private != NULL); + gdb_assert (child->private != NULL + && child->private->arch_private != NULL); + + /* Linux kernel before 2.6.33 commit + 72f674d203cd230426437cdcf7dd6f681dad8b0d + will inherit hardware debug registers from parent + on fork/vfork/clone. Newer Linux kernels create such tasks with + zeroed debug registers. + + GDB core assumes the child inherits the watchpoints/hw + breakpoints of the parent, and will remove them all from the + forked off process. Copy the debug registers mirrors into the + new process so that all breakpoints and watchpoints can be + removed together. The debug registers mirror will become zeroed + in the end before detaching the forked off process, thus making + this compatible with older Linux kernels too. */ + + *child->private->arch_private = *parent->private->arch_private; +} + /* Called when resuming a thread. If the debug regs have changed, update the thread's copies. */ @@ -1299,6 +1326,7 @@ struct linux_target_ops the_low_target = NULL, aarch64_linux_new_process, aarch64_linux_new_thread, + aarch64_linux_new_fork, aarch64_linux_prepare_to_resume, }; diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c index 2cd1668..e435ffc 100644 --- a/gdb/gdbserver/linux-arm-low.c +++ b/gdb/gdbserver/linux-arm-low.c @@ -717,6 +717,47 @@ arm_new_thread (void) return info; } +static void +arm_new_fork (struct process_info *parent, struct process_info *child) +{ + struct arch_process_info *parent_proc_info = parent->private->arch_private; + struct arch_process_info *child_proc_info = child->private->arch_private; + struct lwp_info *child_lwp; + struct arch_lwp_info *child_lwp_info; + int i; + + /* These are allocated by linux_add_process. */ + gdb_assert (parent->private != NULL + && parent->private->arch_private != NULL); + gdb_assert (child->private != NULL + && child->private->arch_private != NULL); + + /* Linux kernel before 2.6.33 commit + 72f674d203cd230426437cdcf7dd6f681dad8b0d + will inherit hardware debug registers from parent + on fork/vfork/clone. Newer Linux kernels create such tasks with + zeroed debug registers. + + GDB core assumes the child inherits the watchpoints/hw + breakpoints of the parent, and will remove them all from the + forked off process. Copy the debug registers mirrors into the + new process so that all breakpoints and watchpoints can be + removed together. The debug registers mirror will become zeroed + in the end before detaching the forked off process, thus making + this compatible with older Linux kernels too. */ + + *child_proc_info = *parent_proc_info; + + /* Mark all the hardware breakpoints and watchpoints as changed to + make sure that the registers will be updated. */ + child_lwp = find_lwp_pid (ptid_of (child)); + child_lwp_info = child_lwp->arch_private; + for (i = 0; i < MAX_BPTS; i++) + child_lwp_info->bpts_changed[i] = 1; + for (i = 0; i < MAX_WPTS; i++) + child_lwp_info->wpts_changed[i] = 1; +} + /* Called when resuming a thread. If the debug regs have changed, update the thread's copies. */ static void @@ -920,6 +961,7 @@ struct linux_target_ops the_low_target = { NULL, /* siginfo_fixup */ arm_new_process, arm_new_thread, + arm_new_fork, arm_prepare_to_resume, }; diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 8372f36..07dc244 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -446,6 +446,10 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat) child_proc->tdesc = tdesc; child_lwp->must_set_ptrace_flags = 1; + /* Clone arch-specific process data. */ + if (the_low_target.new_fork != NULL) + the_low_target.new_fork (parent_proc, child_proc); + /* Save fork info in the parent thread. */ event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED; event_lwp->waitstatus.value.related_pid = ptid; diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index 28cccc7..e114a37 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -186,6 +186,9 @@ struct linux_target_ops allocate it here. */ struct arch_lwp_info * (*new_thread) (void); + /* Hook to call, if any, when a new fork is attached. */ + void (*new_fork) (struct process_info *parent, struct process_info *child); + /* Hook to call prior to resuming a thread. */ void (*prepare_to_resume) (struct lwp_info *); diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c index f081dda..642e6bd 100644 --- a/gdb/gdbserver/linux-mips-low.c +++ b/gdb/gdbserver/linux-mips-low.c @@ -344,6 +344,68 @@ mips_linux_new_thread (void) return info; } +/* Create a new mips_watchpoint and add it to the list. */ + +static void +mips_add_watchpoint (struct arch_process_info *private, CORE_ADDR addr, + int len, int watch_type) +{ + struct mips_watchpoint *new_watch; + struct mips_watchpoint **pw; + + new_watch = xmalloc (sizeof (struct mips_watchpoint)); + new_watch->addr = addr; + new_watch->len = len; + new_watch->type = watch_type; + new_watch->next = NULL; + + pw = &private->current_watches; + while (*pw != NULL) + pw = &(*pw)->next; + *pw = new_watch; +} + +/* Hook to call when a new fork is attached. */ + +static void +mips_linux_new_fork (struct process_info *parent, + struct process_info *child) +{ + struct arch_process_info *parent_private; + struct arch_process_info *child_private; + struct mips_watchpoint *wp; + + /* These are allocated by linux_add_process. */ + gdb_assert (parent->private != NULL + && parent->private->arch_private != NULL); + gdb_assert (child->private != NULL + && child->private->arch_private != NULL); + + /* Linux kernel before 2.6.33 commit + 72f674d203cd230426437cdcf7dd6f681dad8b0d + will inherit hardware debug registers from parent + on fork/vfork/clone. Newer Linux kernels create such tasks with + zeroed debug registers. + + GDB core assumes the child inherits the watchpoints/hw + breakpoints of the parent, and will remove them all from the + forked off process. Copy the debug registers mirrors into the + new process so that all breakpoints and watchpoints can be + removed together. The debug registers mirror will become zeroed + in the end before detaching the forked off process, thus making + this compatible with older Linux kernels too. */ + + parent_private = parent->private->arch_private; + child_private = child->private->arch_private; + + child_private->watch_readback_valid = parent_private->watch_readback_valid; + child_private->watch_readback = parent_private->watch_readback; + + for (wp = parent_private->current_watches; wp != NULL; wp = wp->next) + mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type); + + child_private->watch_mirror = parent_private->watch_mirror; +} /* This is the implementation of linux_target_ops method prepare_to_resume. If the watch regs have changed, update the thread's copies. */ @@ -397,8 +459,6 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr, struct process_info *proc = current_process (); struct arch_process_info *priv = proc->priv->arch_private; struct pt_watch_regs regs; - struct mips_watchpoint *new_watch; - struct mips_watchpoint **pw; int pid; long lwpid; enum target_hw_bp_type watch_type; @@ -425,16 +485,7 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr, return -1; /* It fit. Stick it on the end of the list. */ - new_watch = xmalloc (sizeof (struct mips_watchpoint)); - new_watch->addr = addr; - new_watch->len = len; - new_watch->type = watch_type; - new_watch->next = NULL; - - pw = &priv->current_watches; - while (*pw != NULL) - pw = &(*pw)->next; - *pw = new_watch; + mips_add_watchpoint (priv, addr, len, watch_type); priv->watch_mirror = regs; @@ -845,6 +896,7 @@ struct linux_target_ops the_low_target = { NULL, /* siginfo_fixup */ mips_linux_new_process, mips_linux_new_thread, + mips_linux_new_fork, mips_linux_prepare_to_resume }; diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c index 72478d8..5668840 100644 --- a/gdb/gdbserver/linux-x86-low.c +++ b/gdb/gdbserver/linux-x86-low.c @@ -781,6 +781,34 @@ x86_linux_new_thread (void) return info; } +/* Target routine for linux_new_fork. */ + +static void +x86_linux_new_fork (struct process_info *parent, struct process_info *child) +{ + /* These are allocated by linux_add_process. */ + gdb_assert (parent->priv != NULL + && parent->priv->arch_private != NULL); + gdb_assert (child->priv != NULL + && child->priv->arch_private != NULL); + + /* Linux kernel before 2.6.33 commit + 72f674d203cd230426437cdcf7dd6f681dad8b0d + will inherit hardware debug registers from parent + on fork/vfork/clone. Newer Linux kernels create such tasks with + zeroed debug registers. + + GDB core assumes the child inherits the watchpoints/hw + breakpoints of the parent, and will remove them all from the + forked off process. Copy the debug registers mirrors into the + new process so that all breakpoints and watchpoints can be + removed together. The debug registers mirror will become zeroed + in the end before detaching the forked off process, thus making + this compatible with older Linux kernels too. */ + + *child->priv->arch_private = *parent->priv->arch_private; +} + /* Called when resuming a thread. If the debug regs have changed, update the thread's copies. */ @@ -3441,6 +3469,7 @@ struct linux_target_ops the_low_target = x86_siginfo_fixup, x86_linux_new_process, x86_linux_new_thread, + x86_linux_new_fork, x86_linux_prepare_to_resume, x86_linux_process_qsupported, x86_supports_tracepoints,