Teach linux gdbserver to step-over-breakpoints.

* linux-low.c (can_hardware_single_step): New.
	(supports_breakpoints): New.
	(handle_extended_wait): If stopping threads, read the stop pc of
	the new cloned LWP.
	(get_pc): New.
	(get_stop_pc): Add `lwp' parameter.  Handle it.  Bail out if the
	low target doesn't support retrieving the PC.
	(add_lwp): Set last_resume_kind to resume_continue.
	(linux_attach_lwp_1): Adjust comments.  Always set stop_expected.
	(linux_attach): Don't clear stop_expected.  Set the lwp's
	last_resume_kind to resume_stop.
	(linux_detach_one_lwp): Don't check for removed breakpoints.
	(check_removed_breakpoint): Delete.
	(status_pending_p): Rename to ...
	(status_pending_p_callback): ... this.  Don't check for removed
	breakpoints.  Don't consider threads that are stopped from GDB's
	perspective.
	(linux_wait_for_lwp): Always read the stop_pc here.
	(cancel_breakpoint): New.
	(step_over_bkpt): New global.
	(linux_wait_for_event_1): Implement stepping over breakpoints.
	(gdb_wants_lwp_stopped): New.
	(gdb_wants_all_stopped): New.
	(linux_wait_1): Tag threads as gdb-wants-stopped.  Cancel finished
	single-step traps here.  Store the thread's last reported target
	wait status.
	(send_sigstop): Don't clear stop_expected.  Always set it,
	instead.
	(mark_lwp_dead): Remove reference to pending_is_breakpoint.
	(cancel_finished_single_step): New.
	(cancel_finished_single_steps): New.
	(wait_for_sigstop): Don't cancel finished single-step traps here.
	(linux_resume_one_lwp): Don't check for removed breakpoints.
	Don't set `step' on non-hardware step archs.
	(linux_set_resume_request): Ignore resume_stop requests if already
	stopping or stopped.  Set the lwp's last_resume_kind.
	(resume_status_pending_p): Don't check for removed breakpoints.
	(need_step_over_p): New.
	(start_step_over): New.
	(finish_step_over): New.
	(linux_resume_one_thread): Always queue a sigstop for resume_stop
	requests.  Clear the thread's last reported target waitstatus.
	Don't use the `suspended' flag.  Don't consider pending breakpoints.
	(linux_resume): Start a step-over if necessary.
	(proceed_one_lwp): New.
	(proceed_all_lwps): New.
	(unstop_all_lwps): New.
	* linux-low.h (struct lwp_info): Rewrite comment for the
	`suspended' flag.  Add the `stop_pc' field.  Delete the
	`pending_stop_pc' field.  Tweak the `stepping' flag's comment.
	Add `'last_resume_kind' and `need_step_over' fields.
	* inferiors.c (struct thread_info): Delete, moved elsewhere.
	* mem-break.c (struct breakpoint): Delete `reinserting' flag.
	Delete `breakpoint_to_reinsert' field.  New flag `inserted'.
	(set_raw_breakpoint_at): New.
	(set_breakpoint_at): Rewrite to use it.
	(reinsert_breakpoint_handler): Delete.
	(set_reinsert_breakpoint): New.
	(reinsert_breakpoint_by_bp): Delete.
	(delete_reinsert_breakpoints): New.
	(uninsert_breakpoint): Rewrite.
	(uninsert_breakpoints_at): New.
	(reinsert_breakpoint): Rewrite.
	(reinsert_breakpoints_at): New.
	(check_breakpoints): Rewrite.
	(breakpoint_here): New.
	(breakpoint_inserted_here): New.
	(check_mem_read): Adjust.
	* mem-break.h (breakpoints_supported, breakpoint_here)
	(breakpoint_inserted_here, set_reinsert_breakpoint): Declare.
	(reinsert_breakpoint_by_bp): Delete declaration.
	(delete_reinsert_breakpoints): Declare.
	(reinsert_breakpoint): Delete declaration.
	(reinsert_breakpoints_at): Declare.
	(uninsert_breakpoint): Delete declaration.
	(uninsert_breakpoints_at): Declare.
	(check_breakpoints): Adjust prototype.
	* server.h: Adjust include order.
	(struct thread_info): Declare here.  Add a `last_status' field.
This commit is contained in:
Pedro Alves 2010-03-24 00:05:03 +00:00
parent 8541339168
commit d50171e439
7 changed files with 1290 additions and 443 deletions

View File

@ -1,3 +1,87 @@
2010-03-24 Pedro Alves <pedro@codesourcery.com>
Teach linux gdbserver to step-over-breakpoints.
* linux-low.c (can_hardware_single_step): New.
(supports_breakpoints): New.
(handle_extended_wait): If stopping threads, read the stop pc of
the new cloned LWP.
(get_pc): New.
(get_stop_pc): Add `lwp' parameter. Handle it. Bail out if the
low target doesn't support retrieving the PC.
(add_lwp): Set last_resume_kind to resume_continue.
(linux_attach_lwp_1): Adjust comments. Always set stop_expected.
(linux_attach): Don't clear stop_expected. Set the lwp's
last_resume_kind to resume_stop.
(linux_detach_one_lwp): Don't check for removed breakpoints.
(check_removed_breakpoint): Delete.
(status_pending_p): Rename to ...
(status_pending_p_callback): ... this. Don't check for removed
breakpoints. Don't consider threads that are stopped from GDB's
perspective.
(linux_wait_for_lwp): Always read the stop_pc here.
(cancel_breakpoint): New.
(step_over_bkpt): New global.
(linux_wait_for_event_1): Implement stepping over breakpoints.
(gdb_wants_lwp_stopped): New.
(gdb_wants_all_stopped): New.
(linux_wait_1): Tag threads as gdb-wants-stopped. Cancel finished
single-step traps here. Store the thread's last reported target
wait status.
(send_sigstop): Don't clear stop_expected. Always set it,
instead.
(mark_lwp_dead): Remove reference to pending_is_breakpoint.
(cancel_finished_single_step): New.
(cancel_finished_single_steps): New.
(wait_for_sigstop): Don't cancel finished single-step traps here.
(linux_resume_one_lwp): Don't check for removed breakpoints.
Don't set `step' on non-hardware step archs.
(linux_set_resume_request): Ignore resume_stop requests if already
stopping or stopped. Set the lwp's last_resume_kind.
(resume_status_pending_p): Don't check for removed breakpoints.
(need_step_over_p): New.
(start_step_over): New.
(finish_step_over): New.
(linux_resume_one_thread): Always queue a sigstop for resume_stop
requests. Clear the thread's last reported target waitstatus.
Don't use the `suspended' flag. Don't consider pending breakpoints.
(linux_resume): Start a step-over if necessary.
(proceed_one_lwp): New.
(proceed_all_lwps): New.
(unstop_all_lwps): New.
* linux-low.h (struct lwp_info): Rewrite comment for the
`suspended' flag. Add the `stop_pc' field. Delete the
`pending_stop_pc' field. Tweak the `stepping' flag's comment.
Add `'last_resume_kind' and `need_step_over' fields.
* inferiors.c (struct thread_info): Delete, moved elsewhere.
* mem-break.c (struct breakpoint): Delete `reinserting' flag.
Delete `breakpoint_to_reinsert' field. New flag `inserted'.
(set_raw_breakpoint_at): New.
(set_breakpoint_at): Rewrite to use it.
(reinsert_breakpoint_handler): Delete.
(set_reinsert_breakpoint): New.
(reinsert_breakpoint_by_bp): Delete.
(delete_reinsert_breakpoints): New.
(uninsert_breakpoint): Rewrite.
(uninsert_breakpoints_at): New.
(reinsert_breakpoint): Rewrite.
(reinsert_breakpoints_at): New.
(check_breakpoints): Rewrite.
(breakpoint_here): New.
(breakpoint_inserted_here): New.
(check_mem_read): Adjust.
* mem-break.h (breakpoints_supported, breakpoint_here)
(breakpoint_inserted_here, set_reinsert_breakpoint): Declare.
(reinsert_breakpoint_by_bp): Delete declaration.
(delete_reinsert_breakpoints): Declare.
(reinsert_breakpoint): Delete declaration.
(reinsert_breakpoints_at): Declare.
(uninsert_breakpoint): Delete declaration.
(uninsert_breakpoints_at): Declare.
(check_breakpoints): Adjust prototype.
* server.h: Adjust include order.
(struct thread_info): Declare here. Add a `last_status' field.
2010-03-23 Michael Snyder <msnyder@vmware.com>
* server.c (crc32): New function.

View File

@ -23,14 +23,6 @@
#include "server.h"
struct thread_info
{
struct inferior_list_entry entry;
void *target_data;
void *regcache_data;
unsigned int gdb_id;
};
struct inferior_list all_processes;
struct inferior_list all_threads;
struct inferior_list all_dlls;

File diff suppressed because it is too large Load Diff

View File

@ -137,7 +137,8 @@ struct lwp_info
yet. */
int stop_expected;
/* True if this thread was suspended (with vCont;t). */
/* When this is true, we shall not try to resume this thread, even
if last_resume_kind isn't resume_stop. */
int suspended;
/* If this flag is set, the lwp is known to be stopped right now (stop
@ -152,15 +153,15 @@ struct lwp_info
/* When stopped is set, the last wait status recorded for this lwp. */
int last_status;
/* When stopped is set, this is where the lwp stopped, with
decr_pc_after_break already accounted for. */
CORE_ADDR stop_pc;
/* If this flag is set, STATUS_PENDING is a waitstatus that has not yet
been reported. */
int status_pending_p;
int status_pending;
/* If this flag is set, the pending status is a (GDB-placed) breakpoint. */
int pending_is_breakpoint;
CORE_ADDR pending_stop_pc;
/* STOPPED_BY_WATCHPOINT is non-zero if this LWP stopped with a data
watchpoint trap. */
int stopped_by_watchpoint;
@ -175,8 +176,8 @@ struct lwp_info
stop (SIGTRAP stops only). */
CORE_ADDR bp_reinsert;
/* If this flag is set, the last continue operation on this process
was a single-step. */
/* If this flag is set, the last continue operation at the ptrace
level on this process was a single-step. */
int stepping;
/* If this flag is set, we need to set the event request flags the
@ -189,9 +190,15 @@ struct lwp_info
/* A link used when resuming. It is initialized from the resume request,
and then processed and cleared in linux_resume_one_lwp. */
struct thread_resume *resume;
/* The last resume GDB requested on this thread. */
enum resume_kind last_resume_kind;
/* True if the LWP was seen stop at an internal breakpoint and needs
stepping over later when it is resumed. */
int need_step_over;
int thread_known;
#ifdef HAVE_THREAD_DB_H
/* The thread handle, used for e.g. TLS access. Only valid if

View File

@ -32,38 +32,75 @@ struct breakpoint
CORE_ADDR pc;
unsigned char old_data[MAX_BREAKPOINT_LEN];
/* Non-zero iff we are stepping over this breakpoint. */
int reinserting;
/* Non-NULL iff this breakpoint was inserted to step over
another one. Points to the other breakpoint (which is also
in the *next chain somewhere). */
struct breakpoint *breakpoint_to_reinsert;
/* Non-zero if this breakpoint is currently inserted in the
inferior. */
int inserted;
/* Function to call when we hit this breakpoint. If it returns 1,
the breakpoint will be deleted; 0, it will be reinserted for
another round. */
the breakpoint shall be deleted; 0, it will be left inserted. */
int (*handler) (CORE_ADDR);
};
static struct breakpoint *
set_raw_breakpoint_at (CORE_ADDR where)
{
struct process_info *proc = current_process ();
struct breakpoint *bp;
int err;
if (breakpoint_data == NULL)
error ("Target does not support breakpoints.");
bp = xcalloc (1, sizeof (*bp));
bp->pc = where;
err = (*the_target->read_memory) (where, bp->old_data,
breakpoint_len);
if (err != 0)
{
if (debug_threads)
fprintf (stderr,
"Failed to read shadow memory of"
" breakpoint at 0x%s (%s).\n",
paddress (where), strerror (err));
free (bp);
return NULL;
}
err = (*the_target->write_memory) (where, breakpoint_data,
breakpoint_len);
if (err != 0)
{
if (debug_threads)
fprintf (stderr,
"Failed to insert breakpoint at 0x%s (%s).\n",
paddress (where), strerror (err));
free (bp);
return NULL;
}
/* Link the breakpoint in. */
bp->inserted = 1;
bp->next = proc->breakpoints;
proc->breakpoints = bp;
return bp;
}
void
set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR))
{
struct process_info *proc = current_process ();
struct breakpoint *bp;
if (breakpoint_data == NULL)
error ("Target does not support breakpoints.");
bp = set_raw_breakpoint_at (where);
bp = xmalloc (sizeof (struct breakpoint));
memset (bp, 0, sizeof (struct breakpoint));
if (bp == NULL)
{
/* warn? */
return;
}
(*the_target->read_memory) (where, bp->old_data,
breakpoint_len);
(*the_target->write_memory) (where, breakpoint_data,
breakpoint_len);
bp->pc = where;
bp = xcalloc (1, sizeof (struct breakpoint));
bp->handler = handler;
bp->next = proc->breakpoints;
@ -123,97 +160,145 @@ delete_breakpoint_at (CORE_ADDR addr)
delete_breakpoint (bp);
}
static int
reinsert_breakpoint_handler (CORE_ADDR stop_pc)
void
set_reinsert_breakpoint (CORE_ADDR stop_at)
{
struct breakpoint *stop_bp, *orig_bp;
stop_bp = find_breakpoint_at (stop_pc);
if (stop_bp == NULL)
error ("lost the stopping breakpoint.");
orig_bp = stop_bp->breakpoint_to_reinsert;
if (orig_bp == NULL)
error ("no breakpoint to reinsert");
(*the_target->write_memory) (orig_bp->pc, breakpoint_data,
breakpoint_len);
orig_bp->reinserting = 0;
return 1;
set_breakpoint_at (stop_at, NULL);
}
void
reinsert_breakpoint_by_bp (CORE_ADDR stop_pc, CORE_ADDR stop_at)
delete_reinsert_breakpoints (void)
{
struct breakpoint *bp, *orig_bp;
struct process_info *proc = current_process ();
struct breakpoint *bp, **bp_link;
orig_bp = find_breakpoint_at (stop_pc);
if (orig_bp == NULL)
error ("Could not find original breakpoint in list.");
bp = proc->breakpoints;
bp_link = &proc->breakpoints;
set_breakpoint_at (stop_at, reinsert_breakpoint_handler);
while (bp)
{
*bp_link = bp->next;
delete_breakpoint (bp);
bp = *bp_link;
}
}
bp = find_breakpoint_at (stop_at);
if (bp == NULL)
error ("Could not find breakpoint in list (reinserting by breakpoint).");
bp->breakpoint_to_reinsert = orig_bp;
static void
uninsert_breakpoint (struct breakpoint *bp)
{
if (bp->inserted)
{
int err;
(*the_target->write_memory) (orig_bp->pc, orig_bp->old_data,
breakpoint_len);
orig_bp->reinserting = 1;
bp->inserted = 0;
err = (*the_target->write_memory) (bp->pc, bp->old_data,
breakpoint_len);
if (err != 0)
{
bp->inserted = 1;
if (debug_threads)
fprintf (stderr,
"Failed to uninsert raw breakpoint at 0x%s (%s).\n",
paddress (bp->pc), strerror (err));
}
}
}
void
uninsert_breakpoint (CORE_ADDR stopped_at)
uninsert_breakpoints_at (CORE_ADDR pc)
{
struct breakpoint *bp;
bp = find_breakpoint_at (stopped_at);
bp = find_breakpoint_at (pc);
if (bp == NULL)
error ("Could not find breakpoint in list (uninserting).");
{
/* This can happen when we remove all breakpoints while handling
a step-over. */
if (debug_threads)
fprintf (stderr,
"Could not find breakpoint at 0x%s "
"in list (uninserting).\n",
paddress (pc));
return;
}
(*the_target->write_memory) (bp->pc, bp->old_data,
breakpoint_len);
bp->reinserting = 1;
if (bp->inserted)
uninsert_breakpoint (bp);
}
void
reinsert_breakpoint (CORE_ADDR stopped_at)
static void
reinsert_breakpoint (struct breakpoint *bp)
{
struct breakpoint *bp;
int err;
bp = find_breakpoint_at (stopped_at);
if (bp == NULL)
error ("Could not find breakpoint in list (uninserting).");
if (! bp->reinserting)
if (bp->inserted)
error ("Breakpoint already inserted at reinsert time.");
(*the_target->write_memory) (bp->pc, breakpoint_data,
breakpoint_len);
bp->reinserting = 0;
err = (*the_target->write_memory) (bp->pc, breakpoint_data,
breakpoint_len);
if (err == 0)
bp->inserted = 1;
else if (debug_threads)
fprintf (stderr,
"Failed to reinsert breakpoint at 0x%s (%s).\n",
paddress (bp->pc), strerror (err));
}
int
check_breakpoints (CORE_ADDR stop_pc)
void
reinsert_breakpoints_at (CORE_ADDR pc)
{
struct breakpoint *bp;
bp = find_breakpoint_at (stop_pc);
bp = find_breakpoint_at (pc);
if (bp == NULL)
return 0;
if (bp->reinserting)
{
warning ("Hit a removed breakpoint?");
return 0;
/* This can happen when we remove all breakpoints while handling
a step-over. */
if (debug_threads)
fprintf (stderr,
"Could not find breakpoint at 0x%s "
"in list (reinserting).\n",
paddress (pc));
return;
}
if ((*bp->handler) (bp->pc))
reinsert_breakpoint (bp);
}
void
check_breakpoints (CORE_ADDR stop_pc)
{
struct process_info *proc = current_process ();
struct breakpoint *bp, **bp_link;
bp = proc->breakpoints;
bp_link = &proc->breakpoints;
while (bp)
{
delete_breakpoint (bp);
return 2;
if (bp->pc == stop_pc)
{
if (!bp->inserted)
{
warning ("Hit a removed breakpoint?");
return;
}
if (bp->handler != NULL && (*bp->handler) (stop_pc))
{
*bp_link = bp->next;
delete_breakpoint (bp);
bp = *bp_link;
continue;
}
}
bp_link = &bp->next;
bp = *bp_link;
}
else
return 1;
}
void
@ -223,6 +308,32 @@ set_breakpoint_data (const unsigned char *bp_data, int bp_len)
breakpoint_len = bp_len;
}
int
breakpoint_here (CORE_ADDR addr)
{
struct process_info *proc = current_process ();
struct breakpoint *bp;
for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
if (bp->pc == addr)
return 1;
return 0;
}
int
breakpoint_inserted_here (CORE_ADDR addr)
{
struct process_info *proc = current_process ();
struct breakpoint *bp;
for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
if (bp->pc == addr && bp->inserted)
return 1;
return 0;
}
void
check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
{
@ -288,7 +399,7 @@ check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
buf_offset = start - mem_addr;
memcpy (bp->old_data + copy_offset, buf + buf_offset, copy_len);
if (bp->reinserting == 0)
if (bp->inserted)
memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len);
}
}

View File

@ -24,6 +24,19 @@
/* Breakpoints are opaque. */
/* Returns TRUE if breakpoints are supported on this target. */
int breakpoints_supported (void);
/* Returns TRUE if there's any breakpoint at ADDR in our tables,
inserted, or not. */
int breakpoint_here (CORE_ADDR addr);
/* Returns TRUE if there's any inserted breakpoint set at ADDR. */
int breakpoint_inserted_here (CORE_ADDR addr);
/* Create a new breakpoint at WHERE, and call HANDLER when
it is hit. HANDLER should return 1 if the breakpoint
should be deleted, 0 otherwise. */
@ -36,24 +49,28 @@ void set_breakpoint_at (CORE_ADDR where,
void delete_breakpoint_at (CORE_ADDR addr);
/* Create a reinsertion breakpoint at STOP_AT for the breakpoint
currently at STOP_PC (and temporarily remove the breakpoint at
STOP_PC). */
/* Set a reinsert breakpoint at STOP_AT. */
void reinsert_breakpoint_by_bp (CORE_ADDR stop_pc, CORE_ADDR stop_at);
void set_reinsert_breakpoint (CORE_ADDR stop_at);
/* Change the status of the breakpoint at WHERE to inserted. */
/* Delete all reinsert breakpoints. */
void reinsert_breakpoint (CORE_ADDR where);
void delete_reinsert_breakpoints (void);
/* Change the status of the breakpoint at WHERE to uninserted. */
/* Reinsert breakpoints at WHERE (and change their status to
inserted). */
void uninsert_breakpoint (CORE_ADDR where);
void reinsert_breakpoints_at (CORE_ADDR where);
/* Uninsert breakpoints at WHERE (and change their status to
uninserted). This still leaves the breakpoints in the table. */
void uninsert_breakpoints_at (CORE_ADDR where);
/* See if any breakpoint claims ownership of STOP_PC. Call the handler for
the breakpoint, if found. */
int check_breakpoints (CORE_ADDR stop_pc);
void check_breakpoints (CORE_ADDR stop_pc);
/* See if any breakpoints shadow the target memory area from MEM_ADDR
to MEM_ADDR + MEM_LEN. Update the data already read from the target

View File

@ -162,8 +162,25 @@ struct inferior_list_entry
struct inferior_list_entry *next;
};
/* Opaque type for user-visible threads. */
struct thread_info;
struct process_info;
struct regcache;
#include "regcache.h"
#include "gdb/signals.h"
#include "gdb_signals.h"
#include "target.h"
#include "mem-break.h"
struct thread_info
{
struct inferior_list_entry entry;
void *target_data;
void *regcache_data;
/* The last wait status reported for this thread. */
struct target_waitstatus last_status;
};
struct dll_info
{
@ -203,12 +220,6 @@ struct process_info
struct process_info *current_process (void);
struct process_info *get_thread_process (struct thread_info *);
#include "regcache.h"
#include "gdb/signals.h"
#include "gdb_signals.h"
#include "target.h"
#include "mem-break.h"
/* Target-specific functions */
void initialize_low ();