[gdbserver] x86 agent expression bytecode compiler (speed up conditional tracepoints)

Pedro Alves
2010-06-07T16:00:55Z

This adds a bytecode compiler to the linux x86 ports of
gdbserver, that turns agent expression bytecodes into
native code. This greatly speeds up conditional tracepoints
(by around an order of magnitude) that usually don't reference much
more than a few variables (memory/registers), to around 100ns or
less on a typical modern x86 box.

--
Pedro Alves

2010-06-07 Stan Shebs <stan@codesourcery.com>
Pedro Alves <pedro@codesourcery.com>

Bytecode compiler.

* linux-x86-low.c: Include limits.h.
(add_insns): New.
(always_true): New.
(EMIT_ASM): New.
(EMIT_ASM32): New.
(amd64_emit_prologue, amd64_emit_epilogue, amd64_emit_add)
(amd64_emit_sub, amd64_emit_mul, amd64_emit_lsh)
(amd64_emit_rsh_signed, amd64_emit_rsh_unsigned, amd64_emit_ext,
(amd64_emit_log_not, amd64_emit_bit_and, amd64_emit_bit_or)
(amd64_emit_bit_xor, amd64_emit_bit_not, amd64_emit_equal,
(amd64_emit_less_signed, amd64_emit_less_unsigned, amd64_emit_ref,
(amd64_emit_if_goto, amd64_emit_goto, amd64_write_goto_address)
(amd64_emit_const, amd64_emit_call, amd64_emit_reg)
(amd64_emit_pop, amd64_emit_stack_flush, amd64_emit_zero_ext)
(amd64_emit_swap, amd64_emit_stack_adjust, amd64_emit_int_call_1)
(amd64_emit_void_call_2): New.
(amd64_emit_ops): New.
(i386_emit_prologue, i386_emit_epilogue, i386_emit_add)
(i386_emit_sub,i386_emit_mul, i386_emit_lsh, i386_emit_rsh_signed)
(i386_emit_rsh_unsigned, i386_emit_ext, i386_emit_log_not)
(i386_emit_bit_and, i386_emit_bit_or, i386_emit_bit_xor)
(i386_emit_bit_not, i386_emit_equal, i386_emit_less_signed)
(i386_emit_less_unsigned, i386_emit_ref, i386_emit_if_goto)
(i386_emit_goto, i386_write_goto_address, i386_emit_const)
(i386_emit_call, i386_emit_reg, i386_emit_pop)
(i386_emit_stack_flush, i386_emit_zero_ext, i386_emit_swap)
(i386_emit_stack_adjust, i386_emit_int_call_1)
(i386_emit_void_call_2): New.
(i386_emit_ops): New.
(x86_emit_ops): New.
(the_low_target): Install x86_emit_ops.
* server.h (struct emit_ops): New.
(get_raw_reg_func_addr): Declare.
(current_insn_ptr, emit_error): Declare.
* tracepoint.c (get_raw_reg, get_trace_state_variable_value)
(set_trace_state_variable_value): New defines.
(struct ipa_sym_addresses): New fields addr_get_raw_reg,
addr_get_trace_state_variable_value and
addr_set_trace_state_variable_value.
(symbol_list): New fields for get_raw_reg,
get_trace_state_variable_value and set_trace_state_variable_value.
(condfn): New typedef.
(struct tracepoint): New field `compiled_cond'.
(do_action_at_tracepoint): Clear compiled_cond.
(get_trace_state_variable_value, set_trace_state_variable_value):
Export in the IPA.
(condition_true_at_tracepoint): If there's a compiled condition,
run that.
(current_insn_ptr, emit_error): New globals.
(struct bytecode_address): New.
(get_raw_reg_func_addr): New.
(emit_prologue, emit_epilogue, emit_add, emit_sub, emit_mul)
(emit_lsh, emit_rsh_signed, emit_rsh_unsigned, emit_ext)
(emit_log_not, emit_bit_and, emit_bit_or, emit_bit_xor)
(emit_bit_not, emit_equal, emit_less_signed, emit_less_unsigned)
(emit_ref, emit_if_goto, emit_goto, write_goto_address, emit_const)
(emit_reg, emit_pop, emit_stack_flush, emit_zero_ext, emit_swap)
(emit_stack_adjust, emit_int_call_1, emit_void_call_2): New.
(compile_tracepoint_condition, compile_bytecodes): New.
* target.h (emit_ops): Forward declare.
(struct target_ops): New field emit_ops.
(target_emit_ops): New.
* linux-amd64-ipa.c (gdb_agent_get_raw_reg): New.
* linux-i386-ipa.c (gdb_agent_get_raw_reg): New.
* linux-low.c (linux_emit_ops): New.
(linux_target_ops): Install it.
* linux-low.h (struct linux_target_ops): New field emit_ops.

---
gdb/gdbserver/linux-amd64-ipa.c | 9
gdb/gdbserver/linux-i386-ipa.c | 15
gdb/gdbserver/linux-low.c | 12
gdb/gdbserver/linux-low.h | 4
gdb/gdbserver/linux-x86-low.c | 1032 +++++++++++++++++++++++++++++++++++++++-
gdb/gdbserver/server.h | 50 +
gdb/gdbserver/target.h | 8
gdb/gdbserver/tracepoint.c | 624 ++++++++++++++++++++++++
8 files changed, 1749 insertions(+), 5 deletions(-)

Index: src/gdb/gdbserver/linux-x86-low.c
===================================================================
--- src.orig/gdb/gdbserver/linux-x86-low.c 2010-06-07 16:13:07.000000000 +0100
+++ src/gdb/gdbserver/linux-x86-low.c 2010-06-07 16:43:25.000000000 +0100
@@ -20,6 +20,7 @@

#include <stddef.h>
#include <signal.h>
+#include <limits.h>
#include "server.h"
#include "linux-low.h"
#include "i387-fp.h"
@@ -1470,6 +1471,1034 @@ x86_install_fast_tracepoint_jump_pad (CO
adjusted_insn_addr_end);
}

+static void
+add_insns (unsigned char *start, int len)
+{
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ if (debug_threads)
+ fprintf (stderr, "Adding %d bytes of insn at %s\n",
+ len, paddress (buildaddr));
+
+ append_insns (&buildaddr, len, start);
+ current_insn_ptr = buildaddr;
+}
+
+/* A function used to trick optimizers. */
+
+int
+always_true (void)
+{
+ return 1;
+}
+
+/* Our general strategy for emitting code is to avoid specifying raw
+ bytes whenever possible, and instead copy a block of inline asm
+ that is embedded in the function. This is a little messy, because
+ we need to keep the compiler from discarding what looks like dead
+ code, plus suppress various warnings. */
+
+#define EMIT_ASM(NAME,INSNS) \
+ { extern unsigned char start_ ## NAME, end_ ## NAME; \
+ add_insns (&start_ ## NAME, &end_ ## NAME - &start_ ## NAME); \
+ if (always_true ()) \
+ goto skipover ## NAME; \
+ __asm__ ("start_" #NAME ":\n\t" INSNS "\n\tend_" #NAME ":\n\t"); \
+ skipover ## NAME: \
+ ; }
+
+
+#ifdef __x86_64__
+
+#define EMIT_ASM32(NAME,INSNS) \
+ { extern unsigned char start_ ## NAME, end_ ## NAME; \
+ add_insns (&start_ ## NAME, &end_ ## NAME - &start_ ## NAME); \
+ if (always_true ()) \
+ goto skipover ## NAME; \
+ __asm__ (".code32\n\tstart_" #NAME ":\n\t" INSNS "\n\tend_" #NAME ":\n" \
+ "\t.code64\n\t"); \
+ skipover ## NAME: \
+ ; }
+
+#else
+
+#define EMIT_ASM32(NAME,INSNS) EMIT_ASM(NAME,INSNS)
+
+#endif
+
+#ifdef __x86_64__
+
+static void
+amd64_emit_prologue (void)
+{
+ EMIT_ASM (amd64_prologue,
+ "pushq %rbp\n\t"
+ "movq %rsp,%rbp\n\t"
+ "sub $0x20,%rsp\n\t"
+ "movq %rdi,-8(%rbp)\n\t"
+ "movq %rsi,-16(%rbp)");
+}
+
+
+static void
+amd64_emit_epilogue (void)
+{
+ EMIT_ASM (amd64_epilogue,
+ "movq -16(%rbp),%rdi\n\t"
+ "movq %rax,(%rdi)\n\t"
+ "xor %rax,%rax\n\t"
+ "leave\n\t"
+ "ret");
+}
+
+static void
+amd64_emit_add (void)
+{
+ EMIT_ASM (amd64_add,
+ "add (%rsp),%rax\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_sub (void)
+{
+ EMIT_ASM (amd64_sub,
+ "sub %rax,(%rsp)\n\t"
+ "pop %rax");
+}
+
+static void
+amd64_emit_mul (void)
+{
+ emit_error = 1;
+}
+
+static void
+amd64_emit_lsh (void)
+{
+ emit_error = 1;
+}
+
+static void
+amd64_emit_rsh_signed (void)
+{
+ emit_error = 1;
+}
+
+static void
+amd64_emit_rsh_unsigned (void)
+{
+ emit_error = 1;
+}
+
+static void
+amd64_emit_ext (int arg)
+{
+ switch (arg)
+ {
+ case 8:
+ EMIT_ASM (amd64_ext_8,
+ "cbtw\n\t"
+ "cwtl\n\t"
+ "cltq");
+ break;
+ case 16:
+ EMIT_ASM (amd64_ext_16,
+ "cwtl\n\t"
+ "cltq");
+ break;
+ case 32:
+ EMIT_ASM (amd64_ext_32,
+ "cltq");
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+static void
+amd64_emit_log_not (void)
+{
+ EMIT_ASM (amd64_log_not,
+ "test %rax,%rax\n\t"
+ "sete %cl\n\t"
+ "movzbq %cl,%rax");
+}
+
+static void
+amd64_emit_bit_and (void)
+{
+ EMIT_ASM (amd64_and,
+ "and (%rsp),%rax\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_bit_or (void)
+{
+ EMIT_ASM (amd64_or,
+ "or (%rsp),%rax\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_bit_xor (void)
+{
+ EMIT_ASM (amd64_xor,
+ "xor (%rsp),%rax\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_bit_not (void)
+{
+ EMIT_ASM (amd64_bit_not,
+ "xorq $0xffffffffffffffff,%rax");
+}
+
+static void
+amd64_emit_equal (void)
+{
+ EMIT_ASM (amd64_equal,
+ "cmp %rax,(%rsp)\n\t"
+ "je .Lamd64_equal_true\n\t"
+ "xor %rax,%rax\n\t"
+ "jmp .Lamd64_equal_end\n\t"
+ ".Lamd64_equal_true:\n\t"
+ "mov $0x1,%rax\n\t"
+ ".Lamd64_equal_end:\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_less_signed (void)
+{
+ EMIT_ASM (amd64_less_signed,
+ "cmp %rax,(%rsp)\n\t"
+ "jl .Lamd64_less_signed_true\n\t"
+ "xor %rax,%rax\n\t"
+ "jmp .Lamd64_less_signed_end\n\t"
+ ".Lamd64_less_signed_true:\n\t"
+ "mov $1,%rax\n\t"
+ ".Lamd64_less_signed_end:\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_less_unsigned (void)
+{
+ EMIT_ASM (amd64_less_unsigned,
+ "cmp %rax,(%rsp)\n\t"
+ "jb .Lamd64_less_unsigned_true\n\t"
+ "xor %rax,%rax\n\t"
+ "jmp .Lamd64_less_unsigned_end\n\t"
+ ".Lamd64_less_unsigned_true:\n\t"
+ "mov $1,%rax\n\t"
+ ".Lamd64_less_unsigned_end:\n\t"
+ "lea 0x8(%rsp),%rsp");
+}
+
+static void
+amd64_emit_ref (int size)
+{
+ switch (size)
+ {
+ case 1:
+ EMIT_ASM (amd64_ref1,
+ "movb (%rax),%al");
+ break;
+ case 2:
+ EMIT_ASM (amd64_ref2,
+ "movw (%rax),%ax");
+ break;
+ case 4:
+ EMIT_ASM (amd64_ref4,
+ "movl (%rax),%eax");
+ break;
+ case 8:
+ EMIT_ASM (amd64_ref8,
+ "movq (%rax),%rax");
+ break;
+ }
+}
+
+static void
+amd64_emit_if_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM (amd64_if_goto,
+ "mov %rax,%rcx\n\t"
+ "pop %rax\n\t"
+ "cmp $0,%rcx\n\t"
+ ".byte 0x0f, 0x85, 0x0, 0x0, 0x0, 0x0");
+ if (offset_p)
+ *offset_p = 10;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+amd64_emit_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM (amd64_goto,
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0");
+ if (offset_p)
+ *offset_p = 1;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+amd64_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
+{
+ int diff = (to - (from + size));
+ unsigned char buf[sizeof (int)];
+
+ if (size != 4)
+ {
+ emit_error = 1;
+ return;
+ }
+
+ memcpy (buf, &diff, sizeof (int));
+ write_inferior_memory (from, buf, sizeof (int));
+}
+
+static void
+amd64_emit_const (int64_t num)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ i = 0;
+ buf[i++] = 0x48; buf[i++] = 0xb8; /* mov $<n>,%rax */
+ *((int64_t *) (&buf[i])) = num;
+ i += 8;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+}
+
+static void
+amd64_emit_call (CORE_ADDR fn)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+ int64_t offset64;
+
+ /* The destination function being in the shared library, may be
+ >31-bits away off the compiled code pad. */
+
+ buildaddr = current_insn_ptr;
+
+ offset64 = fn - (buildaddr + 1 /* call op */ + 4 /* 32-bit offset */);
+
+ i = 0;
+
+ if (offset64 > INT_MAX || offset64 < INT_MIN)
+ {
+ /* Offset is too large for a call. Use callq, but that requires
+ a register, so avoid it if possible. Use r10, since it is
+ call-clobbered, we don't have to push/pop it. */
+ buf[i++] = 0x48; /* mov $fn,%r10 */
+ buf[i++] = 0xba;
+ memcpy (buf + i, &fn, 8);
+ i += 8;
+ buf[i++] = 0xff; /* callq *%r10 */
+ buf[i++] = 0xd2;
+ }
+ else
+ {
+ int offset32 = offset64; /* we know we can't overflow here. */
+ memcpy (buf + i, &offset32, 4);
+ i += 4;
+ }
+
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+}
+
+static void
+amd64_emit_reg (int reg)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ /* Assume raw_regs is still in %rdi. */
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xbe; /* mov $<n>,%esi */
+ *((int *) (&buf[i])) = reg;
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ amd64_emit_call (get_raw_reg_func_addr ());
+}
+
+static void
+amd64_emit_pop (void)
+{
+ EMIT_ASM (amd64_pop,
+ "pop %rax");
+}
+
+static void
+amd64_emit_stack_flush (void)
+{
+ EMIT_ASM (amd64_stack_flush,
+ "push %rax");
+}
+
+static void
+amd64_emit_zero_ext (int arg)
+{
+ switch (arg)
+ {
+ case 8:
+ EMIT_ASM (amd64_zero_ext_8,
+ "and $0xff,%rax");
+ break;
+ case 16:
+ EMIT_ASM (amd64_zero_ext_16,
+ "and $0xffff,%rax");
+ break;
+ case 32:
+ EMIT_ASM (amd64_zero_ext_32,
+ "mov $0xffffffff,%rcx\n\t"
+ "and %rcx,%rax");
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+static void
+amd64_emit_swap (void)
+{
+ EMIT_ASM (amd64_swap,
+ "mov %rax,%rcx\n\t"
+ "pop %rax\n\t"
+ "push %rcx");
+}
+
+static void
+amd64_emit_stack_adjust (int n)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ i = 0;
+ buf[i++] = 0x48; /* lea $<n>(%rsp),%rsp */
+ buf[i++] = 0x8d;
+ buf[i++] = 0x64;
+ buf[i++] = 0x24;
+ /* This only handles adjustments up to 16, but we don't expect any more. */
+ buf[i++] = n * 8;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+}
+
+/* FN's prototype is `LONGEST(*fn)(int)'. */
+
+static void
+amd64_emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xbf; /* movl $<n>,%edi */
+ *((int *) (&buf[i])) = arg1;
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ amd64_emit_call (fn);
+}
+
+/* FN's prototype is `void(*fn)(int,int64_t)'. */
+
+static void
+amd64_emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xbf; /* movl $<n>,%edi */
+ *((int *) (&buf[i])) = arg1;
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ EMIT_ASM (amd64_void_call_2_a,
+ /* Save away a copy of the stack top. */
+ "push %rax\n\t"
+ /* Also pass top as the second argument. */
+ "mov %rax,%rsi");
+ amd64_emit_call (fn);
+ EMIT_ASM (amd64_void_call_2_b,
+ /* Restore the stack top, %rax may have been trashed. */
+ "pop %rax");
+}
+
+struct emit_ops amd64_emit_ops =
+ {
+ amd64_emit_prologue,
+ amd64_emit_epilogue,
+ amd64_emit_add,
+ amd64_emit_sub,
+ amd64_emit_mul,
+ amd64_emit_lsh,
+ amd64_emit_rsh_signed,
+ amd64_emit_rsh_unsigned,
+ amd64_emit_ext,
+ amd64_emit_log_not,
+ amd64_emit_bit_and,
+ amd64_emit_bit_or,
+ amd64_emit_bit_xor,
+ amd64_emit_bit_not,
+ amd64_emit_equal,
+ amd64_emit_less_signed,
+ amd64_emit_less_unsigned,
+ amd64_emit_ref,
+ amd64_emit_if_goto,
+ amd64_emit_goto,
+ amd64_write_goto_address,
+ amd64_emit_const,
+ amd64_emit_call,
+ amd64_emit_reg,
+ amd64_emit_pop,
+ amd64_emit_stack_flush,
+ amd64_emit_zero_ext,
+ amd64_emit_swap,
+ amd64_emit_stack_adjust,
+ amd64_emit_int_call_1,
+ amd64_emit_void_call_2
+ };
+
+#endif /* __x86_64__ */
+
+static void
+i386_emit_prologue (void)
+{
+ EMIT_ASM32 (i386_prologue,
+ "push %ebp\n\t"
+ "mov %esp,%ebp");
+ /* At this point, the raw regs base address is at 8(%ebp), and the
+ value pointer is at 12(%ebp). */
+}
+
+static void
+i386_emit_epilogue (void)
+{
+ EMIT_ASM32 (i386_epilogue,
+ "mov 12(%ebp),%ecx\n\t"
+ "mov %eax,(%ecx)\n\t"
+ "mov %ebx,0x4(%ecx)\n\t"
+ "xor %eax,%eax\n\t"
+ "pop %ebp\n\t"
+ "ret");
+}
+
+static void
+i386_emit_add (void)
+{
+ EMIT_ASM32 (i386_add,
+ "add (%esp),%eax\n\t"
+ "adc 0x4(%esp),%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_sub (void)
+{
+ EMIT_ASM32 (i386_sub,
+ "subl %eax,(%esp)\n\t"
+ "sbbl %ebx,4(%esp)\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t");
+}
+
+static void
+i386_emit_mul (void)
+{
+ emit_error = 1;
+}
+
+static void
+i386_emit_lsh (void)
+{
+ emit_error = 1;
+}
+
+static void
+i386_emit_rsh_signed (void)
+{
+ emit_error = 1;
+}
+
+static void
+i386_emit_rsh_unsigned (void)
+{
+ emit_error = 1;
+}
+
+static void
+i386_emit_ext (int arg)
+{
+ switch (arg)
+ {
+ case 8:
+ EMIT_ASM32 (i386_ext_8,
+ "cbtw\n\t"
+ "cwtl\n\t"
+ "movl %eax,%ebx\n\t"
+ "sarl $31,%ebx");
+ break;
+ case 16:
+ EMIT_ASM32 (i386_ext_16,
+ "cwtl\n\t"
+ "movl %eax,%ebx\n\t"
+ "sarl $31,%ebx");
+ break;
+ case 32:
+ EMIT_ASM32 (i386_ext_32,
+ "movl %eax,%ebx\n\t"
+ "sarl $31,%ebx");
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+static void
+i386_emit_log_not (void)
+{
+ EMIT_ASM32 (i386_log_not,
+ "or %ebx,%eax\n\t"
+ "test %eax,%eax\n\t"
+ "sete %cl\n\t"
+ "xor %ebx,%ebx\n\t"
+ "movzbl %cl,%eax");
+}
+
+static void
+i386_emit_bit_and (void)
+{
+ EMIT_ASM32 (i386_and,
+ "and (%esp),%eax\n\t"
+ "and 0x4(%esp),%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_bit_or (void)
+{
+ EMIT_ASM32 (i386_or,
+ "or (%esp),%eax\n\t"
+ "or 0x4(%esp),%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_bit_xor (void)
+{
+ EMIT_ASM32 (i386_xor,
+ "xor (%esp),%eax\n\t"
+ "xor 0x4(%esp),%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_bit_not (void)
+{
+ EMIT_ASM32 (i386_bit_not,
+ "xor $0xffffffff,%eax\n\t"
+ "xor $0xffffffff,%ebx\n\t");
+}
+
+static void
+i386_emit_equal (void)
+{
+ EMIT_ASM32 (i386_equal,
+ "cmpl %ebx,4(%esp)\n\t"
+ "jne .Li386_equal_false\n\t"
+ "cmpl %eax,(%esp)\n\t"
+ "je .Li386_equal_true\n\t"
+ ".Li386_equal_false:\n\t"
+ "xor %eax,%eax\n\t"
+ "jmp .Li386_equal_end\n\t"
+ ".Li386_equal_true:\n\t"
+ "mov $1,%eax\n\t"
+ ".Li386_equal_end:\n\t"
+ "xor %ebx,%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_less_signed (void)
+{
+ EMIT_ASM32 (i386_less_signed,
+ "cmpl %ebx,4(%esp)\n\t"
+ "jl .Li386_less_signed_true\n\t"
+ "jne .Li386_less_signed_false\n\t"
+ "cmpl %eax,(%esp)\n\t"
+ "jl .Li386_less_signed_true\n\t"
+ ".Li386_less_signed_false:\n\t"
+ "xor %eax,%eax\n\t"
+ "jmp .Li386_less_signed_end\n\t"
+ ".Li386_less_signed_true:\n\t"
+ "mov $1,%eax\n\t"
+ ".Li386_less_signed_end:\n\t"
+ "xor %ebx,%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_less_unsigned (void)
+{
+ EMIT_ASM32 (i386_less_unsigned,
+ "cmpl %ebx,4(%esp)\n\t"
+ "jb .Li386_less_unsigned_true\n\t"
+ "jne .Li386_less_unsigned_false\n\t"
+ "cmpl %eax,(%esp)\n\t"
+ "jb .Li386_less_unsigned_true\n\t"
+ ".Li386_less_unsigned_false:\n\t"
+ "xor %eax,%eax\n\t"
+ "jmp .Li386_less_unsigned_end\n\t"
+ ".Li386_less_unsigned_true:\n\t"
+ "mov $1,%eax\n\t"
+ ".Li386_less_unsigned_end:\n\t"
+ "xor %ebx,%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_ref (int size)
+{
+ switch (size)
+ {
+ case 1:
+ EMIT_ASM32 (i386_ref1,
+ "movb (%eax),%al");
+ break;
+ case 2:
+ EMIT_ASM32 (i386_ref2,
+ "movw (%eax),%ax");
+ break;
+ case 4:
+ EMIT_ASM32 (i386_ref4,
+ "movl (%eax),%eax");
+ break;
+ case 8:
+ EMIT_ASM32 (i386_ref8,
+ "movl 4(%eax),%ebx\n\t"
+ "movl (%eax),%eax");
+ break;
+ }
+}
+
+static void
+i386_emit_if_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM32 (i386_if_goto,
+ "mov %eax,%ecx\n\t"
+ "or %ebx,%ecx\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t"
+ "cmpl $0,%ecx\n\t"
+ /* Don't trust the assembler to choose the right jump */
+ ".byte 0x0f, 0x85, 0x0, 0x0, 0x0, 0x0");
+
+ if (offset_p)
+ *offset_p = 11; /* be sure that this matches the sequence above */
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+i386_emit_goto (int *offset_p, int *size_p)
+{
+ EMIT_ASM32 (i386_goto,
+ /* Don't trust the assembler to choose the right jump */
+ ".byte 0xe9, 0x0, 0x0, 0x0, 0x0");
+ if (offset_p)
+ *offset_p = 1;
+ if (size_p)
+ *size_p = 4;
+}
+
+static void
+i386_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size)
+{
+ int diff = (to - (from + size));
+ unsigned char buf[sizeof (int)];
+
+ /* We're only doing 4-byte sizes at the moment. */
+ if (size != 4)
+ {
+ emit_error = 1;
+ return;
+ }
+
+ memcpy (buf, &diff, sizeof (int));
+ write_inferior_memory (from, buf, sizeof (int));
+}
+
+static void
+i386_emit_const (int64_t num)
+{
+ unsigned char buf[16];
+ int i, hi;
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ i = 0;
+ buf[i++] = 0xb8; /* mov $<n>,%eax */
+ *((int *) (&buf[i])) = (num & 0xffffffff);
+ i += 4;
+ hi = ((num >> 32) & 0xffffffff);
+ if (hi)
+ {
+ buf[i++] = 0xbb; /* mov $<n>,%ebx */
+ *((int *) (&buf[i])) = hi;
+ i += 4;
+ }
+ else
+ {
+ buf[i++] = 0x31; buf[i++] = 0xdb; /* xor %ebx,%ebx */
+ }
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+}
+
+static void
+i386_emit_call (CORE_ADDR fn)
+{
+ unsigned char buf[16];
+ int i, offset;
+ CORE_ADDR buildaddr;
+
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xe8; /* call <reladdr> */
+ offset = ((int) fn) - (buildaddr + 5);
+ memcpy (buf + 1, &offset, 4);
+ append_insns (&buildaddr, 5, buf);
+ current_insn_ptr = buildaddr;
+}
+
+static void
+i386_emit_reg (int reg)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ EMIT_ASM32 (i386_reg_a,
+ "sub $0x8,%esp");
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xb8; /* mov $<n>,%eax */
+ *((int *) (&buf[i])) = reg;
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ EMIT_ASM32 (i386_reg_b,
+ "mov %eax,4(%esp)\n\t"
+ "mov 8(%ebp),%eax\n\t"
+ "mov %eax,(%esp)");
+ i386_emit_call (get_raw_reg_func_addr ());
+ EMIT_ASM32 (i386_reg_c,
+ "xor %ebx,%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+static void
+i386_emit_pop (void)
+{
+ EMIT_ASM32 (i386_pop,
+ "pop %eax\n\t"
+ "pop %ebx");
+}
+
+static void
+i386_emit_stack_flush (void)
+{
+ EMIT_ASM32 (i386_stack_flush,
+ "push %ebx\n\t"
+ "push %eax");
+}
+
+static void
+i386_emit_zero_ext (int arg)
+{
+ switch (arg)
+ {
+ case 8:
+ EMIT_ASM32 (i386_zero_ext_8,
+ "and $0xff,%eax\n\t"
+ "xor %ebx,%ebx");
+ break;
+ case 16:
+ EMIT_ASM32 (i386_zero_ext_16,
+ "and $0xffff,%eax\n\t"
+ "xor %ebx,%ebx");
+ break;
+ case 32:
+ EMIT_ASM32 (i386_zero_ext_32,
+ "xor %ebx,%ebx");
+ break;
+ default:
+ emit_error = 1;
+ }
+}
+
+static void
+i386_emit_swap (void)
+{
+ EMIT_ASM32 (i386_swap,
+ "mov %eax,%ecx\n\t"
+ "mov %ebx,%edx\n\t"
+ "pop %eax\n\t"
+ "pop %ebx\n\t"
+ "push %edx\n\t"
+ "push %ecx");
+}
+
+static void
+i386_emit_stack_adjust (int n)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr = current_insn_ptr;
+
+ i = 0;
+ buf[i++] = 0x8d; /* lea $<n>(%esp),%esp */
+ buf[i++] = 0x64;
+ buf[i++] = 0x24;
+ buf[i++] = n * 8;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+}
+
+/* FN's prototype is `LONGEST(*fn)(int)'. */
+
+static void
+i386_emit_int_call_1 (CORE_ADDR fn, int arg1)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ EMIT_ASM32 (i386_int_call_1_a,
+ /* Reserve a bit of stack space. */
+ "sub $0x8,%esp");
+ /* Put the one argument on the stack. */
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xc7; /* movl $<arg1>,(%esp) */
+ buf[i++] = 0x04;
+ buf[i++] = 0x24;
+ *((int *) (&buf[i])) = arg1;
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ i386_emit_call (fn);
+ EMIT_ASM32 (i386_int_call_1_c,
+ "mov %edx,%ebx\n\t"
+ "lea 0x8(%esp),%esp");
+}
+
+/* FN's prototype is `void(*fn)(int,int64_t)'. */
+
+static void
+i386_emit_void_call_2 (CORE_ADDR fn, int arg1)
+{
+ unsigned char buf[16];
+ int i;
+ CORE_ADDR buildaddr;
+
+ EMIT_ASM32 (i386_void_call_2_a,
+ /* Preserve %eax only; we don't have to worry about %ebx. */
+ "push %eax\n\t"
+ /* Reserve a bit of stack space for arguments. */
+ "sub $0x10,%esp\n\t"
+ /* Copy "top" to the second argument position. (Note that
+ we can't assume function won't scribble on its
+ arguments, so don't try to restore from this.) */
+ "mov %eax,4(%esp)\n\t"
+ "mov %ebx,8(%esp)");
+ /* Put the first argument on the stack. */
+ buildaddr = current_insn_ptr;
+ i = 0;
+ buf[i++] = 0xc7; /* movl $<arg1>,(%esp) */
+ buf[i++] = 0x04;
+ buf[i++] = 0x24;
+ *((int *) (&buf[i])) = arg1;
+ i += 4;
+ append_insns (&buildaddr, i, buf);
+ current_insn_ptr = buildaddr;
+ i386_emit_call (fn);
+ EMIT_ASM32 (i386_void_call_2_b,
+ "lea 0x10(%esp),%esp\n\t"
+ /* Restore original stack top. */
+ "pop %eax");
+}
+
+struct emit_ops i386_emit_ops =
+ {
+ i386_emit_prologue,
+ i386_emit_epilogue,
+ i386_emit_add,
+ i386_emit_sub,
+ i386_emit_mul,
+ i386_emit_lsh,
+ i386_emit_rsh_signed,
+ i386_emit_rsh_unsigned,
+ i386_emit_ext,
+ i386_emit_log_not,
+ i386_emit_bit_and,
+ i386_emit_bit_or,
+ i386_emit_bit_xor,
+ i386_emit_bit_not,
+ i386_emit_equal,
+ i386_emit_less_signed,
+ i386_emit_less_unsigned,
+ i386_emit_ref,
+ i386_emit_if_goto,
+ i386_emit_goto,
+ i386_write_goto_address,
+ i386_emit_const,
+ i386_emit_call,
+ i386_emit_reg,
+ i386_emit_pop,
+ i386_emit_stack_flush,
+ i386_emit_zero_ext,
+ i386_emit_swap,
+ i386_emit_stack_adjust,
+ i386_emit_int_call_1,
+ i386_emit_void_call_2
+ };
+
+
+static struct emit_ops *
+x86_emit_ops (void)
+{
+#ifdef __x86_64__
+ int use_64bit = register_size (0) == 8;
+
+ if (use_64bit)
+ return &amd64_emit_ops;
+ else
+#endif
+ return &i386_emit_ops;
+}
+
/* This is initialized assuming an amd64 target.
x86_arch_setup will correct it for i386 or amd64 targets. */

@@ -1504,5 +2533,6 @@ struct linux_target_ops the_low_target =
x86_linux_process_qsupported,
x86_supports_tracepoints,
x86_get_thread_area,
- x86_install_fast_tracepoint_jump_pad
+ x86_install_fast_tracepoint_jump_pad,
+ x86_emit_ops
};
Index: src/gdb/gdbserver/server.h
===================================================================
--- src.orig/gdb/gdbserver/server.h 2010-06-07 16:13:07.000000000 +0100
+++ src/gdb/gdbserver/server.h 2010-06-07 16:13:24.000000000 +0100
@@ -573,6 +573,56 @@ void supply_fast_tracepoint_registers (s
void stop_tracing (void);
#endif

+/* Bytecode compilation function vector. */
+
+struct emit_ops
+{
+ void (*emit_prologue) (void);
+ void (*emit_epilogue) (void);
+ void (*emit_add) (void);
+ void (*emit_sub) (void);
+ void (*emit_mul) (void);
+ void (*emit_lsh) (void);
+ void (*emit_rsh_signed) (void);
+ void (*emit_rsh_unsigned) (void);
+ void (*emit_ext) (int arg);
+ void (*emit_log_not) (void);
+ void (*emit_bit_and) (void);
+ void (*emit_bit_or) (void);
+ void (*emit_bit_xor) (void);
+ void (*emit_bit_not) (void);
+ void (*emit_equal) (void);
+ void (*emit_less_signed) (void);
+ void (*emit_less_unsigned) (void);
+ void (*emit_ref) (int size);
+ void (*emit_if_goto) (int *offset_p, int *size_p);
+ void (*emit_goto) (int *offset_p, int *size_p);
+ void (*write_goto_address) (CORE_ADDR from, CORE_ADDR to, int size);
+ void (*emit_const) (int64_t num);
+ void (*emit_call) (CORE_ADDR fn);
+ void (*emit_reg) (int reg);
+ void (*emit_pop) (void);
+ void (*emit_stack_flush) (void);
+ void (*emit_zero_ext) (int arg);
+ void (*emit_swap) (void);
+ void (*emit_stack_adjust) (int n);
+
+ /* Emit code for a generic function that takes one fixed integer
+ argument and returns a 64-bit int (for instance, tsv getter). */
+ void (*emit_int_call_1) (CORE_ADDR fn, int arg1);
+
+ /* Emit code for a generic function that takes one fixed integer
+ argument and a 64-bit int from the top of the stack, and returns
+ nothing (for instance, tsv setter). */
+ void (*emit_void_call_2) (CORE_ADDR fn, int arg1);
+};
+
+/* Returns the address of the get_raw_reg function in the IPA. */
+CORE_ADDR get_raw_reg_func_addr (void);
+
+CORE_ADDR current_insn_ptr;
+int emit_error;
+
/* Version information, from version.c. */
extern const char version[];
extern const char host_name[];
Index: src/gdb/gdbserver/tracepoint.c
===================================================================
--- src.orig/gdb/gdbserver/tracepoint.c 2010-06-07 16:13:07.000000000 +0100
+++ src/gdb/gdbserver/tracepoint.c 2010-06-07 16:42:28.000000000 +0100
@@ -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 (
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, in
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 tra
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,

#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;
Index: src/gdb/gdbserver/target.h
===================================================================
--- src.orig/gdb/gdbserver/target.h 2010-06-07 16:13:07.000000000 +0100
+++ src/gdb/gdbserver/target.h 2010-06-07 16:13:24.000000000 +0100
@@ -22,6 +22,8 @@
#ifndef TARGET_H
#define TARGET_H

+struct emit_ops;
+
/* Ways to "resume" a thread. */

enum resume_kind
@@ -349,6 +351,10 @@ struct target_ops
ULONGEST *jjump_pad_insn_size,
CORE_ADDR *adjusted_insn_addr,
CORE_ADDR *adjusted_insn_addr_end);
+
+ /* Return the bytecode operations vector for the current inferior.
+ Returns NULL if bytecode compilation is not supported. */
+ struct emit_ops *(*emit_ops) (void);
};

extern struct target_ops *the_target;
@@ -452,6 +458,8 @@ void set_target_ops (struct target_ops *
adjusted_insn_addr, \
adjusted_insn_addr_end)

+#define target_emit_ops() (*the_target->emit_ops) ()
+
/* Start non-stop mode, returns 0 on success, -1 on failure. */

int start_non_stop (int nonstop);
Index: src/gdb/gdbserver/linux-amd64-ipa.c
===================================================================
--- src.orig/gdb/gdbserver/linux-amd64-ipa.c 2010-06-07 16:13:07.000000000 +0100
+++ src/gdb/gdbserver/linux-amd64-ipa.c 2010-06-07 16:13:24.000000000 +0100
@@ -66,6 +66,15 @@ supply_fast_tracepoint_registers (struct
((char *) buf) + x86_64_ft_collect_regmap[i]);
}

+ULONGEST __attribute__ ((visibility("default"), used))
+gdb_agent_get_raw_reg (const unsigned char *raw_regs, int regnum)
+{
+ if (regnum > X86_64_NUM_FT_COLLECT_GREGS)
+ return 0;
+
+ return *(ULONGEST *) (raw_regs + x86_64_ft_collect_regmap[regnum]);
+}
+
/* This is only needed because reg-i386-linux-lib.o references it. We
may use it proper at some point. */
const char *gdbserver_xmltarget;
Index: src/gdb/gdbserver/linux-low.c
===================================================================
--- src.orig/gdb/gdbserver/linux-low.c 2010-06-07 16:13:07.000000000 +0100
+++ src/gdb/gdbserver/linux-low.c 2010-06-07 16:13:24.000000000 +0100
@@ -5054,6 +5054,15 @@ linux_install_fast_tracepoint_jump_pad (
adjusted_insn_addr, adjusted_insn_addr_end);
}

+static struct emit_ops *
+linux_emit_ops (void)
+{
+ if (the_low_target.emit_ops != NULL)
+ return (*the_low_target.emit_ops) ();
+ else
+ return NULL;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -5109,7 +5118,8 @@ static struct target_ops linux_target_op
linux_unpause_all,
linux_cancel_breakpoints,
linux_stabilize_threads,
- linux_install_fast_tracepoint_jump_pad
+ linux_install_fast_tracepoint_jump_pad,
+ linux_emit_ops
};

static void
Index: src/gdb/gdbserver/linux-low.h
===================================================================
--- src.orig/gdb/gdbserver/linux-low.h 2010-06-07 16:13:07.000000000 +0100
+++ src/gdb/gdbserver/linux-low.h 2010-06-07 16:13:24.000000000 +0100
@@ -136,6 +136,10 @@ struct linux_target_ops
ULONGEST *jjump_pad_insn_size,
CORE_ADDR *adjusted_insn_addr,
CORE_ADDR *adjusted_insn_addr_end);
+
+ /* Return the bytecode operations vector for the current inferior.
+ Returns NULL if bytecode compilation is not supported. */
+ struct emit_ops *(*emit_ops) (void);
};

extern struct linux_target_ops the_low_target;
Index: src/gdb/gdbserver/linux-i386-ipa.c
===================================================================
--- src.orig/gdb/gdbserver/linux-i386-ipa.c 2010-06-07 16:43:41.000000000 +0100
+++ src/gdb/gdbserver/linux-i386-ipa.c 2010-06-07 16:44:45.000000000 +0100
@@ -95,6 +95,21 @@ supply_fast_tracepoint_registers (struct
}
}

+ULONGEST __attribute__ ((visibility("default"), used))
+gdb_agent_get_raw_reg (unsigned char *raw_regs, int regnum)
+{
+ /* This should maybe be allowed to return an error code, or perhaps
+ better, have the emit_reg detect this, and emit a constant zero,
+ or something. */
+
+ if (regnum > i386_num_regs)
+ return 0;
+ else if (regnum >= I386_CS_REGNUM && regnum <= I386_GS_REGNUM)
+ return *(short *) (raw_regs + i386_ft_collect_regmap[regnum]);
+ else
+ return *(int *) (raw_regs + i386_ft_collect_regmap[regnum]);
+}
+
/* This is only needed because reg-i386-linux-lib.o references it. We
may use it proper at some point. */
const char *gdbserver_xmltarget;