mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-11-18 17:00:49 +01:00
1384 lines
36 KiB
C
1384 lines
36 KiB
C
/*-
|
||
* This code is derived from software copyrighted by the Free Software
|
||
* Foundation.
|
||
*
|
||
* Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
|
||
* Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
|
||
*/
|
||
|
||
#ifndef lint
|
||
static char sccsid[] = "@(#)breakpoint.c 6.3 (Berkeley) 5/8/91";
|
||
#endif /* not lint */
|
||
|
||
/* Everything about breakpoints, for GDB.
|
||
Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
|
||
|
||
This file is part of GDB.
|
||
|
||
GDB is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 1, or (at your option)
|
||
any later version.
|
||
|
||
GDB is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GDB; see the file COPYING. If not, write to
|
||
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||
|
||
#include <stdio.h>
|
||
#include "defs.h"
|
||
#include "param.h"
|
||
#include "symtab.h"
|
||
#include "frame.h"
|
||
|
||
/* This is the sequence of bytes we insert for a breakpoint. */
|
||
|
||
static char break_insn[] = BREAKPOINT;
|
||
|
||
/* States of enablement of breakpoint.
|
||
`temporary' means disable when hit.
|
||
`delete' means delete when hit. */
|
||
|
||
enum enable { disabled, enabled, temporary, delete};
|
||
|
||
/* Not that the ->silent field is not currently used by any commands
|
||
(though the code is in there if it was to be and set_raw_breakpoint
|
||
does set it to 0). I implemented it because I thought it would be
|
||
useful for a hack I had to put in; I'm going to leave it in because
|
||
I can see how there might be times when it would indeed be useful */
|
||
|
||
struct breakpoint
|
||
{
|
||
struct breakpoint *next;
|
||
/* Number assigned to distinguish breakpoints. */
|
||
int number;
|
||
/* Address to break at. */
|
||
CORE_ADDR address;
|
||
/* Line number of this address. Redundant. */
|
||
int line_number;
|
||
/* Symtab of file of this address. Redundant. */
|
||
struct symtab *symtab;
|
||
/* Zero means disabled; remember the info but don't break here. */
|
||
enum enable enable;
|
||
/* Non-zero means a silent breakpoint (don't print frame info
|
||
if we stop here). */
|
||
unsigned char silent;
|
||
/* Number of stops at this breakpoint that should
|
||
be continued automatically before really stopping. */
|
||
int ignore_count;
|
||
/* "Real" contents of byte where breakpoint has been inserted.
|
||
Valid only when breakpoints are in the program. */
|
||
char shadow_contents[sizeof break_insn];
|
||
/* Nonzero if this breakpoint is now inserted. */
|
||
char inserted;
|
||
/* Nonzero if this is not the first breakpoint in the list
|
||
for the given address. */
|
||
char duplicate;
|
||
/* Chain of command lines to execute when this breakpoint is hit. */
|
||
struct command_line *commands;
|
||
/* Stack depth (address of frame). If nonzero, break only if fp
|
||
equals this. */
|
||
FRAME_ADDR frame;
|
||
/* Conditional. Break only if this expression's value is nonzero. */
|
||
struct expression *cond;
|
||
};
|
||
|
||
#define ALL_BREAKPOINTS(b) for (b = breakpoint_chain; b; b = b->next)
|
||
|
||
/* Chain of all breakpoints defined. */
|
||
|
||
struct breakpoint *breakpoint_chain;
|
||
|
||
/* Number of last breakpoint made. */
|
||
|
||
static int breakpoint_count;
|
||
|
||
/* Default address, symtab and line to put a breakpoint at
|
||
for "break" command with no arg.
|
||
if default_breakpoint_valid is zero, the other three are
|
||
not valid, and "break" with no arg is an error.
|
||
|
||
This set by print_stack_frame, which calls set_default_breakpoint. */
|
||
|
||
int default_breakpoint_valid;
|
||
CORE_ADDR default_breakpoint_address;
|
||
struct symtab *default_breakpoint_symtab;
|
||
int default_breakpoint_line;
|
||
|
||
/* Remaining commands (not yet executed)
|
||
of last breakpoint hit. */
|
||
|
||
struct command_line *breakpoint_commands;
|
||
|
||
static void delete_breakpoint ();
|
||
void clear_momentary_breakpoints ();
|
||
void breakpoint_auto_delete ();
|
||
|
||
/* Flag indicating extra verbosity for xgdb. */
|
||
extern int xgdb_verbose;
|
||
|
||
/* condition N EXP -- set break condition of breakpoint N to EXP. */
|
||
|
||
static void
|
||
condition_command (arg, from_tty)
|
||
char *arg;
|
||
int from_tty;
|
||
{
|
||
register struct breakpoint *b;
|
||
register char *p;
|
||
register int bnum;
|
||
register struct expression *expr;
|
||
|
||
if (arg == 0)
|
||
error_no_arg ("breakpoint number");
|
||
|
||
p = arg;
|
||
while (*p >= '0' && *p <= '9') p++;
|
||
if (p == arg)
|
||
/* There is no number here. (e.g. "cond a == b"). */
|
||
error_no_arg ("breakpoint number");
|
||
bnum = atoi (arg);
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->number == bnum)
|
||
{
|
||
if (b->cond)
|
||
{
|
||
free (b->cond);
|
||
b->cond = 0; /* parse_c_1 can leave this unchanged. */
|
||
}
|
||
if (*p == 0)
|
||
{
|
||
b->cond = 0;
|
||
if (from_tty)
|
||
printf ("Breakpoint %d now unconditional.\n", bnum);
|
||
}
|
||
else
|
||
{
|
||
if (*p != ' ' && *p != '\t')
|
||
error ("Arguments must be an integer (breakpoint number) and an expression.");
|
||
|
||
/* Find start of expression */
|
||
while (*p == ' ' || *p == '\t') p++;
|
||
|
||
arg = p;
|
||
b->cond = (struct expression *) parse_c_1 (&arg, block_for_pc (b->address), 0);
|
||
if (*arg)
|
||
error ("Junk at end of expression");
|
||
}
|
||
return;
|
||
}
|
||
|
||
error ("No breakpoint number %d.", bnum);
|
||
}
|
||
|
||
static void
|
||
commands_command (arg, from_tty)
|
||
char *arg;
|
||
int from_tty;
|
||
{
|
||
register struct breakpoint *b;
|
||
register char *p, *p1;
|
||
register int bnum;
|
||
struct command_line *l;
|
||
|
||
/* If we allowed this, we would have problems with when to
|
||
free the storage, if we change the commands currently
|
||
being read from. */
|
||
|
||
if (breakpoint_commands)
|
||
error ("Can't use the \"commands\" command among a breakpoint's commands.");
|
||
|
||
/* Allow commands by itself to refer to the last breakpoint. */
|
||
if (arg == 0)
|
||
bnum = breakpoint_count;
|
||
else
|
||
{
|
||
p = arg;
|
||
if (! (*p >= '0' && *p <= '9'))
|
||
error ("Argument must be integer (a breakpoint number).");
|
||
|
||
while (*p >= '0' && *p <= '9') p++;
|
||
if (*p)
|
||
error ("Unexpected extra arguments following breakpoint number.");
|
||
|
||
bnum = atoi (arg);
|
||
}
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->number == bnum)
|
||
{
|
||
if (from_tty && input_from_terminal_p ())
|
||
{
|
||
printf ("Type commands for when breakpoint %d is hit, one per line.\n\
|
||
End with a line saying just \"end\".\n", bnum);
|
||
fflush (stdout);
|
||
}
|
||
l = read_command_lines (from_tty);
|
||
free_command_lines (b->commands);
|
||
b->commands = l;
|
||
return;
|
||
}
|
||
error ("No breakpoint number %d.", bnum);
|
||
}
|
||
|
||
/* Called from command loop to execute the commands
|
||
associated with the breakpoint we just stopped at. */
|
||
|
||
void
|
||
do_breakpoint_commands ()
|
||
{
|
||
struct command_line *cmd;
|
||
|
||
while (cmd = breakpoint_commands)
|
||
{
|
||
breakpoint_commands = 0;
|
||
execute_command_lines(cmd);
|
||
/* If command was "cont", breakpoint_commands is now 0,
|
||
of if we stopped at yet another breakpoint which has commands,
|
||
it is now the commands for the new breakpoint. */
|
||
}
|
||
clear_momentary_breakpoints ();
|
||
}
|
||
|
||
/* Used when the program is proceeded, to eliminate any remaining
|
||
commands attached to the previous breakpoint we stopped at. */
|
||
|
||
void
|
||
clear_breakpoint_commands ()
|
||
{
|
||
breakpoint_commands = 0;
|
||
breakpoint_auto_delete (0);
|
||
}
|
||
|
||
/* Functions to get and set the current list of pending
|
||
breakpoint commands. These are used by run_stack_dummy
|
||
to preserve the commands around a function call. */
|
||
|
||
struct command_line *
|
||
get_breakpoint_commands ()
|
||
{
|
||
return breakpoint_commands;
|
||
}
|
||
|
||
void
|
||
set_breakpoint_commands (cmds)
|
||
struct command_line *cmds;
|
||
{
|
||
breakpoint_commands = cmds;
|
||
}
|
||
|
||
/* insert_breakpoints is used when starting or continuing the program.
|
||
remove_breakpoints is used when the program stops.
|
||
Both return zero if successful,
|
||
or an `errno' value if could not write the inferior. */
|
||
|
||
int
|
||
insert_breakpoints ()
|
||
{
|
||
register struct breakpoint *b;
|
||
int val;
|
||
|
||
#ifdef BREAKPOINT_DEBUG
|
||
printf ("Inserting breakpoints.\n");
|
||
#endif /* BREAKPOINT_DEBUG */
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->enable != disabled && ! b->inserted && ! b->duplicate)
|
||
{
|
||
read_memory (b->address, b->shadow_contents, sizeof break_insn);
|
||
val = write_memory (b->address, break_insn, sizeof break_insn);
|
||
if (val)
|
||
return val;
|
||
#ifdef BREAKPOINT_DEBUG
|
||
printf ("Inserted breakpoint at 0x%x, shadow 0x%x, 0x%x.\n",
|
||
b->address, b->shadow_contents[0], b->shadow_contents[1]);
|
||
#endif /* BREAKPOINT_DEBUG */
|
||
b->inserted = 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
remove_breakpoints ()
|
||
{
|
||
register struct breakpoint *b;
|
||
int val;
|
||
|
||
#ifdef BREAKPOINT_DEBUG
|
||
printf ("Removing breakpoints.\n");
|
||
#endif /* BREAKPOINT_DEBUG */
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->inserted)
|
||
{
|
||
val = write_memory (b->address, b->shadow_contents, sizeof break_insn);
|
||
if (val)
|
||
return val;
|
||
b->inserted = 0;
|
||
#ifdef BREAKPOINT_DEBUG
|
||
printf ("Removed breakpoint at 0x%x, shadow 0x%x, 0x%x.\n",
|
||
b->address, b->shadow_contents[0], b->shadow_contents[1]);
|
||
#endif /* BREAKPOINT_DEBUG */
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Clear the "inserted" flag in all breakpoints.
|
||
This is done when the inferior is loaded. */
|
||
|
||
void
|
||
mark_breakpoints_out ()
|
||
{
|
||
register struct breakpoint *b;
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
b->inserted = 0;
|
||
}
|
||
|
||
/* breakpoint_here_p (PC) returns 1 if an enabled breakpoint exists at PC.
|
||
When continuing from a location with a breakpoint,
|
||
we actually single step once before calling insert_breakpoints. */
|
||
|
||
int
|
||
breakpoint_here_p (pc)
|
||
CORE_ADDR pc;
|
||
{
|
||
register struct breakpoint *b;
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->enable != disabled && b->address == pc)
|
||
return 1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Evaluate the expression EXP and return 1 if value is zero.
|
||
This is used inside a catch_errors to evaluate the breakpoint condition. */
|
||
|
||
int
|
||
breakpoint_cond_eval (exp)
|
||
struct expression *exp;
|
||
{
|
||
return value_zerop (evaluate_expression (exp));
|
||
}
|
||
|
||
/* Return 0 if PC is not the address just after a breakpoint,
|
||
or -1 if breakpoint says do not stop now,
|
||
or -2 if breakpoint says it has deleted itself and don't stop,
|
||
or -3 if hit a breakpoint number -3 (delete when program stops),
|
||
or else the number of the breakpoint,
|
||
with 0x1000000 added (or subtracted, for a negative return value) for
|
||
a silent breakpoint. */
|
||
|
||
int
|
||
breakpoint_stop_status (pc, frame_address)
|
||
CORE_ADDR pc;
|
||
FRAME_ADDR frame_address;
|
||
{
|
||
register struct breakpoint *b;
|
||
register int cont = 0;
|
||
|
||
/* Get the address where the breakpoint would have been. */
|
||
pc -= DECR_PC_AFTER_BREAK;
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->enable != disabled && b->address == pc)
|
||
{
|
||
if (b->frame && b->frame != frame_address)
|
||
cont = -1;
|
||
else
|
||
{
|
||
int value_zero;
|
||
if (b->cond)
|
||
{
|
||
/* Need to select the frame, with all that implies
|
||
so that the conditions will have the right context. */
|
||
select_frame (get_current_frame (), 0);
|
||
value_zero
|
||
= catch_errors (breakpoint_cond_eval, b->cond,
|
||
"Error occurred in testing breakpoint condition.");
|
||
free_all_values ();
|
||
}
|
||
if (b->cond && value_zero)
|
||
{
|
||
cont = -1;
|
||
}
|
||
else if (b->ignore_count > 0)
|
||
{
|
||
b->ignore_count--;
|
||
cont = -1;
|
||
}
|
||
else
|
||
{
|
||
if (b->enable == temporary)
|
||
b->enable = disabled;
|
||
breakpoint_commands = b->commands;
|
||
if (b->silent
|
||
|| (breakpoint_commands
|
||
&& !strcmp ("silent", breakpoint_commands->line)))
|
||
{
|
||
if (breakpoint_commands)
|
||
breakpoint_commands = breakpoint_commands->next;
|
||
return (b->number > 0 ?
|
||
0x1000000 + b->number :
|
||
b->number - 0x1000000);
|
||
}
|
||
return b->number;
|
||
}
|
||
}
|
||
}
|
||
|
||
return cont;
|
||
}
|
||
|
||
static void
|
||
breakpoint_1 (bnum)
|
||
int bnum;
|
||
{
|
||
register struct breakpoint *b;
|
||
register struct command_line *l;
|
||
register struct symbol *sym;
|
||
CORE_ADDR last_addr = (CORE_ADDR)-1;
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (bnum == -1 || bnum == b->number)
|
||
{
|
||
printf_filtered ("#%-3d %c 0x%08x", b->number,
|
||
"nyod"[(int) b->enable],
|
||
b->address);
|
||
last_addr = b->address;
|
||
if (b->symtab)
|
||
{
|
||
sym = find_pc_function (b->address);
|
||
if (sym)
|
||
{
|
||
fputs_filtered (" in ", stdout);
|
||
fputs_demangled (SYMBOL_NAME (sym), stdout, 1);
|
||
fputs_filtered (" (", stdout);
|
||
}
|
||
fputs_filtered (b->symtab->filename, stdout);
|
||
printf_filtered (" line %d", b->line_number);
|
||
if (sym) fputs_filtered(")", stdout);
|
||
}
|
||
else
|
||
print_address_symbolic (b->address, stdout);
|
||
|
||
printf_filtered ("\n");
|
||
|
||
if (b->ignore_count)
|
||
printf_filtered ("\tignore next %d hits\n", b->ignore_count);
|
||
if (b->frame)
|
||
printf_filtered ("\tstop only in stack frame at 0x%x\n", b->frame);
|
||
if (b->cond)
|
||
{
|
||
printf_filtered ("\tbreak only if ");
|
||
print_expression (b->cond, stdout);
|
||
printf_filtered ("\n");
|
||
}
|
||
if (l = b->commands)
|
||
while (l)
|
||
{
|
||
printf_filtered ("\t%s\n", l->line);
|
||
l = l->next;
|
||
}
|
||
}
|
||
|
||
/* Compare against (CORE_ADDR)-1 in case some compiler decides
|
||
that a comparison of an unsigned with -1 is always false. */
|
||
if (last_addr != (CORE_ADDR)-1)
|
||
set_next_address (last_addr);
|
||
}
|
||
|
||
static void
|
||
breakpoints_info (bnum_exp)
|
||
char *bnum_exp;
|
||
{
|
||
int bnum = -1;
|
||
|
||
if (bnum_exp)
|
||
bnum = parse_and_eval_address (bnum_exp);
|
||
else if (breakpoint_chain == 0)
|
||
printf_filtered ("No breakpoints.\n");
|
||
else
|
||
printf_filtered ("Breakpoints:\n\
|
||
Num Enb Address Where\n");
|
||
|
||
breakpoint_1 (bnum);
|
||
}
|
||
|
||
/* Print a message describing any breakpoints set at PC. */
|
||
|
||
static void
|
||
describe_other_breakpoints (pc)
|
||
register CORE_ADDR pc;
|
||
{
|
||
register int others = 0;
|
||
register struct breakpoint *b;
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->address == pc)
|
||
others++;
|
||
if (others > 0)
|
||
{
|
||
printf ("Note: breakpoint%s ", (others > 1) ? "s" : "");
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->address == pc)
|
||
{
|
||
others--;
|
||
printf ("%d%s%s ",
|
||
b->number,
|
||
(b->enable == disabled) ? " (disabled)" : "",
|
||
(others > 1) ? "," : ((others == 1) ? " and" : ""));
|
||
}
|
||
printf ("also set at pc 0x%x.\n", pc);
|
||
}
|
||
}
|
||
|
||
/* Set the default place to put a breakpoint
|
||
for the `break' command with no arguments. */
|
||
|
||
void
|
||
set_default_breakpoint (valid, addr, symtab, line)
|
||
int valid;
|
||
CORE_ADDR addr;
|
||
struct symtab *symtab;
|
||
int line;
|
||
{
|
||
default_breakpoint_valid = valid;
|
||
default_breakpoint_address = addr;
|
||
default_breakpoint_symtab = symtab;
|
||
default_breakpoint_line = line;
|
||
}
|
||
|
||
/* Rescan breakpoints at address ADDRESS,
|
||
marking the first one as "first" and any others as "duplicates".
|
||
This is so that the bpt instruction is only inserted once. */
|
||
|
||
static void
|
||
check_duplicates (address)
|
||
CORE_ADDR address;
|
||
{
|
||
register struct breakpoint *b;
|
||
register int count = 0;
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->enable != disabled && b->address == address)
|
||
{
|
||
count++;
|
||
b->duplicate = count > 1;
|
||
}
|
||
}
|
||
|
||
/* Low level routine to set a breakpoint.
|
||
Takes as args the three things that every breakpoint must have.
|
||
Returns the breakpoint object so caller can set other things.
|
||
Does not set the breakpoint number!
|
||
Does not print anything. */
|
||
|
||
static struct breakpoint *
|
||
set_raw_breakpoint (sal)
|
||
struct symtab_and_line sal;
|
||
{
|
||
register struct breakpoint *b, *b1;
|
||
|
||
b = (struct breakpoint *) xmalloc (sizeof (struct breakpoint));
|
||
bzero (b, sizeof *b);
|
||
b->address = sal.pc;
|
||
b->symtab = sal.symtab;
|
||
b->line_number = sal.line;
|
||
b->enable = enabled;
|
||
b->next = 0;
|
||
b->silent = 0;
|
||
|
||
/* Add this breakpoint to the end of the chain
|
||
so that a list of breakpoints will come out in order
|
||
of increasing numbers. */
|
||
|
||
b1 = breakpoint_chain;
|
||
if (b1 == 0)
|
||
breakpoint_chain = b;
|
||
else
|
||
{
|
||
while (b1->next)
|
||
b1 = b1->next;
|
||
b1->next = b;
|
||
}
|
||
|
||
check_duplicates (sal.pc);
|
||
|
||
return b;
|
||
}
|
||
|
||
/* Set a breakpoint that will evaporate an end of command
|
||
at address specified by SAL.
|
||
Restrict it to frame FRAME if FRAME is nonzero. */
|
||
|
||
void
|
||
set_momentary_breakpoint (sal, frame)
|
||
struct symtab_and_line sal;
|
||
FRAME frame;
|
||
{
|
||
register struct breakpoint *b;
|
||
b = set_raw_breakpoint (sal);
|
||
b->number = -3;
|
||
b->enable = delete;
|
||
b->frame = (frame ? FRAME_FP (frame) : 0);
|
||
}
|
||
|
||
void
|
||
clear_momentary_breakpoints ()
|
||
{
|
||
register struct breakpoint *b;
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->number == -3)
|
||
{
|
||
delete_breakpoint (b);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Set a breakpoint from a symtab and line.
|
||
If TEMPFLAG is nonzero, it is a temporary breakpoint.
|
||
Print the same confirmation messages that the breakpoint command prints. */
|
||
|
||
void
|
||
set_breakpoint (s, line, tempflag)
|
||
struct symtab *s;
|
||
int line;
|
||
int tempflag;
|
||
{
|
||
register struct breakpoint *b;
|
||
struct symtab_and_line sal;
|
||
|
||
sal.symtab = s;
|
||
sal.line = line;
|
||
sal.pc = find_line_pc (sal.symtab, sal.line);
|
||
if (sal.pc == 0)
|
||
error ("No line %d in file \"%s\".\n", sal.line, sal.symtab->filename);
|
||
else
|
||
{
|
||
describe_other_breakpoints (sal.pc);
|
||
|
||
b = set_raw_breakpoint (sal);
|
||
b->number = ++breakpoint_count;
|
||
b->cond = 0;
|
||
if (tempflag)
|
||
b->enable = temporary;
|
||
|
||
printf ("Breakpoint %d at 0x%x", b->number, b->address);
|
||
if (b->symtab)
|
||
printf (": file %s, line %d.", b->symtab->filename, b->line_number);
|
||
printf ("\n");
|
||
}
|
||
}
|
||
|
||
/* Set a breakpoint according to ARG (function, linenum or *address)
|
||
and make it temporary if TEMPFLAG is nonzero. */
|
||
|
||
static void
|
||
break_command_1 (arg, tempflag, from_tty)
|
||
char *arg;
|
||
int tempflag, from_tty;
|
||
{
|
||
struct symtabs_and_lines sals;
|
||
struct symtab_and_line sal;
|
||
register struct expression *cond = 0;
|
||
register struct breakpoint *b;
|
||
char *save_arg;
|
||
int i;
|
||
CORE_ADDR pc;
|
||
|
||
sals.sals = NULL;
|
||
sals.nelts = 0;
|
||
|
||
sal.line = sal.pc = sal.end = 0;
|
||
sal.symtab = 0;
|
||
|
||
/* If no arg given, or if first arg is 'if ', use the default breakpoint. */
|
||
|
||
if (!arg || (arg[0] == 'i' && arg[1] == 'f'
|
||
&& (arg[2] == ' ' || arg[2] == '\t')))
|
||
{
|
||
if (default_breakpoint_valid)
|
||
{
|
||
sals.sals = (struct symtab_and_line *)
|
||
malloc (sizeof (struct symtab_and_line));
|
||
sal.pc = default_breakpoint_address;
|
||
sal.line = default_breakpoint_line;
|
||
sal.symtab = default_breakpoint_symtab;
|
||
sals.sals[0] = sal;
|
||
sals.nelts = 1;
|
||
}
|
||
else
|
||
error ("No default breakpoint address now.");
|
||
}
|
||
else
|
||
/* Force almost all breakpoints to be in terms of the
|
||
current_source_symtab (which is decode_line_1's default). This
|
||
should produce the results we want almost all of the time while
|
||
leaving default_breakpoint_* alone. */
|
||
if (default_breakpoint_valid
|
||
&& (!current_source_symtab
|
||
|| (arg && (*arg == '+' || *arg == '-'))))
|
||
sals = decode_line_1 (&arg, 1, default_breakpoint_symtab,
|
||
default_breakpoint_line);
|
||
else
|
||
sals = decode_line_1 (&arg, 1, 0, 0);
|
||
|
||
if (! sals.nelts)
|
||
return;
|
||
|
||
save_arg = arg;
|
||
for (i = 0; i < sals.nelts; i++)
|
||
{
|
||
sal = sals.sals[i];
|
||
if (sal.pc == 0 && sal.symtab != 0)
|
||
{
|
||
pc = find_line_pc (sal.symtab, sal.line);
|
||
if (pc == 0)
|
||
error ("No line %d in file \"%s\".",
|
||
sal.line, sal.symtab->filename);
|
||
}
|
||
else
|
||
pc = sal.pc;
|
||
|
||
while (arg && *arg)
|
||
{
|
||
if (arg[0] == 'i' && arg[1] == 'f'
|
||
&& (arg[2] == ' ' || arg[2] == '\t'))
|
||
cond = (struct expression *) parse_c_1 ((arg += 2, &arg),
|
||
block_for_pc (pc), 0);
|
||
else
|
||
error ("Junk at end of arguments.");
|
||
}
|
||
arg = save_arg;
|
||
sals.sals[i].pc = pc;
|
||
}
|
||
|
||
for (i = 0; i < sals.nelts; i++)
|
||
{
|
||
sal = sals.sals[i];
|
||
|
||
if (from_tty)
|
||
describe_other_breakpoints (sal.pc);
|
||
|
||
b = set_raw_breakpoint (sal);
|
||
b->number = ++breakpoint_count;
|
||
b->cond = cond;
|
||
if (tempflag)
|
||
b->enable = temporary;
|
||
|
||
printf ("Breakpoint %d at 0x%x", b->number, b->address);
|
||
if (b->symtab)
|
||
printf (": file %s, line %d.", b->symtab->filename, b->line_number);
|
||
printf ("\n");
|
||
}
|
||
|
||
if (sals.nelts > 1)
|
||
{
|
||
printf ("Multiple breakpoints were set.\n");
|
||
printf ("Use the \"delete\" command to delete unwanted breakpoints.\n");
|
||
}
|
||
free (sals.sals);
|
||
}
|
||
|
||
static void
|
||
break_command (arg, from_tty)
|
||
char *arg;
|
||
int from_tty;
|
||
{
|
||
break_command_1 (arg, 0, from_tty);
|
||
}
|
||
|
||
static void
|
||
tbreak_command (arg, from_tty)
|
||
char *arg;
|
||
int from_tty;
|
||
{
|
||
break_command_1 (arg, 1, from_tty);
|
||
}
|
||
|
||
/*
|
||
* Helper routine for the until_command routine in infcmd.c. Here
|
||
* because it uses the mechanisms of breakpoints.
|
||
*/
|
||
void
|
||
until_break_command (arg, from_tty)
|
||
char *arg;
|
||
int from_tty;
|
||
{
|
||
struct symtabs_and_lines sals;
|
||
struct symtab_and_line sal;
|
||
FRAME prev_frame = get_prev_frame (selected_frame);
|
||
|
||
clear_proceed_status ();
|
||
|
||
/* Set a breakpoint where the user wants it and at return from
|
||
this function */
|
||
|
||
if (default_breakpoint_valid)
|
||
sals = decode_line_1 (&arg, 1, default_breakpoint_symtab,
|
||
default_breakpoint_line);
|
||
else
|
||
sals = decode_line_1 (&arg, 1, 0, 0);
|
||
|
||
if (sals.nelts != 1)
|
||
error ("Couldn't get information on specified line.");
|
||
|
||
sal = sals.sals[0];
|
||
free (sals.sals); /* malloc'd, so freed */
|
||
|
||
if (*arg)
|
||
error ("Junk at end of arguments.");
|
||
|
||
if (sal.pc == 0 && sal.symtab != 0)
|
||
sal.pc = find_line_pc (sal.symtab, sal.line);
|
||
|
||
if (sal.pc == 0)
|
||
error ("No line %d in file \"%s\".", sal.line, sal.symtab->filename);
|
||
|
||
set_momentary_breakpoint (sal, selected_frame);
|
||
|
||
/* Keep within the current frame */
|
||
|
||
if (prev_frame)
|
||
{
|
||
struct frame_info *fi;
|
||
|
||
fi = get_frame_info (prev_frame);
|
||
sal = find_pc_line (fi->pc, 0);
|
||
sal.pc = fi->pc;
|
||
set_momentary_breakpoint (sal, prev_frame);
|
||
}
|
||
|
||
proceed (-1, -1, 0);
|
||
}
|
||
|
||
static void
|
||
clear_command (arg, from_tty)
|
||
char *arg;
|
||
int from_tty;
|
||
{
|
||
register struct breakpoint *b, *b1;
|
||
struct symtabs_and_lines sals;
|
||
struct symtab_and_line sal;
|
||
register struct breakpoint *found;
|
||
int i;
|
||
|
||
if (arg)
|
||
{
|
||
sals = decode_line_spec (arg, 1);
|
||
}
|
||
else
|
||
{
|
||
sals.sals = (struct symtab_and_line *) malloc (sizeof (struct symtab_and_line));
|
||
sal.line = default_breakpoint_line;
|
||
sal.symtab = default_breakpoint_symtab;
|
||
sal.pc = 0;
|
||
if (sal.symtab == 0)
|
||
error ("No source file specified.");
|
||
|
||
sals.sals[0] = sal;
|
||
sals.nelts = 1;
|
||
}
|
||
|
||
for (i = 0; i < sals.nelts; i++)
|
||
{
|
||
/* If exact pc given, clear bpts at that pc.
|
||
But if sal.pc is zero, clear all bpts on specified line. */
|
||
sal = sals.sals[i];
|
||
found = (struct breakpoint *) 0;
|
||
while (breakpoint_chain
|
||
&& (sal.pc ? breakpoint_chain->address == sal.pc
|
||
: (breakpoint_chain->symtab == sal.symtab
|
||
&& breakpoint_chain->line_number == sal.line)))
|
||
{
|
||
b1 = breakpoint_chain;
|
||
breakpoint_chain = b1->next;
|
||
b1->next = found;
|
||
found = b1;
|
||
}
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
while (b->next
|
||
&& (sal.pc ? b->next->address == sal.pc
|
||
: (b->next->symtab == sal.symtab
|
||
&& b->next->line_number == sal.line)))
|
||
{
|
||
b1 = b->next;
|
||
b->next = b1->next;
|
||
b1->next = found;
|
||
found = b1;
|
||
}
|
||
|
||
if (found == 0)
|
||
error ("No breakpoint at %s.", arg);
|
||
|
||
if (found->next) from_tty = 1; /* Always report if deleted more than one */
|
||
if (from_tty) printf ("Deleted breakpoint%s ", found->next ? "s" : "");
|
||
while (found)
|
||
{
|
||
if (from_tty) printf ("%d ", found->number);
|
||
b1 = found->next;
|
||
delete_breakpoint (found);
|
||
found = b1;
|
||
}
|
||
if (from_tty) putchar ('\n');
|
||
}
|
||
free (sals.sals);
|
||
}
|
||
|
||
/* Delete breakpoint number BNUM if it is a `delete' breakpoint.
|
||
This is called after breakpoint BNUM has been hit.
|
||
Also delete any breakpoint numbered -3 unless there are breakpoint
|
||
commands to be executed. */
|
||
|
||
void
|
||
breakpoint_auto_delete (bnum)
|
||
int bnum;
|
||
{
|
||
register struct breakpoint *b;
|
||
if (bnum != 0)
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->number == bnum)
|
||
{
|
||
if (b->enable == delete)
|
||
delete_breakpoint (b);
|
||
break;
|
||
}
|
||
if (breakpoint_commands == 0)
|
||
clear_momentary_breakpoints ();
|
||
}
|
||
|
||
static void
|
||
delete_breakpoint (bpt)
|
||
struct breakpoint *bpt;
|
||
{
|
||
register struct breakpoint *b;
|
||
|
||
if (bpt->inserted)
|
||
write_memory (bpt->address, bpt->shadow_contents, sizeof break_insn);
|
||
|
||
if (breakpoint_chain == bpt)
|
||
breakpoint_chain = bpt->next;
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->next == bpt)
|
||
{
|
||
b->next = bpt->next;
|
||
break;
|
||
}
|
||
|
||
check_duplicates (bpt->address);
|
||
|
||
free_command_lines (bpt->commands);
|
||
if (bpt->cond)
|
||
free (bpt->cond);
|
||
|
||
if (xgdb_verbose && bpt->number >=0)
|
||
printf ("breakpoint #%d deleted\n", bpt->number);
|
||
|
||
free (bpt);
|
||
}
|
||
|
||
static void map_breakpoint_numbers ();
|
||
|
||
static void
|
||
delete_command (arg, from_tty)
|
||
char *arg;
|
||
int from_tty;
|
||
{
|
||
register struct breakpoint *b, *b1;
|
||
|
||
if (arg == 0)
|
||
{
|
||
/* Ask user only if there are some breakpoints to delete. */
|
||
if (!from_tty
|
||
|| breakpoint_chain && query ("Delete all breakpoints? "))
|
||
{
|
||
/* No arg; clear all breakpoints. */
|
||
while (breakpoint_chain)
|
||
delete_breakpoint (breakpoint_chain);
|
||
}
|
||
}
|
||
else
|
||
map_breakpoint_numbers (arg, delete_breakpoint);
|
||
}
|
||
|
||
/* Delete all breakpoints.
|
||
Done when new symtabs are loaded, since the break condition expressions
|
||
may become invalid, and the breakpoints are probably wrong anyway. */
|
||
|
||
void
|
||
clear_breakpoints ()
|
||
{
|
||
delete_command (0, 0);
|
||
}
|
||
|
||
/* Set ignore-count of breakpoint number BPTNUM to COUNT.
|
||
If from_tty is nonzero, it prints a message to that effect,
|
||
which ends with a period (no newline). */
|
||
|
||
void
|
||
set_ignore_count (bptnum, count, from_tty)
|
||
int bptnum, count, from_tty;
|
||
{
|
||
register struct breakpoint *b;
|
||
|
||
if (count < 0)
|
||
count = 0;
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->number == bptnum)
|
||
{
|
||
b->ignore_count = count;
|
||
if (!from_tty)
|
||
return;
|
||
else if (count == 0)
|
||
printf ("Will stop next time breakpoint %d is reached.", bptnum);
|
||
else if (count == 1)
|
||
printf ("Will ignore next crossing of breakpoint %d.", bptnum);
|
||
else
|
||
printf ("Will ignore next %d crossings of breakpoint %d.",
|
||
count, bptnum);
|
||
return;
|
||
}
|
||
|
||
error ("No breakpoint number %d.", bptnum);
|
||
}
|
||
|
||
/* Clear the ignore counts of all breakpoints. */
|
||
void
|
||
breakpoint_clear_ignore_counts ()
|
||
{
|
||
struct breakpoint *b;
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
b->ignore_count = 0;
|
||
}
|
||
|
||
/* Command to set ignore-count of breakpoint N to COUNT. */
|
||
|
||
static void
|
||
ignore_command (args, from_tty)
|
||
char *args;
|
||
int from_tty;
|
||
{
|
||
register char *p = args;
|
||
register int num;
|
||
|
||
if (p == 0)
|
||
error_no_arg ("a breakpoint number");
|
||
|
||
while (*p >= '0' && *p <= '9') p++;
|
||
if (*p && *p != ' ' && *p != '\t')
|
||
error ("First argument must be a breakpoint number.");
|
||
|
||
num = atoi (args);
|
||
|
||
if (*p == 0)
|
||
error ("Second argument (specified ignore-count) is missing.");
|
||
|
||
set_ignore_count (num, parse_and_eval_address (p), from_tty);
|
||
printf ("\n");
|
||
}
|
||
|
||
/* Call FUNCTION on each of the breakpoints
|
||
whose numbers are given in ARGS. */
|
||
|
||
static void
|
||
map_breakpoint_numbers (args, function)
|
||
char *args;
|
||
void (*function) ();
|
||
{
|
||
register char *p = args;
|
||
register char *p1;
|
||
register int num;
|
||
register struct breakpoint *b;
|
||
|
||
if (p == 0)
|
||
error_no_arg ("one or more breakpoint numbers");
|
||
|
||
while (*p)
|
||
{
|
||
p1 = p;
|
||
while (*p1 >= '0' && *p1 <= '9') p1++;
|
||
if (*p1 && *p1 != ' ' && *p1 != '\t')
|
||
error ("Arguments must be breakpoint numbers.");
|
||
|
||
num = atoi (p);
|
||
|
||
ALL_BREAKPOINTS (b)
|
||
if (b->number == num)
|
||
{
|
||
function (b);
|
||
goto win;
|
||
}
|
||
printf ("No breakpoint number %d.\n", num);
|
||
win:
|
||
p = p1;
|
||
while (*p == ' ' || *p == '\t') p++;
|
||
}
|
||
}
|
||
|
||
static void
|
||
enable_breakpoint (bpt)
|
||
struct breakpoint *bpt;
|
||
{
|
||
bpt->enable = enabled;
|
||
|
||
if (xgdb_verbose && bpt->number >= 0)
|
||
printf ("breakpoint #%d enabled\n", bpt->number);
|
||
|
||
check_duplicates (bpt->address);
|
||
}
|
||
|
||
static void
|
||
enable_command (args)
|
||
char *args;
|
||
{
|
||
struct breakpoint *bpt;
|
||
if (args == 0)
|
||
ALL_BREAKPOINTS (bpt)
|
||
enable_breakpoint (bpt);
|
||
else
|
||
map_breakpoint_numbers (args, enable_breakpoint);
|
||
}
|
||
|
||
static void
|
||
disable_breakpoint (bpt)
|
||
struct breakpoint *bpt;
|
||
{
|
||
bpt->enable = disabled;
|
||
|
||
if (xgdb_verbose && bpt->number >= 0)
|
||
printf ("breakpoint #%d disabled\n", bpt->number);
|
||
|
||
check_duplicates (bpt->address);
|
||
}
|
||
|
||
static void
|
||
disable_command (args)
|
||
char *args;
|
||
{
|
||
register struct breakpoint *bpt;
|
||
if (args == 0)
|
||
ALL_BREAKPOINTS (bpt)
|
||
disable_breakpoint (bpt);
|
||
else
|
||
map_breakpoint_numbers (args, disable_breakpoint);
|
||
}
|
||
|
||
static void
|
||
enable_once_breakpoint (bpt)
|
||
struct breakpoint *bpt;
|
||
{
|
||
bpt->enable = temporary;
|
||
|
||
check_duplicates (bpt->address);
|
||
}
|
||
|
||
static void
|
||
enable_once_command (args)
|
||
char *args;
|
||
{
|
||
map_breakpoint_numbers (args, enable_once_breakpoint);
|
||
}
|
||
|
||
static void
|
||
enable_delete_breakpoint (bpt)
|
||
struct breakpoint *bpt;
|
||
{
|
||
bpt->enable = delete;
|
||
|
||
check_duplicates (bpt->address);
|
||
}
|
||
|
||
static void
|
||
enable_delete_command (args)
|
||
char *args;
|
||
{
|
||
map_breakpoint_numbers (args, enable_delete_breakpoint);
|
||
}
|
||
|
||
/*
|
||
* Use default_breakpoint_'s, or nothing if they aren't valid.
|
||
*/
|
||
struct symtabs_and_lines
|
||
decode_line_spec_1 (string, funfirstline)
|
||
char *string;
|
||
int funfirstline;
|
||
{
|
||
struct symtabs_and_lines sals;
|
||
if (string == 0)
|
||
error ("Empty line specification.");
|
||
if (default_breakpoint_valid)
|
||
sals = decode_line_1 (&string, funfirstline,
|
||
default_breakpoint_symtab, default_breakpoint_line);
|
||
else
|
||
sals = decode_line_1 (&string, funfirstline, 0, 0);
|
||
if (*string)
|
||
error ("Junk at end of line specification: %s", string);
|
||
return sals;
|
||
}
|
||
|
||
|
||
/* Chain containing all defined enable commands. */
|
||
|
||
extern struct cmd_list_element
|
||
*enablelist, *disablelist,
|
||
*deletelist, *enablebreaklist;
|
||
|
||
extern struct cmd_list_element *cmdlist;
|
||
|
||
void
|
||
_initialize_breakpoint ()
|
||
{
|
||
breakpoint_chain = 0;
|
||
breakpoint_count = 0;
|
||
|
||
add_com ("ignore", class_breakpoint, ignore_command,
|
||
"Set ignore-count of breakpoint number N to COUNT.");
|
||
|
||
add_com ("commands", class_breakpoint, commands_command,
|
||
"Set commands to be executed when a breakpoint is hit.\n\
|
||
Give breakpoint number as argument after \"commands\".\n\
|
||
With no argument, the targeted breakpoint is the last one set.\n\
|
||
The commands themselves follow starting on the next line.\n\
|
||
Type a line containing \"end\" to indicate the end of them.\n\
|
||
Give \"silent\" as the first line to make the breakpoint silent;\n\
|
||
then no output is printed when it is hit, except what the commands print.");
|
||
|
||
add_com ("condition", class_breakpoint, condition_command,
|
||
"Specify breakpoint number N to break only if COND is true.\n\
|
||
N is an integer; COND is a C expression to be evaluated whenever\n\
|
||
breakpoint N is reached. Actually break only when COND is nonzero.");
|
||
|
||
add_com ("tbreak", class_breakpoint, tbreak_command,
|
||
"Set a temporary breakpoint. Args like \"break\" command.\n\
|
||
Like \"break\" except the breakpoint is only enabled temporarily,\n\
|
||
so it will be disabled when hit. Equivalent to \"break\" followed\n\
|
||
by using \"enable once\" on the breakpoint number.");
|
||
|
||
add_prefix_cmd ("enable", class_breakpoint, enable_command,
|
||
"Enable some breakpoints or auto-display expressions.\n\
|
||
Give breakpoint numbers (separated by spaces) as arguments.\n\
|
||
With no subcommand, breakpoints are enabled until you command otherwise.\n\
|
||
This is used to cancel the effect of the \"disable\" command.\n\
|
||
With a subcommand you can enable temporarily.\n\
|
||
\n\
|
||
The \"display\" subcommand applies to auto-displays instead of breakpoints.",
|
||
&enablelist, "enable ", 1, &cmdlist);
|
||
|
||
add_abbrev_prefix_cmd ("breakpoints", class_breakpoint, enable_command,
|
||
"Enable some breakpoints or auto-display expressions.\n\
|
||
Give breakpoint numbers (separated by spaces) as arguments.\n\
|
||
With no subcommand, breakpoints are enabled until you command otherwise.\n\
|
||
This is used to cancel the effect of the \"disable\" command.\n\
|
||
May be abbreviates to simply \"enable\".\n\
|
||
With a subcommand you can enable temporarily.",
|
||
&enablebreaklist, "enable breakpoints ", 1, &enablelist);
|
||
|
||
add_cmd ("once", no_class, enable_once_command,
|
||
"Enable breakpoints for one hit. Give breakpoint numbers.\n\
|
||
If a breakpoint is hit while enabled in this fashion, it becomes disabled.\n\
|
||
See the \"tbreak\" command which sets a breakpoint and enables it once.",
|
||
&enablebreaklist);
|
||
|
||
add_cmd ("delete", no_class, enable_delete_command,
|
||
"Enable breakpoints and delete when hit. Give breakpoint numbers.\n\
|
||
If a breakpoint is hit while enabled in this fashion, it is deleted.",
|
||
&enablebreaklist);
|
||
|
||
add_cmd ("delete", no_class, enable_delete_command,
|
||
"Enable breakpoints and delete when hit. Give breakpoint numbers.\n\
|
||
If a breakpoint is hit while enabled in this fashion, it is deleted.",
|
||
&enablelist);
|
||
|
||
add_cmd ("once", no_class, enable_once_command,
|
||
"Enable breakpoints for one hit. Give breakpoint numbers.\n\
|
||
If a breakpoint is hit while enabled in this fashion, it becomes disabled.\n\
|
||
See the \"tbreak\" command which sets a breakpoint and enables it once.",
|
||
&enablelist);
|
||
|
||
add_prefix_cmd ("disable", class_breakpoint, disable_command,
|
||
"Disable some breakpoints or auto-display expressions.\n\
|
||
Arguments are breakpoint numbers with spaces in between.\n\
|
||
To disable all breakpoints, give no argument.\n\
|
||
A disabled breakpoint is not forgotten, but has no effect until reenabled.\n\
|
||
\n\
|
||
The \"display\" subcommand applies to auto-displays instead of breakpoints.",
|
||
&disablelist, "disable ", 1, &cmdlist);
|
||
add_com_alias ("dis", "disable", class_breakpoint, 1);
|
||
add_com_alias ("disa", "disable", class_breakpoint, 1);
|
||
|
||
add_abbrev_cmd ("breakpoints", class_breakpoint, disable_command,
|
||
"Disable some breakpoints or auto-display expressions.\n\
|
||
Arguments are breakpoint numbers with spaces in between.\n\
|
||
To disable all breakpoints, give no argument.\n\
|
||
A disabled breakpoint is not forgotten, but has no effect until reenabled.\n\
|
||
This command may be abbreviated \"disable\".",
|
||
&disablelist);
|
||
|
||
add_prefix_cmd ("delete", class_breakpoint, delete_command,
|
||
"Delete some breakpoints or auto-display expressions.\n\
|
||
Arguments are breakpoint numbers with spaces in between.\n\
|
||
To delete all breakpoints, give no argument.\n\
|
||
\n\
|
||
Also a prefix command for deletion of other GDB objects.\n\
|
||
The \"unset\" command is also an alias for \"delete\".",
|
||
&deletelist, "delete ", 1, &cmdlist);
|
||
add_com_alias ("d", "delete", class_breakpoint, 1);
|
||
add_com_alias ("unset", "delete", class_alias, 1);
|
||
|
||
add_cmd ("breakpoints", class_alias, delete_command,
|
||
"Delete some breakpoints or auto-display expressions.\n\
|
||
Arguments are breakpoint numbers with spaces in between.\n\
|
||
To delete all breakpoints, give no argument.\n\
|
||
This command may be abbreviated \"delete\".",
|
||
&deletelist);
|
||
|
||
add_com ("clear", class_breakpoint, clear_command,
|
||
"Clear breakpoint at specified line or function.\n\
|
||
Argument may be line number, function name, or \"*\" and an address.\n\
|
||
If line number is specified, all breakpoints in that line are cleared.\n\
|
||
If function is specified, breakpoints at beginning of function are cleared.\n\
|
||
If an address is specified, breakpoints at that address are cleared.\n\n\
|
||
With no argument, clears all breakpoints in the line that the selected frame\n\
|
||
is executing in.\n\
|
||
\n\
|
||
See also the \"delete\" command which clears breakpoints by number.");
|
||
|
||
add_com ("break", class_breakpoint, break_command,
|
||
"Set breakpoint at specified line or function.\n\
|
||
Argument may be line number, function name, or \"*\" and an address.\n\
|
||
If line number is specified, break at start of code for that line.\n\
|
||
If 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 arg, uses current execution address of selected stack frame.\n\
|
||
This is useful for breaking on return to a stack frame.\n\
|
||
\n\
|
||
Multiple breakpoints at one place are permitted, and useful if conditional.\n\
|
||
\n\
|
||
Do \"help breakpoints\" for info on other commands dealing with breakpoints.");
|
||
add_com_alias ("b", "break", class_run, 1);
|
||
add_com_alias ("br", "break", class_run, 1);
|
||
add_com_alias ("bre", "break", class_run, 1);
|
||
add_com_alias ("brea", "break", class_run, 1);
|
||
|
||
add_info ("breakpoints", breakpoints_info,
|
||
"Status of all breakpoints, or breakpoint number NUMBER.\n\
|
||
Second column is \"y\" for enabled breakpoint, \"n\" for disabled,\n\
|
||
\"o\" for enabled once (disable when hit), \"d\" for enable but delete when hit.\n\
|
||
Then come the address and the file/line number.\n\n\
|
||
Convenience variable \"$_\" and default examine address for \"x\"\n\
|
||
are set to the address of the last breakpoint listed.");
|
||
}
|
||
|