+/* Implement the "breakpoint_hit" breakpoint_ops method for
+ ranged breakpoints. */
+
+static int
+breakpoint_hit_ranged_breakpoint (const struct bp_location *bl,
+ struct address_space *aspace,
+ CORE_ADDR bp_addr)
+{
+ return breakpoint_address_match_range (bl->pspace->aspace, bl->address,
+ bl->length, aspace, bp_addr);
+}
+
+/* Implement the "resources_needed" breakpoint_ops method for
+ ranged breakpoints. */
+
+static int
+resources_needed_ranged_breakpoint (const struct bp_location *bl)
+{
+ return target_ranged_break_num_registers ();
+}
+
+/* Implement the "print_it" breakpoint_ops method for
+ ranged breakpoints. */
+
+static enum print_stop_action
+print_it_ranged_breakpoint (struct breakpoint *b)
+{
+ struct bp_location *bl = b->loc;
+
+ gdb_assert (b->type == bp_hardware_breakpoint);
+
+ /* Ranged breakpoints have only one location. */
+ gdb_assert (bl && bl->next == NULL);
+
+ annotate_breakpoint (b->number);
+ if (b->disposition == disp_del)
+ ui_out_text (uiout, "\nTemporary ranged breakpoint ");
+ else
+ ui_out_text (uiout, "\nRanged breakpoint ");
+ if (ui_out_is_mi_like_p (uiout))
+ {
+ ui_out_field_string (uiout, "reason",
+ async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT));
+ ui_out_field_string (uiout, "disp", bpdisp_text (b->disposition));
+ }
+ ui_out_field_int (uiout, "bkptno", b->number);
+ ui_out_text (uiout, ", ");
+
+ return PRINT_SRC_AND_LOC;
+}
+
+/* Implement the "print_one" breakpoint_ops method for
+ ranged breakpoints. */
+
+static void
+print_one_ranged_breakpoint (struct breakpoint *b,
+ struct bp_location **last_loc)
+{
+ struct bp_location *bl = b->loc;
+ struct value_print_options opts;
+
+ /* Ranged breakpoints have only one location. */
+ gdb_assert (bl && bl->next == NULL);
+
+ get_user_print_options (&opts);
+
+ if (opts.addressprint)
+ /* We don't print the address range here, it will be printed later
+ by print_one_detail_ranged_breakpoint. */
+ ui_out_field_skip (uiout, "addr");
+ annotate_field (5);
+ print_breakpoint_location (b, bl);
+ *last_loc = bl;
+}
+
+/* Implement the "print_one_detail" breakpoint_ops method for
+ ranged breakpoints. */
+
+static void
+print_one_detail_ranged_breakpoint (const struct breakpoint *b,
+ struct ui_out *uiout)
+{
+ CORE_ADDR address_start, address_end;
+ struct bp_location *bl = b->loc;
+ struct ui_stream *stb = ui_out_stream_new (uiout);
+ struct cleanup *cleanup = make_cleanup_ui_out_stream_delete (stb);
+
+ gdb_assert (bl);
+
+ address_start = bl->address;
+ address_end = address_start + bl->length - 1;
+
+ ui_out_text (uiout, "\taddress range: ");
+ fprintf_unfiltered (stb->stream, "[%s, %s]",
+ print_core_address (bl->gdbarch, address_start),
+ print_core_address (bl->gdbarch, address_end));
+ ui_out_field_stream (uiout, "addr", stb);
+ ui_out_text (uiout, "\n");
+
+ do_cleanups (cleanup);
+}
+
+/* Implement the "print_mention" breakpoint_ops method for
+ ranged breakpoints. */
+
+static void
+print_mention_ranged_breakpoint (struct breakpoint *b)
+{
+ struct bp_location *bl = b->loc;
+
+ gdb_assert (bl);
+ gdb_assert (b->type == bp_hardware_breakpoint);
+
+ if (ui_out_is_mi_like_p (uiout))
+ return;
+
+ printf_filtered (_("Hardware assisted ranged breakpoint %d from %s to %s."),
+ b->number, paddress (bl->gdbarch, bl->address),
+ paddress (bl->gdbarch, bl->address + bl->length - 1));
+}
+
+/* Implement the "print_recreate" breakpoint_ops method for
+ ranged breakpoints. */
+
+static void
+print_recreate_ranged_breakpoint (struct breakpoint *b, struct ui_file *fp)
+{
+ fprintf_unfiltered (fp, "break-range %s, %s", b->addr_string,
+ b->addr_string_range_end);
+}
+
+/* The breakpoint_ops structure to be used in ranged breakpoints. */
+
+static struct breakpoint_ops ranged_breakpoint_ops =
+{
+ NULL, /* insert */
+ 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,
+ print_mention_ranged_breakpoint,
+ print_recreate_ranged_breakpoint
+};
+
+/* Find the address where the end of the breakpoint range should be
+ placed, given the SAL of the end of the range. This is so that if
+ the user provides a line number, the end of the range is set to the
+ last instruction of the given line. */
+
+static CORE_ADDR
+find_breakpoint_range_end (struct symtab_and_line sal)
+{
+ CORE_ADDR end;
+
+ /* If the user provided a PC value, use it. Otherwise,
+ find the address of the end of the given location. */
+ if (sal.explicit_pc)
+ end = sal.pc;
+ else
+ {
+ int ret;
+ CORE_ADDR start;
+
+ ret = find_line_pc_range (sal, &start, &end);
+ if (!ret)
+ error (_("Could not find location of the end of the range."));
+
+ /* find_line_pc_range returns the start of the next line. */
+ end--;
+ }
+
+ return end;
+}
+
+/* Implement the "break-range" CLI command. */
+
+static void
+break_range_command (char *arg, int from_tty)
+{
+ char *arg_start, *addr_string_start, *addr_string_end;
+ struct linespec_result canonical_start, canonical_end;
+ int bp_count, can_use_bp, length;
+ CORE_ADDR end;
+ struct breakpoint *b;
+ struct symtab_and_line sal_start, sal_end;
+ struct symtabs_and_lines sals_start, sals_end;
+ struct cleanup *cleanup_bkpt;
+
+ /* We don't support software ranged breakpoints. */
+ if (target_ranged_break_num_registers () < 0)
+ error (_("This target does not support hardware ranged breakpoints."));
+
+ bp_count = hw_breakpoint_used_count ();
+ bp_count += target_ranged_break_num_registers ();
+ can_use_bp = target_can_use_hardware_watchpoint (bp_hardware_breakpoint,
+ bp_count, 0);
+ if (can_use_bp < 0)
+ error (_("Hardware breakpoints used exceeds limit."));
+
+ if (arg == NULL || arg[0] == '\0')
+ error(_("No address range specified."));
+
+ sals_start.sals = NULL;
+ sals_start.nelts = 0;
+ init_linespec_result (&canonical_start);
+
+ while (*arg == ' ' || *arg == '\t')
+ arg++;
+
+ parse_breakpoint_sals (&arg, &sals_start, &canonical_start);
+
+ sal_start = sals_start.sals[0];
+ addr_string_start = canonical_start.canonical[0];
+ cleanup_bkpt = make_cleanup (xfree, addr_string_start);
+ xfree (sals_start.sals);
+ xfree (canonical_start.canonical);
+
+ if (arg[0] != ',')
+ error (_("Too few arguments."));
+ else if (sals_start.nelts == 0)
+ error (_("Could not find location of the beginning of the range."));
+ else if (sals_start.nelts != 1)
+ error (_("Cannot create a ranged breakpoint with multiple locations."));
+
+ resolve_sal_pc (&sal_start);
+
+ arg++; /* Skip the comma. */
+ while (*arg == ' ' || *arg == '\t')
+ arg++;
+
+ /* Parse the end location. */
+
+ sals_end.sals = NULL;
+ sals_end.nelts = 0;
+ init_linespec_result (&canonical_end);
+ arg_start = arg;
+
+ /* We call decode_line_1 directly here instead of using
+ parse_breakpoint_sals because we need to specify the start location's
+ symtab and line as the default symtab and line for the end of the
+ range. This makes it possible to have ranges like "foo.c:27, +14",
+ where +14 means 14 lines from the start location. */
+ sals_end = decode_line_1 (&arg, 1, sal_start.symtab, sal_start.line,
+ &canonical_end);
+
+ /* canonical_end can be NULL if it was of the form "*0xdeadbeef". */
+ if (canonical_end.canonical == NULL)
+ canonical_end.canonical = xcalloc (1, sizeof (char *));
+ /* Add the string if not present. */
+ if (arg_start != arg && canonical_end.canonical[0] == NULL)
+ canonical_end.canonical[0] = savestring (arg_start, arg - arg_start);
+
+ sal_end = sals_end.sals[0];
+ addr_string_end = canonical_end.canonical[0];
+ make_cleanup (xfree, addr_string_end);
+ xfree (sals_end.sals);
+ xfree (canonical_end.canonical);
+
+ if (sals_end.nelts == 0)
+ error (_("Could not find location of the end of the range."));
+ else if (sals_end.nelts != 1)
+ error (_("Cannot create a ranged breakpoint with multiple locations."));
+
+ resolve_sal_pc (&sal_end);
+
+ end = find_breakpoint_range_end (sal_end);
+ if (sal_start.pc > end)
+ error (_("Invalid address range, end preceeds start."));
+
+ length = end - sal_start.pc + 1;
+ if (length < 0)
+ /* Length overflowed. */
+ error (_("Address range too large."));
+ else if (length == 1)
+ {
+ /* This range is simple enough to be handled by
+ the `hbreak' command. */
+ hbreak_command (addr_string_start, 1);
+
+ do_cleanups (cleanup_bkpt);
+
+ return;
+ }
+
+ /* Now set up the breakpoint. */
+ b = set_raw_breakpoint (get_current_arch (), sal_start,
+ bp_hardware_breakpoint);
+ set_breakpoint_count (breakpoint_count + 1);
+ b->number = breakpoint_count;
+ b->disposition = disp_donttouch;
+ b->addr_string = addr_string_start;
+ b->addr_string_range_end = addr_string_end;
+ b->ops = &ranged_breakpoint_ops;
+ b->loc->length = length;
+
+ discard_cleanups (cleanup_bkpt);
+
+ mention (b);
+ observer_notify_breakpoint_created (b);
+ update_global_location_list (1);
+}
+
+/* Return non-zero if EXP is verified as constant. Returned zero
+ means EXP is variable. Also the constant detection may fail for
+ some constant expressions and in such case still falsely return
+ zero. */
+static int
+watchpoint_exp_is_const (const struct expression *exp)
+{
+ int i = exp->nelts;
+
+ while (i > 0)
+ {
+ int oplenp, argsp;
+
+ /* We are only interested in the descriptor of each element. */
+ operator_length (exp, i, &oplenp, &argsp);
+ i -= oplenp;
+
+ switch (exp->elts[i].opcode)
+ {
+ case BINOP_ADD:
+ case BINOP_SUB:
+ case BINOP_MUL:
+ case BINOP_DIV:
+ case BINOP_REM:
+ case BINOP_MOD:
+ case BINOP_LSH:
+ case BINOP_RSH:
+ case BINOP_LOGICAL_AND:
+ case BINOP_LOGICAL_OR:
+ case BINOP_BITWISE_AND:
+ case BINOP_BITWISE_IOR:
+ case BINOP_BITWISE_XOR:
+ case BINOP_EQUAL:
+ case BINOP_NOTEQUAL:
+ case BINOP_LESS:
+ case BINOP_GTR:
+ case BINOP_LEQ:
+ case BINOP_GEQ:
+ case BINOP_REPEAT:
+ case BINOP_COMMA:
+ case BINOP_EXP:
+ case BINOP_MIN:
+ case BINOP_MAX:
+ case BINOP_INTDIV:
+ case BINOP_CONCAT:
+ case BINOP_IN:
+ case BINOP_RANGE:
+ case TERNOP_COND:
+ case TERNOP_SLICE:
+ case TERNOP_SLICE_COUNT:
+
+ case OP_LONG:
+ case OP_DOUBLE:
+ case OP_DECFLOAT:
+ case OP_LAST:
+ case OP_COMPLEX:
+ case OP_STRING:
+ case OP_BITSTRING:
+ case OP_ARRAY:
+ case OP_TYPE:
+ case OP_NAME:
+ case OP_OBJC_NSSTRING:
+
+ case UNOP_NEG:
+ case UNOP_LOGICAL_NOT:
+ case UNOP_COMPLEMENT:
+ case UNOP_ADDR:
+ case UNOP_HIGH:
+ /* Unary, binary and ternary operators: We have to check
+ their operands. If they are constant, then so is the
+ result of that operation. For instance, if A and B are
+ determined to be constants, then so is "A + B".
+
+ UNOP_IND is one exception to the rule above, because the
+ value of *ADDR is not necessarily a constant, even when
+ ADDR is. */
+ break;
+
+ case OP_VAR_VALUE:
+ /* Check whether the associated symbol is a constant.
+
+ We use SYMBOL_CLASS rather than TYPE_CONST because it's
+ possible that a buggy compiler could mark a variable as
+ constant even when it is not, and TYPE_CONST would return
+ true in this case, while SYMBOL_CLASS wouldn't.
+
+ We also have to check for function symbols because they
+ are always constant. */
+ {
+ struct symbol *s = exp->elts[i + 2].symbol;
+
+ if (SYMBOL_CLASS (s) != LOC_BLOCK
+ && SYMBOL_CLASS (s) != LOC_CONST
+ && SYMBOL_CLASS (s) != LOC_CONST_BYTES)
+ return 0;
+ break;
+ }
+
+ /* The default action is to return 0 because we are using
+ the optimistic approach here: If we don't know something,
+ then it is not a constant. */
+ default:
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Implement the "insert" breakpoint_ops method for hardware watchpoints. */
+
+static int
+insert_watchpoint (struct bp_location *bl)
+{
+ int length = bl->owner->exact? 1 : bl->length;
+
+ return target_insert_watchpoint (bl->address, length, bl->watchpoint_type,
+ bl->owner->cond_exp);
+}
+
+/* Implement the "remove" breakpoint_ops method for hardware watchpoints. */
+
+static int
+remove_watchpoint (struct bp_location *bl)
+{
+ int length = bl->owner->exact? 1 : bl->length;
+
+ return target_remove_watchpoint (bl->address, length, bl->watchpoint_type,
+ bl->owner->cond_exp);
+}
+
+/* Implement the "resources_needed" breakpoint_ops method for
+ hardware watchpoints. */
+
+static int
+resources_needed_watchpoint (const struct bp_location *bl)
+{
+ int length = bl->owner->exact? 1 : bl->length;
+
+ 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 =
+{
+ insert_watchpoint,
+ remove_watchpoint,
+ NULL, /* breakpoint_hit */
+ resources_needed_watchpoint,
+ works_in_software_mode_watchpoint,
+ 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 "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;
+}
+