static void catch_command (char *, int);
-static void watch_command (char *, int);
-
static int can_use_hardware_watchpoint (struct value *);
static void break_command_1 (char *, int, int);
static int breakpoint_1 (int, int, int (*) (const struct breakpoint *));
-static bpstat bpstat_alloc (const struct bp_location *, bpstat);
-
static int breakpoint_cond_eval (void *);
static void cleanup_executing_breakpoints (void *);
static void thbreak_command (char *, int);
-static void watch_command_1 (char *, int, int);
-
-static void rwatch_command (char *, int);
-
-static void awatch_command (char *, int);
-
static void do_enable_breakpoint (struct breakpoint *, enum bpdisp);
static void stop_command (char *arg, int from_tty);
CORE_ADDR pc);
static void free_bp_location (struct bp_location *loc);
+static void incref_bp_location (struct bp_location *loc);
+static void decref_bp_location (struct bp_location **loc);
static struct bp_location *allocate_bp_location (struct breakpoint *bpt);
static void update_global_location_list_nothrow (int);
-static int bpstat_remove_bp_location_callback (struct thread_info *th,
- void *data);
-
static int is_hardware_watchpoint (const struct breakpoint *bpt);
static int is_watchpoint (const struct breakpoint *bpt);
static void trace_pass_command (char *, int);
+/* Assuming we're creating a static tracepoint, does S look like a
+ static tracepoint marker spec ("-m MARKER_ID")? */
+#define is_marker_spec(s) \
+ (s != NULL && strncmp (s, "-m", 2) == 0 && ((s)[2] == ' ' || (s)[2] == '\t'))
+
/* A reference-counted struct command_line. This lets multiple
breakpoints share a single command list. */
struct counted_command_line
{
/* NOTE: the following values are a part of MI protocol and represent
values of 'disp' field returned when inferior stops at a breakpoint. */
- static char *bpdisps[] = {"del", "dstp", "dis", "keep"};
+ static const char * const bpdisps[] = {"del", "dstp", "dis", "keep"};
return bpdisps[(int) disp];
}
static struct cmd_list_element *breakpoint_set_cmdlist;
static struct cmd_list_element *breakpoint_show_cmdlist;
-static struct cmd_list_element *save_cmdlist;
+struct cmd_list_element *save_cmdlist;
/* Return whether a breakpoint is an active enabled breakpoint. */
static int
int
is_tracepoint (const struct breakpoint *b)
{
- return (b->type == bp_tracepoint || b->type == bp_fast_tracepoint);
+ return (b->type == bp_tracepoint
+ || b->type == bp_fast_tracepoint
+ || b->type == bp_static_tracepoint);
}
/* A helper function that validsates that COMMANDS are valid for a
if (c->control_type == while_stepping_control)
{
if (b->type == bp_fast_tracepoint)
- error (_("The 'while-stepping' command cannot be used for fast tracepoint"));
+ error (_("\
+The 'while-stepping' command cannot be used for fast tracepoint"));
+ else if (b->type == bp_static_tracepoint)
+ error (_("\
+The 'while-stepping' command cannot be used for static tracepoint"));
if (while_stepping)
error (_("The 'while-stepping' command can be used only once"));
}
}
+/* Return a vector of all the static tracepoints set at ADDR. The
+ caller is responsible for releasing the vector. */
+
+VEC(breakpoint_p) *
+static_tracepoints_here (CORE_ADDR addr)
+{
+ struct breakpoint *b;
+ VEC(breakpoint_p) *found = 0;
+ struct bp_location *loc;
+
+ ALL_BREAKPOINTS (b)
+ if (b->type == bp_static_tracepoint)
+ {
+ for (loc = b->loc; loc; loc = loc->next)
+ if (loc->address == addr)
+ VEC_safe_push(breakpoint_p, found, b);
+ }
+
+ return found;
+}
+
/* Set the command list of B to COMMANDS. If breakpoint is tracepoint,
validate that only allowed commands are included.
*/
|| bpt->type == bp_watchpoint);
}
-/* Find the current value of a watchpoint on EXP. Return the value in
- *VALP and *RESULTP and the chain of intermediate and final values
- in *VAL_CHAIN. RESULTP and VAL_CHAIN may be NULL if the caller does
- not need them.
-
- If a memory error occurs while evaluating the expression, *RESULTP will
- be set to NULL. *RESULTP may be a lazy value, if the result could
- not be read from memory. It is used to determine whether a value
- is user-specified (we should watch the whole value) or intermediate
- (we should watch only the bit used to locate the final value).
-
- If the final value, or any intermediate value, could not be read
- from memory, *VALP will be set to NULL. *VAL_CHAIN will still be
- set to any referenced values. *VALP will never be a lazy value.
- This is the value which we store in struct breakpoint.
-
- If VAL_CHAIN is non-NULL, *VAL_CHAIN will be released from the
- value chain. The caller must free the values individually. If
- VAL_CHAIN is NULL, all generated values will be left on the value
- chain. */
-
-static void
-fetch_watchpoint_value (struct expression *exp, struct value **valp,
- struct value **resultp, struct value **val_chain)
-{
- struct value *mark, *new_mark, *result;
- volatile struct gdb_exception ex;
-
- *valp = NULL;
- if (resultp)
- *resultp = NULL;
- if (val_chain)
- *val_chain = NULL;
-
- /* Evaluate the expression. */
- mark = value_mark ();
- result = NULL;
-
- TRY_CATCH (ex, RETURN_MASK_ALL)
- {
- result = evaluate_expression (exp);
- }
- if (ex.reason < 0)
- {
- /* Ignore memory errors, we want watchpoints pointing at
- inaccessible memory to still be created; otherwise, throw the
- error to some higher catcher. */
- switch (ex.error)
- {
- case MEMORY_ERROR:
- break;
- default:
- throw_exception (ex);
- break;
- }
- }
-
- new_mark = value_mark ();
- if (mark == new_mark)
- return;
- if (resultp)
- *resultp = result;
-
- /* Make sure it's not lazy, so that after the target stops again we
- have a non-lazy previous value to compare with. */
- if (result != NULL
- && (!value_lazy (result) || gdb_value_fetch_lazy (result)))
- *valp = result;
-
- if (val_chain)
- {
- /* Return the chain of intermediate values. We use this to
- decide which addresses to watch. */
- *val_chain = new_mark;
- value_release_to_mark (mark);
- }
-}
-
/* Assuming that B is a watchpoint: returns true if the current thread
and its running state are safe to evaluate or update watchpoint B.
Watchpoints on local expressions need to be evaluated in the
if (within_current_scope && reparse)
{
char *s;
+
if (b->exp)
{
xfree (b->exp);
b->exp = NULL;
}
- s = b->exp_string;
+ s = b->exp_string_reparse ? b->exp_string_reparse : b->exp_string;
b->exp = parse_exp_1 (&s, b->exp_valid_block, 0);
/* If the meaning of expression itself changed, the old value is
no longer relevant. We don't want to report a watchpoint hit
}
else if (within_current_scope && b->exp)
{
+ int pc = 0;
struct value *val_chain, *v, *result, *next;
struct program_space *frame_pspace;
- fetch_watchpoint_value (b->exp, &v, &result, &val_chain);
+ fetch_subexp_value (b->exp, &pc, &v, &result, &val_chain);
/* Avoid setting b->val if it's already set. The meaning of
b->val is 'the last value' user saw, and we should update
else if (!within_current_scope)
{
printf_filtered (_("\
-Watchpoint %d deleted because the program has left the block \n\
+Watchpoint %d deleted because the program has left the block\n\
in which its expression is valid.\n"),
b->number);
if (b->related_breakpoint)
{
val = target_insert_watchpoint (bpt->address,
bpt->length,
- bpt->watchpoint_type);
+ bpt->watchpoint_type,
+ bpt->owner->cond_exp);
/* If trying to set a read-watchpoint, and it turns out it's not
supported, try emulating one with an access watchpoint. */
{
val = target_insert_watchpoint (bpt->address,
bpt->length,
- hw_access);
+ hw_access,
+ bpt->owner->cond_exp);
if (val == 0)
bpt->watchpoint_type = hw_access;
}
static int internal_breakpoint_number = -1;
+/* Set the breakpoint number of B, depending on the value of INTERNAL.
+ If INTERNAL is non-zero, the breakpoint number will be populated
+ from internal_breakpoint_number and that variable decremented.
+ Otherwis the breakpoint number will be populated from
+ breakpoint_count and that value incremented. Internal breakpoints
+ do not set the internal var bpnum. */
+static void
+set_breakpoint_number (int internal, struct breakpoint *b)
+{
+ if (internal)
+ b->number = internal_breakpoint_number--;
+ else
+ {
+ set_breakpoint_count (breakpoint_count + 1);
+ b->number = breakpoint_count;
+ }
+}
+
static struct breakpoint *
create_internal_breakpoint (struct gdbarch *gdbarch,
CORE_ADDR address, enum bptype type)
do_cleanups (old_chain);
}
+/* Install a master breakpoint on the unwinder's debug hook. */
+
+void
+create_exception_master_breakpoint (void)
+{
+ struct objfile *objfile;
+
+ ALL_OBJFILES (objfile)
+ {
+ struct minimal_symbol *debug_hook;
+
+ debug_hook = lookup_minimal_symbol ("_Unwind_DebugHook", NULL, objfile);
+ if (debug_hook != NULL)
+ {
+ struct breakpoint *b;
+ CORE_ADDR addr = SYMBOL_VALUE_ADDRESS (debug_hook);
+ struct gdbarch *gdbarch = get_objfile_arch (objfile);
+
+ addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr,
+ ¤t_target);
+ b = create_internal_breakpoint (gdbarch, addr, bp_exception_master);
+ b->addr_string = xstrdup ("_Unwind_DebugHook");
+ b->enable_state = bp_disabled;
+ }
+ }
+
+ update_global_location_list (1);
+}
+
void
update_breakpoints_after_exec (void)
{
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
- || b->type == bp_longjmp_master || b->type == bp_std_terminate_master)
+ || b->type == bp_longjmp_master || b->type == bp_std_terminate_master
+ || b->type == bp_exception_master)
{
delete_breakpoint (b);
continue;
/* Longjmp and longjmp-resume breakpoints are also meaningless
after an exec. */
- if (b->type == bp_longjmp || b->type == bp_longjmp_resume)
+ if (b->type == bp_longjmp || b->type == bp_longjmp_resume
+ || b->type == bp_exception || b->type == bp_exception_resume)
{
delete_breakpoint (b);
continue;
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
create_std_terminate_master_breakpoint ("std::terminate()");
+ create_exception_master_breakpoint ();
}
int
else if (b->loc_type == bp_loc_hardware_watchpoint)
{
b->inserted = (is == mark_inserted);
- val = target_remove_watchpoint (b->address, b->length,
- b->watchpoint_type);
+ val = target_remove_watchpoint (b->address, b->length,
+ b->watchpoint_type, b->owner->cond_exp);
/* Failure to remove any of the hardware watchpoints comes here. */
if ((is == mark_uninserted) && (b->inserted))
/* Get rid of the moribund locations. */
for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, bpt); ++ix)
- free_bp_location (bpt);
+ decref_bp_location (&bpt);
VEC_free (bp_location_p, moribund_locations);
}
return (ep->type == bp_catchpoint);
}
-void
+/* Frees any storage that is part of a bpstat. Does not walk the
+ 'next' chain. */
+
+static void
bpstat_free (bpstat bs)
{
if (bs->old_val != NULL)
value_free (bs->old_val);
decref_counted_command_line (&bs->commands);
+ decref_bp_location (&bs->bp_location_at);
xfree (bs);
}
tmp = (bpstat) xmalloc (sizeof (*tmp));
memcpy (tmp, bs, sizeof (*tmp));
incref_counted_command_line (tmp->commands);
+ incref_bp_location (tmp->bp_location_at);
if (bs->old_val != NULL)
{
tmp->old_val = value_copy (bs->old_val);
for (; bsp != NULL; bsp = bsp->next)
{
- if (bsp->breakpoint_at && bsp->breakpoint_at->owner == breakpoint)
+ if (bsp->breakpoint_at == breakpoint)
return bsp;
}
return NULL;
correspond to a single breakpoint -- otherwise,
this function might return the same number more
than once and this will look ugly. */
- b = (*bsp)->breakpoint_at ? (*bsp)->breakpoint_at->owner : NULL;
+ b = (*bsp)->breakpoint_at;
*bsp = (*bsp)->next;
if (b == NULL)
return -1; /* breakpoint that's been deleted since */
interrupt the command list. When the call finishes
successfully, the inferior will be standing at the same
breakpoint as if nothing happened. */
- if (tp->in_infcall)
+ if (tp->control.in_infcall)
return;
}
and only return when it is stopped at the next breakpoint, we
keep doing breakpoint actions until it returns false to
indicate the inferior was not resumed. */
- if (!bpstat_do_actions_1 (&inferior_thread ()->stop_bpstat))
+ if (!bpstat_do_actions_1 (&inferior_thread ()->control.stop_bpstat))
break;
}
which has since been deleted. */
if (bs->breakpoint_at == NULL)
return PRINT_UNKNOWN;
- bl = bs->breakpoint_at;
- /* bl->owner can be NULL if it was a momentary breakpoint
- which has since been placed into moribund_locations. */
- if (bl->owner == NULL)
- return PRINT_UNKNOWN;
- b = bl->owner;
+ gdb_assert (bs->bp_location_at != NULL);
+
+ bl = bs->bp_location_at;
+ b = bs->breakpoint_at;
stb = ui_out_stream_new (uiout);
old_chain = make_cleanup_ui_out_stream_delete (stb);
{
case bp_breakpoint:
case bp_hardware_breakpoint:
- bp_temp = bs->breakpoint_at->owner->disposition == disp_del;
+ bp_temp = b->disposition == disp_del;
if (bl->address != bl->requested_address)
breakpoint_adjustment_warning (bl->requested_address,
bl->address,
result = PRINT_NOTHING;
break;
+ case bp_exception_master:
+ /* These should never be enabled. */
+ printf_filtered (_("Exception Master Breakpoint: gdb should not stop!\n"));
+ result = PRINT_NOTHING;
+ break;
+
case bp_watchpoint:
case bp_hardware_watchpoint:
annotate_watchpoint (b->number);
case bp_none:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
case print_it_normal:
{
- const struct bp_location *bl = bs->breakpoint_at;
- struct breakpoint *b = bl ? bl->owner : NULL;
-
+ struct breakpoint *b = bs->breakpoint_at;
+
/* Normal case. Call the breakpoint's print_it method, or
print_it_typical. */
/* FIXME: how breakpoint can ever be NULL here? */
return i;
}
-/* Allocate a new bpstat and chain it to the current one. */
+/* Allocate a new bpstat. Link it to the FIFO list by BS_LINK_POINTER. */
static bpstat
-bpstat_alloc (const struct bp_location *bl, bpstat cbs /* Current "bs" value */ )
+bpstat_alloc (struct bp_location *bl, bpstat **bs_link_pointer)
{
bpstat bs;
bs = (bpstat) xmalloc (sizeof (*bs));
- cbs->next = bs;
- bs->breakpoint_at = bl;
+ bs->next = NULL;
+ **bs_link_pointer = bs;
+ *bs_link_pointer = &bs->next;
+ bs->breakpoint_at = bl->owner;
+ bs->bp_location_at = bl;
+ incref_bp_location (bl);
/* If the condition is false, etc., don't do the commands. */
bs->commands = NULL;
bs->commands_left = NULL;
struct frame_info *fr;
int within_current_scope;
- /* BS is built for existing struct breakpoint. */
+ /* BS is built from an existing struct breakpoint. */
gdb_assert (bs->breakpoint_at != NULL);
- gdb_assert (bs->breakpoint_at->owner != NULL);
- b = bs->breakpoint_at->owner;
+ b = bs->breakpoint_at;
/* If this is a local watchpoint, we only want to check if the
watchpoint frame is in scope if the current thread is the thread
call free_all_values. We can't call free_all_values because
we might be in the middle of evaluating a function call. */
+ int pc = 0;
struct value *mark = value_mark ();
struct value *new_val;
- fetch_watchpoint_value (b->exp, &new_val, NULL, NULL);
+ fetch_subexp_value (b->exp, &pc, &new_val, NULL, NULL);
/* We use value_equal_contents instead of value_equal because the latter
coerces an array to a pointer, thus comparing just the address of the
struct breakpoint *b;
/* BS is built for existing struct breakpoint. */
- bl = bs->breakpoint_at;
+ bl = bs->bp_location_at;
gdb_assert (bl != NULL);
- b = bl->owner;
+ b = bs->breakpoint_at;
gdb_assert (b != NULL);
if (is_watchpoint (b))
/* Check conditions (condition proper, frame, thread and ignore count)
of breakpoint referred to by BS. If we should not stop for this
breakpoint, set BS->stop to 0. */
+
static void
bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
{
struct breakpoint *b;
/* BS is built for existing struct breakpoint. */
- bl = bs->breakpoint_at;
+ bl = bs->bp_location_at;
gdb_assert (bl != NULL);
- b = bl->owner;
+ b = bs->breakpoint_at;
gdb_assert (b != NULL);
if (frame_id_p (b->frame_id)
int value_is_zero = 0;
struct expression *cond;
- /* If this is a scope breakpoint, mark the associated
- watchpoint as triggered so that we will handle the
- out-of-scope event. We'll get to the watchpoint next
- iteration. */
- if (b->type == bp_watchpoint_scope)
- b->related_breakpoint->watchpoint_triggered = watch_triggered_yes;
-
if (is_watchpoint (b))
cond = b->cond_exp;
else
cond = bl->cond;
- if (cond && bl->owner->disposition != disp_del_at_next_stop)
+ if (cond && b->disposition != disp_del_at_next_stop)
{
int within_current_scope = 1;
struct breakpoint *b = NULL;
struct bp_location *bl;
struct bp_location *loc;
- /* Root of the chain of bpstat's */
- struct bpstats root_bs[1];
+ /* First item of allocated bpstat's. */
+ bpstat bs_head = NULL, *bs_link = &bs_head;
/* Pointer to the last thing in the chain currently. */
- bpstat bs = root_bs;
+ bpstat bs;
int ix;
int need_remove_insert;
+ int removed_any;
- /* ALL_BP_LOCATIONS iteration would break across
- update_global_location_list possibly executed by
- bpstat_check_breakpoint_conditions's inferior call. */
+ /* First, build the bpstat chain with locations that explain a
+ target stop, while being careful to not set the target running,
+ as that may invalidate locations (in particular watchpoint
+ locations are recreated). Resuming will happen here with
+ breakpoint conditions or watchpoint expressions that include
+ inferior function calls. */
ALL_BREAKPOINTS (b)
{
/* Come here if it's a watchpoint, or if the break address matches */
- bs = bpstat_alloc (bl, bs); /* Alloc a bpstat to explain stop */
+ bs = bpstat_alloc (bl, &bs_link); /* Alloc a bpstat to explain stop */
- /* Assume we stop. Should we find watchpoint that is not actually
- triggered, or if condition of breakpoint is false, we'll reset
- 'stop' to 0. */
+ /* Assume we stop. Should we find a watchpoint that is not
+ actually triggered, or if the condition of the breakpoint
+ evaluates as false, we'll reset 'stop' to 0. */
bs->stop = 1;
bs->print = 1;
- bpstat_check_watchpoint (bs);
- if (!bs->stop)
- continue;
+ /* If this is a scope breakpoint, mark the associated
+ watchpoint as triggered so that we will handle the
+ out-of-scope event. We'll get to the watchpoint next
+ iteration. */
+ if (b->type == bp_watchpoint_scope)
+ b->related_breakpoint->watchpoint_triggered = watch_triggered_yes;
+ }
+ }
+
+ for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
+ {
+ if (breakpoint_address_match (loc->pspace->aspace, loc->address,
+ aspace, bp_addr))
+ {
+ bs = bpstat_alloc (loc, &bs_link);
+ /* For hits of moribund locations, we should just proceed. */
+ bs->stop = 0;
+ bs->print = 0;
+ bs->print_it = print_it_noop;
+ }
+ }
+
+ /* Now go through the locations that caused the target to stop, and
+ check whether we're interested in reporting this stop to higher
+ layers, or whether we should resume the target transparently. */
+
+ removed_any = 0;
+
+ for (bs = bs_head; bs != NULL; bs = bs->next)
+ {
+ if (!bs->stop)
+ continue;
+
+ bpstat_check_watchpoint (bs);
+ if (!bs->stop)
+ continue;
+
+ b = bs->breakpoint_at;
if (b->type == bp_thread_event || b->type == bp_overlay_event
|| b->type == bp_longjmp_master
- || b->type == bp_std_terminate_master)
+ || b->type == bp_std_terminate_master
+ || b->type == bp_exception_master)
/* We do not stop for these. */
bs->stop = 0;
else
bpstat_check_breakpoint_conditions (bs, ptid);
-
+
if (bs->stop)
{
++(b->hit_count);
{
if (b->enable_state != bp_permanent)
b->enable_state = bp_disabled;
- update_global_location_list (0);
+ removed_any = 1;
}
if (b->silent)
bs->print = 0;
/* Print nothing for this entry if we dont stop or dont print. */
if (bs->stop == 0 || bs->print == 0)
bs->print_it = print_it_noop;
- }
}
- for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
- {
- if (breakpoint_address_match (loc->pspace->aspace, loc->address,
- aspace, bp_addr))
- {
- bs = bpstat_alloc (loc, bs);
- /* For hits of moribund locations, we should just proceed. */
- bs->stop = 0;
- bs->print = 0;
- bs->print_it = print_it_noop;
- }
- }
-
- bs->next = NULL; /* Terminate the chain */
-
/* If we aren't stopping, the value of some hardware watchpoint may
not have changed, but the intermediate memory locations we are
watching may have. Don't bother if we're stopping; this will get
done later. */
need_remove_insert = 0;
- if (! bpstat_causes_stop (root_bs->next))
- for (bs = root_bs->next; bs != NULL; bs = bs->next)
+ if (! bpstat_causes_stop (bs_head))
+ for (bs = bs_head; bs != NULL; bs = bs->next)
if (!bs->stop
- && bs->breakpoint_at->owner
- && is_hardware_watchpoint (bs->breakpoint_at->owner))
+ && bs->breakpoint_at
+ && is_hardware_watchpoint (bs->breakpoint_at))
{
- update_watchpoint (bs->breakpoint_at->owner, 0 /* don't reparse. */);
- /* Updating watchpoints invalidates bs->breakpoint_at.
- Prevent further code from trying to use it. */
- bs->breakpoint_at = NULL;
+ update_watchpoint (bs->breakpoint_at, 0 /* don't reparse. */);
need_remove_insert = 1;
}
if (need_remove_insert)
update_global_location_list (1);
+ else if (removed_any)
+ update_global_location_list (0);
- return root_bs->next;
+ return bs_head;
}
static void
retval.main_action = BPSTAT_WHAT_KEEP_CHECKING;
retval.call_dummy = STOP_NONE;
+ retval.is_longjmp = 0;
for (; bs != NULL; bs = bs->next)
{
breakpoint which has since been deleted. */
bptype = bp_none;
}
- else if (bs->breakpoint_at->owner == NULL)
+ else if (bs->breakpoint_at == NULL)
bptype = bp_none;
else
- bptype = bs->breakpoint_at->owner->type;
+ bptype = bs->breakpoint_at->type;
switch (bptype)
{
}
break;
case bp_longjmp:
+ case bp_exception:
this_action = BPSTAT_WHAT_SET_LONGJMP_RESUME;
+ retval.is_longjmp = bptype == bp_longjmp;
break;
case bp_longjmp_resume:
+ case bp_exception_resume:
this_action = BPSTAT_WHAT_CLEAR_LONGJMP_RESUME;
+ retval.is_longjmp = bptype == bp_longjmp_resume;
break;
case bp_step_resume:
if (bs->stop)
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
this_action = BPSTAT_WHAT_SINGLE;
break;
case bp_catchpoint:
break;
case bp_tracepoint:
case bp_fast_tracepoint:
+ case bp_static_tracepoint:
/* Tracepoint hits should not be reported back to GDB, and
if one got through somehow, it should have been filtered
out already. */
do_cleanups (old_chain);
}
-/* Print B to gdb_stdout. */
-static void
-print_one_breakpoint_location (struct breakpoint *b,
- struct bp_location *loc,
- int loc_number,
- struct bp_location **last_loc,
- int print_address_bits,
- int allflag)
+static const char *
+bptype_string (enum bptype type)
{
- struct command_line *l;
struct ep_type_description
{
enum bptype type;
{bp_access_watchpoint, "acc watchpoint"},
{bp_longjmp, "longjmp"},
{bp_longjmp_resume, "longjmp resume"},
+ {bp_exception, "exception"},
+ {bp_exception_resume, "exception resume"},
{bp_step_resume, "step resume"},
{bp_watchpoint_scope, "watchpoint scope"},
{bp_call_dummy, "call dummy"},
{bp_overlay_event, "overlay events"},
{bp_longjmp_master, "longjmp master"},
{bp_std_terminate_master, "std::terminate master"},
+ {bp_exception_master, "exception master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
{bp_fast_tracepoint, "fast tracepoint"},
+ {bp_static_tracepoint, "static tracepoint"},
{bp_jit_event, "jit events"},
};
-
+
+ if (((int) type >= (sizeof (bptypes) / sizeof (bptypes[0])))
+ || ((int) type != bptypes[(int) type].type))
+ internal_error (__FILE__, __LINE__,
+ _("bptypes table does not describe type #%d."),
+ (int) type);
+
+ return bptypes[(int) type].description;
+}
+
+/* Print B to gdb_stdout. */
+
+static void
+print_one_breakpoint_location (struct breakpoint *b,
+ struct bp_location *loc,
+ int loc_number,
+ struct bp_location **last_loc,
+ int print_address_bits,
+ int allflag)
+{
+ struct command_line *l;
static char bpenables[] = "nynny";
char wrap_indent[80];
struct ui_stream *stb = ui_out_stream_new (uiout);
annotate_field (1);
if (part_of_multiple)
ui_out_field_skip (uiout, "type");
- else
- {
- if (((int) b->type >= (sizeof (bptypes) / sizeof (bptypes[0])))
- || ((int) b->type != bptypes[(int) b->type].type))
- internal_error (__FILE__, __LINE__,
- _("bptypes table does not describe type #%d."),
- (int) b->type);
- ui_out_field_string (uiout, "type", bptypes[(int) b->type].description);
- }
+ else
+ ui_out_field_string (uiout, "type", bptype_string (b->type));
/* 3 */
annotate_field (2);
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
case bp_tracepoint:
case bp_fast_tracepoint:
+ case bp_static_tracepoint:
case bp_jit_event:
if (opts.addressprint)
{
ui_out_text (uiout, "\n");
+ if (!part_of_multiple && b->static_trace_marker_id)
+ {
+ gdb_assert (b->type == bp_static_tracepoint);
+
+ ui_out_text (uiout, "\tmarker id is ");
+ ui_out_field_string (uiout, "static-tracepoint-marker-string-id",
+ b->static_trace_marker_id);
+ ui_out_text (uiout, "\n");
+ }
+
if (part_of_multiple && frame_id_p (b->frame_id))
{
annotate_field (6);
struct cleanup *bkpttbl_chain;
struct value_print_options opts;
int print_address_bits = 0;
-
+ int print_type_col_width = 14;
+
get_user_print_options (&opts);
/* Compute the number of rows in the table, as well as the
if (filter && !filter (b))
continue;
- if (allflag || user_settable_breakpoint (b))
+ if (allflag || (user_settable_breakpoint (b)
+ && b->number > 0))
{
- int addr_bit = breakpoint_address_bits (b);
+ int addr_bit, type_len;
+
+ addr_bit = breakpoint_address_bits (b);
if (addr_bit > print_address_bits)
print_address_bits = addr_bit;
+ type_len = strlen (bptype_string (b->type));
+ if (type_len > print_type_col_width)
+ print_type_col_width = type_len;
+
nr_printable_breakpoints++;
}
}
ui_out_table_header (uiout, 7, ui_left, "number", "Num"); /* 1 */
if (nr_printable_breakpoints > 0)
annotate_field (1);
- ui_out_table_header (uiout, 14, ui_left, "type", "Type"); /* 2 */
+ ui_out_table_header (uiout, print_type_col_width, ui_left,
+ "type", "Type"); /* 2 */
if (nr_printable_breakpoints > 0)
annotate_field (2);
ui_out_table_header (uiout, 4, ui_left, "disp", "Disp"); /* 3 */
/* We only print out user settable breakpoints unless the
allflag is set. */
- if (allflag || user_settable_breakpoint (b))
+ if (allflag || (user_settable_breakpoint (b)
+ && b->number > 0))
print_one_breakpoint (b, &last_loc, print_address_bits, allflag);
}
}
gdb_assert (loc1->owner != NULL);
gdb_assert (loc2->owner != NULL);
+ /* If the target can evaluate the condition expression in hardware, then we
+ we need to insert both watchpoints even if they are at the same place.
+ Otherwise the watchpoint will only trigger when the condition of whichever
+ watchpoint was inserted evaluates to true, not giving a chance for GDB to
+ check the condition of the other watchpoint. */
+ if ((loc1->owner->cond_exp
+ && target_can_accel_watchpoint_condition (loc1->address, loc1->length,
+ loc1->watchpoint_type,
+ loc1->owner->cond_exp))
+ || (loc2->owner->cond_exp
+ && target_can_accel_watchpoint_condition (loc2->address, loc2->length,
+ loc2->watchpoint_type,
+ loc2->owner->cond_exp)))
+ return 0;
+
/* Note that this checks the owner's type, not the location's. In
case the target does not support read watchpoints, but does
support access watchpoints, we'll have bp_read_watchpoint
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
case bp_hardware_breakpoint:
case bp_catchpoint:
case bp_tracepoint:
case bp_fast_tracepoint:
+ case bp_static_tracepoint:
loc->loc_type = bp_loc_other;
break;
default:
internal_error (__FILE__, __LINE__, _("unknown breakpoint type"));
}
+ loc->refc = 1;
return loc;
}
-static void free_bp_location (struct bp_location *loc)
+static void
+free_bp_location (struct bp_location *loc)
{
- /* Be sure no bpstat's are pointing at it after it's been freed. */
- /* FIXME, how can we find all bpstat's?
- We just check stop_bpstat for now. Note that we cannot just
- remove bpstats pointing at bpt from the stop_bpstat list
- entirely, as breakpoint commands are associated with the bpstat;
- if we remove it here, then the later call to
- bpstat_do_actions (&stop_bpstat);
- in event-top.c won't do anything, and temporary breakpoints
- with commands won't work. */
-
- iterate_over_threads (bpstat_remove_bp_location_callback, loc);
-
if (loc->cond)
xfree (loc->cond);
if (loc->function_name)
xfree (loc->function_name);
-
+
xfree (loc);
}
+/* Increment reference count. */
+
+static void
+incref_bp_location (struct bp_location *bl)
+{
+ ++bl->refc;
+}
+
+/* Decrement reference count. If the reference count reaches 0,
+ destroy the bp_location. Sets *BLP to NULL. */
+
+static void
+decref_bp_location (struct bp_location **blp)
+{
+ gdb_assert ((*blp)->refc > 0);
+
+ if (--(*blp)->refc == 0)
+ free_bp_location (*blp);
+ *blp = NULL;
+}
+
/* Helper to set_raw_breakpoint below. Creates a breakpoint
that has type BPTYPE and has no locations as yet. */
/* This function is used in gdbtk sources and thus can not be made static. */
b->syscalls_to_be_caught = NULL;
b->ops = NULL;
b->condition_not_parsed = 0;
+ b->py_bp_object = NULL;
/* Add this breakpoint to the end of the chain
so that a list of breakpoints will come out in order
}
/* Call this routine when stepping and nexting to enable a breakpoint
- if we do a longjmp() in THREAD. When we hit that breakpoint, call
- set_longjmp_resume_breakpoint() to figure out where we are going. */
+ if we do a longjmp() or 'throw' in TP. FRAME is the frame which
+ initiated the operation. */
void
-set_longjmp_breakpoint (int thread)
+set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
{
struct breakpoint *b, *temp;
+ int thread = tp->num;
/* To avoid having to rescan all objfile symbols at every step,
we maintain a list of continually-inserted but always disabled
clones of those and enable them for the requested thread. */
ALL_BREAKPOINTS_SAFE (b, temp)
if (b->pspace == current_program_space
- && b->type == bp_longjmp_master)
+ && (b->type == bp_longjmp_master
+ || b->type == bp_exception_master))
{
struct breakpoint *clone = clone_momentary_breakpoint (b);
- clone->type = bp_longjmp;
+ clone->type = b->type == bp_longjmp_master ? bp_longjmp : bp_exception;
clone->thread = thread;
}
+
+ tp->initiating_frame = frame;
}
/* Delete all longjmp breakpoints from THREAD. */
struct breakpoint *b, *temp;
ALL_BREAKPOINTS_SAFE (b, temp)
- if (b->type == bp_longjmp)
+ if (b->type == bp_longjmp || b->type == bp_exception)
{
if (b->thread == thread)
delete_breakpoint (b);
printf_filtered (_(" %d"), b->number);
say_where = 1;
break;
+ case bp_static_tracepoint:
+ if (ui_out_is_mi_like_p (uiout))
+ {
+ say_where = 0;
+ break;
+ }
+ printf_filtered (_("Static tracepoint"));
+ printf_filtered (_(" %d"), b->number);
+ say_where = 1;
+ break;
case bp_until:
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_call_dummy:
case bp_std_terminate:
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
break;
}
char *cond_string,
enum bptype type, enum bpdisp disposition,
int thread, int task, int ignore_count,
- struct breakpoint_ops *ops, int from_tty, int enabled)
+ struct breakpoint_ops *ops, int from_tty,
+ int enabled, int internal)
{
struct breakpoint *b = NULL;
int i;
if (i == 0)
{
b = set_raw_breakpoint (gdbarch, sal, type);
- set_breakpoint_count (breakpoint_count + 1);
- b->number = breakpoint_count;
+ set_breakpoint_number (internal, b);
b->thread = thread;
b->task = task;
b->ignore_count = ignore_count;
b->enable_state = enabled ? bp_enabled : bp_disabled;
b->disposition = disposition;
-
b->pspace = sals.sals[0].pspace;
+ if (type == bp_static_tracepoint)
+ {
+ struct static_tracepoint_marker marker;
+
+ if (is_marker_spec (addr_string))
+ {
+ /* We already know the marker exists, otherwise, we
+ wouldn't see a sal for it. */
+ char *p = &addr_string[3];
+ char *endp;
+ char *marker_str;
+ int i;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ endp = p;
+ while (*endp != ' ' && *endp != '\t' && *endp != '\0')
+ endp++;
+
+ marker_str = savestring (p, endp - p);
+ b->static_trace_marker_id = marker_str;
+
+ printf_filtered (_("Probed static tracepoint marker \"%s\"\n"),
+ b->static_trace_marker_id);
+ }
+ else if (target_static_tracepoint_marker_at (sal.pc, &marker))
+ {
+ b->static_trace_marker_id = xstrdup (marker.str_id);
+ release_static_tracepoint_marker (&marker);
+
+ printf_filtered (_("Probed static tracepoint marker \"%s\"\n"),
+ b->static_trace_marker_id);
+ }
+ else
+ warning (_("\
+Couldn't determine the static tracepoint marker to probe"));
+ }
+
if (enabled && b->pspace->executing_startup
&& (b->type == bp_breakpoint
|| b->type == bp_hardware_breakpoint))
= xstrprintf ("*%s", paddress (b->loc->gdbarch, b->loc->address));
b->ops = ops;
- mention (b);
+ if (internal)
+ /* Do not mention breakpoints with a negative number, but do
+ notify observers. */
+ observer_notify_breakpoint_created (b->number);
+ else
+ mention (b);
}
/* Remove element at INDEX_TO_REMOVE from SAL, shifting other
enum bptype type, enum bpdisp disposition,
int thread, int task, int ignore_count,
struct breakpoint_ops *ops, int from_tty,
- int enabled)
+ int enabled, int internal)
{
int i;
create_breakpoint_sal (gdbarch, expanded, addr_string[i],
cond_string, type, disposition,
- thread, task, ignore_count, ops, from_tty, enabled);
+ thread, task, ignore_count, ops,
+ from_tty, enabled, internal);
}
}
inserted as a breakpoint. If it can't throw an error. */
static void
-breakpoint_sals_to_pc (struct symtabs_and_lines *sals,
- char *address)
+breakpoint_sals_to_pc (struct symtabs_and_lines *sals)
{
int i;
}
}
+/* Decode a static tracepoint marker spec. */
+
+static struct symtabs_and_lines
+decode_static_tracepoint_spec (char **arg_p)
+{
+ VEC(static_tracepoint_marker_p) *markers = NULL;
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ struct symbol *sym;
+ struct cleanup *old_chain;
+ char *p = &(*arg_p)[3];
+ char *endp;
+ char *marker_str;
+ int i;
+
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ endp = p;
+ while (*endp != ' ' && *endp != '\t' && *endp != '\0')
+ endp++;
+
+ marker_str = savestring (p, endp - p);
+ old_chain = make_cleanup (xfree, marker_str);
+
+ markers = target_static_tracepoint_markers_by_strid (marker_str);
+ if (VEC_empty(static_tracepoint_marker_p, markers))
+ error (_("No known static tracepoint marker named %s"), marker_str);
+
+ sals.nelts = VEC_length(static_tracepoint_marker_p, markers);
+ sals.sals = xmalloc (sizeof *sals.sals * sals.nelts);
+
+ for (i = 0; i < sals.nelts; i++)
+ {
+ struct static_tracepoint_marker *marker;
+
+ marker = VEC_index (static_tracepoint_marker_p, markers, i);
+
+ init_sal (&sals.sals[i]);
+
+ sals.sals[i] = find_pc_line (marker->address, 0);
+ sals.sals[i].pc = marker->address;
+
+ release_static_tracepoint_marker (marker);
+ }
+
+ do_cleanups (old_chain);
+
+ *arg_p = endp;
+ return sals;
+}
+
/* Set a breakpoint. This function is shared between CLI and MI
functions for setting a breakpoint. This function has two major
modes of operations, selected by the PARSE_CONDITION_AND_THREAD
parameter. If non-zero, the function will parse arg, extracting
breakpoint location, address and thread. Otherwise, ARG is just the
location of breakpoint, with condition and thread specified by the
- COND_STRING and THREAD parameters. Returns true if any breakpoint
- was created; false otherwise. */
+ COND_STRING and THREAD parameters. If INTERNAL is non-zero, the
+ breakpoint number will be allocated from the internal breakpoint
+ count. Returns true if any breakpoint was created; false
+ otherwise. */
int
create_breakpoint (struct gdbarch *gdbarch,
char *arg, char *cond_string, int thread,
int parse_condition_and_thread,
- int tempflag, int hardwareflag, int traceflag,
+ int tempflag, enum bptype type_wanted,
int ignore_count,
enum auto_boolean pending_break_support,
struct breakpoint_ops *ops,
- int from_tty,
- int enabled)
+ int from_tty, int enabled, int internal)
{
struct gdb_exception e;
struct symtabs_and_lines sals;
int i;
int pending = 0;
int not_found = 0;
- enum bptype type_wanted;
int task = 0;
int prev_bkpt_count = breakpoint_count;
parse_args.addr_string_p = &addr_string;
parse_args.not_found_ptr = ¬_found;
+ if (type_wanted == bp_static_tracepoint && is_marker_spec (arg))
+ {
+ int i;
+
+ sals = decode_static_tracepoint_spec (&arg);
+
+ copy_arg = savestring (addr_start, arg - addr_start);
+ addr_string = xcalloc (sals.nelts, sizeof (char **));
+ for (i = 0; i < sals.nelts; i++)
+ addr_string[i] = xstrdup (copy_arg);
+ goto done;
+ }
+
e = catch_exception (uiout, do_captured_parse_breakpoint,
&parse_args, RETURN_MASK_ALL);
/* If pending breakpoint support is auto query and the user
selects no, then simply return the error code. */
if (pending_break_support == AUTO_BOOLEAN_AUTO
- && !nquery ("Make breakpoint pending on future shared library load? "))
+ && !nquery (_("Make breakpoint pending on future shared library load? ")))
return 0;
/* At this point, either the user was queried about setting
return 0;
}
+ done:
+
/* Create a chain of things that always need to be cleaned up. */
old_chain = make_cleanup (null_cleanup, 0);
/* Resolve all line numbers to PC's and verify that the addresses
are ok for the target. */
if (!pending)
- breakpoint_sals_to_pc (&sals, addr_start);
-
- type_wanted = (traceflag
- ? (hardwareflag ? bp_fast_tracepoint : bp_tracepoint)
- : (hardwareflag ? bp_hardware_breakpoint : bp_breakpoint));
+ breakpoint_sals_to_pc (&sals);
/* Fast tracepoints may have additional restrictions on location. */
if (type_wanted == bp_fast_tracepoint)
make_cleanup (xfree, cond_string);
}
}
- create_breakpoints_sal (gdbarch, sals, addr_string, cond_string,
- type_wanted, tempflag ? disp_del : disp_donttouch,
- thread, task, ignore_count, ops, from_tty,
- enabled);
+
+ /* If the user is creating a static tracepoint by marker id
+ (strace -m MARKER_ID), then store the sals index, so that
+ breakpoint_re_set can try to match up which of the newly
+ found markers corresponds to this one, and, don't try to
+ expand multiple locations for each sal, given than SALS
+ already should contain all sals for MARKER_ID. */
+ if (type_wanted == bp_static_tracepoint
+ && is_marker_spec (addr_string[0]))
+ {
+ int i;
+
+ for (i = 0; i < sals.nelts; ++i)
+ {
+ struct symtabs_and_lines expanded;
+ struct breakpoint *tp;
+ struct cleanup *old_chain;
+
+ expanded.nelts = 1;
+ expanded.sals = xmalloc (sizeof (struct symtab_and_line));
+ expanded.sals[0] = sals.sals[i];
+ old_chain = make_cleanup (xfree, expanded.sals);
+
+ create_breakpoint_sal (gdbarch, expanded, addr_string[i],
+ cond_string, type_wanted,
+ tempflag ? disp_del : disp_donttouch,
+ thread, task, ignore_count, ops,
+ from_tty, enabled, internal);
+
+ do_cleanups (old_chain);
+
+ /* Get the tracepoint we just created. */
+ if (internal)
+ tp = get_breakpoint (internal_breakpoint_number);
+ else
+ tp = get_breakpoint (breakpoint_count);
+ gdb_assert (tp != NULL);
+
+ /* Given that its possible to have multiple markers with
+ the same string id, if the user is creating a static
+ tracepoint by marker id ("strace -m MARKER_ID"), then
+ store the sals index, so that breakpoint_re_set can
+ try to match up which of the newly found markers
+ corresponds to this one */
+ tp->static_trace_marker_id_idx = i;
+ }
+ }
+ else
+ create_breakpoints_sal (gdbarch, sals, addr_string, cond_string,
+ type_wanted, tempflag ? disp_del : disp_donttouch,
+ thread, task, ignore_count, ops, from_tty,
+ enabled, internal);
}
else
{
make_cleanup (xfree, copy_arg);
b = set_raw_breakpoint_without_location (gdbarch, type_wanted);
- set_breakpoint_count (breakpoint_count + 1);
- b->number = breakpoint_count;
+ set_breakpoint_number (internal, b);
b->thread = -1;
b->addr_string = addr_string[0];
b->cond_string = NULL;
b->ops = ops;
b->enable_state = enabled ? bp_enabled : bp_disabled;
b->pspace = current_program_space;
+ b->py_bp_object = NULL;
if (enabled && b->pspace->executing_startup
&& (b->type == bp_breakpoint
|| b->type == bp_hardware_breakpoint))
b->enable_state = bp_startup_disabled;
- mention (b);
+ if (internal)
+ /* Do not mention breakpoints with a negative number,
+ but do notify observers. */
+ observer_notify_breakpoint_created (b->number);
+ else
+ mention (b);
}
if (sals.nelts > 1)
static void
break_command_1 (char *arg, int flag, int from_tty)
{
- int hardwareflag = flag & BP_HARDWAREFLAG;
int tempflag = flag & BP_TEMPFLAG;
+ enum bptype type_wanted = (flag & BP_HARDWAREFLAG
+ ? bp_hardware_breakpoint
+ : bp_breakpoint);
create_breakpoint (get_current_arch (),
arg,
NULL, 0, 1 /* parse arg */,
- tempflag, hardwareflag, 0 /* traceflag */,
+ tempflag, type_wanted,
0 /* Ignore count */,
pending_break_support,
NULL /* breakpoint_ops */,
from_tty,
- 1 /* enabled */);
+ 1 /* enabled */,
+ 0 /* internal */);
}
hw_read: watch read,
hw_access: watch access (read or write) */
static void
-watch_command_1 (char *arg, int accessflag, int from_tty)
+watch_command_1 (char *arg, int accessflag, int from_tty,
+ int just_location, int internal)
{
struct breakpoint *b, *scope_breakpoint = NULL;
struct expression *exp;
struct block *exp_valid_block = NULL, *cond_exp_valid_block = NULL;
- struct value *val, *mark;
+ struct value *val, *mark, *result;
struct frame_info *frame;
char *exp_start = NULL;
char *exp_end = NULL;
enum bptype bp_type;
int mem_cnt = 0;
int thread = -1;
+ int pc = 0;
/* Make sure that we actually have parameters to parse. */
if (arg != NULL && arg[0] != '\0')
exp_valid_block = innermost_block;
mark = value_mark ();
- fetch_watchpoint_value (exp, &val, NULL, NULL);
- if (val != NULL)
+ fetch_subexp_value (exp, &pc, &val, &result, NULL);
+
+ if (just_location)
+ {
+ exp_valid_block = NULL;
+ val = value_addr (result);
+ release_value (val);
+ value_free_to_mark (mark);
+ }
+ else if (val != NULL)
release_value (val);
tok = arg;
/* Now set up the breakpoint. */
b = set_raw_breakpoint_without_location (NULL, bp_type);
- set_breakpoint_count (breakpoint_count + 1);
- b->number = breakpoint_count;
+ set_breakpoint_number (internal, b);
b->thread = thread;
b->disposition = disp_donttouch;
b->exp = exp;
b->exp_valid_block = exp_valid_block;
b->cond_exp_valid_block = cond_exp_valid_block;
- b->exp_string = savestring (exp_start, exp_end - exp_start);
+ if (just_location)
+ {
+ struct type *t = value_type (val);
+ CORE_ADDR addr = value_as_address (val);
+ char *name;
+
+ t = check_typedef (TYPE_TARGET_TYPE (check_typedef (t)));
+ name = type_to_string (t);
+
+ b->exp_string_reparse = xstrprintf ("* (%s *) %s", name,
+ core_addr_to_string (addr));
+ xfree (name);
+
+ b->exp_string = xstrprintf ("-location: %.*s",
+ (int) (exp_end - exp_start), exp_start);
+
+ /* The above expression is in C. */
+ b->language = language_c;
+ }
+ else
+ b->exp_string = savestring (exp_start, exp_end - exp_start);
b->val = val;
b->val_valid = 1;
if (cond_start)
scope_breakpoint->related_breakpoint = b;
}
- value_free_to_mark (mark);
+ if (!just_location)
+ value_free_to_mark (mark);
/* Finally update the new watchpoint. This creates the locations
that should be inserted. */
update_watchpoint (b, 1);
-
- mention (b);
+ if (internal)
+ /* Do not mention breakpoints with a negative number, but do
+ notify observers. */
+ observer_notify_breakpoint_created (b->number);
+ else
+ mention (b);
update_global_location_list (1);
}
{
if (VALUE_LVAL (v) == lval_memory)
{
- if (value_lazy (v))
- /* A lazy memory lvalue is one that GDB never needed to fetch;
- we either just used its address (e.g., `a' in `a.b') or
- we never needed it at all (e.g., `a' in `a,b'). */
+ if (v != head && value_lazy (v))
+ /* A lazy memory lvalue in the chain is one that GDB never
+ needed to fetch; we either just used its address (e.g.,
+ `a' in `a.b') or we never needed it at all (e.g., `a'
+ in `a,b'). This doesn't apply to HEAD; if that is
+ lazy then it was not readable, but watch it anyway. */
;
else
{
}
else if (VALUE_LVAL (v) != not_lval
&& deprecated_value_modifiable (v) == 0)
- return 0; /* ??? What does this represent? */
+ return 0; /* These are values from the history (e.g., $1). */
else if (VALUE_LVAL (v) == lval_register)
- return 0; /* cannot watch a register with a HW watchpoint */
+ return 0; /* Cannot watch a register with a HW watchpoint. */
}
/* The expression itself looks suitable for using a hardware
}
void
-watch_command_wrapper (char *arg, int from_tty)
+watch_command_wrapper (char *arg, int from_tty, int internal)
+{
+ watch_command_1 (arg, hw_write, from_tty, 0, internal);
+}
+
+/* A helper function that looks for an argument at the start of a
+ string. The argument must also either be at the end of the string,
+ or be followed by whitespace. Returns 1 if it finds the argument,
+ 0 otherwise. If the argument is found, it updates *STR. */
+
+static int
+check_for_argument (char **str, char *arg, int arg_len)
+{
+ if (strncmp (*str, arg, arg_len) == 0
+ && ((*str)[arg_len] == '\0' || isspace ((*str)[arg_len])))
+ {
+ *str += arg_len;
+ return 1;
+ }
+ return 0;
+}
+
+/* A helper function that looks for the "-location" argument and then
+ calls watch_command_1. */
+
+static void
+watch_maybe_just_location (char *arg, int accessflag, int from_tty)
{
- watch_command (arg, from_tty);
+ int just_location = 0;
+
+ if (arg
+ && (check_for_argument (&arg, "-location", sizeof ("-location") - 1)
+ || check_for_argument (&arg, "-l", sizeof ("-l") - 1)))
+ {
+ ep_skip_leading_whitespace (&arg);
+ just_location = 1;
+ }
+
+ watch_command_1 (arg, accessflag, from_tty, just_location, 0);
}
static void
watch_command (char *arg, int from_tty)
{
- watch_command_1 (arg, hw_write, from_tty);
+ watch_maybe_just_location (arg, hw_write, from_tty);
}
void
-rwatch_command_wrapper (char *arg, int from_tty)
+rwatch_command_wrapper (char *arg, int from_tty, int internal)
{
- rwatch_command (arg, from_tty);
+ watch_command_1 (arg, hw_read, from_tty, 0, internal);
}
static void
rwatch_command (char *arg, int from_tty)
{
- watch_command_1 (arg, hw_read, from_tty);
+ watch_maybe_just_location (arg, hw_read, from_tty);
}
void
-awatch_command_wrapper (char *arg, int from_tty)
+awatch_command_wrapper (char *arg, int from_tty, int internal)
{
- awatch_command (arg, from_tty);
+ watch_command_1 (arg, hw_access, from_tty, 0, internal);
}
static void
awatch_command (char *arg, int from_tty)
{
- watch_command_1 (arg, hw_access, from_tty);
+ watch_maybe_just_location (arg, hw_access, from_tty);
}
\f
{
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2;
+ int thread_num;
};
/* This function is called by fetch_inferior_event via the
delete_breakpoint (a->breakpoint);
if (a->breakpoint2)
delete_breakpoint (a->breakpoint2);
+ delete_longjmp_breakpoint (a->thread_num);
}
void
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2 = NULL;
struct cleanup *old_chain;
+ int thread;
+ struct thread_info *tp;
clear_proceed_status ();
old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ tp = inferior_thread ();
+ thread = tp->num;
+
/* Keep within the current frame, or in frames called by the current
one. */
frame_unwind_caller_id (frame),
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
+
+ set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame));
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
args->breakpoint = breakpoint;
args->breakpoint2 = breakpoint2;
+ args->thread_num = thread;
discard_cleanups (old_chain);
add_continuation (inferior_thread (),
create_breakpoint (get_current_arch (),
trigger_func_name, cond_string, -1,
0 /* condition and thread are valid. */,
- tempflag, 0, 0,
+ tempflag, bp_breakpoint,
0,
AUTO_BOOLEAN_TRUE /* pending */,
&gnu_v3_exception_catchpoint_ops, from_tty,
- 1 /* enabled */);
+ 1 /* enabled */,
+ 0 /* internal */);
return 1;
}
struct breakpoint *b, *temp;
for (; bs; bs = bs->next)
- if (bs->breakpoint_at
- && bs->breakpoint_at->owner
- && bs->breakpoint_at->owner->disposition == disp_del
+ if (bs->breakpoint_at
+ && bs->breakpoint_at->disposition == disp_del
&& bs->stop)
- delete_breakpoint (bs->breakpoint_at->owner);
+ delete_breakpoint (bs->breakpoint_at);
ALL_BREAKPOINTS_SAFE (b, temp)
{
VEC_safe_push (bp_location_p, moribund_locations, old_loc);
}
else
- free_bp_location (old_loc);
+ {
+ old_loc->owner = NULL;
+ decref_bp_location (&old_loc);
+ }
}
}
for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
if (--(loc->events_till_retirement) == 0)
{
- free_bp_location (loc);
+ decref_bp_location (&loc);
VEC_unordered_remove (bp_location_p, moribund_locations, ix);
--ix;
}
update_global_location_list (inserting);
}
-/* Clear LOC from a BPS. */
+/* Clear BKP from a BPS. */
+
static void
-bpstat_remove_bp_location (bpstat bps, struct bp_location *loc)
+bpstat_remove_bp_location (bpstat bps, struct breakpoint *bpt)
{
bpstat bs;
for (bs = bps; bs; bs = bs->next)
- if (bs->breakpoint_at == loc)
+ if (bs->breakpoint_at == bpt)
{
bs->breakpoint_at = NULL;
bs->old_val = NULL;
/* Callback for iterate_over_threads. */
static int
-bpstat_remove_bp_location_callback (struct thread_info *th, void *data)
+bpstat_remove_breakpoint_callback (struct thread_info *th, void *data)
{
- struct bp_location *loc = data;
+ struct breakpoint *bpt = data;
- bpstat_remove_bp_location (th->stop_bpstat, loc);
+ bpstat_remove_bp_location (th->control.stop_bpstat, bpt);
return 0;
}
/* Delete a breakpoint and clean up all traces of it in the data
- structures. */
+ structures. */
void
delete_breakpoint (struct breakpoint *bpt)
xfree (bpt->addr_string);
xfree (bpt->exp);
xfree (bpt->exp_string);
+ xfree (bpt->exp_string_reparse);
value_free (bpt->val);
xfree (bpt->source_file);
xfree (bpt->exec_pathname);
clean_up_filters (&bpt->syscalls_to_be_caught);
+
+ /* Be sure no bpstat's are pointing at the breakpoint after it's
+ been freed. */
+ /* FIXME, how can we find all bpstat's? We just check stop_bpstat
+ in all threeds for now. Note that we cannot just remove bpstats
+ pointing at bpt from the stop_bpstat list entirely, as breakpoint
+ commands are associated with the bpstat; if we remove it here,
+ then the later call to bpstat_do_actions (&stop_bpstat); in
+ event-top.c won't do anything, and temporary breakpoints with
+ commands won't work. */
+
+ iterate_over_threads (bpstat_remove_breakpoint_callback, bpt);
+
/* Now that breakpoint is removed from breakpoint
list, update the global location list. This
will remove locations that used to belong to
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->type != bp_std_terminate_master
+ && b->type != bp_exception_master
&& b->number >= 0)
{
breaks_to_delete = 1;
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->type != bp_std_terminate_master
+ && b->type != bp_exception_master
&& b->number >= 0)
delete_breakpoint (b);
}
return 0;
}
+/* When symbols change, it probably means the sources changed as well,
+ and it might mean the static tracepoint markers are no longer at
+ the same address or line numbers they used to be at last we
+ checked. Losing your static tracepoints whenever you rebuild is
+ undesirable. This function tries to resync/rematch gdb static
+ tracepoints with the markers on the target, for static tracepoints
+ that have not been set by marker id. Static tracepoint that have
+ been set by marker id are reset by marker id in breakpoint_re_set.
+ The heuristic is:
+
+ 1) For a tracepoint set at a specific address, look for a marker at
+ the old PC. If one is found there, assume to be the same marker.
+ If the name / string id of the marker found is different from the
+ previous known name, assume that means the user renamed the marker
+ in the sources, and output a warning.
+
+ 2) For a tracepoint set at a given line number, look for a marker
+ at the new address of the old line number. If one is found there,
+ assume to be the same marker. If the name / string id of the
+ marker found is different from the previous known name, assume that
+ means the user renamed the marker in the sources, and output a
+ warning.
+
+ 3) If a marker is no longer found at the same address or line, it
+ may mean the marker no longer exists. But it may also just mean
+ the code changed a bit. Maybe the user added a few lines of code
+ that made the marker move up or down (in line number terms). Ask
+ the target for info about the marker with the string id as we knew
+ it. If found, update line number and address in the matching
+ static tracepoint. This will get confused if there's more than one
+ marker with the same ID (possible in UST, although unadvised
+ precisely because it confuses tools). */
+
+static struct symtab_and_line
+update_static_tracepoint (struct breakpoint *b, struct symtab_and_line sal)
+{
+ struct static_tracepoint_marker marker;
+ CORE_ADDR pc;
+ int i;
+
+ pc = sal.pc;
+ if (sal.line)
+ find_line_pc (sal.symtab, sal.line, &pc);
+
+ if (target_static_tracepoint_marker_at (pc, &marker))
+ {
+ if (strcmp (b->static_trace_marker_id, marker.str_id) != 0)
+ warning (_("static tracepoint %d changed probed marker from %s to %s"),
+ b->number,
+ b->static_trace_marker_id, marker.str_id);
+
+ xfree (b->static_trace_marker_id);
+ b->static_trace_marker_id = xstrdup (marker.str_id);
+ release_static_tracepoint_marker (&marker);
+
+ return sal;
+ }
+
+ /* Old marker wasn't found on target at lineno. Try looking it up
+ by string ID. */
+ if (!sal.explicit_pc
+ && sal.line != 0
+ && sal.symtab != NULL
+ && b->static_trace_marker_id != NULL)
+ {
+ VEC(static_tracepoint_marker_p) *markers;
+
+ markers
+ = target_static_tracepoint_markers_by_strid (b->static_trace_marker_id);
+
+ if (!VEC_empty(static_tracepoint_marker_p, markers))
+ {
+ struct symtab_and_line sal;
+ struct symbol *sym;
+ struct static_tracepoint_marker *marker;
+
+ marker = VEC_index (static_tracepoint_marker_p, markers, 0);
+
+ xfree (b->static_trace_marker_id);
+ b->static_trace_marker_id = xstrdup (marker->str_id);
+
+ warning (_("marker for static tracepoint %d (%s) not "
+ "found at previous line number"),
+ b->number, b->static_trace_marker_id);
+
+ init_sal (&sal);
+
+ sal.pc = marker->address;
+
+ sal = find_pc_line (marker->address, 0);
+ sym = find_pc_sect_function (marker->address, NULL);
+ ui_out_text (uiout, "Now in ");
+ if (sym)
+ {
+ ui_out_field_string (uiout, "func",
+ SYMBOL_PRINT_NAME (sym));
+ ui_out_text (uiout, " at ");
+ }
+ ui_out_field_string (uiout, "file", sal.symtab->filename);
+ ui_out_text (uiout, ":");
+
+ if (ui_out_is_mi_like_p (uiout))
+ {
+ char *fullname = symtab_to_fullname (sal.symtab);
+
+ if (fullname)
+ ui_out_field_string (uiout, "fullname", fullname);
+ }
+
+ ui_out_field_int (uiout, "line", sal.line);
+ ui_out_text (uiout, "\n");
+
+ b->line_number = sal.line;
+
+ xfree (b->source_file);
+ if (sym)
+ b->source_file = xstrdup (sal.symtab->filename);
+ else
+ b->source_file = NULL;
+
+ xfree (b->addr_string);
+ b->addr_string = xstrprintf ("%s:%d",
+ sal.symtab->filename, b->line_number);
+
+ /* Might be nice to check if function changed, and warn if
+ so. */
+
+ release_static_tracepoint_marker (marker);
+ }
+ }
+ return sal;
+}
+
static void
update_breakpoint_locations (struct breakpoint *b,
struct symtabs_and_lines sals)
update_global_location_list (1);
}
-
/* Reset a breakpoint given it's struct breakpoint * BINT.
The value we return ends up being the return value from catch_errors.
Unused in this case. */
char *s;
struct gdb_exception e;
struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
+ int marker_spec = 0;
switch (b->type)
{
case bp_hardware_breakpoint:
case bp_tracepoint:
case bp_fast_tracepoint:
+ case bp_static_tracepoint:
/* Do not attempt to re-set breakpoints disabled during startup. */
if (b->enable_state == bp_startup_disabled)
return 0;
return 0;
}
- set_language (b->language);
input_radix = b->input_radix;
s = b->addr_string;
save_current_space_and_thread ();
switch_to_program_space_and_thread (b->pspace);
+ marker_spec = b->type == bp_static_tracepoint && is_marker_spec (s);
+
+ set_language (b->language);
TRY_CATCH (e, RETURN_MASK_ERROR)
{
- sals = decode_line_1 (&s, 1, (struct symtab *) NULL, 0, (char ***) NULL,
- not_found_ptr);
+ if (marker_spec)
+ {
+ sals = decode_static_tracepoint_spec (&s);
+ if (sals.nelts > b->static_trace_marker_id_idx)
+ {
+ sals.sals[0] = sals.sals[b->static_trace_marker_id_idx];
+ sals.nelts = 1;
+ }
+ else
+ error (_("marker %s not found"), b->static_trace_marker_id);
+ }
+ else
+ sals = decode_line_1 (&s, 1, (struct symtab *) NULL, 0, (char ***) NULL,
+ not_found_ptr);
}
if (e.reason < 0)
{
b->condition_not_parsed = 0;
}
+ if (b->type == bp_static_tracepoint && !marker_spec)
+ sals.sals[0] = update_static_tracepoint (b, sals.sals[0]);
+
expanded = expand_line_sal_maybe (sals.sals[0]);
}
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
delete_breakpoint (b);
break;
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_jit_event:
break;
}
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
create_std_terminate_master_breakpoint ("std::terminate()");
+ create_exception_master_breakpoint ();
}
\f
/* Reset the thread number of this breakpoint:
case bp_breakpoint:
case bp_tracepoint:
case bp_fast_tracepoint:
+ case bp_static_tracepoint:
case bp_catchpoint:
case bp_hardware_breakpoint:
case bp_watchpoint:
case bp_breakpoint:
case bp_tracepoint:
case bp_fast_tracepoint:
+ case bp_static_tracepoint:
case bp_catchpoint:
case bp_hardware_breakpoint:
case bp_watchpoint:
paddress (gdbarch, next_pc));
}
+/* Check if the breakpoints used for software single stepping
+ were inserted or not. */
+
+int
+single_step_breakpoints_inserted (void)
+{
+ return (single_step_breakpoints[0] != NULL
+ || single_step_breakpoints[1] != NULL);
+}
+
/* Remove and delete any breakpoints used for software single step. */
void
if (create_breakpoint (get_current_arch (),
arg,
NULL, 0, 1 /* parse arg */,
- 0 /* tempflag */, 0 /* hardwareflag */,
- 1 /* traceflag */,
+ 0 /* tempflag */,
+ bp_tracepoint /* type_wanted */,
0 /* Ignore count */,
pending_break_support,
NULL,
from_tty,
- 1 /* enabled */))
+ 1 /* enabled */,
+ 0 /* internal */))
set_tracepoint_count (breakpoint_count);
}
if (create_breakpoint (get_current_arch (),
arg,
NULL, 0, 1 /* parse arg */,
- 0 /* tempflag */, 1 /* hardwareflag */,
- 1 /* traceflag */,
+ 0 /* tempflag */,
+ bp_fast_tracepoint /* type_wanted */,
0 /* Ignore count */,
pending_break_support,
NULL,
from_tty,
- 1 /* enabled */))
+ 1 /* enabled */,
+ 0 /* internal */))
+ set_tracepoint_count (breakpoint_count);
+}
+
+/* strace command implementation. Creates a static tracepoint. */
+
+void
+strace_command (char *arg, int from_tty)
+{
+ if (create_breakpoint (get_current_arch (),
+ arg,
+ NULL, 0, 1 /* parse arg */,
+ 0 /* tempflag */,
+ bp_static_tracepoint /* type_wanted */,
+ 0 /* Ignore count */,
+ pending_break_support,
+ NULL,
+ from_tty,
+ 1 /* enabled */,
+ 0 /* internal */))
set_tracepoint_count (breakpoint_count);
}
addr_str,
utp->cond_string, -1, 0 /* parse cond/thread */,
0 /* tempflag */,
- (utp->type == bp_fast_tracepoint) /* hardwareflag */,
- 1 /* traceflag */,
+ utp->type /* type_wanted */,
0 /* Ignore count */,
pending_break_support,
NULL,
0 /* from_tty */,
- utp->enabled /* enabled */))
+ utp->enabled /* enabled */,
+ 0 /* internal */))
return NULL;
set_tracepoint_count (breakpoint_count);
ALL_BREAKPOINTS (tp)
{
/* Skip internal and momentary breakpoints. */
- if (!user_settable_breakpoint (tp))
+ if (!user_settable_breakpoint (tp) || tp->number < 0)
continue;
/* If we have a filter, only save the breakpoints it accepts. */
ALL_BREAKPOINTS (tp)
{
/* Skip internal and momentary breakpoints. */
- if (!user_settable_breakpoint (tp))
+ if (!user_settable_breakpoint (tp) || tp->number < 0)
continue;
/* If we have a filter, only save the breakpoints it accepts. */
{
if (tp->type == bp_fast_tracepoint)
fprintf_unfiltered (fp, "ftrace");
+ if (tp->type == bp_static_tracepoint)
+ fprintf_unfiltered (fp, "strace");
else if (tp->type == bp_tracepoint)
fprintf_unfiltered (fp, "trace");
else if (tp->type == bp_breakpoint && tp->disposition == disp_del)
fprintf_unfiltered (fp, " commands\n");
ui_out_redirect (uiout, fp);
- TRY_CATCH (ex, RETURN_MASK_ERROR)
+ TRY_CATCH (ex, RETURN_MASK_ALL)
{
print_command_lines (uiout, tp->commands->commands, 2);
}
If a line number is specified, break at start of code for that line.\n\
If a function is specified, break at start of code for that function.\n\
If an address is specified, break at that exact address.\n\
-With no LOCATION, uses current execution address of selected stack frame.\n\
-This is useful for breaking on return to a stack frame.\n\
+With no LOCATION, uses current execution address of the selected\n\
+stack frame. This is useful for breaking on return to a stack frame.\n\
\n\
THREADNUM is the number from \"info threads\".\n\
CONDITION is a boolean expression.\n\
\n\
-Multiple breakpoints at one place are permitted, and useful if conditional.\n\
+Multiple breakpoints at one place are permitted, and useful if their\n\
+conditions are different.\n\
\n\
Do \"help breakpoints\" for info on other commands dealing with breakpoints."
help_list (save_cmdlist, "save ", -1, gdb_stdout);
}
+struct breakpoint *
+iterate_over_breakpoints (int (*callback) (struct breakpoint *, void *),
+ void *data)
+{
+ struct breakpoint *b, *temp;
+
+ ALL_BREAKPOINTS_SAFE (b, temp)
+ {
+ if ((*callback) (b, data))
+ return b;
+ }
+
+ return NULL;
+}
+
void
_initialize_breakpoint (void)
{
is executing in.\n\
\n\
See also the \"delete\" command which clears breakpoints by number."));
+ add_com_alias ("cl", "clear", class_breakpoint, 1);
c = add_com ("break", class_breakpoint, break_command, _("\
Set breakpoint at specified line or function.\n"
c = add_com ("watch", class_breakpoint, watch_command, _("\
Set a watchpoint for an expression.\n\
+Usage: watch [-l|-location] EXPRESSION\n\
A watchpoint stops execution of your program whenever the value of\n\
-an expression changes."));
+an expression changes.\n\
+If -l or -location is given, this evaluates EXPRESSION and watches\n\
+the memory to which it refers."));
set_cmd_completer (c, expression_completer);
c = add_com ("rwatch", class_breakpoint, rwatch_command, _("\
Set a read watchpoint for an expression.\n\
+Usage: rwatch [-l|-location] EXPRESSION\n\
A watchpoint stops execution of your program whenever the value of\n\
-an expression is read."));
+an expression is read.\n\
+If -l or -location is given, this evaluates EXPRESSION and watches\n\
+the memory to which it refers."));
set_cmd_completer (c, expression_completer);
c = add_com ("awatch", class_breakpoint, awatch_command, _("\
Set a watchpoint for an expression.\n\
+Usage: awatch [-l|-location] EXPRESSION\n\
A watchpoint stops execution of your program whenever the value of\n\
-an expression is either read or written."));
+an expression is either read or written.\n\
+If -l or -location is given, this evaluates EXPRESSION and watches\n\
+the memory to which it refers."));
set_cmd_completer (c, expression_completer);
add_info ("watchpoints", watchpoints_info, _("\
Do \"help tracepoints\" for info on other tracepoint commands."));
set_cmd_completer (c, location_completer);
+ c = add_com ("strace", class_breakpoint, strace_command, _("\
+Set a static tracepoint at specified line, function or marker.\n\
+\n\
+strace [LOCATION] [if CONDITION]\n\
+LOCATION may be a line number, function name, \"*\" and an address,\n\
+or -m MARKER_ID.\n\
+If a line number is specified, probe the marker at start of code\n\
+for that line. If a function is specified, probe the marker at start\n\
+of code for that function. If an address is specified, probe the marker\n\
+at that exact address. If a marker id is specified, probe the marker\n\
+with that name. With no LOCATION, uses current execution address of\n\
+the selected stack frame.\n\
+Static tracepoints accept an extra collect action -- ``collect $_sdata''.\n\
+This collects arbitrary user data passed in the probe point call to the\n\
+tracing library. You can inspect it when analyzing the trace buffer,\n\
+by printing the $_sdata variable like any other convenience variable.\n\
+\n\
+CONDITION is a boolean expression.\n\
+\n\
+Multiple tracepoints at one place are permitted, and useful if their\n\
+conditions are different.\n\
+\n\
+Do \"help breakpoints\" for info on other commands dealing with breakpoints.\n\
+Do \"help tracepoints\" for info on other tracepoint commands."));
+ set_cmd_completer (c, location_completer);
+
add_info ("tracepoints", tracepoints_info, _("\
Status of tracepoints, or tracepoint number NUMBER.\n\
Convenience variable \"$tpnum\" contains the number of the\n\
c = add_cmd ("breakpoints", class_breakpoint, save_breakpoints_command, _("\
Save current breakpoint definitions as a script.\n\
-This includes all types of breakpoints (breakpoints, watchpoints, \n\
+This includes all types of breakpoints (breakpoints, watchpoints,\n\
catchpoints, tracepoints). Use the 'source' command in another debug\n\
session to restore them."),
&save_cmdlist);