diff options
-rw-r--r-- | gdb/ChangeLog | 63 | ||||
-rw-r--r-- | gdb/blockframe.c | 23 | ||||
-rw-r--r-- | gdb/breakpoint.c | 480 | ||||
-rw-r--r-- | gdb/hppab-nat.c | 59 | ||||
-rw-r--r-- | gdb/infrun.c | 35 | ||||
-rw-r--r-- | gdb/procfs.c | 54 | ||||
-rw-r--r-- | gdb/value.h | 5 | ||||
-rw-r--r-- | gdb/values.c | 18 |
8 files changed, 584 insertions, 153 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 7ca2b53..7248d73 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 0c79461..bbd4287 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 f8adee1..457ca91 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 990c952..5bc0bb4 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 94f417d..dfe2cc0 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 be94803..2215ce3 100644 --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -2322,6 +2322,10 @@ wait_again: case FLTBPT: case FLTTRACE: statval = (SIGTRAP << 8) | 0177; + break; + case FLTWATCH: + case FLTKWATCH: + statval = (SIGTRAP << 8) | 0177; break; case FLTSTACK: case FLTACCESS: @@ -3529,6 +3533,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 */ "Unix /proc child process", /* to_longname */ diff --git a/gdb/value.h b/gdb/value.h index c71c960..6982258 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 0ea9c4a..244d877 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. */ |