From 999dd04b89b61e097f58c43dbc68e0922917302e Mon Sep 17 00:00:00 2001 From: Jeff Law Date: Wed, 13 Apr 1994 21:57:00 +0000 Subject: [PATCH] * breakpoint.h (enum bptype): Add bp_hardware_watchpoint and bp_watchpoint_scope breakpoints. (struct breakpoint): Add val_chain and related_breakpoint fields for use by watchpoints. * breakpoint.c (within_scope): Delete. No longer used. (TARGET_CAN_USE_HARDWARE_WATCHPOINT): Provide default definition. (target_{remove,insert}_watchpoint): Likewise. (can_use_hardware_watchpoint): New function. (remove_breakpoint): New function to remove a single breakpoint or hardware watchpoint. (insert_breakpoints): Handle insertion of hardware watchpoints. Store a copy of the value chain derived from the watchpoint expression. (remove_breakpoints): Simplify by using remove_breakpoint. (delete_breakpoint): Likewise. (watchpoint_check): Delete the watchpoint and watchpoint scope breakpoints when the watchpoint goes out of scope. Save & restore the current frame after checking watchpoints. (breakpoint_init_inferior): Likewise (restarting the program makes all local watchpoints go out of scope). (bpstat_stop_status): Handle hardware watchpoints much like normal watchpoints. Delete the watchpoint and watchpoint scope breakpoint when the watchpoint goes out of scope. Remove and reinsert all breakpoints before returning if we stopped when a hardware watchpoint fired. (watch_command): Use a hardware watchpoint when possible. If watching a local expression, build a scope breakpoint too. (map_breakpoint_numbers): Also call given function for any related breakpoints. (disable_breakpoint): Never disable a scope breakpoint. (enable_breakpoint): Handle hardware breakpoints much like normal breakpoints, but recompute the watchpoint_scope breakpoint's frame and address (if we have an associated scope breakpoint). (read_memory_nobpt): Handle hardware watchpoints like normal watchpoints. When necessary handle watchpoint_scope breakpoints. (print_it_normal, bpstat_what, breakpoint_1, mention): Likewise. (clear_command, breakpoint_re_set_one, enable_command): Likewise. (disable_command): Likewise. * blockframe.c (find_frame_addr_in_frame_chain): New function. Extern prototype added to frame.h * infrun.c (wait_for_inferior): Set current_frame and select a frame before checking if we stopped due to a hardare watchpoint firing. Handle stepping over hardware watchpoints. (normal_stop): Remove unnecessary call to select_frame. * value.h (value_release_to_mark): Declare. * values.c (value_release_to_mark): New function. * procfs.c (procfs_wait): Add cases for hardware watchpoints. (procfs_set_watchpoint, procfs_stopped_by_watchpoint): New functions. * hppab-nat.c (hppa_set_watchpoint): New function. * config/pa/nm-hppab.h (STOPPED_BY_WATCHPOINT): Define. (HAVE_STEPPABLE_WATCHPOINT): Define. (TARGET_CAN_USE_HARDWARE_WATCHPOINT): Define. (target_{insert,delete}_watchpoint): Define. --- gdb/ChangeLog | 63 +++++++ gdb/blockframe.c | 23 +++ gdb/breakpoint.c | 480 ++++++++++++++++++++++++++++++++--------------- gdb/hppab-nat.c | 59 ++++++ gdb/infrun.c | 35 +++- gdb/procfs.c | 54 ++++++ gdb/value.h | 5 +- gdb/values.c | 18 ++ 8 files changed, 584 insertions(+), 153 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 7ca2b536879..7248d73da80 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,66 @@ +Wed Apr 13 14:52:46 1994 Jeffrey A. Law (law@snake.cs.utah.edu) + + * breakpoint.h (enum bptype): Add bp_hardware_watchpoint and + bp_watchpoint_scope breakpoints. + (struct breakpoint): Add val_chain and related_breakpoint fields + for use by watchpoints. + + * breakpoint.c (within_scope): Delete. No longer used. + (TARGET_CAN_USE_HARDWARE_WATCHPOINT): Provide default definition. + (target_{remove,insert}_watchpoint): Likewise. + (can_use_hardware_watchpoint): New function. + (remove_breakpoint): New function to remove a single breakpoint + or hardware watchpoint. + (insert_breakpoints): Handle insertion of hardware watchpoints. + Store a copy of the value chain derived from the watchpoint + expression. + (remove_breakpoints): Simplify by using remove_breakpoint. + (delete_breakpoint): Likewise. + (watchpoint_check): Delete the watchpoint and watchpoint scope + breakpoints when the watchpoint goes out of scope. Save & restore + the current frame after checking watchpoints. + (breakpoint_init_inferior): Likewise (restarting the program + makes all local watchpoints go out of scope). + (bpstat_stop_status): Handle hardware watchpoints much like normal + watchpoints. Delete the watchpoint and watchpoint scope breakpoint + when the watchpoint goes out of scope. Remove and reinsert all + breakpoints before returning if we stopped when a hardware watchpoint + fired. + (watch_command): Use a hardware watchpoint when possible. If + watching a local expression, build a scope breakpoint too. + (map_breakpoint_numbers): Also call given function for any + related breakpoints. + (disable_breakpoint): Never disable a scope breakpoint. + (enable_breakpoint): Handle hardware breakpoints much like normal + breakpoints, but recompute the watchpoint_scope breakpoint's + frame and address (if we have an associated scope breakpoint). + (read_memory_nobpt): Handle hardware watchpoints like normal + watchpoints. When necessary handle watchpoint_scope breakpoints. + (print_it_normal, bpstat_what, breakpoint_1, mention): Likewise. + (clear_command, breakpoint_re_set_one, enable_command): Likewise. + (disable_command): Likewise. + + * blockframe.c (find_frame_addr_in_frame_chain): New function. + Extern prototype added to frame.h + + * infrun.c (wait_for_inferior): Set current_frame and select + a frame before checking if we stopped due to a hardare watchpoint + firing. Handle stepping over hardware watchpoints. + (normal_stop): Remove unnecessary call to select_frame. + + * value.h (value_release_to_mark): Declare. + * values.c (value_release_to_mark): New function. + + * procfs.c (procfs_wait): Add cases for hardware watchpoints. + (procfs_set_watchpoint, procfs_stopped_by_watchpoint): New functions. + + * hppab-nat.c (hppa_set_watchpoint): New function. + + * config/pa/nm-hppab.h (STOPPED_BY_WATCHPOINT): Define. + (HAVE_STEPPABLE_WATCHPOINT): Define. + (TARGET_CAN_USE_HARDWARE_WATCHPOINT): Define. + (target_{insert,delete}_watchpoint): Define. + Mon Apr 11 19:21:27 1994 Stu Grossman (grossman at cygnus.com) * xcoffread.c (read_xcoff_symtab): Ignore symbols of class C_EXT, diff --git a/gdb/blockframe.c b/gdb/blockframe.c index 0c79461f2d4..bbd428784e2 100644 --- a/gdb/blockframe.c +++ b/gdb/blockframe.c @@ -805,6 +805,29 @@ block_innermost_frame (block) } } +/* Return the full FRAME which corresponds to the given FRAME_ADDR + or NULL if no FRAME on the chain corresponds to FRAME_ADDR. */ + +FRAME +find_frame_addr_in_frame_chain (frame_addr) + FRAME_ADDR frame_addr; +{ + FRAME frame = NULL; + + if (frame_addr == NULL) + return NULL; + + while (1) + { + frame = get_prev_frame (frame); + if (frame == NULL) + return NULL; + + if (FRAME_FP (frame) == frame_addr) + return frame; + } +} + #ifdef SIGCONTEXT_PC_OFFSET /* Get saved user PC for sigtramp from sigcontext for BSD style sigtramp. */ diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index f8adee1e919..457ca912cd2 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -89,6 +89,9 @@ get_catch_sals PARAMS ((int)); static void watch_command PARAMS ((char *, int)); +static int +can_use_hardware_watchpoint PARAMS ((struct breakpoint *)); + static void tbreak_command PARAMS ((char *, int)); @@ -134,6 +137,8 @@ get_number PARAMS ((char **)); static void set_breakpoint_count PARAMS ((int)); +static int +remove_breakpoint PARAMS ((struct breakpoint *)); extern int addressprint; /* Print machine addresses? */ extern int demangle; /* Print de-mangled symbol names? */ @@ -152,6 +157,13 @@ static int executing_breakpoint_commands; b? (tmp=b->next, 1): 0; \ b = tmp) +/* By default no support for hardware watchpoints is assumed. */ +#ifndef TARGET_CAN_USE_HARDWARE_WATCHPOINT +#define TARGET_CAN_USE_HARDWARE_WATCHPOINT(B) 0 +#define target_remove_watchpoint(ADDR,LEN) -1 +#define target_insert_watchpoint(ADDR,LEN) -1 +#endif + /* Chain of all breakpoints defined. */ static struct breakpoint *breakpoint_chain; @@ -357,7 +369,9 @@ read_memory_nobpt (memaddr, myaddr, len) ALL_BREAKPOINTS (b) { - if (b->type == bp_watchpoint || !b->inserted) + if (b->type == bp_watchpoint + || b->type == bp_hardware_watchpoint + || !b->inserted) continue; else if (b->address + memory_breakpoint_size <= memaddr) /* The breakpoint is entirely before the chunk of memory @@ -435,6 +449,7 @@ insert_breakpoints () ALL_BREAKPOINTS (b) if (b->type != bp_watchpoint + && b->type != bp_hardware_watchpoint && b->enable != disabled && ! b->inserted && ! b->duplicate) @@ -473,11 +488,86 @@ insert_breakpoints () else b->inserted = 1; } + else if (b->type == bp_hardware_watchpoint + && b->enable == enabled + && ! b->inserted + && ! b->duplicate) + { + FRAME saved_frame; + int saved_level, within_current_scope; + value_ptr mark = value_mark (); + value_ptr v; + + /* Save the current frame and level so we can restore it after + evaluating the watchpoint expression on its own frame. */ + saved_frame = selected_frame; + saved_level = selected_frame_level; + + /* Determine if the watchpoint is within scope. */ + if (b->exp_valid_block == NULL) + within_current_scope = 1; + else + { + FRAME fr = find_frame_addr_in_frame_chain (b->watchpoint_frame); + within_current_scope = (fr != NULL); + if (within_current_scope) + select_frame (fr, -1); + } + + if (within_current_scope) + { + /* Evaluate the expression and cut the chain of values + produced off from the value chain. */ + v = evaluate_expression (b->exp); + value_release_to_mark (mark); + + b->val_chain = v; + b->inserted = 1; + + /* Look at each value on the value chain. */ + for ( ; v; v=v->next) + { + /* If it's a memory location, then we must watch it. */ + if (v->lval == lval_memory) + { + int addr, len; + + addr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); + len = TYPE_LENGTH (VALUE_TYPE (v)); + val = target_insert_watchpoint (addr, len); + if (val == -1) + { + b->inserted = 0; + break; + } + val = 0; + } + } + /* Failure to insert a watchpoint on any memory value in the + value chain brings us here. */ + if (!b->inserted) + warning ("Hardware watchpoint %d: Could not insert watchpoint\n", + b->number); + } + else + { + printf_filtered ("\ +Hardware watchpoint %d deleted because the program has left the block in\n\ +which its expression is valid.\n", b->number); + if (b->related_breakpoint) + delete_breakpoint (b->related_breakpoint); + delete_breakpoint (b); + } + + /* Restore the frame and level. */ + select_frame (saved_frame, saved_level); + } if (disabled_breaks) printf_filtered ("\n"); return val; } + int remove_breakpoints () { @@ -485,14 +575,70 @@ remove_breakpoints () int val; ALL_BREAKPOINTS (b) - if (b->type != bp_watchpoint && b->inserted) - { - val = target_remove_breakpoint(b->address, b->shadow_contents); - if (val) - return val; - b->inserted = 0; - } + { + if (b->inserted) + { + val = remove_breakpoint (b); + if (val != 0) + return val; + } + } + return 0; +} + +static int +remove_breakpoint (b) + struct breakpoint *b; +{ + int val; + + if (b->type != bp_watchpoint + && b->type != bp_hardware_watchpoint) + { + val = target_remove_breakpoint(b->address, b->shadow_contents); + if (val) + return val; + b->inserted = 0; + } + else if (b->type == bp_hardware_watchpoint + && b->enable == enabled + && ! b->duplicate) + { + value_ptr v, n; + + b->inserted = 0; + /* Walk down the saved value chain. */ + for (v = b->val_chain; v; v = v->next) + { + /* For each memory reference remove the watchpoint + at that address. */ + if (v->lval == lval_memory) + { + int addr, len; + + addr = VALUE_ADDRESS (v) + VALUE_OFFSET (v); + len = TYPE_LENGTH (VALUE_TYPE (v)); + val = target_remove_watchpoint (addr, len); + if (val == -1) + b->inserted = 1; + val = 0; + } + } + /* Failure to remove any of the hardware watchpoints comes here. */ + if (b->inserted) + error ("Hardware watchpoint %d: Could not remove watchpoint\n", + b->number); + + /* Free the saved value chain. We will construct a new one + the next time the watchpoint is inserted. */ + for (v = b->val_chain; v; v = n) + { + n = v->next; + value_free (v); + } + b->val_chain = NULL; + } return 0; } @@ -524,6 +670,15 @@ breakpoint_init_inferior () get rid of it. */ if (b->type == bp_call_dummy) delete_breakpoint (b); + + /* Likewise for scope breakpoints. */ + if (b->type == bp_watchpoint_scope) + delete_breakpoint (b); + + /* Likewise for watchpoints on local expressions. */ + if ((b->type == bp_watchpoint || b->type == bp_hardware_watchpoint) + && b->exp_valid_block != NULL) + delete_breakpoint (b); } } @@ -771,7 +926,8 @@ print_it_normal (bs) which has since been deleted. */ if (bs->breakpoint_at == NULL || (bs->breakpoint_at->type != bp_breakpoint - && bs->breakpoint_at->type != bp_watchpoint)) + && bs->breakpoint_at->type != bp_watchpoint + && bs->breakpoint_at->type != bp_hardware_watchpoint)) return 0; if (bs->breakpoint_at->type == bp_breakpoint) @@ -860,125 +1016,12 @@ bpstat_alloc (b, cbs) return bs; } -/* Return the frame which we can use to evaluate the expression - whose valid block is valid_block, or NULL if not in scope. - - This whole concept is probably not the way to do things (it is incredibly - slow being the main reason, not to mention fragile (e.g. the sparc - frame pointer being fetched as 0 bug causes it to stop)). Instead, - introduce a version of "struct frame" which survives over calls to the - inferior, but which is better than FRAME_ADDR in the sense that it lets - us evaluate expressions relative to that frame (on some machines, it - can just be a FRAME_ADDR). Save one of those instead of (or in addition - to) the exp_valid_block, and then use it to evaluate the watchpoint - expression, with no need to do all this backtracing every time. - - Or better yet, what if it just copied the struct frame and its next - frame? Off the top of my head, I would think that would work - because things like (a29k) rsize and msize, or (sparc) bottom just - depend on the frame, and aren't going to be different just because - the inferior has done something. Trying to recalculate them - strikes me as a lot of work, possibly even impossible. Saving the - next frame is needed at least on a29k, where get_saved_register - uses fi->next->saved_msp. For figuring out whether that frame is - still on the stack, I guess this needs to be machine-specific (e.g. - a29k) but I think - - read_fp () INNER_THAN watchpoint_frame->frame - - would generally work. - - Of course the scope of the expression could be less than a whole - function; perhaps if the innermost frame is the one which the - watchpoint is relative to (another machine-specific thing, usually - - FRAMELESS_FUNCTION_INVOCATION (get_current_frame(), fromleaf) - read_fp () == wp_frame->frame - && !fromleaf - - ), *then* it could do a - - contained_in (get_current_block (), wp->exp_valid_block). - - */ - -FRAME -within_scope (valid_block) - struct block *valid_block; -{ - FRAME fr = get_current_frame (); - struct frame_info *fi = get_frame_info (fr); - CORE_ADDR func_start; - - /* If caller_pc_valid is true, we are stepping through - a function prologue, which is bounded by callee_func_start - (inclusive) and callee_prologue_end (exclusive). - caller_pc is the pc of the caller. - - Yes, this is hairy. */ - static int caller_pc_valid = 0; - static CORE_ADDR caller_pc; - static CORE_ADDR callee_func_start; - static CORE_ADDR callee_prologue_end; - - find_pc_partial_function (fi->pc, (PTR)NULL, &func_start, (CORE_ADDR *)NULL); - func_start += FUNCTION_START_OFFSET; - if (fi->pc == func_start) - { - /* We just called a function. The only other case I - can think of where the pc would equal the pc of the - start of a function is a frameless function (i.e. - no prologue) where we branch back to the start - of the function. In that case, SKIP_PROLOGUE won't - find one, and we'll clear caller_pc_valid a few lines - down. */ - caller_pc_valid = 1; - caller_pc = SAVED_PC_AFTER_CALL (fr); - callee_func_start = func_start; - SKIP_PROLOGUE (func_start); - callee_prologue_end = func_start; - } - if (caller_pc_valid) - { - if (fi->pc < callee_func_start - || fi->pc >= callee_prologue_end) - caller_pc_valid = 0; - } - - if (contained_in (block_for_pc (caller_pc_valid - ? caller_pc - : fi->pc), - valid_block)) - { - return fr; - } - fr = get_prev_frame (fr); - - /* If any active frame is in the exp_valid_block, then it's - OK. Note that this might not be the same invocation of - the exp_valid_block that we were watching a little while - ago, or the same one as when the watchpoint was set (e.g. - we are watching a local variable in a recursive function. - When we return from a recursive invocation, then we are - suddenly watching a different instance of the variable). - - At least for now I am going to consider this a feature. */ - for (; fr != NULL; fr = get_prev_frame (fr)) - { - fi = get_frame_info (fr); - if (contained_in (block_for_pc (fi->pc), - valid_block)) - { - return fr; - } - } - return NULL; -} + /* Possible return values for watchpoint_check (this can't be an enum because of check_errors). */ -/* The watchpoint has been disabled. */ -#define WP_DISABLED 1 +/* The watchpoint has been deleted. */ +#define WP_DELETED 1 /* The value has changed. */ #define WP_VALUE_CHANGED 2 /* The value has not changed. */ @@ -990,15 +1033,21 @@ watchpoint_check (p) char *p; { bpstat bs = (bpstat) p; - FRAME fr; + struct breakpoint *b; + FRAME saved_frame, fr; + int within_current_scope, saved_level; + + /* Save the current frame and level so we can restore it after + evaluating the watchpoint expression on its own frame. */ + saved_frame = selected_frame; + saved_level = selected_frame_level; - int within_current_scope; if (bs->breakpoint_at->exp_valid_block == NULL) within_current_scope = 1; else { - fr = within_scope (bs->breakpoint_at->exp_valid_block); - within_current_scope = fr != NULL; + fr = find_frame_addr_in_frame_chain (bs->breakpoint_at->watchpoint_frame); + within_current_scope = (fr != NULL); if (within_current_scope) /* If we end up stopping, the current frame will get selected in normal_stop. So this call to select_frame won't affect @@ -1022,6 +1071,7 @@ watchpoint_check (p) bs->old_val = bs->breakpoint_at->val; bs->breakpoint_at->val = new_val; /* We will stop here */ + select_frame (saved_frame, saved_level); return WP_VALUE_CHANGED; } else @@ -1029,6 +1079,7 @@ watchpoint_check (p) /* Nothing changed, don't do anything. */ value_free_to_mark (mark); /* We won't stop here */ + select_frame (saved_frame, saved_level); return WP_VALUE_NOT_CHANGED; } } @@ -1042,11 +1093,15 @@ watchpoint_check (p) So we can't even detect the first assignment to it and watch after that (since the garbage may or may not equal the first value assigned). */ - bs->breakpoint_at->enable = disabled; printf_filtered ("\ -Watchpoint %d disabled because the program has left the block in\n\ +Watchpoint %d deleted because the program has left the block in\n\ which its expression is valid.\n", bs->breakpoint_at->number); - return WP_DISABLED; + if (bs->breakpoint_at->related_breakpoint) + delete_breakpoint (bs->breakpoint_at->related_breakpoint); + delete_breakpoint (bs->breakpoint_at); + + select_frame (saved_frame, saved_level); + return WP_DELETED; } } @@ -1115,10 +1170,14 @@ bpstat_stop_status (pc, frame_address, not_a_breakpoint) if (b->enable == disabled) continue; - if (b->type != bp_watchpoint && b->address != bp_addr) + if (b->type != bp_watchpoint + && b->type != bp_hardware_watchpoint + && b->address != bp_addr) continue; - if (b->type != bp_watchpoint && not_a_breakpoint) + if (b->type != bp_watchpoint + && b->type != bp_hardware_watchpoint + && not_a_breakpoint) continue; /* Come here if it's a watchpoint, or if the break address matches */ @@ -1128,7 +1187,7 @@ bpstat_stop_status (pc, frame_address, not_a_breakpoint) bs->stop = 1; bs->print = 1; - if (b->type == bp_watchpoint) + if (b->type == bp_watchpoint || b->type == bp_hardware_watchpoint) { static char message1[] = "Error evaluating expression for watchpoint %d\n"; @@ -1137,7 +1196,7 @@ bpstat_stop_status (pc, frame_address, not_a_breakpoint) switch (catch_errors (watchpoint_check, (char *) bs, message, RETURN_MASK_ALL)) { - case WP_DISABLED: + case WP_DELETED: /* We've already printed what needs to be printed. */ bs->print_it = print_it_done; /* Stop. */ @@ -1155,10 +1214,13 @@ bpstat_stop_status (pc, frame_address, not_a_breakpoint) /* FALLTHROUGH */ case 0: /* Error from catch_errors. */ - b->enable = disabled; - printf_filtered ("Watchpoint %d disabled.\n", b->number); + printf_filtered ("Watchpoint %d deleted.\n", b->number); + if (b->related_breakpoint) + delete_breakpoint (b->related_breakpoint); + delete_breakpoint (b); /* We've already printed what needs to be printed. */ bs->print_it = print_it_done; + /* Stop. */ break; } @@ -1231,6 +1293,15 @@ bpstat_stop_status (pc, frame_address, not_a_breakpoint) } } #endif /* DECR_PC_AFTER_BREAK != 0. */ + + /* The value of a hardware watchpoint hasn't changed, but the + intermediate memory locations we are watching may have. */ + if (bs && ! bs->stop + && bs->breakpoint_at->type == bp_hardware_watchpoint) + { + remove_breakpoints (); + insert_breakpoints (); + } return bs; } @@ -1361,6 +1432,7 @@ bpstat_what (bs) bs_class = bp_nostop; break; case bp_watchpoint: + case bp_hardware_watchpoint: if (bs->stop) { if (bs->print) @@ -1391,6 +1463,10 @@ bpstat_what (bs) case bp_through_sigtramp: bs_class = through_sig; break; + case bp_watchpoint_scope: + bs_class = bp_nostop; + break; + case bp_call_dummy: /* Make sure the action is stop (silent or noisy), so infrun.c pops the dummy frame. */ @@ -1433,8 +1509,9 @@ breakpoint_1 (bnum, allflag) CORE_ADDR last_addr = (CORE_ADDR)-1; int found_a_breakpoint = 0; static char *bptypes[] = {"breakpoint", "until", "finish", "watchpoint", - "longjmp", "longjmp resume", "step resume", - "call dummy" }; + "hardware watchpoint", "longjmp", + "longjmp resume", "step resume", + "watchpoint scope", "call dummy" }; static char *bpdisps[] = {"del", "dis", "keep"}; static char bpenables[] = "ny"; char wrap_indent[80]; @@ -1446,7 +1523,8 @@ breakpoint_1 (bnum, allflag) /* We only print out user settable breakpoints unless the allflag is set. */ if (!allflag && b->type != bp_breakpoint - && b->type != bp_watchpoint) + && b->type != bp_watchpoint + && b->type != bp_hardware_watchpoint) continue; if (!found_a_breakpoint++) @@ -1464,6 +1542,7 @@ breakpoint_1 (bnum, allflag) switch (b->type) { case bp_watchpoint: + case bp_hardware_watchpoint: print_expression (b->exp, gdb_stdout); break; @@ -1474,6 +1553,7 @@ breakpoint_1 (bnum, allflag) case bp_longjmp_resume: case bp_step_resume: case bp_through_sigtramp: + case bp_watchpoint_scope: case bp_call_dummy: if (addressprint) printf_filtered ("%s ", local_hex_string_custom ((unsigned long) b->address, "08l")); @@ -1829,6 +1909,10 @@ mention (b) printf_filtered ("Watchpoint %d: ", b->number); print_expression (b->exp, gdb_stdout); break; + case bp_hardware_watchpoint: + printf_filtered ("Hardware watchpoint %d: ", b->number); + print_expression (b->exp, gdb_stdout); + break; case bp_breakpoint: printf_filtered ("Breakpoint %d at ", b->number); print_address_numeric (b->address, gdb_stdout); @@ -1843,6 +1927,7 @@ mention (b) case bp_step_resume: case bp_through_sigtramp: case bp_call_dummy: + case bp_watchpoint_scope: break; } printf_filtered ("\n"); @@ -2107,6 +2192,7 @@ watch_command (arg, from_tty) struct expression *exp; struct block *exp_valid_block; struct value *val; + FRAME frame, prev_frame; sal.pc = 0; sal.symtab = NULL; @@ -2125,7 +2211,6 @@ watch_command (arg, from_tty) b = set_raw_breakpoint (sal); set_breakpoint_count (breakpoint_count + 1); b->number = breakpoint_count; - b->type = bp_watchpoint; b->disposition = donttouch; b->exp = exp; b->exp_valid_block = exp_valid_block; @@ -2133,8 +2218,93 @@ watch_command (arg, from_tty) b->cond = 0; b->cond_string = NULL; b->exp_string = savestring (arg, strlen (arg)); + + frame = block_innermost_frame (exp_valid_block); + if (frame) + { + prev_frame = get_prev_frame (frame); + b->watchpoint_frame = FRAME_FP (frame); + } + else + b->watchpoint_frame = NULL; + + if (can_use_hardware_watchpoint (b)) + b->type = bp_hardware_watchpoint; + else + b->type = bp_watchpoint; + + /* If the expression is "local", then set up a "watchpoint scope" + breakpoint at the point where we've left the scope of the watchpoint + expression. */ + if (innermost_block) + { + struct breakpoint *scope_breakpoint; + struct symtab_and_line scope_sal; + + if (prev_frame) + { + scope_sal.pc = get_frame_pc (prev_frame); + scope_sal.symtab = NULL; + scope_sal.line = 0; + + scope_breakpoint = set_raw_breakpoint (scope_sal); + set_breakpoint_count (breakpoint_count + 1); + scope_breakpoint->number = breakpoint_count; + + scope_breakpoint->type = bp_watchpoint_scope; + scope_breakpoint->enable = enabled; + + /* Automatically delete the breakpoint when it hits. */ + scope_breakpoint->disposition = delete; + + /* Only break in the proper frame (help with recursion). */ + scope_breakpoint->frame = prev_frame->frame; + + /* Set the address at which we will stop. */ + scope_breakpoint->address = get_frame_pc (prev_frame); + + /* The scope breakpoint is related to the watchpoint. We + will need to act on them together. */ + b->related_breakpoint = scope_breakpoint; + } + } + mention (b); } + +/* Return nonzero if the watchpoint described by B can be handled + completely in hardware. If the watchpoint can not be handled + in hardware return zero. */ + +static int +can_use_hardware_watchpoint (b) + struct breakpoint *b; +{ + value_ptr mark = value_mark (); + value_ptr v = evaluate_expression (b->exp); + int found_memory = 0; + + /* Make sure all the intermediate values are in memory. Also make sure + we found at least one memory expression. Guards against watch 0x12345, + which is meaningless, but could cause errors if one tries to insert a + hardware watchpoint for the constant expression. */ + for ( ; v != mark; v = v->next) + { + if (!(v->lval == lval_memory) + || v->lval == not_lval + || (v->lval != not_lval + && v->modifiable == false)) + return 0; + else + if (v->lval == lval_memory) + found_memory = 1; + } + + /* The expression itself looks suitable for using a hardware + watchpoint, but give the target machine a chance to reject it. */ + return found_memory && TARGET_CAN_USE_HARDWARE_WATCHPOINT (b); +} + /* * Helper routine for the until_command routine in infcmd.c. Here @@ -2577,6 +2747,7 @@ clear_command (arg, from_tty) ALL_BREAKPOINTS (b) while (b->next && b->next->type != bp_watchpoint + && b->next->type != bp_hardware_watchpoint && (sal.pc ? b->next->address == sal.pc : (b->next->source_file != NULL @@ -2635,8 +2806,8 @@ delete_breakpoint (bpt) register bpstat bs; if (bpt->inserted) - target_remove_breakpoint(bpt->address, bpt->shadow_contents); - + remove_breakpoint (bpt); + if (breakpoint_chain == bpt) breakpoint_chain = bpt->next; @@ -2650,7 +2821,8 @@ delete_breakpoint (bpt) check_duplicates (bpt->address); /* If this breakpoint was inserted, and there is another breakpoint at the same address, we need to insert the other breakpoint. */ - if (bpt->inserted) + if (bpt->inserted + && bpt->type != bp_hardware_watchpoint) { ALL_BREAKPOINTS (b) if (b->address == bpt->address @@ -2797,6 +2969,7 @@ breakpoint_re_set_one (bint) break; case bp_watchpoint: + case bp_hardware_watchpoint: innermost_block = NULL; /* The issue arises of what context to evaluate this in. The same one as when it was set, but what does that mean when symbols have @@ -2829,6 +3002,7 @@ breakpoint_re_set_one (bint) case bp_finish: case bp_longjmp: case bp_longjmp_resume: + case bp_watchpoint_scope: case bp_call_dummy: delete_breakpoint (b); break; @@ -2958,7 +3132,10 @@ map_breakpoint_numbers (args, function) ALL_BREAKPOINTS (b) if (b->number == num) { + struct breakpoint *related_breakpoint = b->related_breakpoint; function (b); + if (related_breakpoint) + function (related_breakpoint); goto win; } printf_unfiltered ("No breakpoint number %d.\n", num); @@ -2980,11 +3157,11 @@ enable_breakpoint (bpt) printf_unfiltered ("breakpoint #%d enabled\n", bpt->number); check_duplicates (bpt->address); - if (bpt->type == bp_watchpoint) + if (bpt->type == bp_watchpoint || bpt->type == bp_hardware_watchpoint) { if (bpt->exp_valid_block != NULL) { - FRAME fr = within_scope (bpt->exp_valid_block); + FRAME fr = find_frame_addr_in_frame_chain (bpt->watchpoint_frame); if (fr == NULL) { printf_filtered ("\ @@ -2993,6 +3170,7 @@ is valid is not currently in scope.\n", bpt->number); bpt->enable = disabled; return; } + save_selected_frame = selected_frame; save_selected_frame_level = selected_frame_level; select_frame (fr, -1); @@ -3023,6 +3201,7 @@ enable_command (args, from_tty) { case bp_breakpoint: case bp_watchpoint: + case bp_hardware_watchpoint: enable_breakpoint (bpt); default: continue; @@ -3035,6 +3214,12 @@ static void disable_breakpoint (bpt) struct breakpoint *bpt; { + /* Never disable a watchpoint scope breakpoint; we want to + hit them when we leave scope so we can delete both the + watchpoint and its scope breakpoint at that time. */ + if (bpt->type == bp_watchpoint_scope) + return; + bpt->enable = disabled; if (xgdb_verbose && bpt->type == bp_breakpoint) @@ -3056,6 +3241,7 @@ disable_command (args, from_tty) { case bp_breakpoint: case bp_watchpoint: + case bp_hardware_watchpoint: disable_breakpoint (bpt); default: continue; diff --git a/gdb/hppab-nat.c b/gdb/hppab-nat.c index 990c952660f..5bc0bb43089 100644 --- a/gdb/hppab-nat.c +++ b/gdb/hppab-nat.c @@ -140,3 +140,62 @@ store_inferior_registers (regno) return; } + +/* PT_PROT is specific to the PA BSD kernel and isn't documented + anywhere (except here). + + PT_PROT allows one to enable/disable the data memory break bit + for pages of memory in an inferior process. This bit is used + to cause "Data memory break traps" to occur when the appropriate + page is written to. + + The arguments are as follows: + + PT_PROT -- The ptrace action to perform. + + INFERIOR_PID -- The pid of the process who's page table entries + will be modified. + + PT_ARGS -- The *address* of a 3 word block of memory which has + additional information: + + word 0 -- The start address to watch. This should be a page-aligned + address. + + word 1 -- The ending address to watch. Again, this should be a + page aligned address. + + word 2 -- Nonzero to enable the data memory break bit on the + given address range or zero to disable the data memory break + bit on the given address range. + + This call may fail if the given addresses are not valid in the inferior + process. This most often happens when restarting a program which + as watchpoints inserted on heap or stack memory. */ + +#define PT_PROT 21 + +int +hppa_set_watchpoint (addr, len, flag) + int addr, len, flag; +{ + int pt_args[3]; + pt_args[0] = addr; + pt_args[1] = addr + len; + pt_args[2] = flag; + + /* Mask off the lower 12 bits since we want to work on a page basis. */ + pt_args[0] >>= 12; + pt_args[1] >>= 12; + + /* Rounding adjustments. */ + pt_args[1] -= pt_args[0]; + pt_args[1]++; + + /* Put the lower 12 bits back as zero. */ + pt_args[0] <<= 12; + pt_args[1] <<= 12; + + /* Do it. */ + return ptrace (PT_PROT, inferior_pid, (PTRACE_ARG3_TYPE) pt_args, 0); +} diff --git a/gdb/infrun.c b/gdb/infrun.c index 94f417d9b92..dfe2cc0ae78 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -624,8 +624,9 @@ switch_thread: single_step (0); /* This actually cleans up the ss */ #endif /* NO_SINGLE_STEP */ -/* If PC is pointing at a nullified instruction, then step beyond it so that - the user won't be confused when GDB appears to be ready to execute it. */ + /* If PC is pointing at a nullified instruction, then step beyond + it so that the user won't be confused when GDB appears to be ready + to execute it. */ if (INSTRUCTION_NULLIFIED) { @@ -633,7 +634,33 @@ switch_thread: continue; } - set_current_frame ( create_new_frame (read_fp (), stop_pc)); + set_current_frame (create_new_frame (read_fp (), stop_pc)); + select_frame (get_current_frame (), 0); + +#ifdef HAVE_STEPPABLE_WATCHPOINT + /* It may not be necessary to disable the watchpoint to stop over + it. For example, the PA can (with some kernel cooperation) + single step over a watchpoint without disabling the watchpoint. */ + if (STOPPED_BY_WATCHPOINT (w)) + { + resume (1, 0); + continue; + } +#endif + +#ifdef HAVE_NONSTEPPABLE_WATCHPOINT + /* It is far more common to need to disable a watchpoint + to step the inferior over it. FIXME. What else might + a debug register or page protection watchpoint scheme need + here? */ + if (STOPPED_BY_WATCHPOINT (w)) + { + remove_breakpoints (); + resume (1, 0); + insert_breakpoints (); + continue; + } +#endif stop_frame_address = FRAME_FP (get_current_frame ()); stop_sp = read_sp (); @@ -1404,8 +1431,6 @@ Further execution is probably impossible.\n"); if we have one. */ if (!stop_stack_dummy) { - select_frame (get_current_frame (), 0); - if (stop_print_frame) { int source_only; diff --git a/gdb/procfs.c b/gdb/procfs.c index be948037a7a..2215ce38e5e 100644 --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -2321,6 +2321,10 @@ wait_again: break; case FLTBPT: case FLTTRACE: + statval = (SIGTRAP << 8) | 0177; + break; + case FLTWATCH: + case FLTKWATCH: statval = (SIGTRAP << 8) | 0177; break; case FLTSTACK: @@ -3528,6 +3532,56 @@ procfs_can_run () { return(1); } + +/* Insert a watchpoint */ +int +procfs_set_watchpoint(pid, addr, len, rw) + int pid; + CORE_ADDR addr; + int len; + int rw; +{ + struct procinfo *pi; + prwatch_t wpt; + + pi = find_procinfo (pid == -1 ? inferior_pid : pid, 0); + wpt.pr_vaddr = (caddr_t)addr; + wpt.pr_size = len; + wpt.pr_wflags = ((rw & 1) ? MA_READ : 0) | ((rw & 2) ? MA_WRITE : 0); + if (ioctl (pi->fd, PIOCSWATCH, &wpt) < 0) + { + if (errno == E2BIG) + return -1; + /* Currently it sometimes happens that the same watchpoint gets + deleted twice - don't die in this case (FIXME please) */ + if (errno == ESRCH && len == 0) + return 0; + print_sys_errmsg (pi->pathname, errno); + error ("PIOCSWATCH failed"); + } + return 0; +} + +int +procfs_stopped_by_watchpoint(pid) + int pid; +{ + struct procinfo *pi; + short what; + short why; + + pi = find_procinfo (pid == -1 ? inferior_pid : pid, 0); + if (pi->prstatus.pr_flags & (PR_STOPPED | PR_ISTOP)) + { + why = pi->prstatus.pr_why; + what = pi->prstatus.pr_what; + if (why == PR_FAULTED + && (what == FLTWATCH) || (what == FLTKWATCH)) + return what; + } + return 0; +} + struct target_ops procfs_ops = { "procfs", /* to_shortname */ diff --git a/gdb/value.h b/gdb/value.h index c71c96053e3..69822586fd2 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -280,7 +280,7 @@ extern value_ptr value_neg PARAMS ((value_ptr arg1)); extern value_ptr value_complement PARAMS ((value_ptr arg1)); -extern value_ptr value_struct_elt PARAMS ((value_ptr *argp, value *args, +extern value_ptr value_struct_elt PARAMS ((value_ptr *argp, value_ptr *args, char *name, int *static_memfuncp, char *err)); @@ -433,6 +433,9 @@ print_floating PARAMS ((char *valaddr, struct type *type, GDB_FILE *stream)); extern int value_print PARAMS ((value_ptr val, GDB_FILE *stream, int format, enum val_prettyprint pretty)); +extern value_ptr +value_release_to_mark PARAMS ((value_ptr mark)); + extern int val_print PARAMS ((struct type *type, char *valaddr, CORE_ADDR address, GDB_FILE *stream, int format, int deref_ref, diff --git a/gdb/values.c b/gdb/values.c index 0ea9c4ad95e..244d877fb6b 100644 --- a/gdb/values.c +++ b/gdb/values.c @@ -189,6 +189,24 @@ release_value (val) } } +/* Release all values up to mark */ +value_ptr +value_release_to_mark (mark) + value_ptr mark; +{ + value_ptr val, next; + + for (val = next = all_values; next; next = VALUE_NEXT (next)) + if (VALUE_NEXT (next) == mark) + { + all_values = VALUE_NEXT (next); + VALUE_NEXT (next) = 0; + return val; + } + all_values = 0; + return val; +} + /* Return a copy of the value ARG. It contains the same contents, for same memory address, but it's a different block of storage. */ -- 2.30.2