2009-04-17 Ross Morley doc * doc/gdb.texinfo: Mention program breakpoints and associated remote protocol 'TAAtrap:r' stop-reply packet extension, and MI async record. gdb * NEWS: GDB recognizes program breakpoints (trap instructions in the target program and unknown to GDB) and can step over them. Includes remote protocol extension and requires target support. * gdbthread.h (struct thread_info): Add program_breakpoint_hit and program_breakpoint_size. * remote.c (remote_stopped_by_trap_instruction_p): New. (remote_trap_instruction_size): New. (struct stop_reply): New stopped_by_trap_instruction_p and trap_instruction_size. (remote_parse_stop_reply): Handle 'trap:size' remote protocol extension. (process_stop_reply): Set remote_stopped_by_trap_instruction_p and remote_trap_instruction_size from stop_reply. (remote_wait_as): Initialize remote_stopped_by_trap_instruction_p. (remote_stopped_by_trap_instruction): New. (init_remote_ops): Initialize new callback in remote_ops. * target.h (struct target_ops): Add to_stopped_by_trap_instruction. (STOPPED_BY_TRAP_INSTRUCTION): New. * target.c (update_current_target): Inherit and de_fault to_stopped_by_trap_instruction. (debug_to_stopped_by_trap_instruction): New. (setup_target_debug): Initialize to_stopped_by_trap_instruction. * infcmd.c: Include mi/mi-common.h. (step_1): Step-i over program breakpoint and report; consolidate tp. * infrun.c (resume): Skip program breakpoint. (enum inferior_stop_reason): Add PROGRAM_BREAKPOINT stop reason. (handle_inferior_event): Recognize program breakpoint hit and avoid treating it as a random signal. Add debug diagnostic. (process_event_stop_test): Handle program breakpoint hit. (print_stop_reason): Handle PROGRAM_BREAKPOINT stop reason. (normal_stop): Print stop reason and location for program breakpoint. * mi/mi_common.h (enum async_reply_reason): New async reply reason EXEC_ASYNC_PROGRAM_BREAKPOINT. * mi/mi_common.c (async_reason_string_lookup): New program-breakpoint reason string for EXEC_ASYNC_PROGRAM_BREAKPOINT. gdbserver * NEWS: gdbserver recognizes trap instructions whether or not they are used by GDB for breakpoints, and reports them to the host GDB. * linux-low.c (linux_stopped_by_trap_instruction): New. (struct target_ops): Add linux_stopped_by_trap_instruction. * linux-low.h (struct linux_target_ops): New trap_size_at function. Comment breakpoint_at to clarify distinction from trap_size_at. * linux-xtensa-low.c (XTENSA_BREAKPOINT): Comment only 'density'. (xtensa_trap_size-at): New. (struct linux_target_ops): Add xtensa_trap_size_at + padding. Change '0' entries to 'NULL' for pointer fields. * remote-utils.c (prepare_resume_reply): Append 'trap:r' extension to 'TAA' stop-reply for SIGTRAP when stopped by trap instruction. * target.h (struct target_ops): Add stopped_by_trap_instruction. Index: gdb/doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.580 diff -u -r1.580 gdb.texinfo --- gdb/doc/gdb.texinfo 15 Apr 2009 22:20:32 -0000 1.580 +++ gdb/doc/gdb.texinfo 17 Apr 2009 18:55:23 -0000 @@ -2961,6 +2961,14 @@ (for example, routines that are arguments in a @code{pthread_create} call). +Some programs may contain embedded break or trap instructions that are +unknown to GDB. These are called @dfn{program breakpoints} because they +belong to the program itself. If encountered, a target may stop execution +and report this to GDB along with how much to increment the PC to step +over it (a target is not required to do this). Because they are part of +the program code, program breakpoints cannot be deleted or disabled and +do not show up in @value{GDBN}'s list of breakpoints. + @cindex watchpoints @cindex data breakpoints @cindex memory tracing @@ -20171,6 +20179,8 @@ The inferior exited normally. @item signal-received A signal was received by the inferior. +@item program-breakpoint +A program breakpoint (trap instruction unknown to GDB) was reached. @end table The @var{id} field identifies the thread that directly caused the stop @@ -26723,6 +26731,12 @@ @value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new list of loaded libraries. @var{r} is ignored. +@item trap +The packet indicates that a target-specific break or trap instruction +was hit. @var{r} is the size of the instruction if the PC is pointing +to it, else 0 (for example if the hardware already incremented the PC). +@var{r} is ignored if the instruction was planted by @value{GDBN}. + The packet indicates that the target cannot continue replaying Index: gdb/NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.305 diff -u -r1.305 NEWS --- gdb/NEWS 31 Mar 2009 20:21:06 -0000 1.305 +++ gdb/NEWS 17 Apr 2009 18:55:25 -0000 @@ -3,6 +3,10 @@ *** Changes since GDB 6.8 +* GDB now supports handling (embedded) program break or trap instructions +that are unknown to GDB. These are called program breakpoints because they +belong to the program itself. + * GDB now has support for multi-byte and wide character sets on the target. Strings whose character type is wchar_t, char16_t, or char32_t are now correctly printed. GDB supports wide- and unicode- Index: gdb/gdbthread.h =================================================================== RCS file: /cvs/src/src/gdb/gdbthread.h,v retrieving revision 1.47 diff -u -r1.47 gdbthread.h --- gdb/gdbthread.h 31 Mar 2009 15:23:57 -0000 1.47 +++ gdb/gdbthread.h 17 Apr 2009 18:55:26 -0000 @@ -131,6 +131,13 @@ back to user code before stopping and reporting the event. */ int stepping_through_solib_after_catch; + /* program_breakpoint_hit is set when a break or trap instruction was hit at + an address not in the breakpoint table (it's part of the program). If so, + program_breakpoint_size is the size of the instruction if it needs to be + skipped, else 0 (for example, the target already incremented the PC). */ + int program_breakpoint_hit; + int program_breakpoint_size; + /* When stepping_through_solib_after_catch is TRUE, this is a list of the catchpoints that should be reported as triggering when we finally do stop stepping. */ Index: gdb/remote.c =================================================================== RCS file: /cvs/src/src/gdb/remote.c,v retrieving revision 1.354 diff -u -r1.354 remote.c --- gdb/remote.c 16 Apr 2009 19:31:03 -0000 1.354 +++ gdb/remote.c 17 Apr 2009 18:55:29 -0000 @@ -528,6 +528,17 @@ /* This is non-zero if target stopped for a watchpoint. */ static int remote_stopped_by_watchpoint_p; +/* This is non-zero if target stopped for a trap instruction, whether or + not it was a software breakpoint planted by GDB. */ +static int remote_stopped_by_trap_instruction_p = 0; + +/* When remote_stopped_by_trap_instruction_p is non-zero, this is the size + of the instruction (or 0 if it should not be skipped) as reported by the + target. If non-zero GDB must increment the PC by size before resuming. + Generally this is non-zero if and only if the target stopped with PC at + the breakpoint. */ +static int remote_trap_instruction_size = 0; + static struct target_ops remote_ops; static struct target_ops extended_remote_ops; @@ -4157,6 +4168,9 @@ int stopped_by_watchpoint_p; CORE_ADDR watch_data_address; + int stopped_by_trap_instruction_p; + int trap_instruction_size; + int solibs_changed; int replay_event; }; @@ -4321,6 +4335,7 @@ event->solibs_changed = 0; event->replay_event = 0; event->stopped_by_watchpoint_p = 0; + event->stopped_by_trap_instruction_p = 0; event->regcache = NULL; switch (buf[0]) @@ -4343,6 +4358,7 @@ char *p_temp; int fieldsize; LONGEST pnum = 0; + ULONGEST size; /* If the packet contains a register number, save it in pnum and set p1 to point to the character following it. @@ -4387,6 +4403,12 @@ event->solibs_changed = 1; p = p_temp; } + else if (strncmp (p, "trap", p1 - p) == 0) + { + event->stopped_by_trap_instruction_p = 1; + p = unpack_varlen_hex (++p1, &size); + event->trap_instruction_size = (int) size; + } else if (strncmp (p, "replaylog", p1 - p) == 0) { /* NO_HISTORY event. @@ -4637,6 +4659,10 @@ remote_stopped_by_watchpoint_p = stop_reply->stopped_by_watchpoint_p; remote_watch_data_address = stop_reply->watch_data_address; + remote_stopped_by_trap_instruction_p + = stop_reply->stopped_by_trap_instruction_p; + remote_trap_instruction_size = stop_reply->trap_instruction_size; + remote_notice_new_inferior (ptid, 0); } @@ -4756,6 +4782,7 @@ buf = rs->buf; remote_stopped_by_watchpoint_p = 0; + remote_stopped_by_trap_instruction_p = 0; /* We got something. */ rs->waiting_for_stop_reply = 0; @@ -7041,6 +7068,13 @@ return rc; } +static int +remote_stopped_by_trap_instruction (int *size) +{ + if (remote_stopped_by_trap_instruction_p && size) + *size = remote_trap_instruction_size; + return remote_stopped_by_trap_instruction_p; +} static int remote_insert_hw_breakpoint (struct bp_target_info *bp_tgt) @@ -8800,6 +8834,8 @@ remote_ops.to_remove_breakpoint = remote_remove_breakpoint; remote_ops.to_stopped_by_watchpoint = remote_stopped_by_watchpoint; remote_ops.to_stopped_data_address = remote_stopped_data_address; + remote_ops.to_stopped_by_trap_instruction = + remote_stopped_by_trap_instruction; remote_ops.to_can_use_hw_breakpoint = remote_check_watch_resources; remote_ops.to_insert_hw_breakpoint = remote_insert_hw_breakpoint; remote_ops.to_remove_hw_breakpoint = remote_remove_hw_breakpoint; Index: gdb/target.h =================================================================== RCS file: /cvs/src/src/gdb/target.h,v retrieving revision 1.149 diff -u -r1.149 target.h --- gdb/target.h 17 Mar 2009 19:28:09 -0000 1.149 +++ gdb/target.h 17 Apr 2009 18:55:31 -0000 @@ -372,6 +372,7 @@ int (*to_watchpoint_addr_within_range) (struct target_ops *, CORE_ADDR, CORE_ADDR, int); int (*to_region_ok_for_hw_watchpoint) (CORE_ADDR, int); + int (*to_stopped_by_trap_instruction) (int *); void (*to_terminal_init) (void); void (*to_terminal_inferior) (void); void (*to_terminal_ours_for_output) (void); @@ -1127,6 +1128,14 @@ extern const struct target_desc *target_read_description (struct target_ops *); +/* Returns non-zero if we were stopped by a trap or break instruction, + whether or not it is a software break planted by GDB. If size != 0, + sets *size to that of the instruction or 0 if it need not be skipped. */ + +#ifndef STOPPED_BY_TRAP_INSTRUCTION +#define STOPPED_BY_TRAP_INSTRUCTION(size) \ + (*current_target.to_stopped_by_trap_instruction) (size) +#endif #define target_get_ada_task_ptid(lwp, tid) \ (*current_target.to_get_ada_task_ptid) (lwp,tid) Index: gdb/target.c =================================================================== RCS file: /cvs/src/src/gdb/target.c,v retrieving revision 1.206 diff -u -r1.206 target.c --- gdb/target.c 14 Apr 2009 16:48:07 -0000 1.206 +++ gdb/target.c 17 Apr 2009 18:55:32 -0000 @@ -115,6 +115,8 @@ static int debug_to_remove_watchpoint (CORE_ADDR, int, int); +static int debug_to_stopped_by_trap_instruction (int *); + static int debug_to_stopped_by_watchpoint (void); static int debug_to_stopped_data_address (struct target_ops *, CORE_ADDR *); @@ -435,6 +437,7 @@ INHERIT (to_insert_watchpoint, t); INHERIT (to_remove_watchpoint, t); INHERIT (to_stopped_data_address, t); + INHERIT (to_stopped_by_trap_instruction, t); INHERIT (to_have_steppable_watchpoint, t); INHERIT (to_have_continuable_watchpoint, t); INHERIT (to_stopped_by_watchpoint, t); @@ -551,6 +554,9 @@ de_fault (to_stopped_data_address, (int (*) (struct target_ops *, CORE_ADDR *)) return_zero); + de_fault (to_stopped_by_trap_instruction, + (int (*) (int*)) + return_zero); de_fault (to_watchpoint_addr_within_range, default_watchpoint_addr_within_range); de_fault (to_region_ok_for_hw_watchpoint, @@ -2994,6 +3000,19 @@ return retval; } +static int +debug_to_stopped_by_trap_instruction (int *size) +{ + int retval; + + retval = debug_target.to_stopped_by_trap_instruction (size); + + fprintf_unfiltered (gdb_stdlog, + "target_stopped_by_trap_instruction (%d) = %d\n", + *size, retval); + return retval; +} + static void debug_to_terminal_init (void) { @@ -3230,6 +3249,7 @@ current_target.to_remove_watchpoint = debug_to_remove_watchpoint; current_target.to_stopped_by_watchpoint = debug_to_stopped_by_watchpoint; current_target.to_stopped_data_address = debug_to_stopped_data_address; + current_target.to_stopped_by_trap_instruction = debug_to_stopped_by_trap_instruction; current_target.to_watchpoint_addr_within_range = debug_to_watchpoint_addr_within_range; current_target.to_region_ok_for_hw_watchpoint = debug_to_region_ok_for_hw_watchpoint; current_target.to_terminal_init = debug_to_terminal_init; Index: gdb/infcmd.c =================================================================== RCS file: /cvs/src/src/gdb/infcmd.c,v retrieving revision 1.238 diff -u -r1.238 infcmd.c --- gdb/infcmd.c 25 Mar 2009 21:42:34 -0000 1.238 +++ gdb/infcmd.c 17 Apr 2009 18:55:34 -0000 @@ -52,6 +52,7 @@ #include "cli/cli-decode.h" #include "gdbthread.h" #include "valprint.h" +#include "mi/mi-common.h" /* Functions exported for general use, in inferior.h: */ @@ -786,6 +787,7 @@ { int count = 1; struct frame_info *frame; + struct thread_info *tp = inferior_thread (); struct cleanup *cleanups = make_cleanup (null_cleanup, NULL); int async_exec = 0; int thread = -1; @@ -821,13 +823,40 @@ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread); } + if (tp->program_breakpoint_hit && tp->program_breakpoint_size != 0 + && execution_direction != EXEC_REVERSE && read_pc () == stop_pc + && count > 0) + { + /* "stepi" off program breakpoint: the first step is to just increment + the PC past the break, then there are count-1 steps to go. + Note proceed() won't be called the first time, and on subsequent + steps the PC will already be off the break, so the entire handling + of "stepi" off a program breakpoint is done here. If stopping after + the break, display location information as for normal_stop. */ + count--; + write_pc (read_pc () + tp->program_breakpoint_size); + if (count == 0) + { + reinit_frame_cache (); + if (ui_out_is_mi_like_p (uiout)) + { + ui_out_field_string + (uiout, "reason", + async_reason_lookup (EXEC_ASYNC_END_STEPPING_RANGE)); + ui_out_field_int (uiout, "thread-id", + pid_to_thread_id (inferior_ptid)); + print_stack_frame (get_selected_frame (NULL), -1, LOC_AND_ADDRESS); + } + else + print_stack_frame (get_selected_frame (NULL), -1, SRC_LINE); + } + } /* In synchronous case, all is well; each step_once call will step once. */ if (!target_can_async_p ()) { for (; count > 0; count--) { - struct thread_info *tp; step_once (skip_subroutines, single_inst, count, thread); if (target_has_execution Index: gdb/infrun.c =================================================================== RCS file: /cvs/src/src/gdb/infrun.c,v retrieving revision 1.368 diff -u -r1.368 infrun.c --- gdb/infrun.c 14 Apr 2009 00:59:47 -0000 1.368 +++ gdb/infrun.c 17 Apr 2009 18:55:36 -0000 @@ -1029,6 +1029,11 @@ a command like `return' or `jump' to continue execution.")); } + /* Skip a program breakpoint (unless PC changed without running inferior). */ + if (tp->program_breakpoint_hit && tp->program_breakpoint_size != 0 + && execution_direction != EXEC_REVERSE && read_pc () == stop_pc) + write_pc (read_pc () + tp->program_breakpoint_size); + /* If enabled, step over breakpoints by executing a copy of the instruction at a different address. @@ -1564,7 +1569,9 @@ /* Inferior received signal, and user asked to be notified. */ SIGNAL_RECEIVED, /* Reverse execution -- target ran out of history info. */ - NO_HISTORY + NO_HISTORY, + /* Inferior stopped because of program breakpoint. */ + PROGRAM_BREAKPOINT }; /* The PTID we'll do a target_wait on.*/ @@ -2937,6 +2944,13 @@ || stop_soon == STOP_QUIETLY || stop_soon == STOP_QUIETLY_NO_SIGSTOP || stop_soon == STOP_QUIETLY_REMOTE) { + /* If we hit a trap not inserted by GDB, it must be in the program. */ + ecs->event_thread->program_breakpoint_hit + = (STOPPED_BY_TRAP_INSTRUCTION (&ecs->event_thread->program_breakpoint_size) + && !software_breakpoint_inserted_here_p (stop_pc)); + if (debug_infrun && ecs->event_thread->program_breakpoint_hit) + fprintf_unfiltered (gdb_stdlog, "infrun: program breakpoint hit\n"); + if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP && stop_after_trap) { if (debug_infrun) @@ -3016,6 +3030,7 @@ if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP) ecs->random_signal = !(bpstat_explains_signal (ecs->event_thread->stop_bpstat) + || ecs->event_thread->program_breakpoint_hit || ecs->event_thread->trap_expected || (ecs->event_thread->step_range_end && ecs->event_thread->step_resume_breakpoint == NULL)); @@ -3136,6 +3151,21 @@ return; } + /* Handle case of hitting a program breakpoint (break instruction + in target program, not planted by or known to GDB). */ + + if (ecs->event_thread->program_breakpoint_hit) + { + stop_print_frame = 1; + + /* We are about to nuke the step_resume_breakpoint and + through_sigtramp_breakpoint via the cleanup chain, so + no need to worry about it here. */ + + stop_stepping (ecs); + return; + } + /* Handle cases caused by hitting a breakpoint. */ { CORE_ADDR jmp_buf_pc; @@ -4232,6 +4262,17 @@ /* Reverse execution: target ran out of history info. */ ui_out_text (uiout, "\nNo more reverse-execution history.\n"); break; + case PROGRAM_BREAKPOINT: + /* The inferior was stopped by a break/trap inherent in the program. */ + { + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string + (uiout, "reason", + async_reason_lookup (EXEC_ASYNC_PROGRAM_BREAKPOINT)); + ui_out_text (uiout, "\nProgram breakpoint, "); + /* to be followed by source location, as for planted breakpoint */ + } + break; default: internal_error (__FILE__, __LINE__, _("print_stop_reason: unrecognized enum value")); @@ -4370,45 +4411,54 @@ int do_frame_printing = 1; struct thread_info *tp = inferior_thread (); - bpstat_ret = bpstat_print (tp->stop_bpstat); - switch (bpstat_ret) + if (tp->program_breakpoint_hit) + { + /* Print program break stop reason and description. */ + print_stop_reason (PROGRAM_BREAKPOINT, 0); + source_flag = SRC_AND_LOC; /* print location and source line */ + } + else { - case PRINT_UNKNOWN: - /* If we had hit a shared library event breakpoint, - bpstat_print would print out this message. If we hit - an OS-level shared library event, do the same - thing. */ - if (last.kind == TARGET_WAITKIND_LOADED) + bpstat_ret = bpstat_print (tp->stop_bpstat); + switch (bpstat_ret) { - printf_filtered (_("Stopped due to shared library event\n")); + case PRINT_UNKNOWN: + /* If we had hit a shared library event breakpoint, + bpstat_print would print out this message. If we hit + an OS-level shared library event, do the same + thing. */ + if (last.kind == TARGET_WAITKIND_LOADED) + { + printf_filtered (_("Stopped due to shared library event\n")); + source_flag = SRC_LINE; /* something bogus */ + do_frame_printing = 0; + break; + } + + /* FIXME: cagney/2002-12-01: Given that a frame ID does + (or should) carry around the function and does (or + should) use that when doing a frame comparison. */ + if (tp->stop_step + && frame_id_eq (tp->step_frame_id, + get_frame_id (get_current_frame ())) + && step_start_function == find_pc_function (stop_pc)) + source_flag = SRC_LINE; /* finished step, print source line */ + else + source_flag = SRC_AND_LOC; /* print location and source line */ + break; + case PRINT_SRC_AND_LOC: + source_flag = SRC_AND_LOC; /* print location and source line */ + break; + case PRINT_SRC_ONLY: + source_flag = SRC_LINE; + break; + case PRINT_NOTHING: source_flag = SRC_LINE; /* something bogus */ do_frame_printing = 0; break; + default: + internal_error (__FILE__, __LINE__, _("Unknown value.")); } - - /* FIXME: cagney/2002-12-01: Given that a frame ID does - (or should) carry around the function and does (or - should) use that when doing a frame comparison. */ - if (tp->stop_step - && frame_id_eq (tp->step_frame_id, - get_frame_id (get_current_frame ())) - && step_start_function == find_pc_function (stop_pc)) - source_flag = SRC_LINE; /* finished step, just print source line */ - else - source_flag = SRC_AND_LOC; /* print location and source line */ - break; - case PRINT_SRC_AND_LOC: - source_flag = SRC_AND_LOC; /* print location and source line */ - break; - case PRINT_SRC_ONLY: - source_flag = SRC_LINE; - break; - case PRINT_NOTHING: - source_flag = SRC_LINE; /* something bogus */ - do_frame_printing = 0; - break; - default: - internal_error (__FILE__, __LINE__, _("Unknown value.")); } /* The behavior of this routine with respect to the source Index: gdb/mi/mi-common.h =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-common.h,v retrieving revision 1.7 diff -u -r1.7 mi-common.h --- gdb/mi/mi-common.h 3 Jan 2009 05:57:57 -0000 1.7 +++ gdb/mi/mi-common.h 17 Apr 2009 18:55:37 -0000 @@ -35,6 +35,7 @@ EXEC_ASYNC_EXITED, EXEC_ASYNC_EXITED_NORMALLY, EXEC_ASYNC_SIGNAL_RECEIVED, + EXEC_ASYNC_PROGRAM_BREAKPOINT, /* This is here only to represent the number of enums. */ EXEC_ASYNC_LAST }; Index: gdb/mi/mi-common.c =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-common.c,v retrieving revision 1.7 diff -u -r1.7 mi-common.c --- gdb/mi/mi-common.c 21 Feb 2009 16:14:50 -0000 1.7 +++ gdb/mi/mi-common.c 17 Apr 2009 18:55:38 -0000 @@ -33,6 +33,7 @@ "exited", "exited-normally", "signal-received", + "program-breakpoint", NULL }; Index: gdb/gdbserver/linux-low.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v retrieving revision 1.97 diff -u -r1.97 linux-low.c --- gdb/gdbserver/linux-low.c 3 Apr 2009 11:40:02 -0000 1.97 +++ gdb/gdbserver/linux-low.c 17 Apr 2009 18:55:39 -0000 @@ -2886,6 +2886,29 @@ return 0; } +static int +linux_stopped_by_trap_instruction (int *size) +{ + CORE_ADDR stop_pc = get_stop_pc(); + int trap_size = 0; + + if (the_low_target.trap_size_at != NULL) + trap_size = (*the_low_target.trap_size_at) (stop_pc); + else if (the_low_target.breakpoint_at != NULL + && (*the_low_target.breakpoint_at) (stop_pc)) + trap_size = the_low_target.breakpoint_len; + + if (trap_size != 0) + { + *size = (the_low_target.get_pc != NULL + && (*the_low_target.get_pc) () == stop_pc) + ? trap_size : 0; + return 1; + } + else + return 0; +} + static struct target_ops linux_target_ops = { linux_create_inferior, linux_attach, @@ -2923,6 +2946,7 @@ linux_supports_non_stop, linux_async, linux_start_non_stop, + linux_stopped_by_trap_instruction, }; static void Index: gdb/gdbserver/linux-low.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-low.h,v retrieving revision 1.28 diff -u -r1.28 linux-low.h --- gdb/gdbserver/linux-low.h 1 Apr 2009 22:50:24 -0000 1.28 +++ gdb/gdbserver/linux-low.h 17 Apr 2009 18:55:40 -0000 @@ -77,6 +77,9 @@ int decr_pc_after_break; + + /* Returns non-zero if there is a breakpoint at the PC (specifically, if + the target memory at PC matches the breakpoint field of this struct). */ int (*breakpoint_at) (CORE_ADDR pc); /* Watchpoint related functions. See target.h for comments. */ @@ -89,6 +92,11 @@ for registers smaller than an xfer unit). */ void (*collect_ptrace_register) (int regno, char *buf); void (*supply_ptrace_register) (int regno, const char *buf); + + /* Returns non-zero if there is any kind of trap instruction at the PC, + including but not limited to the instruction used for GDB breakpoints. + If so, the result is the size of the trap (PC increment to skip). */ + int (*trap_size_at) (CORE_ADDR pc); }; extern struct linux_target_ops the_low_target; Index: gdb/gdbserver/linux-xtensa-low.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-xtensa-low.c,v retrieving revision 1.4 diff -u -r1.4 linux-xtensa-low.c --- gdb/gdbserver/linux-xtensa-low.c 22 Mar 2009 23:57:10 -0000 1.4 +++ gdb/gdbserver/linux-xtensa-low.c 17 Apr 2009 18:55:41 -0000 @@ -140,6 +140,7 @@ { 0, 0, -1, -1, NULL, NULL } }; +/* Currently assumes the Xtensa 'density' option is configured: 'break.n 0'. */ #if XCHAL_HAVE_BE #define XTENSA_BREAKPOINT {0xd2,0x0f} #else @@ -175,12 +176,37 @@ return memcmp((char *)&insn, xtensa_breakpoint, xtensa_breakpoint_len) == 0; } +int +xtensa_trap_size_at (CORE_ADDR pc) +{ + unsigned char insn[3]; + + (*the_target->read_memory) (pc, insn, 3); + +#if XCHAL_HAVE_BE + if (insn[1] == 0xd2 && (insn[2] & 0x0f) == 0x0f) +#else + if (insn[0] == 0x2d && (insn[1] & 0xf0) == 0xf0) +#endif + /* break.n s (density) */ + return 2; +#if XCHAL_HAVE_BE + else if ((insn[2] & 0xf0) == 0x00 && (insn[1] & 0x0f) == 0x04 && insn[0] == 0x00) +#else + else if ((insn[0] & 0x0f) == 0x00 && (insn[1] & 0xf0) == 0x40 && insn[2] == 0x00) +#endif + /* break s, t */ + return 3; + else + return 0; +} + struct linux_target_ops the_low_target = { init_registers_xtensa, 0, - 0, - 0, - 0, + NULL, + NULL, + NULL, xtensa_get_pc, xtensa_set_pc, xtensa_breakpoint, @@ -188,4 +214,11 @@ NULL, 0, xtensa_breakpoint_at, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + xtensa_trap_size_at, }; Index: gdb/gdbserver/remote-utils.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/remote-utils.c,v retrieving revision 1.66 diff -u -r1.66 remote-utils.c --- gdb/gdbserver/remote-utils.c 3 Apr 2009 14:38:38 -0000 1.66 +++ gdb/gdbserver/remote-utils.c 17 Apr 2009 18:55:42 -0000 @@ -1083,6 +1083,7 @@ { struct thread_info *saved_inferior; const char **regp; + int size; sprintf (buf, "T%02x", status->value.sig); buf += strlen (buf); @@ -1113,6 +1114,14 @@ *buf++ = ';'; } + else if (the_target->stopped_by_trap_instruction != NULL + && (*the_target->stopped_by_trap_instruction) (&size)) + { + sprintf (buf, "trap:%1x", size); + buf += 6; + *buf++ = ';'; + } + while (*regp) { buf = outreg (find_regno (*regp), buf); Index: gdb/gdbserver/target.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/target.h,v retrieving revision 1.36 diff -u -r1.36 target.h --- gdb/gdbserver/target.h 1 Apr 2009 22:50:24 -0000 1.36 +++ gdb/gdbserver/target.h 17 Apr 2009 18:55:43 -0000 @@ -275,6 +275,14 @@ /* Switch to non-stop (1) or all-stop (0) mode. Return 0 on success, -1 otherwise. */ int (*start_non_stop) (int); + + /* Returns non-zero if target was stopped by any trap instruction, else 0. + It may or may not have been a breakpoint planted by gdbserver or GDB. + If stopped by a trap and size != NULL, then *size is set to the size of + the trap instruction if the PC still points to it (for skipping), else 0. + This information is passed via the remote protocol to GDB. */ + + int (*stopped_by_trap_instruction) (int *size); }; extern struct target_ops *the_target;