aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbserver/tracepoint.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/gdbserver/tracepoint.c')
-rw-r--r--gdb/gdbserver/tracepoint.c624
1 files changed, 621 insertions, 3 deletions
diff --git a/gdb/gdbserver/tracepoint.c b/gdb/gdbserver/tracepoint.c
index dc71b20..bafd82a 100644
--- a/gdb/gdbserver/tracepoint.c
+++ b/gdb/gdbserver/tracepoint.c
@@ -132,6 +132,9 @@ trace_vdebug (const char *fmt, ...)
# define traceframe_write_count gdb_agent_traceframe_write_count
# define traceframes_created gdb_agent_traceframes_created
# define trace_state_variables gdb_agent_trace_state_variables
+# define get_raw_reg gdb_agent_get_raw_reg
+# define get_trace_state_variable_value gdb_agent_get_trace_state_variable_value
+# define set_trace_state_variable_value gdb_agent_set_trace_state_variable_value
#endif
#ifndef IN_PROCESS_AGENT
@@ -162,6 +165,9 @@ struct ipa_sym_addresses
CORE_ADDR addr_traceframe_write_count;
CORE_ADDR addr_traceframes_created;
CORE_ADDR addr_trace_state_variables;
+ CORE_ADDR addr_get_raw_reg;
+ CORE_ADDR addr_get_trace_state_variable_value;
+ CORE_ADDR addr_set_trace_state_variable_value;
};
#define STRINGIZE_1(STR) #STR
@@ -200,6 +206,9 @@ static struct
IPA_SYM(traceframe_write_count),
IPA_SYM(traceframes_created),
IPA_SYM(trace_state_variables),
+ IPA_SYM(get_raw_reg),
+ IPA_SYM(get_trace_state_variable_value),
+ IPA_SYM(set_trace_state_variable_value),
};
struct ipa_sym_addresses ipa_sym_addrs;
@@ -564,6 +573,9 @@ enum tracepoint_type
struct tracepoint_hit_ctx;
+typedef enum eval_result_type (*condfn) (struct tracepoint_hit_ctx *,
+ ULONGEST *);
+
/* The definition of a tracepoint. */
/* Tracepoints may have multiple locations, each at a different
@@ -612,6 +624,8 @@ struct tracepoint
Note that while-stepping steps are not counted as "hits". */
long hit_count;
+ CORE_ADDR compiled_cond;
+
/* Link to the next tracepoint in the list. */
struct tracepoint *next;
@@ -1189,6 +1203,8 @@ static void collect_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
static void collect_data_at_step (struct tracepoint_hit_ctx *ctx,
CORE_ADDR stop_pc,
struct tracepoint *tpoint, int current_step);
+static void compile_tracepoint_condition (struct tracepoint *tpoint,
+ CORE_ADDR *jump_entry);
#endif
static void do_action_at_tracepoint (struct tracepoint_hit_ctx *ctx,
CORE_ADDR stop_pc,
@@ -1610,6 +1626,7 @@ add_tracepoint (int num, CORE_ADDR addr)
tpoint->type = trap_tracepoint;
tpoint->orig_size = -1;
tpoint->source_strings = NULL;
+ tpoint->compiled_cond = 0;
tpoint->handle = NULL;
tpoint->next = NULL;
@@ -1854,7 +1871,7 @@ create_trace_state_variable (int num, int gdb)
return tsv;
}
-static LONGEST
+IP_AGENT_EXPORT LONGEST
get_trace_state_variable_value (int num)
{
struct trace_state_variable *tsv;
@@ -1880,7 +1897,7 @@ get_trace_state_variable_value (int num)
return tsv->value;
}
-static void
+IP_AGENT_EXPORT void
set_trace_state_variable_value (int num, LONGEST val)
{
struct trace_state_variable *tsv;
@@ -3906,7 +3923,10 @@ condition_true_at_tracepoint (struct tracepoint_hit_ctx *ctx,
ULONGEST value = 0;
enum eval_result_type err;
- err = eval_agent_expr (ctx, NULL, tpoint->cond, &value);
+ if (tpoint->compiled_cond)
+ err = ((condfn) (uintptr_t) (tpoint->compiled_cond)) (ctx, &value);
+ else
+ err = eval_agent_expr (ctx, NULL, tpoint->cond, &value);
if (err != expr_eval_no_error)
{
@@ -4944,6 +4964,582 @@ gdb_collect (struct tracepoint *tpoint, unsigned char *regs)
#ifndef IN_PROCESS_AGENT
+/* Bytecode compilation. */
+
+CORE_ADDR current_insn_ptr;
+
+int emit_error;
+
+struct bytecode_address
+{
+ int pc;
+ CORE_ADDR address;
+ int goto_pc;
+ /* Offset and size of field to be modified in the goto block. */
+ int from_offset, from_size;
+ struct bytecode_address *next;
+} *bytecode_address_table;
+
+CORE_ADDR
+get_raw_reg_func_addr (void)
+{
+ return ipa_sym_addrs.addr_get_raw_reg;
+}
+
+static void
+emit_prologue (void)
+{
+ target_emit_ops ()->emit_prologue ();
+}
+
+static void
+emit_epilogue (void)
+{
+ target_emit_ops ()->emit_epilogue ();
+}
+
+static void
+emit_add (void)
+{
+ target_emit_ops ()->emit_add ();
+}
+
+static void
+emit_sub (void)
+{
+ target_emit_ops ()->emit_sub ();
+}
+
+static void
+emit_mul (void)
+{
+ target_emit_ops ()->emit_mul ();
+}
+
+static void
+emit_lsh (void)
+{
+ target_emit_ops ()->emit_lsh ();
+}
+
+static void
+emit_rsh_signed (void)
+{
+ target_emit_ops ()->emit_rsh_signed ();
+}
+
+static void
+emit_rsh_unsigned (void)
+{
+ target_emit_ops ()->emit_rsh_unsigned ();
+}
+
+static void
+emit_ext (int arg)
+{
+ target_emit_ops ()->emit_ext (arg);
+}
+
+static void
+emit_log_not (void)
+{
+ target_emit_ops ()->emit_log_not ();
+}
+
+static void
+emit_bit_and (void)
+{
+ target_emit_ops ()->emit_bit_and ();
+}
+
+static void
+emit_bit_or (void)
+{
+ target_emit_ops ()->emit_bit_or ();
+}
+
+static void
+emit_bit_xor (void)
+{
+ target_emit_ops ()->emit_bit_xor ();
+}
+
+static void
+emit_bit_not (void)
+{
+ target_emit_ops ()->emit_bit_not ();
+}
+
+static void
+emit_equal (void)
+{
+ target_emit_ops ()->emit_equal ();
+}
+
+static void
+emit_less_signed (void)
+{
+ target_emit_ops ()->emit_less_signed ();
+}
+
+static void
+emit_less_unsigned (void)
+{
+ target_emit_ops ()->emit_less_unsigned ();
+}
+
+static void
+emit_ref (int size)
+{
+ target_emit_ops ()->emit_ref (size);
+}
+
+static void
+emit_if_goto (int *offset_p, int *size_p)
+{
+ target_emit_ops ()->emit_if_goto (offset_p, size_p);
+}
+
+static void
+emit_goto (int *offset_p, int *size_p)
+{
+ target_emit_ops ()->emit_goto (offset_p, size_p);
+}
+
+static void
+write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
+{
+ target_emit_ops ()->write_goto_address (from, to, size);
+}
+
+static void
+emit_const (int64_t num)
+{
+ target_emit_ops ()->emit_const (num);
+}
+
+static void
+emit_reg (int reg)
+{
+ target_emit_ops ()->emit_reg (reg);
+}
+
+static void
+emit_pop (void)
+{
+ target_emit_ops ()->emit_pop ();
+}
+
+static void
+emit_stack_flush (void)
+{
+ target_emit_ops ()->emit_stack_flush ();
+}
+
+static void
+emit_zero_ext (int arg)
+{
+ target_emit_ops ()->emit_zero_ext (arg);
+}
+
+static void
+emit_swap (void)
+{
+ target_emit_ops ()->emit_swap ();
+}
+
+static void
+emit_stack_adjust (int n)
+{
+ target_emit_ops ()->emit_stack_adjust (n);
+}
+
+/* FN's prototype is `LONGEST(*fn)(int)'. */
+
+static void
+emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ target_emit_ops ()->emit_int_call_1 (fn, arg1);
+}
+
+/* FN's prototype is `void(*fn)(int,int64_t)'. */
+
+static void
+emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ target_emit_ops ()->emit_void_call_2 (fn, arg1);
+}
+
+static enum eval_result_type compile_bytecodes (struct agent_expr *aexpr);
+
+static void
+compile_tracepoint_condition (struct tracepoint *tpoint, CORE_ADDR *jump_entry)
+{
+ CORE_ADDR entry_point = *jump_entry;
+ enum eval_result_type err;
+
+ trace_debug ("Starting condition compilation for tracepoint %d\n",
+ tpoint->number);
+
+ /* Initialize the global pointer to the code being built. */
+ current_insn_ptr = *jump_entry;
+
+ emit_prologue ();
+
+ err = compile_bytecodes (tpoint->cond);
+
+ if (err == expr_eval_no_error)
+ {
+ emit_epilogue ();
+
+ /* Record the beginning of the compiled code. */
+ tpoint->compiled_cond = entry_point;
+
+ trace_debug ("Condition compilation for tracepoint %d complete\n",
+ tpoint->number);
+ }
+ else
+ {
+ /* Leave the unfinished code in situ, but don't point to it. */
+
+ tpoint->compiled_cond = 0;
+
+ trace_debug ("Condition compilation for tracepoint %d failed, "
+ "error code %d",
+ tpoint->number, err);
+ }
+
+ /* Update the code pointer passed in. Note that we do this even if
+ the compile fails, so that we can look at the partial results
+ instead of letting them be overwritten. */
+ *jump_entry = current_insn_ptr;
+
+ /* Leave a gap, to aid dump decipherment. */
+ *jump_entry += 16;
+}
+
+/* Given an agent expression, turn it into native code. */
+
+static enum eval_result_type
+compile_bytecodes (struct agent_expr *aexpr)
+{
+ int pc = 0;
+ int done = 0;
+ unsigned char op;
+ int arg;
+ /* This is only used to build 64-bit value for constants. */
+ ULONGEST top;
+ struct bytecode_address *aentry, *aentry2;
+
+#define UNHANDLED \
+ do \
+ { \
+ trace_debug ("Cannot compile op 0x%x\n", op); \
+ return expr_eval_unhandled_opcode; \
+ } while (0)
+
+ if (aexpr->length == 0)
+ {
+ trace_debug ("empty agent expression\n");
+ return expr_eval_empty_expression;
+ }
+
+ bytecode_address_table = NULL;
+
+ while (!done)
+ {
+ op = aexpr->bytes[pc];
+
+ trace_debug ("About to compile op 0x%x, pc=%d\n", op, pc);
+
+ /* Record the compiled-code address of the bytecode, for use by
+ jump instructions. */
+ aentry = xmalloc (sizeof (struct bytecode_address));
+ aentry->pc = pc;
+ aentry->address = current_insn_ptr;
+ aentry->goto_pc = -1;
+ aentry->from_offset = aentry->from_size = 0;
+ aentry->next = bytecode_address_table;
+ bytecode_address_table = aentry;
+
+ ++pc;
+
+ emit_error = 0;
+
+ switch (op)
+ {
+ case gdb_agent_op_add:
+ emit_add ();
+ break;
+
+ case gdb_agent_op_sub:
+ emit_sub ();
+ break;
+
+ case gdb_agent_op_mul:
+ emit_mul ();
+ break;
+
+ case gdb_agent_op_div_signed:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_div_unsigned:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_rem_signed:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_rem_unsigned:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_lsh:
+ emit_lsh ();
+ break;
+
+ case gdb_agent_op_rsh_signed:
+ emit_rsh_signed ();
+ break;
+
+ case gdb_agent_op_rsh_unsigned:
+ emit_rsh_unsigned ();
+ break;
+
+ case gdb_agent_op_trace:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_trace_quick:
+ UNHANDLED;
+ break;
+
+ case gdb_agent_op_log_not:
+ emit_log_not ();
+ break;
+
+ case gdb_agent_op_bit_and:
+ emit_bit_and ();
+ break;
+
+ case gdb_agent_op_bit_or:
+ emit_bit_or ();
+ break;
+
+ case gdb_agent_op_bit_xor:
+ emit_bit_xor ();
+ break;
+
+ case gdb_agent_op_bit_not:
+ emit_bit_not ();
+ break;
+
+ case gdb_agent_op_equal:
+ emit_equal ();
+ break;
+
+ case gdb_agent_op_less_signed:
+ emit_less_signed ();
+ break;
+
+ case gdb_agent_op_less_unsigned:
+ emit_less_unsigned ();
+ break;
+
+ case gdb_agent_op_ext:
+ arg = aexpr->bytes[pc++];
+ if (arg < (sizeof (LONGEST) * 8))
+ emit_ext (arg);
+ break;
+
+ case gdb_agent_op_ref8:
+ emit_ref (1);
+ break;
+
+ case gdb_agent_op_ref16:
+ emit_ref (2);
+ break;
+
+ case gdb_agent_op_ref32:
+ emit_ref (4);
+ break;
+
+ case gdb_agent_op_ref64:
+ emit_ref (8);
+ break;
+
+ case gdb_agent_op_if_goto:
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ aentry->goto_pc = arg;
+ emit_if_goto (&(aentry->from_offset), &(aentry->from_size));
+ break;
+
+ case gdb_agent_op_goto:
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ aentry->goto_pc = arg;
+ emit_goto (&(aentry->from_offset), &(aentry->from_size));
+ break;
+
+ case gdb_agent_op_const8:
+ emit_stack_flush ();
+ top = aexpr->bytes[pc++];
+ emit_const (top);
+ break;
+
+ case gdb_agent_op_const16:
+ emit_stack_flush ();
+ top = aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ emit_const (top);
+ break;
+
+ case gdb_agent_op_const32:
+ emit_stack_flush ();
+ top = aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ emit_const (top);
+ break;
+
+ case gdb_agent_op_const64:
+ emit_stack_flush ();
+ top = aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ top = (top << 8) + aexpr->bytes[pc++];
+ emit_const (top);
+ break;
+
+ case gdb_agent_op_reg:
+ emit_stack_flush ();
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ emit_reg (arg);
+ break;
+
+ case gdb_agent_op_end:
+ trace_debug ("At end of expression\n");
+
+ /* Assume there is one stack element left, and that it is
+ cached in "top" where emit_epilogue can get to it. */
+ emit_stack_adjust (1);
+
+ done = 1;
+ break;
+
+ case gdb_agent_op_dup:
+ /* In our design, dup is equivalent to stack flushing. */
+ emit_stack_flush ();
+ break;
+
+ case gdb_agent_op_pop:
+ emit_pop ();
+ break;
+
+ case gdb_agent_op_zero_ext:
+ arg = aexpr->bytes[pc++];
+ if (arg < (sizeof (LONGEST) * 8))
+ emit_zero_ext (arg);
+ break;
+
+ case gdb_agent_op_swap:
+ emit_swap ();
+ break;
+
+ case gdb_agent_op_getv:
+ emit_stack_flush ();
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ emit_int_call_1 (ipa_sym_addrs.addr_get_trace_state_variable_value,
+ arg);
+ break;
+
+ case gdb_agent_op_setv:
+ arg = aexpr->bytes[pc++];
+ arg = (arg << 8) + aexpr->bytes[pc++];
+ emit_void_call_2 (ipa_sym_addrs.addr_set_trace_state_variable_value,
+ arg);
+ break;
+
+ case gdb_agent_op_tracev:
+ UNHANDLED;
+ break;
+
+ /* GDB never (currently) generates any of these ops. */
+ case gdb_agent_op_float:
+ case gdb_agent_op_ref_float:
+ case gdb_agent_op_ref_double:
+ case gdb_agent_op_ref_long_double:
+ case gdb_agent_op_l_to_d:
+ case gdb_agent_op_d_to_l:
+ case gdb_agent_op_trace16:
+ UNHANDLED;
+ break;
+
+ default:
+ trace_debug ("Agent expression op 0x%x not recognized\n", op);
+ /* Don't struggle on, things will just get worse. */
+ return expr_eval_unrecognized_opcode;
+ }
+
+ /* This catches errors that occur in target-specific code
+ emission. */
+ if (emit_error)
+ {
+ trace_debug ("Error %d while emitting code for %s\n",
+ emit_error, gdb_agent_op_names[op]);
+ return expr_eval_unhandled_opcode;
+ }
+
+ trace_debug ("Op %s compiled\n", gdb_agent_op_names[op]);
+ }
+
+ /* Now fill in real addresses as goto destinations. */
+ for (aentry = bytecode_address_table; aentry; aentry = aentry->next)
+ {
+ int written = 0;
+
+ if (aentry->goto_pc < 0)
+ continue;
+
+ /* Find the location that we are going to, and call back into
+ target-specific code to write the actual address or
+ displacement. */
+ for (aentry2 = bytecode_address_table; aentry2; aentry2 = aentry2->next)
+ {
+ if (aentry2->pc == aentry->goto_pc)
+ {
+ trace_debug ("Want to jump from %s to %s\n",
+ paddress (aentry->address),
+ paddress (aentry2->address));
+ write_goto_address (aentry->address + aentry->from_offset,
+ aentry2->address, aentry->from_size);
+ written = 1;
+ break;
+ }
+ }
+
+ /* Error out if we didn't find a destination. */
+ if (!written)
+ {
+ trace_debug ("Destination of goto %d not found\n",
+ aentry->goto_pc);
+ return expr_eval_invalid_goto;
+ }
+ }
+
+ return expr_eval_no_error;
+}
+
/* We'll need to adjust these when we consider bi-arch setups, and big
endian machines. */
@@ -5022,6 +5618,28 @@ download_tracepoints (void)
if (tpoint->type != fast_tracepoint)
continue;
+ /* Maybe download a compiled condition. */
+ if (tpoint->cond != NULL && target_emit_ops () != NULL)
+ {
+ CORE_ADDR jentry, jump_entry;
+
+ jentry = jump_entry = get_jump_space_head ();
+
+ if (tpoint->cond != NULL)
+ {
+ /* Pad to 8-byte alignment. (needed?) */
+ /* Actually this should be left for the target to
+ decide. */
+ jentry = UALIGN (jentry, 8);
+
+ compile_tracepoint_condition (tpoint, &jentry);
+ }
+
+ /* Pad to 8-byte alignment. */
+ jentry = UALIGN (jentry, 8);
+ claim_jump_space (jentry - jump_entry);
+ }
+
target_tracepoint = *tpoint;
prev_tpptr = tpptr;