Re: [native AAarch64 GNU/Linux] Access debug register mirror from the corresponding process.

Yufeng Zhang
2013-02-14T10:13:24Z

[-- Attachment #1: Type: text/plain, Size: 2440 bytes --]

On 02/13/13 19:03, Pedro Alves wrote:
> On 02/13/2013 06:56 PM, Yufeng Zhang wrote:
>> On 02/13/13 16:01, Pedro Alves wrote:
>>> On 02/13/2013 03:21 PM, Marcus Shawcroft wrote:
>>>>> I'll send you an aarch64 version of this patch, unless you've
>>>>> already started porting the necessary bits over.
>>>>
>>>> We were just about to get started... but if you have a patch in hand
>>>> that would be great!
>>>
>>> Here it is. Completely untested, of course. But hopefully not
>>> too far off.
>>
>> Not far off at all; thanks! I have tested your patch and made some further changes; please find the attached patch. The two patches together seem to be working.
>
> Those changes look fine. Please merge them, and go ahead
> post/commit the resulting patch.
>

Thanks. The attached is the merged patch. I'll commit it in a couple
of hours if no more comment is received.

Thanks,
Yufeng

2013-02-14 Pedro Alves <palves@redhat.com>
Yufeng Zhang <yufeng.zhang@arm.com>

* aarch64-linux-nat.c (aarch64_init_debug_reg_state): Delete.
(aarch64_inferior_data, struct aarch64_inferior_data):
Delete.
(struct aarch64_process_info): New.
(aarch64_process_list): New global.
(aarch64_find_process_pid, aarch64_add_process)
(aarch64_process_info_get): New functions.
(aarch64_inferior_data_get): Delete.
(aarch64_process_info_get): New function.
(aarch64_forget_process): New function.
(aarch64_get_debug_reg_state): New parameter 'pid'. Reimplement.
(aarch64_linux_prepare_to_resume): Pass the lwp's pid to
aarch64_get_debug_reg_state.
(aarch64_notify_debug_reg_change): Use iterate_over_lwps
instead of linux_nat_iterate_watchpoint_lwps.
(aarch64_linux_new_fork): New function.
(aarch64_linux_child_post_startup_inferior): Use
aarch64_forget_process instead of aarch64_init_debug_reg_state.
(aarch64_handle_breakpoint, aarch64_linux_insert_hw_breakpoint)
(aarch64_linux_remove_hw_breakpoint)
(aarch64_handle_aligned_watchpoint)
(aarch64_handle_unaligned_watchpoint)
(aarch64_linux_insert_watchpoint)
(aarch64_linux_remove_watchpoint)
(aarch64_linux_stopped_data_address): Adjust to pass the current
process id to aarch64_debug_reg_state.
(_initialize_aarch64_linux_nat): Install aarch64_linux_new_fork as
linux_nat_new_fork hook, and aarch64_forget_process as
linux_nat_forget_process hook; remove the call to
register_inferior_data_with_cleanup.

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: aarch64-dregs.patch --]
[-- Type: text/x-patch; name=aarch64-dregs.patch, Size: 12030 bytes --]

diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 507ba81..846f156 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -189,75 +189,103 @@ struct aarch64_debug_reg_state
unsigned int dr_ref_count_wp[AARCH64_HWP_MAX_NUM];
};

-/* Clear the reference counts and forget everything we knew about the
- debug registers. */
+/* Per-process data. We don't bind this to a per-inferior registry
+ because of targets like x86 GNU/Linux that need to keep track of
+ processes that aren't bound to any inferior (e.g., fork children,
+ checkpoints). */

-static void
-aarch64_init_debug_reg_state (struct aarch64_debug_reg_state *state)
+struct aarch64_process_info
{
- int i;
+ /* Linked list. */
+ struct aarch64_process_info *next;

- for (i = 0; i < AARCH64_HBP_MAX_NUM; ++i)
- {
- state->dr_addr_bp[i] = 0;
- state->dr_ctrl_bp[i] = 0;
- state->dr_ref_count_bp[i] = 0;
- }
+ /* The process identifier. */
+ pid_t pid;

- for (i = 0; i < AARCH64_HWP_MAX_NUM; ++i)
- {
- state->dr_addr_wp[i] = 0;
- state->dr_ctrl_wp[i] = 0;
- state->dr_ref_count_wp[i] = 0;
- }
+ /* Copy of aarch64 hardware debug registers. */
+ struct aarch64_debug_reg_state state;
+};
+
+static struct aarch64_process_info *aarch64_process_list = NULL;
+
+/* Find process data for process PID. */
+
+static struct aarch64_process_info *
+aarch64_find_process_pid (pid_t pid)
+{
+ struct aarch64_process_info *proc;
+
+ for (proc = aarch64_process_list; proc; proc = proc->next)
+ if (proc->pid == pid)
+ return proc;
+
+ return NULL;
}

