diff options
-rw-r--r-- | gdb/ChangeLog | 40 | ||||
-rw-r--r-- | gdb/NEWS | 14 | ||||
-rw-r--r-- | gdb/ada-lang.c | 3 | ||||
-rw-r--r-- | gdb/breakpoint.c | 418 | ||||
-rw-r--r-- | gdb/breakpoint.h | 8 | ||||
-rw-r--r-- | gdb/doc/ChangeLog | 7 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 28 | ||||
-rw-r--r-- | gdb/ppc-linux-nat.c | 81 | ||||
-rw-r--r-- | gdb/target.c | 72 | ||||
-rw-r--r-- | gdb/target.h | 26 |
10 files changed, 621 insertions, 76 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1306b880307..61b8b15891b 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,43 @@ +2011-05-06 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com> + Thiago Jung Bauermann <bauerman@br.ibm.com> + + Implement support for PowerPC BookE masked watchpoints. + * NEWS: Mention masked watchpoint support. Create "Changed commands" + section. + * breakpoint.h (struct breakpoint_ops) <works_in_software_mode>: New + method. Initialize to NULL in all existing breakpoint_ops instances. + (struct breakpoint) <hw_wp_mask>: New field. + * breakpoint.c (is_masked_watchpoint): Add prototype. + (update_watchpoint): Don't set b->val for masked watchpoints. Call + breakpoint's breakpoint_ops.works_in_software_mode if available. + (watchpoints_triggered): Handle the case of a hardware masked + watchpoint trigger. + (watchpoint_check): Likewise. + (works_in_software_mode_watchpoint): New function. + (insert_masked_watchpoint, remove_masked_watchpoint) + (resources_needed_masked_watchpoint) + (works_in_software_mode_masked_watchpoint, print_it_masked_watchpoint) + (print_one_detail_masked_watchpoint, print_mention_masked_watchpoint) + (print_recreate_masked_watchpoint, is_masked_watchpoint): New + functions. + (masked_watchpoint_breakpoint_ops): New structure. + (watch_command_1): Check for the existence of the `mask' parameter. + Set b->ops according to the type of hardware watchpoint being created. + * ppc-linux-nat.c (ppc_linux_insert_mask_watchpoint) + (ppc_linux_remove_mask_watchpoint) + (ppc_linux_masked_watch_num_registers): New functions. + (_initialize_ppc_linux_nat): Initialize to_insert_mask_watchpoint, + to_remove_mask_watchpoint and to_masked_watch_num_registers. + * target.c (update_current_target): Mention to_insert_mask_watchpoint, + to_remove_mask_watchpoint, and to_masked_watch_num_registers. + (target_insert_mask_watchpoint, target_remove_mask_watchpoint) + (target_masked_watch_num_registers): New functions. + * target.h (struct target_ops) <to_insert_mask_watchpoint>, + <to_remove_mask_watchpoint>, <to_masked_watch_num_registers>: New + methods. + (target_insert_mask_watchpoint, target_remove_mask_watchpoint) + (target_masked_watch_num_registers): Add prototypes. + 2011-05-06 Jan Kratochvil <jan.kratochvil@redhat.com> PR 12573 @@ -3,10 +3,24 @@ *** Changes since GDB 7.3 +* When natively debugging programs on PowerPC BookE processors running + a Linux kernel version 2.6.34 or later, GDB supports masked hardware + watchpoints, which specify a mask in addition to an address to watch. + The mask specifies that some bits of an address (the bits which are + reset in the mask) should be ignored when matching the address accessed + by the inferior against the watchpoint address. See the "PowerPC Embedded" + section in the user manual for more details. + * The new option --once causes GDBserver to stop listening for connections once the first connection is made. The listening port used by GDBserver will become available after that. +* Changed commands + +watch EXPRESSION mask MASK_VALUE + The watch command now supports the mask argument which allows creation + of masked watchpoints, if the current architecture supports this feature. + *** Changes in GDB 7.3 * GDB has a new command: "thread find [REGEXP]". diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c index d434521be7d..ebd5fd20700 100644 --- a/gdb/ada-lang.c +++ b/gdb/ada-lang.c @@ -10950,6 +10950,7 @@ static struct breakpoint_ops catch_exception_breakpoint_ops = NULL, /* remove */ NULL, /* breakpoint_hit */ NULL, /* resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_exception, print_one_catch_exception, NULL, /* print_one_detail */ @@ -10990,6 +10991,7 @@ static struct breakpoint_ops catch_exception_unhandled_breakpoint_ops = { NULL, /* remove */ NULL, /* breakpoint_hit */ NULL, /* resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_exception_unhandled, print_one_catch_exception_unhandled, NULL, /* print_one_detail */ @@ -11028,6 +11030,7 @@ static struct breakpoint_ops catch_assert_breakpoint_ops = { NULL, /* remove */ NULL, /* breakpoint_hit */ NULL, /* resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_assert, print_one_catch_assert, NULL, /* print_one_detail */ diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index b5fc44865a4..e582c36c8a4 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -224,6 +224,8 @@ static void disable_trace_command (char *, int); static void trace_pass_command (char *, int); +static int is_masked_watchpoint (const struct breakpoint *b); + /* Assuming we're creating a static tracepoint, does S look like a static tracepoint marker spec ("-m MARKER_ID")? */ #define is_marker_spec(s) \ @@ -1345,8 +1347,10 @@ update_watchpoint (struct breakpoint *b, int reparse) /* Avoid setting b->val if it's already set. The meaning of b->val is 'the last value' user saw, and we should update it only if we reported that last value to user. As it - happens, the code that reports it updates b->val directly. */ - if (!b->val_valid) + happens, the code that reports it updates b->val directly. + We don't keep track of the memory value for masked + watchpoints. */ + if (!b->val_valid && !is_masked_watchpoint (b)) { b->val = v; b->val_valid = 1; @@ -1435,19 +1439,23 @@ update_watchpoint (struct breakpoint *b, int reparse) (b->type, i, other_type_used); if (target_resources_ok <= 0) { - if (target_resources_ok == 0 - && b->type != bp_hardware_watchpoint) + /* If there's no works_in_software_mode method, we + assume that the watchpoint works in software mode. */ + int sw_mode = (!b->ops || !b->ops->works_in_software_mode + || b->ops->works_in_software_mode (b)); + + if (target_resources_ok == 0 && !sw_mode) error (_("Target does not support this type of " "hardware watchpoint.")); - else if (target_resources_ok < 0 - && b->type != bp_hardware_watchpoint) - error (_("Target can only support one kind " - "of HW watchpoint at a time.")); + else if (target_resources_ok < 0 && !sw_mode) + error (_("There are not enough available hardware " + "resources for this watchpoint.")); else b->type = bp_watchpoint; } } - else if (b->type != bp_hardware_watchpoint) + else if (b->ops && b->ops->works_in_software_mode + && !b->ops->works_in_software_mode (b)) error (_("Expression cannot be implemented with " "read/access watchpoint.")); else @@ -3694,15 +3702,27 @@ watchpoints_triggered (struct target_waitstatus *ws) b->watchpoint_triggered = watch_triggered_no; for (loc = b->loc; loc; loc = loc->next) - /* Exact match not required. Within range is - sufficient. */ - if (target_watchpoint_addr_within_range (¤t_target, - addr, loc->address, - loc->length)) - { - b->watchpoint_triggered = watch_triggered_yes; - break; - } + { + if (is_masked_watchpoint (loc->owner)) + { + CORE_ADDR newaddr = addr & loc->owner->hw_wp_mask; + CORE_ADDR start = loc->address & loc->owner->hw_wp_mask; + + if (newaddr == start) + { + b->watchpoint_triggered = watch_triggered_yes; + break; + } + } + /* Exact match not required. Within range is sufficient. */ + else if (target_watchpoint_addr_within_range (¤t_target, + addr, loc->address, + loc->length)) + { + b->watchpoint_triggered = watch_triggered_yes; + break; + } + } } return 1; @@ -3799,9 +3819,16 @@ watchpoint_check (void *p) might be in the middle of evaluating a function call. */ int pc = 0; - struct value *mark = value_mark (); + struct value *mark; struct value *new_val; + if (is_masked_watchpoint (b)) + /* Since we don't know the exact trigger address (from + stopped_data_address), just tell the user we've triggered + a mask watchpoint. */ + return WP_VALUE_CHANGED; + + mark = value_mark (); fetch_subexp_value (b->exp, &pc, &new_val, NULL, NULL); /* We use value_equal_contents instead of value_equal because @@ -6303,6 +6330,7 @@ static struct breakpoint_ops catch_fork_breakpoint_ops = remove_catch_fork, breakpoint_hit_catch_fork, NULL, /* resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_fork, print_one_catch_fork, NULL, /* print_one_detail */ @@ -6401,6 +6429,7 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops = remove_catch_vfork, breakpoint_hit_catch_vfork, NULL, /* resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_vfork, print_one_catch_vfork, NULL, /* print_one_detail */ @@ -6688,6 +6717,7 @@ static struct breakpoint_ops catch_syscall_breakpoint_ops = remove_catch_syscall, breakpoint_hit_catch_syscall, NULL, /* resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_syscall, print_one_catch_syscall, NULL, /* print_one_detail */ @@ -6845,6 +6875,7 @@ static struct breakpoint_ops catch_exec_breakpoint_ops = remove_catch_exec, breakpoint_hit_catch_exec, NULL, /* resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_exec, print_one_catch_exec, NULL, /* print_one_detail */ @@ -8470,6 +8501,7 @@ static struct breakpoint_ops ranged_breakpoint_ops = NULL, /* remove */ breakpoint_hit_ranged_breakpoint, resources_needed_ranged_breakpoint, + NULL, /* works_in_software_mode */ print_it_ranged_breakpoint, print_one_ranged_breakpoint, print_one_detail_ranged_breakpoint, @@ -8777,6 +8809,15 @@ resources_needed_watchpoint (const struct bp_location *bl) return target_region_ok_for_hw_watchpoint (bl->address, length); } +/* Implement the "works_in_software_mode" breakpoint_ops method for + hardware watchpoints. */ + +int +works_in_software_mode_watchpoint (const struct breakpoint *b) +{ + return b->type == bp_hardware_watchpoint; +} + /* The breakpoint_ops structure to be used in hardware watchpoints. */ static struct breakpoint_ops watchpoint_breakpoint_ops = @@ -8785,6 +8826,7 @@ static struct breakpoint_ops watchpoint_breakpoint_ops = remove_watchpoint, NULL, /* breakpoint_hit */ resources_needed_watchpoint, + works_in_software_mode_watchpoint, NULL, /* print_it */ NULL, /* print_one */ NULL, /* print_one_detail */ @@ -8792,6 +8834,192 @@ static struct breakpoint_ops watchpoint_breakpoint_ops = NULL /* print_recreate */ }; +/* Implement the "insert" breakpoint_ops method for + masked hardware watchpoints. */ + +static int +insert_masked_watchpoint (struct bp_location *bl) +{ + return target_insert_mask_watchpoint (bl->address, bl->owner->hw_wp_mask, + bl->watchpoint_type); +} + +/* Implement the "remove" breakpoint_ops method for + masked hardware watchpoints. */ + +static int +remove_masked_watchpoint (struct bp_location *bl) +{ + return target_remove_mask_watchpoint (bl->address, bl->owner->hw_wp_mask, + bl->watchpoint_type); +} + +/* Implement the "resources_needed" breakpoint_ops method for + masked hardware watchpoints. */ + +static int +resources_needed_masked_watchpoint (const struct bp_location *bl) +{ + return target_masked_watch_num_registers (bl->address, + bl->owner->hw_wp_mask); +} + +/* Implement the "works_in_software_mode" breakpoint_ops method for + masked hardware watchpoints. */ + +static int +works_in_software_mode_masked_watchpoint (const struct breakpoint *b) +{ + return 0; +} + +/* Implement the "print_it" breakpoint_ops method for + masked hardware watchpoints. */ + +static enum print_stop_action +print_it_masked_watchpoint (struct breakpoint *b) +{ + /* Masked watchpoints have only one location. */ + gdb_assert (b->loc && b->loc->next == NULL); + + switch (b->type) + { + case bp_hardware_watchpoint: + annotate_watchpoint (b->number); + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string + (uiout, "reason", + async_reason_lookup (EXEC_ASYNC_WATCHPOINT_TRIGGER)); + break; + + case bp_read_watchpoint: + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string + (uiout, "reason", + async_reason_lookup (EXEC_ASYNC_READ_WATCHPOINT_TRIGGER)); + break; + + case bp_access_watchpoint: + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string + (uiout, "reason", + async_reason_lookup (EXEC_ASYNC_ACCESS_WATCHPOINT_TRIGGER)); + break; + default: + internal_error (__FILE__, __LINE__, + _("Invalid hardware watchpoint type.")); + } + + mention (b); + ui_out_text (uiout, _("\n\ +Check the underlying instruction at PC for the memory\n\ +address and value which triggered this watchpoint.\n")); + ui_out_text (uiout, "\n"); + + /* More than one watchpoint may have been triggered. */ + return PRINT_UNKNOWN; +} + +/* Implement the "print_one_detail" breakpoint_ops method for + masked hardware watchpoints. */ + +static void +print_one_detail_masked_watchpoint (const struct breakpoint *b, + struct ui_out *uiout) +{ + /* Masked watchpoints have only one location. */ + gdb_assert (b->loc && b->loc->next == NULL); + + ui_out_text (uiout, "\tmask "); + ui_out_field_core_addr (uiout, "mask", b->loc->gdbarch, b->hw_wp_mask); + ui_out_text (uiout, "\n"); +} + +/* Implement the "print_mention" breakpoint_ops method for + masked hardware watchpoints. */ + +static void +print_mention_masked_watchpoint (struct breakpoint *b) +{ + struct cleanup *ui_out_chain; + + switch (b->type) + { + case bp_hardware_watchpoint: + ui_out_text (uiout, "Masked hardware watchpoint "); + ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "wpt"); + break; + case bp_read_watchpoint: + ui_out_text (uiout, "Masked hardware read watchpoint "); + ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "hw-rwpt"); + break; + case bp_access_watchpoint: + ui_out_text (uiout, "Masked hardware access (read/write) watchpoint "); + ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "hw-awpt"); + break; + default: + internal_error (__FILE__, __LINE__, + _("Invalid hardware watchpoint type.")); + } + + ui_out_field_int (uiout, "number", b->number); + ui_out_text (uiout, ": "); + ui_out_field_string (uiout, "exp", b->exp_string); + do_cleanups (ui_out_chain); +} + +/* Implement the "print_recreate" breakpoint_ops method for + masked hardware watchpoints. */ + +static void +print_recreate_masked_watchpoint (struct breakpoint *b, struct ui_file *fp) +{ + char tmp[40]; + + switch (b->type) + { + case bp_hardware_watchpoint: + fprintf_unfiltered (fp, "watch"); + break; + case bp_read_watchpoint: + fprintf_unfiltered (fp, "rwatch"); + break; + case bp_access_watchpoint: + fprintf_unfiltered (fp, "awatch"); + break; + default: + internal_error (__FILE__, __LINE__, + _("Invalid hardware watchpoint type.")); + } + + sprintf_vma (tmp, b->hw_wp_mask); + fprintf_unfiltered (fp, " %s mask 0x%s", b->exp_string, tmp); +} + +/* The breakpoint_ops structure to be used in masked hardware watchpoints. */ + +static struct breakpoint_ops masked_watchpoint_breakpoint_ops = +{ + insert_masked_watchpoint, + remove_masked_watchpoint, + NULL, /* breakpoint_hit */ + resources_needed_masked_watchpoint, + works_in_software_mode_masked_watchpoint, + print_it_masked_watchpoint, + NULL, /* print_one */ + print_one_detail_masked_watchpoint, + print_mention_masked_watchpoint, + print_recreate_masked_watchpoint +}; + +/* Tell whether the given watchpoint is a masked hardware watchpoint. */ + +static int +is_masked_watchpoint (const struct breakpoint *b) +{ + return b->ops == &masked_watchpoint_breakpoint_ops; +} + /* accessflag: hw_write: watch write, hw_read: watch read, hw_access: watch access (read or write) */ @@ -8807,73 +9035,97 @@ watch_command_1 (char *arg, int accessflag, int from_tty, struct frame_info *frame; char *exp_start = NULL; char *exp_end = NULL; - char *tok, *id_tok_start, *end_tok; - int toklen; + char *tok, *end_tok; + int toklen = -1; char *cond_start = NULL; char *cond_end = NULL; enum bptype bp_type; int thread = -1; int pc = 0; + /* Flag to indicate whether we are going to use masks for + the hardware watchpoint. */ + int use_mask = 0; + CORE_ADDR mask = 0; /* Make sure that we actually have parameters to parse. */ if (arg != NULL && arg[0] != '\0') { - toklen = strlen (arg); /* Size of argument list. */ + char *value_start; - /* Points tok to the end of the argument list. */ - tok = arg + toklen - 1; + /* Look for "parameter value" pairs at the end + of the arguments string. */ + for (tok = arg + strlen (arg) - 1; tok > arg; tok--) + { + /* Skip whitespace at the end of the argument list. */ + while (tok > arg && (*tok == ' ' || *tok == '\t')) + tok--; + + /* Find the beginning of the last token. + This is the value of the parameter. */ + while (tok > arg && (*tok != ' ' && *tok != '\t')) + tok--; + value_start = tok + 1; + + /* Skip whitespace. */ + while (tok > arg && (*tok == ' ' || *tok == '\t')) + tok--; + + end_tok = tok; + + /* Find the beginning of the second to last token. + This is the parameter itself. */ + while (tok > arg && (*tok != ' ' && *tok != '\t')) + tok--; + tok++; + toklen = end_tok - tok + 1; + + if (toklen == 6 && !strncmp (tok, "thread", 6)) + { + /* At this point we've found a "thread" token, which means + the user is trying to set a watchpoint that triggers + only in a specific thread. */ + char *endp; - /* Go backwards in the parameters list. Skip the last - parameter. If we're expecting a 'thread <thread_num>' - parameter, this should be the thread identifier. */ - while (tok > arg && (*tok == ' ' || *tok == '\t')) - tok--; - while (tok > arg && (*tok != ' ' && *tok != '\t')) - tok--; + if (thread != -1) + error(_("You can specify only one thread.")); - /* Points end_tok to the beginning of the last token. */ - id_tok_start = tok + 1; + /* Extract the thread ID from the next token. */ + thread = strtol (value_start, &endp, 0); - /* Go backwards in the parameters list. Skip one more - parameter. If we're expecting a 'thread <thread_num>' - parameter, we should reach a "thread" token. */ - while (tok > arg && (*tok == ' ' || *tok == '\t')) - tok--; + /* Check if the user provided a valid numeric value for the + thread ID. */ + if (*endp != ' ' && *endp != '\t' && *endp != '\0') + error (_("Invalid thread ID specification %s."), value_start); - end_tok = tok; + /* Check if the thread actually exists. */ + if (!valid_thread_id (thread)) + error (_("Unknown thread %d."), thread); + } + else if (toklen == 4 && !strncmp (tok, "mask", 4)) + { + /* We've found a "mask" token, which means the user wants to + create a hardware watchpoint that is going to have the mask + facility. */ + struct value *mask_value, *mark; - while (tok > arg && (*tok != ' ' && *tok != '\t')) - tok--; + if (use_mask) + error(_("You can specify only one mask.")); - /* Move the pointer forward to skip the whitespace and - calculate the length of the token. */ - tok++; - toklen = end_tok - tok; + use_mask = just_location = 1; - if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0) - { - /* At this point we've found a "thread" token, which means - the user is trying to set a watchpoint that triggers - only in a specific thread. */ - char *endp; - - /* Extract the thread ID from the next token. */ - thread = strtol (id_tok_start, &endp, 0); - - /* Check if the user provided a valid numeric value for the - thread ID. */ - if (*endp != ' ' && *endp != '\t' && *endp != '\0') - error (_("Invalid thread ID specification %s."), id_tok_start); - - /* Check if the thread actually exists. */ - if (!valid_thread_id (thread)) - error (_("Unknown thread %d."), thread); - - /* Truncate the string and get rid of the thread <thread_num> - parameter before the parameter list is parsed by the - evaluate_expression() function. */ - *tok = '\0'; - } + mark = value_mark (); + mask_value = parse_to_comma_and_eval (&value_start); + mask = value_as_address (mask_value); + value_free_to_mark (mark); + } + else + /* We didn't recognize what we found. We should stop here. */ + break; + + /* Truncate the string and get rid of the "parameter value" pair before + the arguments string is parsed by the parse_exp_1 function. */ + *tok = '\0'; + } } /* Parse the rest of the arguments. */ @@ -8904,10 +9156,22 @@ watch_command_1 (char *arg, int accessflag, int from_tty, if (just_location) { + int ret; + exp_valid_block = NULL; val = value_addr (result); release_value (val); value_free_to_mark (mark); + + if (use_mask) + { + ret = target_masked_watch_num_registers (value_as_address (val), + mask); + if (ret == -1) + error (_("This target does not support masked watchpoints.")); + else if (ret == -2) + error (_("Invalid mask or memory region.")); + } } else if (val != NULL) release_value (val); @@ -9004,9 +9268,18 @@ watch_command_1 (char *arg, int accessflag, int from_tty, } else b->exp_string = savestring (exp_start, exp_end - exp_start); - b->val = val; - b->val_valid = 1; - b->ops = &watchpoint_breakpoint_ops; + + if (use_mask) + { + b->hw_wp_mask = mask; + b->ops = &masked_watchpoint_breakpoint_ops; + } + else + { + b->val = val; + b->val_valid = 1; + b->ops = &watchpoint_breakpoint_ops; + } if (cond_start) b->cond_string = savestring (cond_start, cond_end - cond_start); @@ -9543,6 +9816,7 @@ static struct breakpoint_ops gnu_v3_exception_catchpoint_ops = { NULL, /* remove */ NULL, /* breakpoint_hit */ NULL, /* resources_needed */ + NULL, /* works_in_software_mode */ print_it_exception_catchpoint, print_one_exception_catchpoint, NULL, /* print_one_detail */ diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 7a9c2d4729d..7fa705fa2f9 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -410,6 +410,11 @@ struct breakpoint_ops the breakpoint or watchpoint needs one debug register. */ int (*resources_needed) (const struct bp_location *); + /* Tell whether we can downgrade from a hardware watchpoint to a software + one. If not, the user will not be able to enable the watchpoint when + there are not enough hardware resources available. */ + int (*works_in_software_mode) (const struct breakpoint *); + /* The normal print routine for this breakpoint, called when we hit it. */ enum print_stop_action (*print_it) (struct breakpoint *); @@ -651,6 +656,9 @@ struct breakpoint /* Whether this watchpoint is exact (see target_exact_watchpoints). */ int exact; + + /* The mask address for a masked hardware watchpoint. */ + CORE_ADDR hw_wp_mask; }; typedef struct breakpoint *breakpoint_p; diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index d7e610d3a9f..2eacb32cf30 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,10 @@ +2011-05-06 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com> + Thiago Jung Bauermann <bauerman@br.ibm.com> + + Implement support for PowerPC BookE masked watchpoints. + * gdb.texinfo (Set Watchpoints): Document mask parameter. + (PowerPC Embedded): Mention support of masked watchpoints. + 2011-05-03 Joel Brobecker <brobecker@adacore.com> * gdb.texinfo (In Memoriam): Replace litteral uses of `GDB' with diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 0ac56722c70..3618b703590 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -3725,7 +3725,7 @@ watchpoints, which do not slow down the running of your program. @table @code @kindex watch -@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} +@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]} Set a watchpoint for an expression. @value{GDBN} will break when the expression @var{expr} is written into by the program and its value changes. The simplest (and the most popular) use of this command is @@ -3736,7 +3736,7 @@ to watch the value of a single variable: @end smallexample If the command includes a @code{@r{[}thread @var{threadnum}@r{]}} -clause, @value{GDBN} breaks only when the thread identified by +argument, @value{GDBN} breaks only when the thread identified by @var{threadnum} changes the value of @var{expr}. If any other threads change the value of @var{expr}, @value{GDBN} will not break. Note that watchpoints restricted to a single thread in this way only work @@ -3751,13 +3751,30 @@ to determine the size of the watched memory. If the expression's result does not have an address, then @value{GDBN} will print an error. +The @code{@r{[}mask @var{maskvalue}@r{]}} argument allows creation +of masked watchpoints, if the current architecture supports this +feature (e.g., PowerPC Embedded architecture, see @ref{PowerPC +Embedded}.) A @dfn{masked watchpoint} specifies a mask in addition +to an address to watch. The mask specifies that some bits of an address +(the bits which are reset in the mask) should be ignored when matching +the address accessed by the inferior against the watchpoint address. +Thus, a masked watchpoint watches many addresses simultaneously---those +addresses whose unmasked bits are identical to the unmasked bits in the +watchpoint address. The @code{mask} argument implies @code{-location}. +Examples: + +@smallexample +(@value{GDBP}) watch foo mask 0xffff00ff +(@value{GDBP}) watch *0xdeadbeef mask 0xffffff00 +@end smallexample + @kindex rwatch -@item rwatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} +@item rwatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]} Set a watchpoint that will break when the value of @var{expr} is read by the program. @kindex awatch -@item awatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} +@item awatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]} Set a watchpoint that will break when @var{expr} is either read from or written into by the program. @@ -18780,6 +18797,9 @@ region using one of the following commands (@pxref{Expressions}): (@value{GDBP}) watch @{char[@var{length}]@} @var{address} @end smallexample +PowerPC embedded processors support masked watchpoints. See the discussion +about the @code{mask} argument in @ref{Set Watchpoints}. + @cindex ranged breakpoint PowerPC embedded processors support hardware accelerated @dfn{ranged breakpoints}. A ranged breakpoint stops execution of diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c index 6f117152d45..275de7885c3 100644 --- a/gdb/ppc-linux-nat.c +++ b/gdb/ppc-linux-nat.c @@ -1739,6 +1739,64 @@ get_trigger_type (int rw) return t; } +/* Insert a new masked watchpoint at ADDR using the mask MASK. + RW may be hw_read for a read watchpoint, hw_write for a write watchpoint + or hw_access for an access watchpoint. Returns 0 on success and throws + an error on failure. */ + +static int +ppc_linux_insert_mask_watchpoint (struct target_ops *ops, CORE_ADDR addr, + CORE_ADDR mask, int rw) +{ + ptid_t ptid; + struct lwp_info *lp; + struct ppc_hw_breakpoint p; + + gdb_assert (have_ptrace_booke_interface ()); + + p.version = PPC_DEBUG_CURRENT_VERSION; + p.trigger_type = get_trigger_type (rw); + p.addr_mode = PPC_BREAKPOINT_MODE_MASK; + p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; + p.addr = addr; + p.addr2 = mask; + p.condition_value = 0; + + ALL_LWPS (lp, ptid) + booke_insert_point (&p, TIDGET (ptid)); + + return 0; +} + +/* Remove a masked watchpoint at ADDR with the mask MASK. + RW may be hw_read for a read watchpoint, hw_write for a write watchpoint + or hw_access for an access watchpoint. Returns 0 on success and throws + an error on failure. */ + +static int +ppc_linux_remove_mask_watchpoint (struct target_ops *ops, CORE_ADDR addr, + CORE_ADDR mask, int rw) +{ + ptid_t ptid; + struct lwp_info *lp; + struct ppc_hw_breakpoint p; + + gdb_assert (have_ptrace_booke_interface ()); + + p.version = PPC_DEBUG_CURRENT_VERSION; + p.trigger_type = get_trigger_type (rw); + p.addr_mode = PPC_BREAKPOINT_MODE_MASK; + p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; + p.addr = addr; + p.addr2 = mask; + p.condition_value = 0; + + ALL_LWPS (lp, ptid) + booke_remove_point (&p, TIDGET (ptid)); + + return 0; +} + /* Check whether we have at least one free DVC register. */ static int can_use_watchpoint_cond_accel (void) @@ -2224,6 +2282,26 @@ ppc_linux_watchpoint_addr_within_range (struct target_ops *target, return start <= addr + mask && start + length - 1 >= addr; } +/* Return the number of registers needed for a masked hardware watchpoint. */ + +static int +ppc_linux_masked_watch_num_registers (struct target_ops *target, + CORE_ADDR addr, CORE_ADDR mask) +{ + if (!have_ptrace_booke_interface () + || (booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK) == 0) + return -1; + else if ((mask & 0xC0000000) != 0xC0000000) + { + warning (_("The given mask covers kernel address space " + "and cannot be used.\n")); + + return -2; + } + else + return 2; +} + static void ppc_linux_store_inferior_registers (struct target_ops *ops, struct regcache *regcache, int regno) @@ -2438,11 +2516,14 @@ _initialize_ppc_linux_nat (void) t->to_region_ok_for_hw_watchpoint = ppc_linux_region_ok_for_hw_watchpoint; t->to_insert_watchpoint = ppc_linux_insert_watchpoint; t->to_remove_watchpoint = ppc_linux_remove_watchpoint; + t->to_insert_mask_watchpoint = ppc_linux_insert_mask_watchpoint; + t->to_remove_mask_watchpoint = ppc_linux_remove_mask_watchpoint; t->to_stopped_by_watchpoint = ppc_linux_stopped_by_watchpoint; t->to_stopped_data_address = ppc_linux_stopped_data_address; t->to_watchpoint_addr_within_range = ppc_linux_watchpoint_addr_within_range; t->to_can_accel_watchpoint_condition = ppc_linux_can_accel_watchpoint_condition; + t->to_masked_watch_num_registers = ppc_linux_masked_watch_num_registers; t->to_ranged_break_num_registers = ppc_linux_ranged_break_num_registers; t->to_read_description = ppc_linux_read_description; diff --git a/gdb/target.c b/gdb/target.c index 1e1c38e0ea6..9c522cb39a3 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -595,6 +595,8 @@ update_current_target (void) /* Do not inherit to_ranged_break_num_registers. */ INHERIT (to_insert_watchpoint, t); INHERIT (to_remove_watchpoint, t); + /* Do not inherit to_insert_mask_watchpoint. */ + /* Do not inherit to_remove_mask_watchpoint. */ INHERIT (to_stopped_data_address, t); INHERIT (to_have_steppable_watchpoint, t); INHERIT (to_have_continuable_watchpoint, t); @@ -602,6 +604,7 @@ update_current_target (void) INHERIT (to_watchpoint_addr_within_range, t); INHERIT (to_region_ok_for_hw_watchpoint, t); INHERIT (to_can_accel_watchpoint_condition, t); + /* Do not inherit to_masked_watch_num_registers. */ INHERIT (to_terminal_init, t); INHERIT (to_terminal_inferior, t); INHERIT (to_terminal_ours_for_output, t); @@ -3518,6 +3521,75 @@ target_verify_memory (const gdb_byte *data, CORE_ADDR memaddr, ULONGEST size) tcomplain (); } +/* The documentation for this function is in its prototype declaration in + target.h. */ + +int +target_insert_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, int rw) +{ + struct target_ops *t; + + for (t = current_target.beneath; t != NULL; t = t->beneath) + if (t->to_insert_mask_watchpoint != NULL) + { + int ret; + + ret = t->to_insert_mask_watchpoint (t, addr, mask, rw); + + if (targetdebug) + fprintf_unfiltered (gdb_stdlog, "\ +target_insert_mask_watchpoint (%s, %s, %d) = %d\n", + core_addr_to_string (addr), + core_addr_to_string (mask), rw, ret); + + return ret; + } + + return 1; +} + +/* The documentation for this function is in its prototype declaration in + target.h. */ + +int +target_remove_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, int rw) +{ + struct target_ops *t; + + for (t = current_target.beneath; t != NULL; t = t->beneath) + if (t->to_remove_mask_watchpoint != NULL) + { + int ret; + + ret = t->to_remove_mask_watchpoint (t, addr, mask, rw); + + if (targetdebug) + fprintf_unfiltered (gdb_stdlog, "\ +target_remove_mask_watchpoint (%s, %s, %d) = %d\n", + core_addr_to_string (addr), + core_addr_to_string (mask), rw, ret); + + return ret; + } + + return 1; +} + +/* The documentation for this function is in its prototype declaration + in target.h. */ + +int +target_masked_watch_num_registers (CORE_ADDR addr, CORE_ADDR mask) +{ + struct target_ops *t; + + for (t = current_target.beneath; t != NULL; t = t->beneath) + if (t->to_masked_watch_num_registers != NULL) + return t->to_masked_watch_num_registers (t, addr, mask); + + return -1; +} + /* The documentation for this function is in its prototype declaration in target.h. */ diff --git a/gdb/target.h b/gdb/target.h index 11380edf299..52e02761a03 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -459,6 +459,10 @@ struct target_ops int (*to_remove_watchpoint) (CORE_ADDR, int, int, struct expression *); int (*to_insert_watchpoint) (CORE_ADDR, int, int, struct expression *); + int (*to_insert_mask_watchpoint) (struct target_ops *, + CORE_ADDR, CORE_ADDR, int); + int (*to_remove_mask_watchpoint) (struct target_ops *, + CORE_ADDR, CORE_ADDR, int); int (*to_stopped_by_watchpoint) (void); int to_have_steppable_watchpoint; int to_have_continuable_watchpoint; @@ -472,6 +476,8 @@ struct target_ops int (*to_can_accel_watchpoint_condition) (CORE_ADDR, int, int, struct expression *); + int (*to_masked_watch_num_registers) (struct target_ops *, + CORE_ADDR, CORE_ADDR); void (*to_terminal_init) (void); void (*to_terminal_inferior) (void); void (*to_terminal_ours_for_output) (void); @@ -1362,6 +1368,20 @@ extern char *target_thread_name (struct thread_info *); #define target_remove_watchpoint(addr, len, type, cond) \ (*current_target.to_remove_watchpoint) (addr, len, type, cond) +/* Insert a new masked watchpoint at ADDR using the mask MASK. + RW may be hw_read for a read watchpoint, hw_write for a write watchpoint + or hw_access for an access watchpoint. Returns 0 for success, 1 if + masked watchpoints are not supported, -1 for failure. */ + +extern int target_insert_mask_watchpoint (CORE_ADDR, CORE_ADDR, int); + +/* Remove a masked watchpoint at ADDR with the mask MASK. + RW may be hw_read for a read watchpoint, hw_write for a write watchpoint + or hw_access for an access watchpoint. Returns 0 for success, non-zero + for failure. */ + +extern int target_remove_mask_watchpoint (CORE_ADDR, CORE_ADDR, int); + #define target_insert_hw_breakpoint(gdbarch, bp_tgt) \ (*current_target.to_insert_hw_breakpoint) (gdbarch, bp_tgt) @@ -1395,6 +1415,12 @@ extern int target_ranged_break_num_registers (void); #define target_can_accel_watchpoint_condition(addr, len, type, cond) \ (*current_target.to_can_accel_watchpoint_condition) (addr, len, type, cond) +/* Return number of debug registers needed for a masked watchpoint, + -1 if masked watchpoints are not supported or -2 if the given address + and mask combination cannot be used. */ + +extern int target_masked_watch_num_registers (CORE_ADDR addr, CORE_ADDR mask); + /* Target can execute in reverse? */ #define target_can_execute_reverse \ (current_target.to_can_execute_reverse ? \ |