--- gdb/breakpoint.c | 54 +++++++++++++++++++++++++++++++++++++++++++----------- gdb/breakpoint.h | 5 ++++- gdb/gdb_ptrace.h | 6 ++++++ gdb/inf-ptrace.c | 5 ++++- gdb/inferior.h | 2 ++ gdb/infrun.c | 19 +++++++++++++++++-- gdb/linux-nat.c | 29 +++++++++++++++++++++-------- 7 files changed, 97 insertions(+), 23 deletions(-) diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 4340c5d..84727eb 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -1198,7 +1198,10 @@ update_watchpoint (struct breakpoint *b, int reparse) addr = value_address (v); len = TYPE_LENGTH (value_type (v)); type = hw_write; - if (b->type == bp_read_watchpoint) + if (v == val_chain && value_lazy (v)) + type = hw_mmap; + else if (b->type == bp_read_watchpoint) + type = hw_read; else if (b->type == bp_access_watchpoint) type = hw_access; @@ -1472,9 +1475,17 @@ Note: automatically using hardware breakpoints for read-only addresses.\n")); watchpoints. It's not clear that it's necessary... */ && bpt->owner->disposition != disp_del_at_next_stop) { - val = target_insert_watchpoint (bpt->address, - bpt->length, - bpt->watchpoint_type); + if (bpt->watchpoint_type == hw_mmap) + { + struct inferior *inf = current_inferior (); + + inf->trap_syscalls = 1; + val = 0; + } + else + val = target_insert_watchpoint (bpt->address, + bpt->length, + bpt->watchpoint_type); bpt->inserted = (val != -1); } @@ -2100,9 +2111,11 @@ remove_breakpoint_1 (struct bp_location *b, insertion_state_t is) struct value *n; b->inserted = (is == mark_inserted); - val = target_remove_watchpoint (b->address, b->length, - b->watchpoint_type); - + if (b->watchpoint_type == hw_mmap) + val = 0; + else + val = target_remove_watchpoint (b->address, b->length, + b->watchpoint_type); /* Failure to remove any of the hardware watchpoints comes here. */ if ((is == mark_uninserted) && (b->inserted)) warning (_("Could not remove hardware watchpoint %d."), @@ -3162,6 +3175,25 @@ watchpoints_triggered (struct target_waitstatus *ws) return 1; } +void +mmap_watchpoints_triggered (void) +{ + struct breakpoint *b; + + ALL_BREAKPOINTS (b) + if (b->type == bp_hardware_watchpoint) + { + struct bp_location *loc; + + for (loc = b->loc; loc; loc = loc->next) + if (loc->watchpoint_type == hw_mmap) + { + b->watchpoint_triggered = watch_triggered_yes; + break; + } + } +} + /* Possible return values for watchpoint_check (this can't be an enum because of check_errors). */ /* The watchpoint has been deleted. */ @@ -7084,7 +7116,7 @@ watch_command_1 (char *arg, int accessflag, int from_tty) struct breakpoint *b, *scope_breakpoint = NULL; struct expression *exp; struct block *exp_valid_block; - struct value *val, *mark; + struct value *val, *mark, *result; struct frame_info *frame; char *exp_start = NULL; char *exp_end = NULL; @@ -7171,7 +7203,7 @@ watch_command_1 (char *arg, int accessflag, int from_tty) exp_valid_block = innermost_block; mark = value_mark (); - fetch_watchpoint_value (exp, &val, NULL, NULL); + fetch_watchpoint_value (exp, &val, &result, NULL); if (val != NULL) release_value (val); @@ -7206,7 +7238,7 @@ watch_command_1 (char *arg, int accessflag, int from_tty) mem_cnt = can_use_hardware_watchpoint (val); if (mem_cnt == 0 && bp_type != bp_hardware_watchpoint) error (_("Expression cannot be implemented with read/access watchpoint.")); - if (mem_cnt != 0) + if (mem_cnt != 0 || !val) { i = hw_watchpoint_used_count (bp_type, &other_type_used); target_resources_ok = @@ -7221,7 +7253,7 @@ watch_command_1 (char *arg, int accessflag, int from_tty) /* Change the type of breakpoint to an ordinary watchpoint if a hardware watchpoint could not be set. */ - if (!mem_cnt || target_resources_ok <= 0) + if ((!mem_cnt && val) || target_resources_ok <= 0) bp_type = bp_watchpoint; frame = block_innermost_frame (exp_valid_block); diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 5ebd36c..1836745 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -168,7 +168,8 @@ enum target_hw_bp_type hw_write = 0, /* Common HW watchpoint */ hw_read = 1, /* Read HW watchpoint */ hw_access = 2, /* Access HW watchpoint */ - hw_execute = 3 /* Execute HW breakpoint */ + hw_execute = 3, /* Execute HW breakpoint */ + hw_mmap = 4 /* Memory area gets mmap(2)ed. */ }; @@ -957,6 +958,8 @@ extern int deprecated_remove_raw_breakpoint (struct gdbarch *, void *); target. */ int watchpoints_triggered (struct target_waitstatus *); +extern void mmap_watchpoints_triggered (void); + /* Update BUF, which is LEN bytes read from the target address MEMADDR, by replacing any memory breakpoints with their shadowed contents. */ void breakpoint_restore_shadows (gdb_byte *buf, ULONGEST memaddr, diff --git a/gdb/gdb_ptrace.h b/gdb/gdb_ptrace.h index 8e32e88..853a702 100644 --- a/gdb/gdb_ptrace.h +++ b/gdb/gdb_ptrace.h @@ -92,6 +92,12 @@ # endif #endif + +#ifndef PT_SYSCALL +# define PT_SYSCALL 24 /* Continue and stop at the next (return from) + syscall. */ +#endif + /* Not all systems support attaching and detaching. */ #ifndef PT_ATTACH diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c index a138701..d0cedd7 100644 --- a/gdb/inf-ptrace.c +++ b/gdb/inf-ptrace.c @@ -346,7 +346,10 @@ inf_ptrace_resume (struct target_ops *ops, else request = PT_CONTINUE; - if (step) + if (step == 2) + request = PT_SYSCALL; + else if (step) + { /* If this system does not support PT_STEP, a higher level function will have called single_step() to transmute the step diff --git a/gdb/inferior.h b/gdb/inferior.h index 14b99e2..76ba9b5 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -478,6 +478,8 @@ struct inferior /* Per inferior data-pointers required by other GDB modules. */ void **data; unsigned num_data; + + unsigned trap_syscalls : 1; }; /* Keep a registry of per-inferior data-pointers required by other GDB diff --git a/gdb/infrun.c b/gdb/infrun.c index cb5278c..bfbe5cf 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1436,7 +1436,7 @@ a command like `return' or `jump' to continue execution.")); } /* Do we need to do it the hard way, w/temp breakpoints? */ - else if (step) + else if (step == 1) step = maybe_software_singlestep (gdbarch, pc); if (should_resume) @@ -1608,6 +1608,7 @@ clear_proceed_status (void) inferior = current_inferior (); inferior->stop_soon = NO_STOP_QUIETLY; + inferior->trap_syscalls = 0; } stop_after_trap = 0; @@ -1708,6 +1709,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step) CORE_ADDR pc; struct address_space *aspace; int oneproc = 0; + struct inferior *inf; /* If we're stopped at a fork/vfork, follow the branch set by the "set follow-fork-mode" command; otherwise, we'll just proceed @@ -1871,8 +1873,12 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step) /* Reset to normal state. */ init_infwait_state (); + inf = current_inferior (); + /* Resume inferior. */ - resume (oneproc || step || bpstat_should_step (), tp->stop_signal); + resume (oneproc || step || bpstat_should_step () ? 1 : + (inf->trap_syscalls ? 2 : 0), + tp->stop_signal); /* Wait for it to stop (if not standalone) and in any case decode why it stopped, and act accordingly. */ @@ -3405,6 +3411,15 @@ targets should add new threads to the thread list themselves in non-stop mode.") else stopped_by_watchpoint = watchpoints_triggered (&ecs->ws); + if (ecs->ws.kind == TARGET_WAITKIND_STOPPED + && ecs->ws.value.sig == TARGET_SIGNAL_TRAP) + { + struct inferior *inferior = current_inferior (); + + if (inferior->trap_syscalls) + mmap_watchpoints_triggered (); + } + /* If necessary, step over this watchpoint. We'll be back to display it in a moment. */ if (stopped_by_watchpoint diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index c0afecd..48e4339 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -1908,6 +1908,22 @@ resume_set_callback (struct lwp_info *lp, void *data) return 0; } +static const char * +step_string (int step) +{ + switch (step) + { + case 0: + return "PTRACE_CONT"; + case 1: + return "PTRACE_SINGLESTEP"; + case 2: + return "PTRACE_SYSCALL"; + default: + gdb_assert (0); + } +} + static void linux_nat_resume (struct target_ops *ops, ptid_t ptid, int step, enum target_signal signo) @@ -2025,7 +2041,7 @@ linux_nat_resume (struct target_ops *ops, if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, "LLR: %s %s, %s (resume event thread)\n", - step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT", + step_string(step), target_pid_to_str (ptid), signo ? strsignal (signo) : "0"); @@ -3185,8 +3201,7 @@ linux_nat_filter_event (int lwpid, int status, int options) if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, "LLW: %s %s, 0, 0 (discard SIGSTOP)\n", - lp->step ? - "PTRACE_SINGLESTEP" : "PTRACE_CONT", + step_string(lp->step), target_pid_to_str (lp->ptid)); lp->stopped = 0; @@ -3215,8 +3230,7 @@ linux_nat_filter_event (int lwpid, int status, int options) if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, "LLW: %s %s, 0, 0 (discard SIGINT)\n", - lp->step ? - "PTRACE_SINGLESTEP" : "PTRACE_CONT", + step_string(lp->step), target_pid_to_str (lp->ptid)); lp->stopped = 0; @@ -3350,7 +3364,7 @@ retry: if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, "LLW: %s %s, 0, 0 (expect SIGSTOP)\n", - lp->step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT", + step_string(lp->step), target_pid_to_str (lp->ptid)); lp->stopped = 0; gdb_assert (lp->resumed); @@ -3562,8 +3576,7 @@ retry: if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, "LLW: %s %s, %s (preempt 'handle')\n", - lp->step ? - "PTRACE_SINGLESTEP" : "PTRACE_CONT", + step_string(lp->step), target_pid_to_str (lp->ptid), signo ? strsignal (signo) : "0"); lp->stopped = 0; -- 1.6.5.2