-/* Per-inferior data key. */
-static const struct inferior_data *aarch64_inferior_data;
+/* Add process data for process PID. Returns newly allocated info
+ object. */

-/* Per-inferior data. */
-struct aarch64_inferior_data
+static struct aarch64_process_info *
+aarch64_add_process (pid_t pid)
{
- /* Copy of AArch64 hardware debug registers for performance reasons. */
- struct aarch64_debug_reg_state state;
-};
+ struct aarch64_process_info *proc;

-/* Per-inferior hook for register_inferior_data_with_cleanup. */
+ proc = xcalloc (1, sizeof (*proc));
+ proc->pid = pid;

-static void
-aarch64_inferior_data_cleanup (struct inferior *inf, void *arg)
+ proc->next = aarch64_process_list;
+ aarch64_process_list = proc;
+
+ return proc;
+}
+
+/* Get data specific info for process PID, creating it if necessary.
+ Never returns NULL. */
+
+static struct aarch64_process_info *
+aarch64_process_info_get (pid_t pid)
{
- struct aarch64_inferior_data *inf_data = arg;
+ struct aarch64_process_info *proc;
+
+ proc = aarch64_find_process_pid (pid);
+ if (proc == NULL)
+ proc = aarch64_add_process (pid);

- xfree (inf_data);
+ return proc;
}

-/* Get data specific for INFERIOR_PTID LWP. Return special data area
- for processes being detached. */
+/* Called whenever GDB is no longer debugging process PID. It deletes
+ data structures that keep track of debug register state. */

-static struct aarch64_inferior_data *
-aarch64_inferior_data_get (void)
+static void
+aarch64_forget_process (pid_t pid)
{
- struct inferior *inf = current_inferior ();
- struct aarch64_inferior_data *inf_data;
+ struct aarch64_process_info *proc, **proc_link;

- inf_data = inferior_data (inf, aarch64_inferior_data);
- if (inf_data == NULL)
+ proc = aarch64_process_list;
+ proc_link = &aarch64_process_list;
+
+ while (proc != NULL)
{
- inf_data = xzalloc (sizeof (*inf_data));
- set_inferior_data (inf, aarch64_inferior_data, inf_data);
- }
+ if (proc->pid == pid)
+ {
+ *proc_link = proc->next;

- return inf_data;
+ xfree (proc);
+ return;
+ }
+
+ proc_link = &proc->next;
+ proc = *proc_link;
+ }
}

-/* Get debug registers state for INFERIOR_PTID, see
- aarch64_inferior_data_get. */
+/* Get debug registers state for process PID. */

static struct aarch64_debug_reg_state *
-aarch64_get_debug_reg_state (void)
+aarch64_get_debug_reg_state (pid_t pid)
{
- return &aarch64_inferior_data_get ()->state;
+ return &aarch64_process_info_get (pid)->state;
}

/* Per-thread arch-specific data we want to keep. */
@@ -308,7 +336,7 @@ struct aarch64_dr_update_callback_param
unsigned int idx;
};

-/* Callback for linux_nat_iterate_watchpoint_lwps. Records the
+/* Callback for iterate_over_lwps. Records the
information about the change of one hardware breakpoint/watchpoint
setting for the thread LWP.
The information is passed in via PTR.
@@ -382,12 +410,12 @@ aarch64_notify_debug_reg_change (const struct aarch64_debug_reg_state *state,
int is_watchpoint, unsigned int idx)
{
struct aarch64_dr_update_callback_param param;
+ ptid_t pid_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));

param.is_watchpoint = is_watchpoint;
param.idx = idx;

- linux_nat_iterate_watchpoint_lwps (debug_reg_change_callback,
- (void *) &param);
+ iterate_over_lwps (pid_ptid, debug_reg_change_callback, (void *) &param);
}

/* Print the values of the cached breakpoint/watchpoint registers. */
@@ -655,7 +683,8 @@ aarch64_linux_prepare_to_resume (struct lwp_info *lwp)
|| DR_HAS_CHANGED (info->dr_changed_wp))
{
int tid = GET_LWP (lwp->ptid);
- struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (lwp->ptid));

if (debug_hw_points)
fprintf_unfiltered (gdb_stdlog, "prepare_to_resume thread %d\n", tid);
@@ -688,6 +717,32 @@ aarch64_linux_new_thread (struct lwp_info *lp)

lp->arch_private = info;
}
+
+/* linux_nat_new_fork hook. */
+
+static void
+aarch64_linux_new_fork (struct lwp_info *parent, pid_t child_pid)
+{
+ pid_t parent_pid;
+ struct aarch64_debug_reg_state *parent_state;
+ struct aarch64_debug_reg_state *child_state;
+
+ /* NULL means no watchpoint has ever been set in the parent. In
+ that case, there's nothing to do. */
+ if (parent->arch_private == NULL)
+ return;
+
+ /* 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. */
+
+ parent_pid = ptid_get_pid (parent->ptid);
+ parent_state = aarch64_get_debug_reg_state (parent_pid);
+ child_state = aarch64_get_debug_reg_state (child_pid);
+ *child_state = *parent_state;
+}
\f

