From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 3751 invoked by alias); 23 Dec 2010 19:07:51 -0000 Received: (qmail 3724 invoked by uid 22791); 23 Dec 2010 19:07:43 -0000 X-SWARE-Spam-Status: No, hits=-2.2 required=5.0 tests=AWL,BAYES_00,TW_EG,TW_XF,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from e24smtp02.br.ibm.com (HELO e24smtp02.br.ibm.com) (32.104.18.86) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 23 Dec 2010 19:07:35 +0000 Received: from mailhub3.br.ibm.com (mailhub3.br.ibm.com [9.18.232.110]) by e24smtp02.br.ibm.com (8.14.4/8.13.1) with ESMTP id oBNJQDGV004447 for ; Thu, 23 Dec 2010 17:26:13 -0200 Received: from d24av04.br.ibm.com (d24av04.br.ibm.com [9.8.31.97]) by mailhub3.br.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id oBNJEtrM2883764 for ; Thu, 23 Dec 2010 17:14:55 -0200 Received: from d24av04.br.ibm.com (loopback [127.0.0.1]) by d24av04.br.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id oBNJ7Ukg018522 for ; Thu, 23 Dec 2010 17:07:30 -0200 Received: from [9.8.1.74] ([9.8.1.74]) by d24av04.br.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id oBNJ7TGe018499; Thu, 23 Dec 2010 17:07:29 -0200 Subject: Re: [patch 1/3] Implement support for PowerPC BookE masked watchpoints From: Thiago Jung Bauermann To: gdb-patches ml Cc: Jan Kratochvil , Joel Brobecker , Eli Zaretskii In-Reply-To: <1290548544.3164.37.camel@hactar> References: <1290548544.3164.37.camel@hactar> Content-Type: text/plain; charset="UTF-8" Date: Thu, 23 Dec 2010 19:31:00 -0000 Message-ID: <1293131247.14239.35.camel@hactar> Mime-Version: 1.0 Content-Transfer-Encoding: 7bit X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-12/txt/msg00441.txt.bz2 Hi, This is the latest version of this patch, refreshed to apply on top of the ranged watchpoints patch. I also got rid of the hw_point_flag enum, which was a source of criticism before, at the expense of one additional target method per new type of special breakpoint or watchpoint: target_masked_watch_num_extra_regs and target_ranged_break_num_extra_regs (for the upcoming new version of the ranged breakpoints patch). The documentation part of the patch was already reviewed and approved. No regressions on ppc-linux, ppc64-linux or i386-linux. Ok to commit? To ease review, here are the changes on parsing the command line in watch_command_1, with a diff ignoring indentation change: ---- 8< ---- 8< ---- @@ -8219,64 +8463,93 @@ watch_command_1 (char *arg, int accessflag, int from_tty, int reg_cnt = 0; 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 hw_wp_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; - - /* Go backwards in the parameters list. Skip the last parameter. - If we're expecting a 'thread ' parameter, this should - be the thread identifier. */ + /* 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; - /* Points end_tok to the beginning of the last token. */ - id_tok_start = tok + 1; - - /* Go backwards in the parameters list. Skip one more parameter. - If we're expecting a 'thread ' parameter, we should - reach a "thread" token. */ + /* 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--; - - /* Move the pointer forward to skip the whitespace and - calculate the length of the token. */ tok++; - toklen = end_tok - tok; + toklen = end_tok - tok + 1; - if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0) + 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; + if (thread != -1) + error(_("You can specify only one thread.")); + /* Extract the thread ID from the next token. */ - thread = strtol (id_tok_start, &endp, 0); + thread = strtol (value_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); + error (_("Invalid thread ID specification %s."), value_start); /* 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; + + /* Does the target support masked watchpoints? */ + if (!target_masked_watch_num_extra_regs () < 0) + error (_("\ +This target does not support the usage of masks with hardware watchpoints.")); - /* Truncate the string and get rid of the thread - parameter before the parameter list is parsed by the - evaluate_expression() function. */ + if (use_mask) + error(_("You can specify only one mask.")); + + use_mask = 1; + + mark = value_mark (); + mask_value = parse_to_comma_and_eval (&value_start); + hw_wp_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'; } } ---- >8 ---- >8 ---- -- []'s Thiago Jung Bauermann IBM Linux Technology Center 2010-12-23 Sergio Durigan Junior Thiago Jung Bauermann Implement support for PowerPC BookE masked watchpoints. gdb/ *NEWS: Mention masked watchpoint support. Create "Changed commands" sections and populate it based on other NEWS items. * breakpoint.h (struct ui_out): New opaque declaration. (struct breakpoint_ops) , : New methods. Initialize them to NULL in all existing breakpoint_ops instances. (struct breakpoint_ops) : Add OLD_VAL parameter. Update all implementations of the method. (struct breakpoint) : New field. * breakpoint.c (is_masked_watchpoint): Add prototype. (update_watchpoint): 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. (print_one_breakpoint_location): Call breakpoint's breakpoint_ops.print_one_detail if available. (insert_masked_watchpoint, remove_masked_watchpoint) (extra_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_extra_regs): New functions. (_initialize_ppc_linux_nat): Initialize to_insert_mask_watchpoint, to_remove_mask_watchpoint and to_masked_watch_num_extra_regs. * target.c (update_current_target): Mention to_insert_mask_watchpoint, to_remove_mask_watchpoint, and to_masked_watch_num_extra_regs. (target_insert_mask_watchpoint, target_remove_mask_watchpoint) (target_masked_watch_num_extra_regs): New functions. * target.h (struct target_ops) , , : New methods. (target_insert_mask_watchpoint, target_remove_mask_watchpoint) (target_masked_watch_num_extra_regs): Add prototypes. gdb/doc/ * gdb.texinfo (Set Watchpoints): Document mask parameter. (PowerPC Embedded): Document masked watchpoints. diff --git a/gdb/NEWS b/gdb/NEWS index 91f1835..c4a9c79 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -106,6 +106,14 @@ libthread_db library with the "set libthread-db-search-path" command. See the user manual for more details on this command. +* When locally debugging programs on PowerPC BookE processors running + a Linux kernel version 2.6.34 or later, GDB supports masked hardware + watchpoints, which specifiy 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. + * New features in the GDB remote stub, GDBserver ** GDBserver is now supported on PowerPC LynxOS (versions 4.x and 5.x), @@ -122,6 +130,19 @@ ** The --map-info flag lists all known core mappings. +* 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. + +watch -location EXPRESSION +watch -l EXPRESSION + The "watch" command now accepts an optional "-location" argument. + When used, this causes GDB to watch the memory referred to by the + expression. Such a watchpoint is never deleted due to it going out + of scope. + *** Changes in GDB 7.2 * Shared library support for remote targets by default diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c index 3a2a266..cd87a47 100644 --- a/gdb/ada-lang.c +++ b/gdb/ada-lang.c @@ -10793,7 +10793,7 @@ print_recreate_exception (enum exception_catchpoint_kind ex, /* Virtual table for "catch exception" breakpoints. */ static enum print_stop_action -print_it_catch_exception (struct breakpoint *b) +print_it_catch_exception (struct breakpoint *b, const struct value *old_val) { return print_it_exception (ex_catch_exception, b); } @@ -10822,8 +10822,10 @@ static struct breakpoint_ops catch_exception_breakpoint_ops = NULL, /* remove */ NULL, /* breakpoint_hit */ NULL, /* extra_resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_exception, print_one_catch_exception, + NULL, /* print_one_detail */ print_mention_catch_exception, print_recreate_catch_exception }; @@ -10831,7 +10833,8 @@ static struct breakpoint_ops catch_exception_breakpoint_ops = /* Virtual table for "catch exception unhandled" breakpoints. */ static enum print_stop_action -print_it_catch_exception_unhandled (struct breakpoint *b) +print_it_catch_exception_unhandled (struct breakpoint *b, + const struct value *old_val) { return print_it_exception (ex_catch_exception_unhandled, b); } @@ -10861,8 +10864,10 @@ static struct breakpoint_ops catch_exception_unhandled_breakpoint_ops = { NULL, /* remove */ NULL, /* breakpoint_hit */ NULL, /* extra_resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_exception_unhandled, print_one_catch_exception_unhandled, + NULL, /* print_one_detail */ print_mention_catch_exception_unhandled, print_recreate_catch_exception_unhandled }; @@ -10870,7 +10875,7 @@ static struct breakpoint_ops catch_exception_unhandled_breakpoint_ops = { /* Virtual table for "catch assert" breakpoints. */ static enum print_stop_action -print_it_catch_assert (struct breakpoint *b) +print_it_catch_assert (struct breakpoint *b, const struct value *old_val) { return print_it_exception (ex_catch_assert, b); } @@ -10898,8 +10903,10 @@ static struct breakpoint_ops catch_assert_breakpoint_ops = { NULL, /* remove */ NULL, /* breakpoint_hit */ NULL, /* extra_resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_assert, print_one_catch_assert, + NULL, /* print_one_detail */ print_mention_catch_assert, print_recreate_catch_assert }; diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 9c10b30..9213289 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -219,6 +219,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) \ @@ -1402,7 +1404,14 @@ update_watchpoint (struct breakpoint *b, int reparse) reg_cnt = can_use_hardware_watchpoint (val_chain, b->exact); if (!reg_cnt) - b->type = bp_watchpoint; + { + if (b->type == bp_hardware_watchpoint + && b->ops && b->ops->works_in_software_mode + && !b->ops->works_in_software_mode (b)) + error (_("This watchpoint cannot be used in software mode.")); + else + b->type = bp_watchpoint; + } else { int i, other_type_used, target_resources_ok; @@ -1425,7 +1434,17 @@ update_watchpoint (struct breakpoint *b, int reparse) target_resources_ok = target_can_use_hardware_watchpoint (bp_hardware_watchpoint, i + reg_cnt, other_type_used) if (target_resources_ok <= 0) - b->type = bp_watchpoint; + { + if (orig_type == bp_hardware_watchpoint + && b->ops && b->ops->works_in_software_mode + && !b->ops->works_in_software_mode (b)) + { + b->type = bp_hardware_watchpoint; + error (_("This watchpoint cannot be used in software mode.")); + } + else + b->type = bp_watchpoint; + } else b->type = bp_hardware_watchpoint; } @@ -3433,7 +3452,7 @@ print_bp_stop_message (bpstat bs) print_it_typical. */ /* FIXME: how breakpoint can ever be NULL here? */ if (b != NULL && b->ops != NULL && b->ops->print_it != NULL) - return b->ops->print_it (b); + return b->ops->print_it (b, bs->old_val); else return print_it_typical (bs); } @@ -3569,15 +3588,30 @@ 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; - } + { + CORE_ADDR newaddr, start; + + if (is_masked_watchpoint (loc->owner)) + { + newaddr = addr & loc->owner->hw_wp_mask; + start = loc->address & loc->owner->hw_wp_mask; + } + else + { + newaddr = addr; + start = loc->address; + } + + /* Exact match not required. Within range is + sufficient. */ + if (target_watchpoint_addr_within_range (¤t_target, + newaddr, start, + loc->length)) + { + b->watchpoint_triggered = watch_triggered_yes; + break; + } + } } return 1; @@ -3691,6 +3725,11 @@ watchpoint_check (void *p) b->val_valid = 1; return WP_VALUE_CHANGED; } + else 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; else { /* Nothing changed. */ @@ -4774,9 +4813,12 @@ print_one_breakpoint_location (struct breakpoint *b, ui_out_field_int (uiout, "task", b->task); } } - + ui_out_text (uiout, "\n"); - + + if (!part_of_multiple && b->ops && b->ops->print_one_detail) + b->ops->print_one_detail (b, uiout); + if (!part_of_multiple && b->static_trace_marker_id) { gdb_assert (b->type == bp_static_tracepoint); @@ -5957,7 +5999,7 @@ breakpoint_hit_catch_fork (struct breakpoint *b) /* Implement the "print_it" breakpoint_ops method for fork catchpoints. */ static enum print_stop_action -print_it_catch_fork (struct breakpoint *b) +print_it_catch_fork (struct breakpoint *b, const struct value *old_val) { annotate_catchpoint (b->number); printf_filtered (_("\nCatchpoint %d (forked process %d), "), @@ -6016,8 +6058,10 @@ static struct breakpoint_ops catch_fork_breakpoint_ops = remove_catch_fork, breakpoint_hit_catch_fork, NULL, /* extra_resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_fork, print_one_catch_fork, + NULL, /* print_one_detail */ print_mention_catch_fork, print_recreate_catch_fork }; @@ -6050,7 +6094,7 @@ breakpoint_hit_catch_vfork (struct breakpoint *b) /* Implement the "print_it" breakpoint_ops method for vfork catchpoints. */ static enum print_stop_action -print_it_catch_vfork (struct breakpoint *b) +print_it_catch_vfork (struct breakpoint *b, const struct value *old_val) { annotate_catchpoint (b->number); printf_filtered (_("\nCatchpoint %d (vforked process %d), "), @@ -6108,8 +6152,10 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops = remove_catch_vfork, breakpoint_hit_catch_vfork, NULL, /* extra_resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_vfork, print_one_catch_vfork, + NULL, /* print_one_detail */ print_mention_catch_vfork, print_recreate_catch_vfork }; @@ -6229,7 +6275,7 @@ breakpoint_hit_catch_syscall (struct breakpoint *b) catchpoints. */ static enum print_stop_action -print_it_catch_syscall (struct breakpoint *b) +print_it_catch_syscall (struct breakpoint *b, const struct value *old_val) { /* These are needed because we want to know in which state a syscall is. It can be in the TARGET_WAITKIND_SYSCALL_ENTRY @@ -6390,8 +6436,10 @@ static struct breakpoint_ops catch_syscall_breakpoint_ops = remove_catch_syscall, breakpoint_hit_catch_syscall, NULL, /* extra_resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_syscall, print_one_catch_syscall, + NULL, /* print_one_detail */ print_mention_catch_syscall, print_recreate_catch_syscall }; @@ -6493,7 +6541,7 @@ breakpoint_hit_catch_exec (struct breakpoint *b) } static enum print_stop_action -print_it_catch_exec (struct breakpoint *b) +print_it_catch_exec (struct breakpoint *b, const struct value *old_val) { annotate_catchpoint (b->number); printf_filtered (_("\nCatchpoint %d (exec'd %s), "), b->number, @@ -6544,8 +6592,10 @@ static struct breakpoint_ops catch_exec_breakpoint_ops = remove_catch_exec, breakpoint_hit_catch_exec, NULL, /* extra_resources_needed */ + NULL, /* works_in_software_mode */ print_it_catch_exec, print_one_catch_exec, + NULL, /* print_one_detail */ print_mention_catch_exec, print_recreate_catch_exec }; @@ -8190,12 +8240,206 @@ static struct breakpoint_ops watchpoint_breakpoint_ops = remove_watchpoint, NULL, /* breakpoint_hit */ extra_resources_needed_watchpoint, + NULL, /* works_in_software_mode */ NULL, /* print_it */ NULL, /* print_one */ + NULL, /* print_one_detail */ NULL, /* print_mention */ 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 "extra_resources_needed" breakpoint_ops method for + masked hardware watchpoints. */ + +static int +extra_resources_needed_masked_watchpoint (const struct breakpoint *b, + const struct bp_location *bl) +{ + return target_masked_watch_num_extra_regs (); +} + +/* Implement the "works_in_software_mode_masked_watchpoint" 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, const struct value *old_val) +{ + struct ui_stream *stb; + struct cleanup *old_chain; + + stb = ui_out_stream_new (uiout); + old_chain = make_cleanup_ui_out_stream_delete (stb); + + 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 (old_val != NULL) + annotate_watchpoint (b->number); + 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, _("\nCheck the underlying instruction \ +at PC for address and value related to this watchpoint trigger.\n")); + ui_out_text (uiout, "\n"); + + do_cleanups (old_chain); + + /* 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) +{ + ui_out_text (uiout, "\tmask "); + + /* I don't know whether it's possible to get here without a b->loc, + but we can handle the situation. */ + if (b->loc) + ui_out_field_core_addr (uiout, "mask", b->loc->gdbarch, b->hw_wp_mask); + else + ui_out_field_string (uiout, "mask", core_addr_to_string (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); +} + +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 */ + extra_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 +}; + +/* Tells 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) */ @@ -8210,8 +8454,8 @@ 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; int i, other_type_used, target_resources_ok = 0; @@ -8219,66 +8463,95 @@ watch_command_1 (char *arg, int accessflag, int from_tty, int reg_cnt = 0; 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 hw_wp_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 ' 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 ' 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--; + /* Does the target support masked watchpoints? */ + if (!target_masked_watch_num_extra_regs () < 0) + error (_("\ +This target does not support the usage of masks with hardware watchpoints.")); - /* Move the pointer forward to skip the whitespace and - calculate the length of the token. */ - tok++; - toklen = end_tok - tok; + if (use_mask) + error(_("You can specify only one mask.")); - 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 - parameter before the parameter list is parsed by the - evaluate_expression() function. */ - *tok = '\0'; - } + use_mask = 1; + + mark = value_mark (); + mask_value = parse_to_comma_and_eval (&value_start); + hw_wp_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. */ @@ -8356,6 +8629,11 @@ watch_command_1 (char *arg, int accessflag, int from_tty, error (_("Expression cannot be implemented with read/access watchpoint.")); if (reg_cnt != 0) { + /* If we are going to use masks, then we may need more + slots in order to use the hardware watchpoint. */ + if (use_mask) + reg_cnt += target_masked_watch_num_extra_regs (); + i = hw_watchpoint_used_count (bp_type, &other_type_used); target_resources_ok = target_can_use_hardware_watchpoint (bp_type, i + reg_cnt, @@ -8367,10 +8645,19 @@ watch_command_1 (char *arg, int accessflag, int from_tty, error (_("Target can only support one kind of HW watchpoint at a time.")); } - /* Change the type of breakpoint to an ordinary watchpoint if a hardware - watchpoint could not be set. */ if (!reg_cnt || target_resources_ok <= 0) - bp_type = bp_watchpoint; + { + if (use_mask && !reg_cnt) + error (_("Cannot use masks with the given expression.")); + else if (use_mask && target_resources_ok <= 0) + error (_("\ +Cannot use masks without enough available hardware watchpoints (need %d)."), + reg_cnt); + else + /* Change the type of breakpoint to an ordinary watchpoint if a + hardware watchpoint could not be set. */ + bp_type = bp_watchpoint; + } frame = block_innermost_frame (exp_valid_block); @@ -8438,7 +8725,14 @@ watch_command_1 (char *arg, int accessflag, int from_tty, 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 = hw_wp_mask; + b->ops = &masked_watchpoint_breakpoint_ops; + } + else + b->ops = &watchpoint_breakpoint_ops; /* Use an exact watchpoint only when there's one memory region to be watched, which needs only one debug register. */ @@ -8890,7 +9184,8 @@ catch_exec_command_1 (char *arg, int from_tty, } static enum print_stop_action -print_exception_catchpoint (struct breakpoint *b) +print_it_exception_catchpoint (struct breakpoint *b, + const struct value *old_val) { int bp_temp, bp_throw; @@ -8980,8 +9275,10 @@ static struct breakpoint_ops gnu_v3_exception_catchpoint_ops = { NULL, /* remove */ NULL, /* breakpoint_hit */ NULL, /* extra_resources_needed */ - print_exception_catchpoint, + NULL, /* works_in_software_mode */ + print_it_exception_catchpoint, print_one_exception_catchpoint, + NULL, /* print_one_detail */ print_mention_exception_catchpoint, print_recreate_exception_catchpoint }; diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 8e68532..74565c0 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -27,6 +27,7 @@ struct value; struct block; struct breakpoint_object; +struct ui_out; /* This is the maximum number of bytes a breakpoint instruction can take. Feel free to increase it. It's just used in a few places to size @@ -382,13 +383,33 @@ struct breakpoint_ops int (*extra_resources_needed) (const struct breakpoint *, 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 *); + enum print_stop_action (*print_it) (struct breakpoint *, + const struct value *old_val); /* Display information about this breakpoint, for "info breakpoints". */ void (*print_one) (struct breakpoint *, struct bp_location **); + /* Display extra information about this breakpoint, below the normal + breakpoint description in "info breakpoints". + + In the example below, the line with "mask 0xffffff00" was printed + by print_one_detail_ranged_watchpoint. + + (gdb) info breakpoints + Num Type Disp Enb Address What + 2 hw watchpoint keep y *0x10094420 + mask 0xffffff00 + + */ + void (*print_one_detail) (const struct breakpoint *, struct ui_out *); + /* Display information about this breakpoint after setting it (roughly speaking; this is called from "mention"). */ void (*print_mention) (struct breakpoint *); @@ -597,6 +618,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/gdb.texinfo b/gdb/doc/gdb.texinfo index cef320e..b7ab17b 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -3745,7 +3745,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 @@ -3756,12 +3756,17 @@ 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 with Hardware Watchpoints. +The @code{@r{[}mask @var{maskvalue}@r{]}} argument allows creation +of masked watchpoints, if the current architecture supports this +feature. (Currently, this is only available on PowerPC Embedded +architecture, see @ref{PowerPC Embedded}.) + Ordinarily a watchpoint respects the scope of variables in @var{expr} (see below). The @code{-location} argument tells @value{GDBN} to instead watch the memory referred to by @var{expr}. In this case, @@ -3772,12 +3777,12 @@ result does not have an address, then @value{GDBN} will print an error. @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. @@ -18748,6 +18753,23 @@ 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. + +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. + +To set a masked watchpoint in @value{GDBN}, use the @code{mask} argument in +the @code{watch} command (@pxref{Set Watchpoints}), as in: + +@smallexample +(@value{GDBP}) watch *0xdeadbeef mask 0xffffff00 +@end smallexample + @value{GDBN} provides the following PowerPC-specific commands: @table @code diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c index 7c853a9..7806df0 100644 --- a/gdb/ppc-linux-nat.c +++ b/gdb/ppc-linux-nat.c @@ -1692,6 +1692,59 @@ get_trigger_type (int rw) return t; } +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 ()); + + if ((mask & 0xC0000000) != 0xC0000000) + error (_("\ +The given mask covers kernel address space and cannot be used.\n\ +You have to delete the masked watchpoint to continue the debugging session.")); + + 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; +} + +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) @@ -2158,6 +2211,14 @@ ppc_linux_watchpoint_addr_within_range (struct target_ops *target, return start <= addr + mask && start + length - 1 >= addr; } +static int +ppc_linux_masked_watch_num_extra_regs (struct target_ops *target) +{ + return ((have_ptrace_booke_interface () + && booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK)? + 1 : -1); +} + static void ppc_linux_store_inferior_registers (struct target_ops *ops, struct regcache *regcache, int regno) @@ -2371,10 +2432,13 @@ _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_extra_regs = ppc_linux_masked_watch_num_extra_regs; t->to_read_description = ppc_linux_read_description; t->to_auxv_parse = ppc_linux_auxv_parse; diff --git a/gdb/target.c b/gdb/target.c index dd976c9..0283425 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -606,6 +606,8 @@ update_current_target (void) INHERIT (to_remove_hw_breakpoint, t); 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); @@ -613,6 +615,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_extra_regs. */ INHERIT (to_terminal_init, t); INHERIT (to_terminal_inferior, t); INHERIT (to_terminal_ours_for_output, t); @@ -3334,6 +3337,64 @@ target_verify_memory (const gdb_byte *data, CORE_ADDR memaddr, ULONGEST size) tcomplain (); } +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 return_minus_one (); +} + +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 return_minus_one (); +} + +int +target_masked_watch_num_extra_regs (void) +{ + struct target_ops *t; + + for (t = current_target.beneath; t != NULL; t = t->beneath) + if (t->to_masked_watch_num_extra_regs != NULL) + return t->to_masked_watch_num_extra_regs (t); + + return return_minus_one (); +} + static void debug_to_prepare_to_store (struct regcache *regcache) { diff --git a/gdb/target.h b/gdb/target.h index 2ef5c56..d956e98 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -446,6 +446,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; @@ -459,6 +463,7 @@ struct target_ops int (*to_can_accel_watchpoint_condition) (CORE_ADDR, int, int, struct expression *); + int (*to_masked_watch_num_extra_regs) (struct target_ops *); void (*to_terminal_init) (void); void (*to_terminal_inferior) (void); void (*to_terminal_ours_for_output) (void); @@ -1336,6 +1341,11 @@ extern char *normal_pid_to_str (ptid_t ptid); #define target_remove_watchpoint(addr, len, type, cond) \ (*current_target.to_remove_watchpoint) (addr, len, type, cond) +/* Hardware watchpoints with an associated address mask. */ + +extern int target_insert_mask_watchpoint (CORE_ADDR, CORE_ADDR, int); +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) @@ -1364,6 +1374,11 @@ extern char *normal_pid_to_str (ptid_t ptid); #define target_can_accel_watchpoint_condition(addr, len, type, cond) \ (*current_target.to_can_accel_watchpoint_condition) (addr, len, type, cond) +/* Return number of extra debug registers needed for a masked watchpoint, + or -1 if masked watchpoints are not supported. */ + +extern int target_masked_watch_num_extra_regs (void); + /* Target can execute in reverse? */ #define target_can_execute_reverse \ (current_target.to_can_execute_reverse ? \