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 |