| Index: gdb/gdbserver/mem-break.c
|
| diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
|
| index 60b83b103be605d147539f38f8f3ecd7e302b2b3..39dcb865966626103be2fdab65fc79b58ba5b2cb 100644
|
| --- a/gdb/gdbserver/mem-break.c
|
| +++ b/gdb/gdbserver/mem-break.c
|
| @@ -20,6 +20,9 @@
|
| along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
| #include "server.h"
|
| +#include "regcache.h"
|
| +#include "ax.h"
|
| +#include <stdint.h>
|
|
|
| const unsigned char *breakpoint_data;
|
| int breakpoint_len;
|
| @@ -85,6 +88,30 @@ enum bkpt_type
|
| other_breakpoint,
|
| };
|
|
|
| +struct point_cond_list
|
| +{
|
| + /* Pointer to the agent expression that is the breakpoint's
|
| + conditional. */
|
| + struct agent_expr *cond;
|
| +
|
| + /* Pointer to the next condition. */
|
| + struct point_cond_list *next;
|
| +};
|
| +
|
| +struct point_command_list
|
| +{
|
| + /* Pointer to the agent expression that is the breakpoint's
|
| + commands. */
|
| + struct agent_expr *cmd;
|
| +
|
| + /* Flag that is true if this command should run even while GDB is
|
| + disconnected. */
|
| + int persistence;
|
| +
|
| + /* Pointer to the next command. */
|
| + struct point_command_list *next;
|
| +};
|
| +
|
| /* A high level (in gdbserver's perspective) breakpoint. */
|
| struct breakpoint
|
| {
|
| @@ -93,6 +120,15 @@ struct breakpoint
|
| /* The breakpoint's type. */
|
| enum bkpt_type type;
|
|
|
| + /* Pointer to the condition list that should be evaluated on
|
| + the target or NULL if the breakpoint is unconditional or
|
| + if GDB doesn't want us to evaluate the conditionals on the
|
| + target's side. */
|
| + struct point_cond_list *cond_list;
|
| +
|
| + /* Point to the list of commands to run when this is hit. */
|
| + struct point_command_list *command_list;
|
| +
|
| /* Link to this breakpoint's raw breakpoint. This is always
|
| non-NULL. */
|
| struct raw_breakpoint *raw;
|
| @@ -103,6 +139,23 @@ struct breakpoint
|
| int (*handler) (CORE_ADDR);
|
| };
|
|
|
| +int
|
| +any_persistent_commands ()
|
| +{
|
| + struct process_info *proc = current_process ();
|
| + struct breakpoint *bp;
|
| + struct point_command_list *cl;
|
| +
|
| + for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
|
| + {
|
| + for (cl = bp->command_list; cl != NULL; cl = cl->next)
|
| + if (cl->persistence)
|
| + return 1;
|
| + }
|
| +
|
| + return 0;
|
| +}
|
| +
|
| static struct raw_breakpoint *
|
| find_raw_breakpoint_at (CORE_ADDR where)
|
| {
|
| @@ -632,7 +685,7 @@ delete_breakpoint (struct breakpoint *todel)
|
| return delete_breakpoint_1 (proc, todel);
|
| }
|
|
|
| -static struct breakpoint *
|
| +struct breakpoint *
|
| find_gdb_breakpoint_at (CORE_ADDR where)
|
| {
|
| struct process_info *proc = current_process ();
|
| @@ -692,6 +745,9 @@ delete_gdb_breakpoint_at (CORE_ADDR addr)
|
| if (bp == NULL)
|
| return -1;
|
|
|
| + /* Before deleting the breakpoint, make sure to free
|
| + its condition list. */
|
| + clear_gdb_breakpoint_conditions (addr);
|
| err = delete_breakpoint (bp);
|
| if (err)
|
| return -1;
|
| @@ -699,12 +755,220 @@ delete_gdb_breakpoint_at (CORE_ADDR addr)
|
| return 0;
|
| }
|
|
|
| +/* Clear all conditions associated with this breakpoint address. */
|
| +
|
| +void
|
| +clear_gdb_breakpoint_conditions (CORE_ADDR addr)
|
| +{
|
| + struct breakpoint *bp = find_gdb_breakpoint_at (addr);
|
| + struct point_cond_list *cond;
|
| +
|
| + if (bp == NULL || bp->cond_list == NULL)
|
| + return;
|
| +
|
| + cond = bp->cond_list;
|
| +
|
| + while (cond != NULL)
|
| + {
|
| + struct point_cond_list *cond_next;
|
| +
|
| + cond_next = cond->next;
|
| + free (cond->cond->bytes);
|
| + free (cond->cond);
|
| + free (cond);
|
| + cond = cond_next;
|
| + }
|
| +
|
| + bp->cond_list = NULL;
|
| +}
|
| +
|
| +/* Add condition CONDITION to GDBserver's breakpoint BP. */
|
| +
|
| +void
|
| +add_condition_to_breakpoint (struct breakpoint *bp,
|
| + struct agent_expr *condition)
|
| +{
|
| + struct point_cond_list *new_cond;
|
| +
|
| + /* Create new condition. */
|
| + new_cond = xcalloc (1, sizeof (*new_cond));
|
| + new_cond->cond = condition;
|
| +
|
| + /* Add condition to the list. */
|
| + new_cond->next = bp->cond_list;
|
| + bp->cond_list = new_cond;
|
| +}
|
| +
|
| +/* Add a target-side condition CONDITION to the breakpoint at ADDR. */
|
| +
|
| +int
|
| +add_breakpoint_condition (CORE_ADDR addr, char **condition)
|
| +{
|
| + struct breakpoint *bp = find_gdb_breakpoint_at (addr);
|
| + char *actparm = *condition;
|
| + struct agent_expr *cond;
|
| +
|
| + if (bp == NULL)
|
| + return 1;
|
| +
|
| + if (condition == NULL)
|
| + return 1;
|
| +
|
| + cond = gdb_parse_agent_expr (&actparm);
|
| +
|
| + if (cond == NULL)
|
| + {
|
| + fprintf (stderr, "Condition evaluation failed. "
|
| + "Assuming unconditional.\n");
|
| + return 0;
|
| + }
|
| +
|
| + add_condition_to_breakpoint (bp, cond);
|
| +
|
| + *condition = actparm;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +/* Evaluate condition (if any) at breakpoint BP. Return 1 if
|
| + true and 0 otherwise. */
|
| +
|
| int
|
| -gdb_breakpoint_here (CORE_ADDR where)
|
| +gdb_condition_true_at_breakpoint (CORE_ADDR where)
|
| {
|
| + /* Fetch registers for the current inferior. */
|
| struct breakpoint *bp = find_gdb_breakpoint_at (where);
|
| + ULONGEST value = 0;
|
| + struct point_cond_list *cl;
|
| + int err = 0;
|
|
|
| - return (bp != NULL);
|
| + struct regcache *regcache = get_thread_regcache (current_inferior, 1);
|
| +
|
| + if (bp == NULL)
|
| + return 0;
|
| +
|
| + /* Check if the breakpoint is unconditional. If it is,
|
| + the condition always evaluates to TRUE. */
|
| + if (bp->cond_list == NULL)
|
| + return 1;
|
| +
|
| + /* Evaluate each condition in the breakpoint's list of conditions.
|
| + Return true if any of the conditions evaluates to TRUE.
|
| +
|
| + If we failed to evaluate the expression, TRUE is returned. This
|
| + forces GDB to reevaluate the conditions. */
|
| + for (cl = bp->cond_list;
|
| + cl && !value && !err; cl = cl->next)
|
| + {
|
| + /* Evaluate the condition. */
|
| + err = gdb_eval_agent_expr (regcache, NULL, cl->cond, &value);
|
| + }
|
| +
|
| + if (err)
|
| + return 1;
|
| +
|
| + return (value != 0);
|
| +}
|
| +
|
| +/* Add commands COMMANDS to GDBserver's breakpoint BP. */
|
| +
|
| +void
|
| +add_commands_to_breakpoint (struct breakpoint *bp,
|
| + struct agent_expr *commands, int persist)
|
| +{
|
| + struct point_command_list *new_cmd;
|
| +
|
| + /* Create new command. */
|
| + new_cmd = xcalloc (1, sizeof (*new_cmd));
|
| + new_cmd->cmd = commands;
|
| + new_cmd->persistence = persist;
|
| +
|
| + /* Add commands to the list. */
|
| + new_cmd->next = bp->command_list;
|
| + bp->command_list = new_cmd;
|
| +}
|
| +
|
| +/* Add a target-side command COMMAND to the breakpoint at ADDR. */
|
| +
|
| +int
|
| +add_breakpoint_commands (CORE_ADDR addr, char **command, int persist)
|
| +{
|
| + struct breakpoint *bp = find_gdb_breakpoint_at (addr);
|
| + char *actparm = *command;
|
| + struct agent_expr *cmd;
|
| +
|
| + if (bp == NULL)
|
| + return 1;
|
| +
|
| + if (command == NULL)
|
| + return 1;
|
| +
|
| + cmd = gdb_parse_agent_expr (&actparm);
|
| +
|
| + if (cmd == NULL)
|
| + {
|
| + fprintf (stderr, "Command evaluation failed. "
|
| + "Disabling.\n");
|
| + return 0;
|
| + }
|
| +
|
| + add_commands_to_breakpoint (bp, cmd, persist);
|
| +
|
| + *command = actparm;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +/* Return true if there are no commands to run at this location,
|
| + which likely means we want to report back to GDB. */
|
| +int
|
| +gdb_no_commands_at_breakpoint (CORE_ADDR where)
|
| +{
|
| + struct breakpoint *bp = find_gdb_breakpoint_at (where);
|
| +
|
| + if (bp == NULL)
|
| + return 0;
|
| +
|
| + if (debug_threads)
|
| + fprintf (stderr, "at 0x%s, bp command_list is 0x%s\n",
|
| + paddress (where),
|
| + phex_nz ((uintptr_t) bp->command_list, 0));
|
| + return (bp->command_list == NULL);
|
| +}
|
| +
|
| +void
|
| +run_breakpoint_commands (CORE_ADDR where)
|
| +{
|
| + /* Fetch registers for the current inferior. */
|
| + struct breakpoint *bp = find_gdb_breakpoint_at (where);
|
| + ULONGEST value = 0;
|
| + struct point_command_list *cl;
|
| + int err = 0;
|
| +
|
| + struct regcache *regcache = get_thread_regcache (current_inferior, 1);
|
| +
|
| + if (bp == NULL)
|
| + return;
|
| +
|
| + for (cl = bp->command_list;
|
| + cl && !value && !err; cl = cl->next)
|
| + {
|
| + /* Run the command. */
|
| + err = gdb_eval_agent_expr (regcache, NULL, cl->cmd, &value);
|
| +
|
| + /* If one command has a problem, stop digging the hole deeper. */
|
| + if (err)
|
| + break;
|
| + }
|
| +}
|
| +
|
| +/* Return 1 if there is a breakpoint inserted in address WHERE
|
| + and if its condition, if it exists, is true. */
|
| +
|
| +int
|
| +gdb_breakpoint_here (CORE_ADDR where)
|
| +{
|
| + return (find_gdb_breakpoint_at (where) != NULL);
|
| }
|
|
|
| void
|
|
|