/* Called by libthread_db. Returns a pointer to the thread local
@@ -776,9 +831,7 @@ static void (*super_post_startup_inferior) (ptid_t ptid);
static void
aarch64_linux_child_post_startup_inferior (ptid_t ptid)
{
- struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
-
- aarch64_init_debug_reg_state (state);
+ aarch64_forget_process (ptid_get_pid (ptid));
aarch64_linux_get_debug_reg_capacity ();
super_post_startup_inferior (ptid);
}
@@ -1132,7 +1185,7 @@ aarch64_handle_breakpoint (int type, CORE_ADDR addr, int len, int is_insert)
if (!aarch64_point_is_aligned (0 /* is_watchpoint */ , addr, len))
return -1;

- state = aarch64_get_debug_reg_state ();
+ state = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));

if (is_insert)
return aarch64_dr_state_insert_one_point (state, type, addr, len);
@@ -1161,8 +1214,13 @@ aarch64_linux_insert_hw_breakpoint (struct gdbarch *gdbarch,
ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */);

if (debug_hw_points > 1)
- aarch64_show_debug_reg_state (aarch64_get_debug_reg_state (),
- "insert_hw_watchpoint", addr, len, type);
+ {
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
+
+ aarch64_show_debug_reg_state (state,
+ "insert_hw_watchpoint", addr, len, type);
+ }

return ret;
}
@@ -1187,8 +1245,13 @@ aarch64_linux_remove_hw_breakpoint (struct gdbarch *gdbarch,
ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */);

if (debug_hw_points > 1)
- aarch64_show_debug_reg_state (aarch64_get_debug_reg_state (),
- "remove_hw_watchpoint", addr, len, type);
+ {
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
+
+ aarch64_show_debug_reg_state (state,
+ "remove_hw_watchpoint", addr, len, type);
+ }

return ret;
}
@@ -1200,7 +1263,8 @@ static int
aarch64_handle_aligned_watchpoint (int type, CORE_ADDR addr, int len,
int is_insert)
{
- struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));

if (is_insert)
return aarch64_dr_state_insert_one_point (state, type, addr, len);
@@ -1219,7 +1283,8 @@ static int
aarch64_handle_unaligned_watchpoint (int type, CORE_ADDR addr, int len,
int is_insert)
{
- struct aarch64_debug_reg_state *state = aarch64_get_debug_reg_state ();
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));

while (len > 0)
{
@@ -1283,8 +1348,13 @@ aarch64_linux_insert_watchpoint (CORE_ADDR addr, int len, int type,
ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */);

if (debug_hw_points > 1)
- aarch64_show_debug_reg_state (aarch64_get_debug_reg_state (),
- "insert_watchpoint", addr, len, type);
+ {
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
+
+ aarch64_show_debug_reg_state (state,
+ "insert_watchpoint", addr, len, type);
+ }

return ret;
}
@@ -1310,8 +1380,13 @@ aarch64_linux_remove_watchpoint (CORE_ADDR addr, int len, int type,
ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */);

if (debug_hw_points > 1)
- aarch64_show_debug_reg_state (aarch64_get_debug_reg_state (),
- "remove_watchpoint", addr, len, type);
+ {
+ struct aarch64_debug_reg_state *state
+ = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
+
+ aarch64_show_debug_reg_state (state,
+ "remove_watchpoint", addr, len, type);
+ }

return ret;
}
@@ -1374,7 +1449,7 @@ aarch64_linux_stopped_data_address (struct target_ops *target,
return 0;

/* Check if the address matches any watched address. */
- state = aarch64_get_debug_reg_state ();
+ state = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
{
const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
@@ -1465,10 +1540,6 @@ _initialize_aarch64_linux_nat (void)
t->to_stopped_data_address = aarch64_linux_stopped_data_address;
t->to_watchpoint_addr_within_range =
aarch64_linux_watchpoint_addr_within_range;
- if (aarch64_inferior_data == NULL)
- aarch64_inferior_data
- = register_inferior_data_with_cleanup (NULL,
- aarch64_inferior_data_cleanup);

/* Override the GNU/Linux inferior startup hook. */
super_post_startup_inferior = t->to_post_startup_inferior;
@@ -1477,5 +1548,7 @@ _initialize_aarch64_linux_nat (void)
/* Register the target. */
linux_nat_add_target (t);
linux_nat_set_new_thread (t, aarch64_linux_new_thread);
+ linux_nat_set_new_fork (t, aarch64_linux_new_fork);
+ linux_nat_set_forget_process (t, aarch64_forget_process);
linux_nat_set_prepare_to_resume (t, aarch64_linux_prepare_to_resume);
}