/* Tracepoint code for remote server for GDB.
- Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 2009-2013 Free Software Foundation, Inc.
This file is part of GDB.
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "server.h"
+#include "gdbthread.h"
+#include "agent.h"
+
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <stddef.h>
-#if HAVE_STDINT_H
+#include <inttypes.h>
#include <stdint.h>
-#endif
-/* This file is built for both both GDBserver, and the in-process
+#include "ax.h"
+
+/* This file is built for both GDBserver, and the in-process
agent (IPA), a shared library that includes a tracing agent that is
loaded by the inferior to support fast tracepoints. Fast
tracepoints (or more accurately, jump based tracepoints) are
*/
-static void trace_vdebug (const char *, ...) ATTR_FORMAT (printf, 1, 2);
+static void trace_vdebug (const char *, ...) ATTRIBUTE_PRINTF (1, 2);
static void
trace_vdebug (const char *fmt, ...)
va_start (ap, fmt);
vsprintf (buf, fmt, ap);
- fprintf (stderr, "gdbserver/tracepoint: %s\n", buf);
+ fprintf (stderr, PROG "/tracepoint: %s\n", buf);
va_end (ap);
}
#define trace_debug_1(level, fmt, args...) \
do { \
- if (level <= debug_threads) \
+ if (level <= debug_threads) \
trace_vdebug ((fmt), ##args); \
} while (0)
# define gdb_tp_heap_buffer gdb_agent_gdb_tp_heap_buffer
# define gdb_jump_pad_buffer gdb_agent_gdb_jump_pad_buffer
# define gdb_jump_pad_buffer_end gdb_agent_gdb_jump_pad_buffer_end
+# define gdb_trampoline_buffer gdb_agent_gdb_trampoline_buffer
+# define gdb_trampoline_buffer_end gdb_agent_gdb_trampoline_buffer_end
+# define gdb_trampoline_buffer_error gdb_agent_gdb_trampoline_buffer_error
# define collecting gdb_agent_collecting
# define gdb_collect gdb_agent_gdb_collect
# define stop_tracing gdb_agent_stop_tracing
CORE_ADDR addr_gdb_tp_heap_buffer;
CORE_ADDR addr_gdb_jump_pad_buffer;
CORE_ADDR addr_gdb_jump_pad_buffer_end;
+ CORE_ADDR addr_gdb_trampoline_buffer;
+ CORE_ADDR addr_gdb_trampoline_buffer_end;
+ CORE_ADDR addr_gdb_trampoline_buffer_error;
CORE_ADDR addr_collecting;
CORE_ADDR addr_gdb_collect;
CORE_ADDR addr_stop_tracing;
CORE_ADDR addr_get_trace_state_variable_value;
CORE_ADDR addr_set_trace_state_variable_value;
CORE_ADDR addr_ust_loaded;
- CORE_ADDR addr_helper_thread_id;
- CORE_ADDR addr_cmd_buf;
};
-#define STRINGIZE_1(STR) #STR
-#define STRINGIZE(STR) STRINGIZE_1(STR)
-#define IPA_SYM(SYM) \
- { \
- STRINGIZE (gdb_agent_ ## SYM), \
- offsetof (struct ipa_sym_addresses, addr_ ## SYM) \
- }
-
static struct
{
const char *name;
IPA_SYM(gdb_tp_heap_buffer),
IPA_SYM(gdb_jump_pad_buffer),
IPA_SYM(gdb_jump_pad_buffer_end),
+ IPA_SYM(gdb_trampoline_buffer),
+ IPA_SYM(gdb_trampoline_buffer_end),
+ IPA_SYM(gdb_trampoline_buffer_error),
IPA_SYM(collecting),
IPA_SYM(gdb_collect),
IPA_SYM(stop_tracing),
IPA_SYM(get_trace_state_variable_value),
IPA_SYM(set_trace_state_variable_value),
IPA_SYM(ust_loaded),
- IPA_SYM(helper_thread_id),
- IPA_SYM(cmd_buf),
};
-struct ipa_sym_addresses ipa_sym_addrs;
-
-int all_tracepoint_symbols_looked_up;
-
-int
-in_process_agent_loaded (void)
-{
- return all_tracepoint_symbols_looked_up;
-}
+static struct ipa_sym_addresses ipa_sym_addrs;
static int read_inferior_integer (CORE_ADDR symaddr, int *val);
/* Returns true if both the in-process agent library and the static
- tracepoints libraries are loaded in the inferior. */
+ tracepoints libraries are loaded in the inferior, and agent has
+ capability on static tracepoints. */
static int
-in_process_agent_loaded_ust (void)
+in_process_agent_supports_ust (void)
{
int loaded = 0;
- if (!in_process_agent_loaded ())
+ if (!agent_loaded_p ())
{
warning ("In-process agent not loaded");
return 0;
}
- if (read_inferior_integer (ipa_sym_addrs.addr_ust_loaded, &loaded))
+ if (agent_capability_check (AGENT_CAPA_STATIC_TRACE))
{
- warning ("Error reading ust_loaded in lib");
- return 0;
- }
+ /* Agent understands static tracepoint, then check whether UST is in
+ fact loaded in the inferior. */
+ if (read_inferior_integer (ipa_sym_addrs.addr_ust_loaded, &loaded))
+ {
+ warning ("Error reading ust_loaded in lib");
+ return 0;
+ }
- return loaded;
+ return loaded;
+ }
+ else
+ return 0;
}
static void
static int
maybe_write_ipa_not_loaded (char *buffer)
{
- if (!in_process_agent_loaded ())
+ if (!agent_loaded_p ())
{
write_e_ipa_not_loaded (buffer);
return 1;
static int
maybe_write_ipa_ust_not_loaded (char *buffer)
{
- if (!in_process_agent_loaded ())
+ if (!agent_loaded_p ())
{
write_e_ipa_not_loaded (buffer);
return 1;
}
- else if (!in_process_agent_loaded_ust ())
+ else if (!in_process_agent_supports_ust ())
{
write_e_ust_not_loaded (buffer);
return 1;
void
tracepoint_look_up_symbols (void)
{
- int all_ok;
int i;
- if (all_tracepoint_symbols_looked_up)
+ if (agent_loaded_p ())
return;
- all_ok = 1;
for (i = 0; i < sizeof (symbol_list) / sizeof (symbol_list[0]); i++)
{
CORE_ADDR *addrp =
{
if (debug_threads)
fprintf (stderr, "symbol `%s' not found\n", symbol_list[i].name);
- all_ok = 0;
+ return;
}
}
- all_tracepoint_symbols_looked_up = all_ok;
+ agent_look_up_symbols (NULL);
}
#endif
GDBserver side. */
#ifdef IN_PROCESS_AGENT
-int debug_threads = 0;
-
int
read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
{
struct breakpoint *flush_trace_buffer_bkpt;
static int flush_trace_buffer_handler (CORE_ADDR);
-static void download_tracepoints (void);
static void download_trace_state_variables (void);
static void upload_fast_traceframes (void);
-static int run_inferior_command (char *cmd);
+static int run_inferior_command (char *cmd, int len);
static int
read_inferior_integer (CORE_ADDR symaddr, int *val)
sizeof (*val));
}
+struct tracepoint;
+static int tracepoint_send_agent (struct tracepoint *tpoint);
+
static int
read_inferior_uinteger (CORE_ADDR symaddr, unsigned int *val)
{
return write_inferior_memory (symaddr, (unsigned char *) &val, sizeof (val));
}
-#endif
+static CORE_ADDR target_malloc (ULONGEST size);
+static int write_inferior_data_ptr (CORE_ADDR where, CORE_ADDR ptr);
-/* This enum must exactly match what is documented in
- gdb/doc/agentexpr.texi, including all the numerical values. */
+#define COPY_FIELD_TO_BUF(BUF, OBJ, FIELD) \
+ do { \
+ memcpy (BUF, &(OBJ)->FIELD, sizeof ((OBJ)->FIELD)); \
+ BUF += sizeof ((OBJ)->FIELD); \
+ } while (0)
-enum gdb_agent_op
- {
-#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE) \
- gdb_agent_op_ ## NAME = VALUE,
-#include "ax.def"
-#undef DEFOP
- gdb_agent_op_last
- };
+#endif
-static const char *gdb_agent_op_names [gdb_agent_op_last] =
- {
- "?undef?"
-#define DEFOP(NAME, SIZE, DATA_SIZE, CONSUMED, PRODUCED, VALUE) , # NAME
-#include "ax.def"
-#undef DEFOP
- };
+/* Operations on various types of tracepoint actions. */
-struct agent_expr
+struct tracepoint_action;
+
+struct tracepoint_action_ops
{
- int length;
+ /* Download tracepoint action ACTION to IPA. Return the address of action
+ in IPA/inferior. */
+ CORE_ADDR (*download) (const struct tracepoint_action *action);
- unsigned char *bytes;
+ /* Send ACTION to agent via command buffer started from BUFFER. Return
+ updated head of command buffer. */
+ char* (*send) (char *buffer, const struct tracepoint_action *action);
};
/* Base action. Concrete actions inherit this. */
struct tracepoint_action
{
+#ifndef IN_PROCESS_AGENT
+ const struct tracepoint_action_ops *ops;
+#endif
char type;
};
ULONGEST addr;
ULONGEST len;
- int basereg;
+ int32_t basereg;
};
/* An 'R' (collect registers) action. */
struct tracepoint_action base;
};
+#ifndef IN_PROCESS_AGENT
+static CORE_ADDR
+m_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ int size_in_ipa = (sizeof (struct collect_memory_action)
+ - offsetof (struct tracepoint_action, type));
+ CORE_ADDR ipa_action = target_malloc (size_in_ipa);
+
+ write_inferior_memory (ipa_action, (unsigned char *) &action->type,
+ size_in_ipa);
+
+ return ipa_action;
+}
+static char *
+m_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
+{
+ struct collect_memory_action *maction
+ = (struct collect_memory_action *) action;
+
+ COPY_FIELD_TO_BUF (buffer, maction, addr);
+ COPY_FIELD_TO_BUF (buffer, maction, len);
+ COPY_FIELD_TO_BUF (buffer, maction, basereg);
+
+ return buffer;
+}
+
+static const struct tracepoint_action_ops m_tracepoint_action_ops =
+{
+ m_tracepoint_action_download,
+ m_tracepoint_action_send,
+};
+
+static CORE_ADDR
+r_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ int size_in_ipa = (sizeof (struct collect_registers_action)
+ - offsetof (struct tracepoint_action, type));
+ CORE_ADDR ipa_action = target_malloc (size_in_ipa);
+
+ write_inferior_memory (ipa_action, (unsigned char *) &action->type,
+ size_in_ipa);
+
+ return ipa_action;
+}
+
+static char *
+r_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
+{
+ return buffer;
+}
+
+static const struct tracepoint_action_ops r_tracepoint_action_ops =
+{
+ r_tracepoint_action_download,
+ r_tracepoint_action_send,
+};
+
+static CORE_ADDR download_agent_expr (struct agent_expr *expr);
+
+static CORE_ADDR
+x_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ int size_in_ipa = (sizeof (struct eval_expr_action)
+ - offsetof (struct tracepoint_action, type));
+ CORE_ADDR ipa_action = target_malloc (size_in_ipa);
+ CORE_ADDR expr;
+
+ write_inferior_memory (ipa_action, (unsigned char *) &action->type,
+ size_in_ipa);
+ expr = download_agent_expr (((struct eval_expr_action *)action)->expr);
+ write_inferior_data_ptr (ipa_action + offsetof (struct eval_expr_action, expr)
+ - offsetof (struct tracepoint_action, type),
+ expr);
+
+ return ipa_action;
+}
+
+/* Copy agent expression AEXPR to buffer pointed by P. If AEXPR is NULL,
+ copy 0 to P. Return updated header of buffer. */
+
+static char *
+agent_expr_send (char *p, const struct agent_expr *aexpr)
+{
+ /* Copy the length of condition first, and then copy its
+ content. */
+ if (aexpr == NULL)
+ {
+ memset (p, 0, 4);
+ p += 4;
+ }
+ else
+ {
+ memcpy (p, &aexpr->length, 4);
+ p +=4;
+
+ memcpy (p, aexpr->bytes, aexpr->length);
+ p += aexpr->length;
+ }
+ return p;
+}
+
+static char *
+x_tracepoint_action_send ( char *buffer, const struct tracepoint_action *action)
+{
+ struct eval_expr_action *eaction = (struct eval_expr_action *) action;
+
+ return agent_expr_send (buffer, eaction->expr);
+}
+
+static const struct tracepoint_action_ops x_tracepoint_action_ops =
+{
+ x_tracepoint_action_download,
+ x_tracepoint_action_send,
+};
+
+static CORE_ADDR
+l_tracepoint_action_download (const struct tracepoint_action *action)
+{
+ int size_in_ipa = (sizeof (struct collect_static_trace_data_action)
+ - offsetof (struct tracepoint_action, type));
+ CORE_ADDR ipa_action = target_malloc (size_in_ipa);
+
+ write_inferior_memory (ipa_action, (unsigned char *) &action->type,
+ size_in_ipa);
+
+ return ipa_action;
+}
+
+static char *
+l_tracepoint_action_send (char *buffer, const struct tracepoint_action *action)
+{
+ return buffer;
+}
+
+static const struct tracepoint_action_ops l_tracepoint_action_ops =
+{
+ l_tracepoint_action_download,
+ l_tracepoint_action_send,
+};
+#endif
+
/* This structure describes a piece of the source-level definition of
the tracepoint. The contents are not interpreted by the target,
but preserved verbatim for uploading upon reconnection. */
{
/* The number of the tracepoint, as specified by GDB. Several
tracepoint objects here may share a number. */
- int number;
+ uint32_t number;
/* Address at which the tracepoint is supposed to trigger. Several
tracepoints may share an address. */
enum tracepoint_type type;
/* True if the tracepoint is currently enabled. */
- int enabled;
+ int8_t enabled;
/* The number of single steps that will be performed after each
tracepoint hit. */
- long step_count;
+ uint64_t step_count;
/* The number of times the tracepoint may be hit before it will
terminate the entire tracing run. */
- long pass_count;
+ uint64_t pass_count;
/* Pointer to the agent expression that is the tracepoint's
conditional, or NULL if the tracepoint is unconditional. */
struct agent_expr *cond;
/* The list of actions to take when the tracepoint triggers. */
- int numactions;
+ uint32_t numactions;
struct tracepoint_action **actions;
/* Count of the times we've hit this tracepoint during the run.
Note that while-stepping steps are not counted as "hits". */
- long hit_count;
+ uint64_t hit_count;
+
+ /* Cached sum of the sizes of traceframes created by this point. */
+ uint64_t traceframe_usage;
CORE_ADDR compiled_cond;
/* The number of bytes displaced by fast tracepoints. It may subsume
multiple instructions, for multi-byte fast tracepoints. This
field is only valid for fast tracepoints. */
- int orig_size;
+ uint32_t orig_size;
/* Only for fast tracepoints. */
CORE_ADDR obj_addr_on_target;
CORE_ADDR jump_pad;
CORE_ADDR jump_pad_end;
+ /* The address range of the piece of the trampoline buffer that was
+ assigned to this fast tracepoint. (_end is actually one byte
+ past the end). */
+ CORE_ADDR trampoline;
+ CORE_ADDR trampoline_end;
+
/* The list of actions to take while in a stepping loop. These
fields are only valid for patch-based tracepoints. */
int num_step_actions;
IP_AGENT_EXPORT int trace_buffer_is_full;
-/* Enumeration of the different kinds of things that can happen during
- agent expression evaluation. */
-
-enum eval_result_type
- {
- expr_eval_no_error,
- expr_eval_empty_expression,
- expr_eval_empty_stack,
- expr_eval_stack_overflow,
- expr_eval_stack_underflow,
- expr_eval_unhandled_opcode,
- expr_eval_unrecognized_opcode,
- expr_eval_divide_by_zero,
- expr_eval_invalid_goto
- };
-
static enum eval_result_type expr_eval_result = expr_eval_no_error;
#ifndef IN_PROCESS_AGENT
static int tracing_stop_tpnum;
+/* 64-bit timestamps for the trace run's start and finish, expressed
+ in microseconds from the Unix epoch. */
+
+LONGEST tracing_start_time;
+LONGEST tracing_stop_time;
+
+/* The (optional) user-supplied name of the user that started the run.
+ This is an arbitrary string, and may be NULL. */
+
+char *tracing_user_name;
+
+/* Optional user-supplied text describing the run. This is
+ an arbitrary string, and may be NULL. */
+
+char *tracing_notes;
+
+/* Optional user-supplied text explaining a tstop command. This is an
+ arbitrary string, and may be NULL. */
+
+char *tracing_stop_note;
+
#endif
/* Functions local to this file. */
#endif
-#ifndef IN_PROCESS_AGENT
-static struct agent_expr *parse_agent_expr (char **actparm);
-static char *unparse_agent_expr (struct agent_expr *aexpr);
-#endif
-static enum eval_result_type eval_agent_expr (struct tracepoint_hit_ctx *ctx,
- struct traceframe *tframe,
- struct agent_expr *aexpr,
- ULONGEST *rslt);
-
-static int agent_mem_read (struct traceframe *tframe,
- unsigned char *to, CORE_ADDR from, ULONGEST len);
-static int agent_tsv_read (struct traceframe *tframe, int n);
-
#ifndef IN_PROCESS_AGENT
static CORE_ADDR traceframe_get_pc (struct traceframe *tframe);
static int traceframe_read_tsv (int num, LONGEST *val);
#ifndef IN_PROCESS_AGENT
static struct tracepoint *fast_tracepoint_from_ipa_tpoint_address (CORE_ADDR);
+
+static void install_tracepoint (struct tracepoint *, char *own_buf);
+static void download_tracepoint (struct tracepoint *);
+static int install_fast_tracepoint (struct tracepoint *, char *errbuf);
+static void clone_fast_tracepoint (struct tracepoint *to,
+ const struct tracepoint *from);
#endif
+static LONGEST get_timestamp (void);
+
#if defined(__GNUC__)
# define memory_barrier() asm volatile ("" : : : "memory")
#else
#define cmpxchg(mem, oldval, newval) \
__sync_val_compare_and_swap (mem, oldval, newval)
-/* The size in bytes of the buffer used to talk to the IPA helper
- thread. */
-#define CMD_BUF_SIZE 1024
-
/* Record that an error occurred during expression evaluation. */
static void
#ifdef IN_PROCESS_AGENT
/* Build the tentative token. */
- commit_count = (((prev & 0x0007ff00) + 0x100) & 0x0007ff00);
- commit = (((prev & 0x0007ff00) << 12)
+ commit_count = (((prev & GDBSERVER_FLUSH_COUNT_MASK_CURR) + 0x100)
+ & GDBSERVER_FLUSH_COUNT_MASK_CURR);
+ commit = (((prev & GDBSERVER_FLUSH_COUNT_MASK_CURR) << 12)
| commit_count
| curr);
refetch = trace_buffer_ctrl_curr;
- if ((refetch == commit
- || ((refetch & 0x7ff00000) >> 12) == commit_count))
+ if (refetch == commit
+ || ((refetch & GDBSERVER_FLUSH_COUNT_MASK_PREV) >> 12) == commit_count)
{
/* effective */
trace_debug ("change is effective: (prev=%08x, commit=%08x, "
static int seen_step_action_flag;
-/* Create a tracepoint (location) with given number and address. */
+/* Create a tracepoint (location) with given number and address. Add this
+ new tracepoint to list and sort this list. */
static struct tracepoint *
add_tracepoint (int num, CORE_ADDR addr)
{
- struct tracepoint *tpoint;
+ struct tracepoint *tpoint, **tp_next;
tpoint = xmalloc (sizeof (struct tracepoint));
tpoint->number = num;
tpoint->handle = NULL;
tpoint->next = NULL;
- if (!last_tracepoint)
- tracepoints = tpoint;
- else
- last_tracepoint->next = tpoint;
+ /* Find a place to insert this tracepoint into list in order to keep
+ the tracepoint list still in the ascending order. There may be
+ multiple tracepoints at the same address as TPOINT's, and this
+ guarantees TPOINT is inserted after all the tracepoints which are
+ set at the same address. For example, fast tracepoints A, B, C are
+ set at the same address, and D is to be insert at the same place as
+ well,
+
+ -->| A |--> | B |-->| C |->...
+
+ One jump pad was created for tracepoint A, B, and C, and the target
+ address of A is referenced/used in jump pad. So jump pad will let
+ inferior jump to A. If D is inserted in front of A, like this,
+
+ -->| D |-->| A |--> | B |-->| C |->...
+
+ without updating jump pad, D is not reachable during collect, which
+ is wrong. As we can see, the order of B, C and D doesn't matter, but
+ A should always be the `first' one. */
+ for (tp_next = &tracepoints;
+ (*tp_next) != NULL && (*tp_next)->address <= tpoint->address;
+ tp_next = &(*tp_next)->next)
+ ;
+ tpoint->next = *tp_next;
+ *tp_next = tpoint;
last_tracepoint = tpoint;
seen_step_action_flag = 0;
return NULL;
}
+/* Remove TPOINT from global list. */
+
+static void
+remove_tracepoint (struct tracepoint *tpoint)
+{
+ struct tracepoint *tp, *tp_prev;
+
+ for (tp = tracepoints, tp_prev = NULL; tp && tp != tpoint;
+ tp_prev = tp, tp = tp->next)
+ ;
+
+ if (tp)
+ {
+ if (tp_prev)
+ tp_prev->next = tp->next;
+ else
+ tracepoints = tp->next;
+
+ xfree (tp);
+ }
+}
+
/* There may be several tracepoints with the same number (because they
are "locations", in GDB parlance); return the next one after the
given tracepoint, or search from the beginning of the list if the
#endif
-static char *
-save_string (const char *str, size_t len)
-{
- char *s;
-
- s = xmalloc (len + 1);
- memcpy (s, str, len);
- s[len] = '\0';
-
- return s;
-}
-
/* Append another action to perform when the tracepoint triggers. */
static void
maction = xmalloc (sizeof *maction);
maction->base.type = *act;
+ maction->base.ops = &m_tracepoint_action_ops;
action = &maction->base;
++act;
raction = xmalloc (sizeof *raction);
raction->base.type = *act;
+ raction->base.ops = &r_tracepoint_action_ops;
action = &raction->base;
trace_debug ("Want to collect registers");
raction = xmalloc (sizeof *raction);
raction->base.type = *act;
+ raction->base.ops = &l_tracepoint_action_ops;
action = &raction->base;
trace_debug ("Want to collect static trace data");
xaction = xmalloc (sizeof (*xaction));
xaction->base.type = *act;
+ xaction->base.ops = &x_tracepoint_action_ops;
action = &xaction->base;
trace_debug ("Want to evaluate expression");
- xaction->expr = parse_agent_expr (&act);
+ xaction->expr = gdb_parse_agent_expr (&act);
break;
}
default:
* tpoint->num_step_actions));
tpoint->step_actions[tpoint->num_step_actions - 1] = action;
tpoint->step_actions_str[tpoint->num_step_actions - 1]
- = save_string (act_start, act - act_start);
+ = savestring (act_start, act - act_start);
}
else
{
sizeof (*tpoint->actions_str) * tpoint->numactions);
tpoint->actions[tpoint->numactions - 1] = action;
tpoint->actions_str[tpoint->numactions - 1]
- = save_string (act_start, act - act_start);
+ = savestring (act_start, act - act_start);
}
}
}
tsv->value = val;
}
+LONGEST
+agent_get_trace_state_variable_value (int num)
+{
+ return get_trace_state_variable_value (num);
+}
+
+void
+agent_set_trace_state_variable_value (int num, LONGEST val)
+{
+ set_trace_state_variable_value (num, val);
+}
+
static void
set_trace_state_variable_name (int num, const char *name)
{
/* Add a block to the traceframe currently being worked on. */
static unsigned char *
-add_traceframe_block (struct traceframe *tframe, int amt)
+add_traceframe_block (struct traceframe *tframe,
+ struct tracepoint *tpoint, int amt)
{
unsigned char *block;
if (!block)
return NULL;
+ gdb_assert (tframe->tpnum == tpoint->number);
+
tframe->data_size += amt;
+ tpoint->traceframe_usage += amt;
return block;
}
static void
unprobe_marker_at (CORE_ADDR address)
{
- char cmd[CMD_BUF_SIZE];
+ char cmd[IPA_CMD_BUF_SIZE];
sprintf (cmd, "unprobe_marker_at:%s", paddress (address));
- run_inferior_command (cmd);
+ run_inferior_command (cmd, strlen (cmd) + 1);
}
/* Restore the program to its pre-tracing state. This routine may be called
/* Restore any bytes overwritten by tracepoints. */
for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
{
- if (!tpoint->enabled)
- continue;
-
/* Catch the case where we might try to remove a tracepoint that
was never actually installed. */
if (tpoint->handle == NULL)
cmd_qtdp (char *own_buf)
{
int tppacket;
+ /* Whether there is a trailing hyphen at the end of the QTDP packet. */
+ int trail_hyphen = 0;
ULONGEST num;
ULONGEST addr;
ULONGEST count;
else if (*packet == 'X')
{
actparm = (char *) packet;
- tpoint->cond = parse_agent_expr (&actparm);
+ tpoint->cond = gdb_parse_agent_expr (&actparm);
packet = actparm;
}
else if (*packet == '-')
trace_debug ("Unknown optional tracepoint field");
}
if (*packet == '-')
- trace_debug ("Also has actions\n");
+ {
+ trail_hyphen = 1;
+ trace_debug ("Also has actions\n");
+ }
trace_debug ("Defined %stracepoint %d at 0x%s, "
- "enabled %d step %ld pass %ld",
+ "enabled %d step %" PRIu64 " pass %" PRIu64,
tpoint->type == fast_tracepoint ? "fast "
- : "",
+ : tpoint->type == static_tracepoint ? "static " : "",
tpoint->number, paddress (tpoint->address), tpoint->enabled,
tpoint->step_count, tpoint->pass_count);
}
return;
}
+ /* Install tracepoint during tracing only once for each tracepoint location.
+ For each tracepoint loc, GDB may send multiple QTDP packets, and we can
+ determine the last QTDP packet for one tracepoint location by checking
+ trailing hyphen in QTDP packet. */
+ if (tracing && !trail_hyphen)
+ {
+ struct tracepoint *tp = NULL;
+
+ /* Pause all threads temporarily while we patch tracepoints. */
+ pause_all (0);
+
+ /* download_tracepoint will update global `tracepoints'
+ list, so it is unsafe to leave threads in jump pad. */
+ stabilize_threads ();
+
+ /* Freeze threads. */
+ pause_all (1);
+
+
+ if (tpoint->type != trap_tracepoint)
+ {
+ /* Find another fast or static tracepoint at the same address. */
+ for (tp = tracepoints; tp; tp = tp->next)
+ {
+ if (tp->address == tpoint->address && tp->type == tpoint->type
+ && tp->number != tpoint->number)
+ break;
+ }
+
+ /* TPOINT is installed at the same address as TP. */
+ if (tp)
+ {
+ if (tpoint->type == fast_tracepoint)
+ clone_fast_tracepoint (tpoint, tp);
+ else if (tpoint->type == static_tracepoint)
+ tpoint->handle = (void *) -1;
+ }
+ }
+
+ if (use_agent && tpoint->type == fast_tracepoint
+ && agent_capability_check (AGENT_CAPA_FAST_TRACE))
+ {
+ /* Download and install fast tracepoint by agent. */
+ if (tracepoint_send_agent (tpoint) == 0)
+ write_ok (own_buf);
+ else
+ {
+ write_enn (own_buf);
+ remove_tracepoint (tpoint);
+ }
+ }
+ else
+ {
+ download_tracepoint (tpoint);
+
+ if (tpoint->type == trap_tracepoint || tp == NULL)
+ {
+ install_tracepoint (tpoint, own_buf);
+ if (strcmp (own_buf, "OK") != 0)
+ remove_tracepoint (tpoint);
+ }
+ else
+ write_ok (own_buf);
+ }
+
+ unpause_all (1);
+ return;
+ }
+
write_ok (own_buf);
}
write_ok (own_buf);
}
+static void
+cmd_qtenable_disable (char *own_buf, int enable)
+{
+ char *packet = own_buf;
+ ULONGEST num, addr;
+ struct tracepoint *tp;
+
+ packet += strlen (enable ? "QTEnable:" : "QTDisable:");
+ packet = unpack_varlen_hex (packet, &num);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &addr);
+
+ tp = find_tracepoint (num, addr);
+
+ if (tp)
+ {
+ if ((enable && tp->enabled) || (!enable && !tp->enabled))
+ {
+ trace_debug ("Tracepoint %d at 0x%s is already %s",
+ (int) num, paddress (addr),
+ enable ? "enabled" : "disabled");
+ write_ok (own_buf);
+ return;
+ }
+
+ trace_debug ("%s tracepoint %d at 0x%s",
+ enable ? "Enabling" : "Disabling",
+ (int) num, paddress (addr));
+
+ tp->enabled = enable;
+
+ if (tp->type == fast_tracepoint || tp->type == static_tracepoint)
+ {
+ int ret;
+ int offset = offsetof (struct tracepoint, enabled);
+ CORE_ADDR obj_addr = tp->obj_addr_on_target + offset;
+
+ ret = prepare_to_access_memory ();
+ if (ret)
+ {
+ trace_debug ("Failed to temporarily stop inferior threads");
+ write_enn (own_buf);
+ return;
+ }
+
+ ret = write_inferior_integer (obj_addr, enable);
+ done_accessing_memory ();
+
+ if (ret)
+ {
+ trace_debug ("Cannot write enabled flag into "
+ "inferior process memory");
+ write_enn (own_buf);
+ return;
+ }
+ }
+
+ write_ok (own_buf);
+ }
+ else
+ {
+ trace_debug ("Tracepoint %d at 0x%s not found",
+ (int) num, paddress (addr));
+ write_enn (own_buf);
+ }
+}
+
static void
cmd_qtv (char *own_buf)
{
char *packet = own_buf;
packet += strlen ("qTV:");
- packet = unpack_varlen_hex (packet, &num);
+ unpack_varlen_hex (packet, &num);
if (current_traceframe >= 0)
{
gdb_jump_pad_head += used;
}
-/* Sort tracepoints by PC, using a bubble sort. */
-
-static void
-sort_tracepoints (void)
-{
- struct tracepoint *lst, *tmp, *prev = NULL;
- int i, j, n = 0;
+static CORE_ADDR trampoline_buffer_head = 0;
+static CORE_ADDR trampoline_buffer_tail;
- if (tracepoints == NULL)
- return;
+/* Reserve USED bytes from the trampoline buffer and return the
+ address of the start of the reserved space in TRAMPOLINE. Returns
+ non-zero if the space is successfully claimed. */
- /* Count nodes. */
- for (tmp = tracepoints; tmp->next; tmp = tmp->next)
- n++;
+int
+claim_trampoline_space (ULONGEST used, CORE_ADDR *trampoline)
+{
+ if (!trampoline_buffer_head)
+ {
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer,
+ &trampoline_buffer_tail))
+ {
+ fatal ("error extracting trampoline_buffer");
+ return 0;
+ }
- for (i = 0; i < n - 1; i++)
- for (j = 0, lst = tracepoints;
- lst && lst->next && (j <= n - 1 - i);
- j++)
- {
- /* If we're at beginning, the start node is the prev
- node. */
- if (j == 0)
- prev = lst;
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+ &trampoline_buffer_head))
+ {
+ fatal ("error extracting trampoline_buffer_end");
+ return 0;
+ }
+ }
- /* Compare neighbors. */
- if (lst->next->address < lst->address)
- {
- struct tracepoint *p;
+ /* Start claiming space from the top of the trampoline space. If
+ the space is located at the bottom of the virtual address space,
+ this reduces the possibility that corruption will occur if a null
+ pointer is used to write to memory. */
+ if (trampoline_buffer_head - trampoline_buffer_tail < used)
+ {
+ trace_debug ("claim_trampoline_space failed to reserve %s bytes",
+ pulongest (used));
+ return 0;
+ }
- /* Swap'em. */
- tmp = (lst->next ? lst->next->next : NULL);
+ trampoline_buffer_head -= used;
- if (j == 0 && prev == tracepoints)
- tracepoints = lst->next;
+ trace_debug ("claim_trampoline_space reserves %s bytes at %s",
+ pulongest (used), paddress (trampoline_buffer_head));
- p = lst->next;
- prev->next = lst->next;
- lst->next->next = lst;
- lst->next = tmp;
- prev = p;
- }
- else
- {
- lst = lst->next;
- /* Keep track of the previous node. We need it if we need
- to swap nodes. */
- if (j != 0)
- prev = prev->next;
- }
- }
+ *trampoline = trampoline_buffer_head;
+ return 1;
}
-/* Ask the IPA to probe the marker at ADDRESS. Returns -1 if running
- the command fails, or 0 otherwise. If the command ran
- successfully, but probing the marker failed, ERROUT will be filled
- with the error to reply to GDB, and -1 is also returned. This
- allows directly passing IPA errors to GDB. */
+/* Returns non-zero if there is space allocated for use in trampolines
+ for fast tracepoints. */
-static int
-probe_marker_at (CORE_ADDR address, char *errout)
+int
+have_fast_tracepoint_trampoline_buffer (char *buf)
{
- char cmd[CMD_BUF_SIZE];
- int err;
+ CORE_ADDR trampoline_end, errbuf;
+
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+ &trampoline_end))
+ {
+ fatal ("error extracting trampoline_buffer_end");
+ return 0;
+ }
+
+ if (buf)
+ {
+ buf[0] = '\0';
+ strcpy (buf, "was claiming");
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_error,
+ &errbuf))
+ {
+ fatal ("error extracting errbuf");
+ return 0;
+ }
+
+ read_inferior_memory (errbuf, (unsigned char *) buf, 100);
+ }
+
+ return trampoline_end != 0;
+}
+
+/* Ask the IPA to probe the marker at ADDRESS. Returns -1 if running
+ the command fails, or 0 otherwise. If the command ran
+ successfully, but probing the marker failed, ERROUT will be filled
+ with the error to reply to GDB, and -1 is also returned. This
+ allows directly passing IPA errors to GDB. */
+
+static int
+probe_marker_at (CORE_ADDR address, char *errout)
+{
+ char cmd[IPA_CMD_BUF_SIZE];
+ int err;
sprintf (cmd, "probe_marker_at:%s", paddress (address));
- err = run_inferior_command (cmd);
+ err = run_inferior_command (cmd, strlen (cmd) + 1);
if (err == 0)
{
return err;
}
-#define MAX_JUMP_SIZE 20
-
static void
-cmd_qtstart (char *packet)
+clone_fast_tracepoint (struct tracepoint *to, const struct tracepoint *from)
{
- struct tracepoint *tpoint, *prev_ftpoint, *prev_stpoint;
- int slow_tracepoint_count, fast_count;
- CORE_ADDR jump_entry;
+ to->jump_pad = from->jump_pad;
+ to->jump_pad_end = from->jump_pad_end;
+ to->trampoline = from->trampoline;
+ to->trampoline_end = from->trampoline_end;
+ to->adjusted_insn_addr = from->adjusted_insn_addr;
+ to->adjusted_insn_addr_end = from->adjusted_insn_addr_end;
+ to->handle = from->handle;
+
+ gdb_assert (from->handle);
+ inc_ref_fast_tracepoint_jump ((struct fast_tracepoint_jump *) from->handle);
+}
+
+#define MAX_JUMP_SIZE 20
+
+/* Install fast tracepoint. Return 0 if successful, otherwise return
+ non-zero. */
+static int
+install_fast_tracepoint (struct tracepoint *tpoint, char *errbuf)
+{
+ CORE_ADDR jentry, jump_entry;
+ CORE_ADDR trampoline;
+ ULONGEST trampoline_size;
+ int err = 0;
/* The jump to the jump pad of the last fast tracepoint
installed. */
unsigned char fjump[MAX_JUMP_SIZE];
ULONGEST fjump_size;
- trace_debug ("Starting the trace");
+ if (tpoint->orig_size < target_get_min_fast_tracepoint_insn_len ())
+ {
+ trace_debug ("Requested a fast tracepoint on an instruction "
+ "that is of less than the minimum length.");
+ return 0;
+ }
+
+ jentry = jump_entry = get_jump_space_head ();
+
+ trampoline = 0;
+ trampoline_size = 0;
+
+ /* Install the jump pad. */
+ err = install_fast_tracepoint_jump_pad (tpoint->obj_addr_on_target,
+ tpoint->address,
+ ipa_sym_addrs.addr_gdb_collect,
+ ipa_sym_addrs.addr_collecting,
+ tpoint->orig_size,
+ &jentry,
+ &trampoline, &trampoline_size,
+ fjump, &fjump_size,
+ &tpoint->adjusted_insn_addr,
+ &tpoint->adjusted_insn_addr_end,
+ errbuf);
+
+ if (err)
+ return 1;
+
+ /* Wire it in. */
+ tpoint->handle = set_fast_tracepoint_jump (tpoint->address, fjump,
+ fjump_size);
+
+ if (tpoint->handle != NULL)
+ {
+ tpoint->jump_pad = jump_entry;
+ tpoint->jump_pad_end = jentry;
+ tpoint->trampoline = trampoline;
+ tpoint->trampoline_end = trampoline + trampoline_size;
+
+ /* Pad to 8-byte alignment. */
+ jentry = ((jentry + 7) & ~0x7);
+ claim_jump_space (jentry - jump_entry);
+ }
+
+ return 0;
+}
+
+
+/* Install tracepoint TPOINT, and write reply message in OWN_BUF. */
+
+static void
+install_tracepoint (struct tracepoint *tpoint, char *own_buf)
+{
+ tpoint->handle = NULL;
+ *own_buf = '\0';
+
+ if (tpoint->type == trap_tracepoint)
+ {
+ /* Tracepoints are installed as memory breakpoints. Just go
+ ahead and install the trap. The breakpoints module
+ handles duplicated breakpoints, and the memory read
+ routine handles un-patching traps from memory reads. */
+ tpoint->handle = set_breakpoint_at (tpoint->address,
+ tracepoint_handler);
+ }
+ else if (tpoint->type == fast_tracepoint || tpoint->type == static_tracepoint)
+ {
+ if (!agent_loaded_p ())
+ {
+ trace_debug ("Requested a %s tracepoint, but fast "
+ "tracepoints aren't supported.",
+ tpoint->type == static_tracepoint ? "static" : "fast");
+ write_e_ipa_not_loaded (own_buf);
+ return;
+ }
+ if (tpoint->type == static_tracepoint
+ && !in_process_agent_supports_ust ())
+ {
+ trace_debug ("Requested a static tracepoint, but static "
+ "tracepoints are not supported.");
+ write_e_ust_not_loaded (own_buf);
+ return;
+ }
+
+ if (tpoint->type == fast_tracepoint)
+ install_fast_tracepoint (tpoint, own_buf);
+ else
+ {
+ if (probe_marker_at (tpoint->address, own_buf) == 0)
+ tpoint->handle = (void *) -1;
+ }
+
+ }
+ else
+ internal_error (__FILE__, __LINE__, "Unknown tracepoint type");
+
+ if (tpoint->handle == NULL)
+ {
+ if (*own_buf == '\0')
+ write_enn (own_buf);
+ }
+ else
+ write_ok (own_buf);
+}
+
+static void download_tracepoint_1 (struct tracepoint *tpoint);
- slow_tracepoint_count = fast_count = 0;
+static void
+cmd_qtstart (char *packet)
+{
+ struct tracepoint *tpoint, *prev_ftpoint, *prev_stpoint;
+ CORE_ADDR tpptr = 0, prev_tpptr = 0;
- /* Sort tracepoints by ascending address. This makes installing
- fast tracepoints at the same address easier to handle. */
- sort_tracepoints ();
+ trace_debug ("Starting the trace");
/* Pause all threads temporarily while we patch tracepoints. */
pause_all (0);
pause_all (1);
/* Sync the fast tracepoints list in the inferior ftlib. */
- if (in_process_agent_loaded ())
- {
- download_tracepoints ();
- download_trace_state_variables ();
- }
+ if (agent_loaded_p ())
+ download_trace_state_variables ();
/* No previous fast tpoint yet. */
prev_ftpoint = NULL;
*packet = '\0';
- /* Install tracepoints. */
+ /* Start out empty. */
+ if (agent_loaded_p ())
+ write_inferior_data_ptr (ipa_sym_addrs.addr_tracepoints, 0);
+
+ /* Download and install tracepoints. */
for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
{
/* Ensure all the hit counts start at zero. */
tpoint->hit_count = 0;
-
- if (!tpoint->enabled)
- continue;
+ tpoint->traceframe_usage = 0;
if (tpoint->type == trap_tracepoint)
{
- ++slow_tracepoint_count;
-
/* Tracepoints are installed as memory breakpoints. Just go
ahead and install the trap. The breakpoints module
handles duplicated breakpoints, and the memory read
tpoint->handle = set_breakpoint_at (tpoint->address,
tracepoint_handler);
}
- else if (tpoint->type == fast_tracepoint)
+ else if (tpoint->type == fast_tracepoint
+ || tpoint->type == static_tracepoint)
{
- ++fast_count;
-
if (maybe_write_ipa_not_loaded (packet))
{
- trace_debug ("Requested a fast tracepoint, but fast "
- "tracepoints aren't supported.");
+ trace_debug ("Requested a %s tracepoint, but fast "
+ "tracepoints aren't supported.",
+ tpoint->type == static_tracepoint
+ ? "static" : "fast");
break;
}
- if (prev_ftpoint != NULL && prev_ftpoint->address == tpoint->address)
- {
- tpoint->handle = set_fast_tracepoint_jump (tpoint->address,
- fjump,
- fjump_size);
- tpoint->jump_pad = prev_ftpoint->jump_pad;
- tpoint->jump_pad_end = prev_ftpoint->jump_pad_end;
- tpoint->adjusted_insn_addr = prev_ftpoint->adjusted_insn_addr;
- tpoint->adjusted_insn_addr_end
- = prev_ftpoint->adjusted_insn_addr_end;
- }
- else
+ if (tpoint->type == fast_tracepoint)
{
- CORE_ADDR jentry;
- int err = 0;
-
- prev_ftpoint = NULL;
-
- jentry = jump_entry = get_jump_space_head ();
-
- /* Install the jump pad. */
- err = install_fast_tracepoint_jump_pad
- (tpoint->obj_addr_on_target,
- tpoint->address,
- ipa_sym_addrs.addr_gdb_collect,
- ipa_sym_addrs.addr_collecting,
- tpoint->orig_size,
- &jentry,
- fjump, &fjump_size,
- &tpoint->adjusted_insn_addr,
- &tpoint->adjusted_insn_addr_end);
-
- /* Wire it in. */
- if (!err)
- tpoint->handle = set_fast_tracepoint_jump (tpoint->address,
- fjump, fjump_size);
-
- if (tpoint->handle != NULL)
- {
- tpoint->jump_pad = jump_entry;
- tpoint->jump_pad_end = jentry;
+ int use_agent_p
+ = use_agent && agent_capability_check (AGENT_CAPA_FAST_TRACE);
- /* Pad to 8-byte alignment. */
- jentry = ((jentry + 7) & ~0x7);
- claim_jump_space (jentry - jump_entry);
+ if (prev_ftpoint != NULL
+ && prev_ftpoint->address == tpoint->address)
+ {
+ if (use_agent_p)
+ tracepoint_send_agent (tpoint);
+ else
+ download_tracepoint_1 (tpoint);
- /* So that we can handle multiple fast tracepoints
- at the same address easily. */
- prev_ftpoint = tpoint;
+ clone_fast_tracepoint (tpoint, prev_ftpoint);
+ }
+ else
+ {
+ /* Tracepoint is installed successfully? */
+ int installed = 0;
+
+ /* Download and install fast tracepoint by agent. */
+ if (use_agent_p)
+ installed = !tracepoint_send_agent (tpoint);
+ else
+ {
+ download_tracepoint_1 (tpoint);
+ installed = !install_fast_tracepoint (tpoint, packet);
+ }
+
+ if (installed)
+ prev_ftpoint = tpoint;
}
- }
- }
- else if (tpoint->type == static_tracepoint)
- {
- if (maybe_write_ipa_ust_not_loaded (packet))
- {
- trace_debug ("Requested a static tracepoint, but static "
- "tracepoints are not supported.");
- break;
- }
-
- /* Can only probe a given marker once. */
- if (prev_stpoint != NULL && prev_stpoint->address == tpoint->address)
- {
- tpoint->handle = (void *) -1;
}
else
{
- if (probe_marker_at (tpoint->address, packet) == 0)
+ if (!in_process_agent_supports_ust ())
{
- tpoint->handle = (void *) -1;
+ trace_debug ("Requested a static tracepoint, but static "
+ "tracepoints are not supported.");
+ break;
+ }
- /* So that we can handle multiple static tracepoints
- at the same address easily. */
- prev_stpoint = tpoint;
+ download_tracepoint_1 (tpoint);
+ /* Can only probe a given marker once. */
+ if (prev_stpoint != NULL
+ && prev_stpoint->address == tpoint->address)
+ tpoint->handle = (void *) -1;
+ else
+ {
+ if (probe_marker_at (tpoint->address, packet) == 0)
+ {
+ tpoint->handle = (void *) -1;
+
+ /* So that we can handle multiple static tracepoints
+ at the same address easily. */
+ prev_stpoint = tpoint;
+ }
}
}
+
+ prev_tpptr = tpptr;
+ tpptr = tpoint->obj_addr_on_target;
+
+ if (tpoint == tracepoints)
+ /* First object in list, set the head pointer in the
+ inferior. */
+ write_inferior_data_ptr (ipa_sym_addrs.addr_tracepoints, tpptr);
+ else
+ write_inferior_data_ptr (prev_tpptr + offsetof (struct tracepoint,
+ next),
+ tpptr);
}
/* Any failure in the inner loop is sufficient cause to give
trace_buffer_is_full = 0;
expr_eval_result = expr_eval_no_error;
error_tracepoint = NULL;
+ tracing_start_time = get_timestamp ();
/* Tracing is now active, hits will now start being logged. */
tracing = 1;
- if (in_process_agent_loaded ())
+ if (agent_loaded_p ())
{
if (write_inferior_integer (ipa_sym_addrs.addr_tracing, 1))
fatal ("Error setting tracing variable in lib");
/* Stop logging. Tracepoints can still be hit, but they will not be
recorded. */
tracing = 0;
- if (in_process_agent_loaded ())
+ if (agent_loaded_p ())
{
if (write_inferior_integer (ipa_sym_addrs.addr_tracing, 0))
fatal ("Error clearing tracing variable in lib");
}
+ tracing_stop_time = get_timestamp ();
tracing_stop_reason = "t???";
tracing_stop_tpnum = 0;
if (stopping_tracepoint)
{
trace_debug ("Stopping the trace because "
- "tracepoint %d was hit %ld times",
+ "tracepoint %d was hit %" PRIu64 " times",
stopping_tracepoint->number,
stopping_tracepoint->pass_count);
tracing_stop_reason = "tpasscount";
/* Clear out the tracepoints. */
clear_installed_tracepoints ();
- if (in_process_agent_loaded ())
+ if (agent_loaded_p ())
{
/* Pull in fast tracepoint trace frames from the inferior lib
buffer into our buffer, even if our buffer is already full,
if (strncmp (packet, "pc:", strlen ("pc:")) == 0)
{
packet += strlen ("pc:");
- packet = unpack_varlen_hex (packet, &pc);
+ unpack_varlen_hex (packet, &pc);
trace_debug ("Want to find next traceframe at pc=0x%s", paddress (pc));
tframe = find_next_traceframe_in_range (pc, pc, 1, &tfnum);
}
packet += strlen ("range:");
packet = unpack_varlen_hex (packet, &lo);
++packet;
- packet = unpack_varlen_hex (packet, &hi);
+ unpack_varlen_hex (packet, &hi);
trace_debug ("Want to find next traceframe in the range 0x%s to 0x%s",
paddress (lo), paddress (hi));
tframe = find_next_traceframe_in_range (lo, hi, 1, &tfnum);
packet += strlen ("outside:");
packet = unpack_varlen_hex (packet, &lo);
++packet;
- packet = unpack_varlen_hex (packet, &hi);
+ unpack_varlen_hex (packet, &hi);
trace_debug ("Want to find next traceframe "
"outside the range 0x%s to 0x%s",
paddress (lo), paddress (hi));
else if (strncmp (packet, "tdp:", strlen ("tdp:")) == 0)
{
packet += strlen ("tdp:");
- packet = unpack_varlen_hex (packet, &num);
+ unpack_varlen_hex (packet, &num);
tpnum = (int) num;
trace_debug ("Want to find next traceframe for tracepoint %d", tpnum);
tframe = find_next_traceframe_by_tracepoint (tpnum, &tfnum);
cmd_qtstatus (char *packet)
{
char *stop_reason_rsp = NULL;
+ char *buf1, *buf2, *buf3, *str;
+ int slen;
+
+ /* Translate the plain text of the notes back into hex for
+ transmission. */
+
+ str = (tracing_user_name ? tracing_user_name : "");
+ slen = strlen (str);
+ buf1 = (char *) alloca (slen * 2 + 1);
+ hexify (buf1, str, slen);
+
+ str = (tracing_notes ? tracing_notes : "");
+ slen = strlen (str);
+ buf2 = (char *) alloca (slen * 2 + 1);
+ hexify (buf2, str, slen);
+
+ str = (tracing_stop_note ? tracing_stop_note : "");
+ slen = strlen (str);
+ buf3 = (char *) alloca (slen * 2 + 1);
+ hexify (buf3, str, slen);
trace_debug ("Returning trace status as %d, stop reason %s",
tracing, tracing_stop_reason);
- if (in_process_agent_loaded ())
+ if (agent_loaded_p ())
{
pause_all (1);
stop_reason_rsp = (char *) tracing_stop_reason;
/* The user visible error string in terror needs to be hex encoded.
- We leave it as plain string in `tracepoint_stop_reason' to ease
+ We leave it as plain string in `tracing_stop_reason' to ease
debugging. */
if (strncmp (stop_reason_rsp, "terror:", strlen ("terror:")) == 0)
{
convert_int_to_ascii ((gdb_byte *) result_name, p, strlen (result_name));
}
+ /* If this was a forced stop, include any stop note that was supplied. */
+ if (strcmp (stop_reason_rsp, "tstop") == 0)
+ {
+ stop_reason_rsp = alloca (strlen ("tstop:") + strlen (buf3) + 1);
+ strcpy (stop_reason_rsp, "tstop:");
+ strcat (stop_reason_rsp, buf3);
+ }
+
sprintf (packet,
"T%d;"
"%s:%x;"
"tframes:%x;tcreated:%x;"
"tfree:%x;tsize:%s;"
"circular:%d;"
- "disconn:%d",
+ "disconn:%d;"
+ "starttime:%s;stoptime:%s;"
+ "username:%s:;notes:%s:",
tracing ? 1 : 0,
stop_reason_rsp, tracing_stop_tpnum,
traceframe_count, traceframes_created,
free_space (), phex_nz (trace_buffer_hi - trace_buffer_lo, 0),
circular_trace_buffer,
- disconnected_tracing);
+ disconnected_tracing,
+ plongest (tracing_start_time), plongest (tracing_stop_time),
+ buf1, buf2);
+}
+
+static void
+cmd_qtp (char *own_buf)
+{
+ ULONGEST num, addr;
+ struct tracepoint *tpoint;
+ char *packet = own_buf;
+
+ packet += strlen ("qTP:");
+
+ packet = unpack_varlen_hex (packet, &num);
+ ++packet; /* skip a colon */
+ packet = unpack_varlen_hex (packet, &addr);
+
+ /* See if we already have this tracepoint. */
+ tpoint = find_tracepoint (num, addr);
+
+ if (!tpoint)
+ {
+ trace_debug ("Tracepoint error: tracepoint %d at 0x%s not found",
+ (int) num, paddress (addr));
+ write_enn (own_buf);
+ return;
+ }
+
+ sprintf (own_buf, "V%" PRIu64 ":%" PRIu64 "", tpoint->hit_count,
+ tpoint->traceframe_usage);
}
/* State variables to help return all the tracepoint bits. */
{
char *buf;
- sprintf (packet, "T%x:%s:%c:%lx:%lx", tpoint->number,
+ sprintf (packet, "T%x:%s:%c:%" PRIx64 ":%" PRIx64, tpoint->number,
paddress (tpoint->address),
(tpoint->enabled ? 'E' : 'D'), tpoint->step_count,
tpoint->pass_count);
if (tpoint->cond)
{
- buf = unparse_agent_expr (tpoint->cond);
+ buf = gdb_unparse_agent_expr (tpoint->cond);
sprintf (packet + strlen (packet), ":X%x,%s",
tpoint->cond->length, buf);
free (buf);
static void
cmd_qtsv (char *packet)
{
- trace_debug ("Returning first trace state variable definition");
+ trace_debug ("Returning additional trace state variable definition");
- if (!cur_tpoint)
- {
- /* This case would normally never occur, but be prepared for
- GDB misbehavior. */
- strcpy (packet, "l");
- }
- else if (cur_tsv)
+ if (cur_tsv)
{
cur_tsv = cur_tsv->next;
if (cur_tsv)
cmd_qtfstm (char *packet)
{
if (!maybe_write_ipa_ust_not_loaded (packet))
- run_inferior_command (packet);
+ run_inferior_command (packet, strlen (packet) + 1);
}
/* Return additional static tracepoints markers. */
cmd_qtsstm (char *packet)
{
if (!maybe_write_ipa_ust_not_loaded (packet))
- run_inferior_command (packet);
+ run_inferior_command (packet, strlen (packet) + 1);
}
/* Return the definition of the static tracepoint at a given address.
cmd_qtstmat (char *packet)
{
if (!maybe_write_ipa_ust_not_loaded (packet))
- run_inferior_command (packet);
+ run_inferior_command (packet, strlen (packet) + 1);
+}
+
+/* Sent the agent a command to close it. */
+
+void
+gdb_agent_about_to_close (int pid)
+{
+ char buf[IPA_CMD_BUF_SIZE];
+
+ if (!maybe_write_ipa_not_loaded (buf))
+ {
+ struct thread_info *save_inferior;
+ struct inferior_list_entry *inf = all_threads.head;
+
+ save_inferior = current_inferior;
+
+ /* Find a certain thread which belongs to process PID. */
+ while (inf != NULL)
+ {
+ if (ptid_get_pid (inf->id) == pid)
+ break;
+ inf = inf->next;
+ }
+
+ current_inferior = (struct thread_info *) inf;
+
+ strcpy (buf, "close");
+
+ run_inferior_command (buf, strlen (buf) + 1);
+
+ current_inferior = save_inferior;
+ }
+}
+
+/* Return the minimum instruction size needed for fast tracepoints as a
+ hexadecimal number. */
+
+static void
+cmd_qtminftpilen (char *packet)
+{
+ if (current_inferior == NULL)
+ {
+ /* Indicate that the minimum length is currently unknown. */
+ strcpy (packet, "0");
+ return;
+ }
+
+ sprintf (packet, "%x", target_get_min_fast_tracepoint_insn_len ());
}
/* Respond to qTBuffer packet with a block of raw data from the trace
packet = unpack_varlen_hex (packet, &offset);
++packet; /* skip a comma */
- packet = unpack_varlen_hex (packet, &num);
+ unpack_varlen_hex (packet, &num);
trace_debug ("Want to get trace buffer, %d bytes at offset 0x%s",
- (int) num, pulongest (offset));
+ (int) num, phex_nz (offset, 0));
tot = (trace_buffer_hi - trace_buffer_lo) - free_space ();
num = (PBUFSIZ - 16) / 2;
convert_int_to_ascii (tbp, own_buf, num);
- own_buf[num] = '\0';
}
static void
-cmd_bigqtbuffer (char *own_buf)
+cmd_bigqtbuffer_circular (char *own_buf)
{
ULONGEST val;
char *packet = own_buf;
- packet += strlen ("QTBuffer:");
+ packet += strlen ("QTBuffer:circular:");
+
+ unpack_varlen_hex (packet, &val);
+ circular_trace_buffer = val;
+ trace_debug ("Trace buffer is now %s",
+ circular_trace_buffer ? "circular" : "linear");
+ write_ok (own_buf);
+}
+
+static void
+cmd_qtnotes (char *own_buf)
+{
+ size_t nbytes;
+ char *saved, *user, *notes, *stopnote;
+ char *packet = own_buf;
+
+ packet += strlen ("QTNotes:");
- if (strncmp ("circular:", packet, strlen ("circular:")) == 0)
+ while (*packet)
{
- packet += strlen ("circular:");
- packet = unpack_varlen_hex (packet, &val);
- circular_trace_buffer = val;
- trace_debug ("Trace buffer is now %s",
- circular_trace_buffer ? "circular" : "linear");
- write_ok (own_buf);
+ if (strncmp ("user:", packet, strlen ("user:")) == 0)
+ {
+ packet += strlen ("user:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ user = xmalloc (nbytes + 1);
+ nbytes = unhexify (user, saved, nbytes);
+ user[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("User is '%s'", user);
+ xfree (tracing_user_name);
+ tracing_user_name = user;
+ }
+ else if (strncmp ("notes:", packet, strlen ("notes:")) == 0)
+ {
+ packet += strlen ("notes:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ notes = xmalloc (nbytes + 1);
+ nbytes = unhexify (notes, saved, nbytes);
+ notes[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("Notes is '%s'", notes);
+ xfree (tracing_notes);
+ tracing_notes = notes;
+ }
+ else if (strncmp ("tstop:", packet, strlen ("tstop:")) == 0)
+ {
+ packet += strlen ("tstop:");
+ saved = packet;
+ packet = strchr (packet, ';');
+ nbytes = (packet - saved) / 2;
+ stopnote = xmalloc (nbytes + 1);
+ nbytes = unhexify (stopnote, saved, nbytes);
+ stopnote[nbytes] = '\0';
+ ++packet; /* skip the semicolon */
+ trace_debug ("tstop note is '%s'", stopnote);
+ xfree (tracing_stop_note);
+ tracing_stop_note = stopnote;
+ }
+ else
+ break;
}
- else
- write_enn (own_buf);
+
+ write_ok (own_buf);
}
int
cmd_qtdpsrc (packet);
return 1;
}
+ else if (strncmp ("QTEnable:", packet, strlen ("QTEnable:")) == 0)
+ {
+ cmd_qtenable_disable (packet, 1);
+ return 1;
+ }
+ else if (strncmp ("QTDisable:", packet, strlen ("QTDisable:")) == 0)
+ {
+ cmd_qtenable_disable (packet, 0);
+ return 1;
+ }
else if (strncmp ("QTDV:", packet, strlen ("QTDV:")) == 0)
{
cmd_qtdv (packet);
cmd_qtframe (packet);
return 1;
}
- else if (strncmp ("QTBuffer:", packet, strlen ("QTBuffer:")) == 0)
+ else if (strncmp ("QTBuffer:circular:", packet, strlen ("QTBuffer:circular:")) == 0)
+ {
+ cmd_bigqtbuffer_circular (packet);
+ return 1;
+ }
+ else if (strncmp ("QTNotes:", packet, strlen ("QTNotes:")) == 0)
{
- cmd_bigqtbuffer (packet);
+ cmd_qtnotes (packet);
return 1;
}
cmd_qtstatus (packet);
return 1;
}
+ else if (strncmp ("qTP:", packet, strlen ("qTP:")) == 0)
+ {
+ cmd_qtp (packet);
+ return 1;
+ }
else if (strcmp ("qTfP", packet) == 0)
{
cmd_qtfp (packet);
cmd_qtstmat (packet);
return 1;
}
+ else if (strcmp ("qTMinFTPILen", packet) == 0)
+ {
+ cmd_qtminftpilen (packet);
+ return 1;
+ }
return 0;
}
/* Pull in fast tracepoint trace frames from the inferior lib buffer into
our buffer. */
- if (in_process_agent_loaded ())
+ if (agent_loaded_p ())
upload_fast_traceframes ();
/* Check if we were indeed collecting data for one of more
/* Unlink. */
*wstep_link = wstep->next;
release_while_stepping_state (wstep);
+ wstep = *wstep_link;
continue;
}
/* Pull in fast tracepoint trace frames from the inferior in-process
agent's buffer into our buffer. */
- if (!in_process_agent_loaded ())
+ if (!agent_loaded_p ())
return 0;
upload_fast_traceframes ();
{
/* Note that we collect fast tracepoints here as well. We'll
step over the fast tracepoint jump later, which avoids the
- double collect. */
- if (tpoint->enabled && stop_pc == tpoint->address)
+ double collect. However, we don't collect for static
+ tracepoints here, because UST markers are compiled in program,
+ and probes will be executed in program. So static tracepoints
+ are collected there. */
+ if (tpoint->enabled && stop_pc == tpoint->address
+ && tpoint->type != static_tracepoint)
{
trace_debug ("Thread %s at address of tracepoint %d at 0x%s",
target_pid_to_str (tinfo->entry.id),
#if defined IN_PROCESS_AGENT && defined HAVE_UST
struct ust_marker_data;
static void collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
- CORE_ADDR stop_pc,
- struct tracepoint *tpoint,
struct traceframe *tframe);
#endif
&& stopping_tracepoint == NULL)
stopping_tracepoint = tpoint;
- trace_debug ("Making new traceframe for tracepoint %d at 0x%s, hit %ld",
+ trace_debug ("Making new traceframe for tracepoint %d at 0x%s, hit %" PRIu64,
tpoint->number, paddress (tpoint->address), tpoint->hit_count);
tframe = add_traceframe (tpoint);
int acti;
trace_debug ("Making new step traceframe for "
- "tracepoint %d at 0x%s, step %d of %ld, hit %ld",
+ "tracepoint %d at 0x%s, step %d of %" PRIu64 ", hit %" PRIu64,
tpoint->number, paddress (tpoint->address),
current_step, tpoint->step_count,
tpoint->hit_count);
case 'M':
{
struct collect_memory_action *maction;
+ struct eval_agent_expr_context ax_ctx;
maction = (struct collect_memory_action *) taction;
+ ax_ctx.regcache = NULL;
+ ax_ctx.tframe = tframe;
+ ax_ctx.tpoint = tpoint;
trace_debug ("Want to collect %s bytes at 0x%s (basereg %d)",
pulongest (maction->len),
paddress (maction->addr), maction->basereg);
/* (should use basereg) */
- agent_mem_read (tframe, NULL,
- (CORE_ADDR) maction->addr, maction->len);
+ agent_mem_read (&ax_ctx, NULL, (CORE_ADDR) maction->addr,
+ maction->len);
break;
}
case 'R':
trace_debug ("Want to collect registers");
/* Collect all registers for now. */
- regspace = add_traceframe_block (tframe,
+ regspace = add_traceframe_block (tframe, tpoint,
1 + register_cache_size ());
if (regspace == NULL)
{
preemptively), since the PC had already been adjusted to
contain the tracepoint's address by the jump pad. */
trace_debug ("Storing stop pc (0x%s) in regblock",
- paddress (tpoint->address));
+ paddress (stop_pc));
/* This changes the regblock, not the thread's
regcache. */
case 'X':
{
struct eval_expr_action *eaction;
+ struct eval_agent_expr_context ax_ctx;
eaction = (struct eval_expr_action *) taction;
+ ax_ctx.regcache = get_context_regcache (ctx);
+ ax_ctx.tframe = tframe;
+ ax_ctx.tpoint = tpoint;
trace_debug ("Want to evaluate expression");
- err = eval_agent_expr (ctx, tframe, eaction->expr, NULL);
+ err = gdb_eval_agent_expr (&ax_ctx, eaction->expr, NULL);
if (err != expr_eval_no_error)
{
{
#if defined IN_PROCESS_AGENT && defined HAVE_UST
trace_debug ("Want to collect static trace data");
- collect_ust_data_at_tracepoint (ctx, stop_pc,
- tpoint, tframe);
+ collect_ust_data_at_tracepoint (ctx, tframe);
#else
trace_debug ("warning: collecting static trace data, "
"but static tracepoints are not supported");
err = ((condfn) (uintptr_t) (tpoint->compiled_cond)) (ctx, &value);
else
#endif
- err = eval_agent_expr (ctx, NULL, tpoint->cond, &value);
+ {
+ struct eval_agent_expr_context ax_ctx;
+
+ ax_ctx.regcache = get_context_regcache (ctx);
+ ax_ctx.tframe = NULL;
+ ax_ctx.tpoint = tpoint;
+ err = gdb_eval_agent_expr (&ax_ctx, tpoint->cond, &value);
+ }
if (err != expr_eval_no_error)
{
record_tracepoint_error (tpoint, "condition", err);
return (value ? 1 : 0);
}
-#ifndef IN_PROCESS_AGENT
-
-/* The packet form of an agent expression consists of an 'X', number
- of bytes in expression, a comma, and then the bytes. */
-
-static struct agent_expr *
-parse_agent_expr (char **actparm)
-{
- char *act = *actparm;
- ULONGEST xlen;
- struct agent_expr *aexpr;
-
- ++act; /* skip the X */
- act = unpack_varlen_hex (act, &xlen);
- ++act; /* skip a comma */
- aexpr = xmalloc (sizeof (struct agent_expr));
- aexpr->length = xlen;
- aexpr->bytes = xmalloc (xlen);
- convert_ascii_to_int (act, aexpr->bytes, xlen);
- *actparm = act + (xlen * 2);
- return aexpr;
-}
-
-/* Convert the bytes of an agent expression back into hex digits, so
- they can be printed or uploaded. This allocates the buffer,
- callers should free when they are done with it. */
-
-static char *
-unparse_agent_expr (struct agent_expr *aexpr)
-{
- char *rslt;
-
- rslt = xmalloc (2 * aexpr->length + 1);
- convert_int_to_ascii (aexpr->bytes, rslt, aexpr->length);
- return rslt;
-}
-
-#endif
-
-/* A wrapper for gdb_agent_op_names that does some bounds-checking. */
-
-static const char *
-gdb_agent_op_name (int op)
-{
- if (op < 0 || op >= gdb_agent_op_last || gdb_agent_op_names[op] == NULL)
- return "?undef?";
- return gdb_agent_op_names[op];
-}
+/* Do memory copies for bytecodes. */
+/* Do the recording of memory blocks for actions and bytecodes. */
int
-tp_printf (const char *format, ...)
+agent_mem_read (struct eval_agent_expr_context *ctx,
+ unsigned char *to, CORE_ADDR from, ULONGEST len)
{
- va_list ap;
- va_start (ap, format);
- vprintf (format, ap);
- va_end (ap);
- return 0;
-}
+ unsigned char *mspace;
+ ULONGEST remaining = len;
+ unsigned short blocklen;
-/* The agent expression evaluator, as specified by the GDB docs. It
- returns 0 if everything went OK, and a nonzero error code
- otherwise. */
-
-static enum eval_result_type
-eval_agent_expr (struct tracepoint_hit_ctx *ctx,
- struct traceframe *tframe,
- struct agent_expr *aexpr,
- ULONGEST *rslt)
-{
- int pc = 0;
-#define STACK_MAX 100
- ULONGEST stack[STACK_MAX], top;
- int sp = 0;
- unsigned char op;
- int arg;
-
- /* This union is a convenient way to convert representations. For
- now, assume a standard architecture where the hardware integer
- types have 8, 16, 32, 64 bit types. A more robust solution would
- be to import stdint.h from gnulib. */
- union
- {
- union
- {
- unsigned char bytes[1];
- unsigned char val;
- } u8;
- union
- {
- unsigned char bytes[2];
- unsigned short val;
- } u16;
- union
- {
- unsigned char bytes[4];
- unsigned int val;
- } u32;
- union
- {
- unsigned char bytes[8];
- ULONGEST val;
- } u64;
- } cnv;
-
- if (aexpr->length == 0)
- {
- trace_debug ("empty agent expression");
- return expr_eval_empty_expression;
- }
-
- /* Cache the stack top in its own variable. Much of the time we can
- operate on this variable, rather than dinking with the stack. It
- needs to be copied to the stack when sp changes. */
- top = 0;
-
- while (1)
- {
- op = aexpr->bytes[pc++];
-
- trace_debug ("About to interpret byte 0x%x", op);
-
- switch (op)
- {
- case gdb_agent_op_add:
- top += stack[--sp];
- break;
-
- case gdb_agent_op_sub:
- top = stack[--sp] - top;
- break;
-
- case gdb_agent_op_mul:
- top *= stack[--sp];
- break;
-
- case gdb_agent_op_div_signed:
- if (top == 0)
- {
- trace_debug ("Attempted to divide by zero");
- return expr_eval_divide_by_zero;
- }
- top = ((LONGEST) stack[--sp]) / ((LONGEST) top);
- break;
-
- case gdb_agent_op_div_unsigned:
- if (top == 0)
- {
- trace_debug ("Attempted to divide by zero");
- return expr_eval_divide_by_zero;
- }
- top = stack[--sp] / top;
- break;
-
- case gdb_agent_op_rem_signed:
- if (top == 0)
- {
- trace_debug ("Attempted to divide by zero");
- return expr_eval_divide_by_zero;
- }
- top = ((LONGEST) stack[--sp]) % ((LONGEST) top);
- break;
-
- case gdb_agent_op_rem_unsigned:
- if (top == 0)
- {
- trace_debug ("Attempted to divide by zero");
- return expr_eval_divide_by_zero;
- }
- top = stack[--sp] % top;
- break;
-
- case gdb_agent_op_lsh:
- top = stack[--sp] << top;
- break;
-
- case gdb_agent_op_rsh_signed:
- top = ((LONGEST) stack[--sp]) >> top;
- break;
-
- case gdb_agent_op_rsh_unsigned:
- top = stack[--sp] >> top;
- break;
-
- case gdb_agent_op_trace:
- agent_mem_read (tframe,
- NULL, (CORE_ADDR) stack[--sp], (ULONGEST) top);
- if (--sp >= 0)
- top = stack[sp];
- break;
-
- case gdb_agent_op_trace_quick:
- arg = aexpr->bytes[pc++];
- agent_mem_read (tframe, NULL, (CORE_ADDR) top, (ULONGEST) arg);
- break;
-
- case gdb_agent_op_log_not:
- top = !top;
- break;
-
- case gdb_agent_op_bit_and:
- top &= stack[--sp];
- break;
-
- case gdb_agent_op_bit_or:
- top |= stack[--sp];
- break;
-
- case gdb_agent_op_bit_xor:
- top ^= stack[--sp];
- break;
-
- case gdb_agent_op_bit_not:
- top = ~top;
- break;
-
- case gdb_agent_op_equal:
- top = (stack[--sp] == top);
- break;
-
- case gdb_agent_op_less_signed:
- top = (((LONGEST) stack[--sp]) < ((LONGEST) top));
- break;
-
- case gdb_agent_op_less_unsigned:
- top = (stack[--sp] < top);
- break;
-
- case gdb_agent_op_ext:
- arg = aexpr->bytes[pc++];
- if (arg < (sizeof (LONGEST) * 8))
- {
- LONGEST mask = 1 << (arg - 1);
- top &= ((LONGEST) 1 << arg) - 1;
- top = (top ^ mask) - mask;
- }
- break;
-
- case gdb_agent_op_ref8:
- agent_mem_read (tframe, cnv.u8.bytes, (CORE_ADDR) top, 1);
- top = cnv.u8.val;
- break;
-
- case gdb_agent_op_ref16:
- agent_mem_read (tframe, cnv.u16.bytes, (CORE_ADDR) top, 2);
- top = cnv.u16.val;
- break;
-
- case gdb_agent_op_ref32:
- agent_mem_read (tframe, cnv.u32.bytes, (CORE_ADDR) top, 4);
- top = cnv.u32.val;
- break;
-
- case gdb_agent_op_ref64:
- agent_mem_read (tframe, cnv.u64.bytes, (CORE_ADDR) top, 8);
- top = cnv.u64.val;
- break;
-
- case gdb_agent_op_if_goto:
- if (top)
- pc = (aexpr->bytes[pc] << 8) + (aexpr->bytes[pc + 1]);
- else
- pc += 2;
- if (--sp >= 0)
- top = stack[sp];
- break;
-
- case gdb_agent_op_goto:
- pc = (aexpr->bytes[pc] << 8) + (aexpr->bytes[pc + 1]);
- break;
-
- case gdb_agent_op_const8:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- top = aexpr->bytes[pc++];
- break;
-
- case gdb_agent_op_const16:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- top = aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- break;
-
- case gdb_agent_op_const32:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- top = aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- top = (top << 8) + aexpr->bytes[pc++];
- break;
-
- case gdb_agent_op_const64:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- 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++];
- break;
-
- case gdb_agent_op_reg:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- {
- int regnum = arg;
- struct regcache *regcache;
-
- regcache = get_context_regcache (ctx);
-
- switch (register_size (regnum))
- {
- case 8:
- collect_register (regcache, regnum, cnv.u64.bytes);
- top = cnv.u64.val;
- break;
- case 4:
- collect_register (regcache, regnum, cnv.u32.bytes);
- top = cnv.u32.val;
- break;
- case 2:
- collect_register (regcache, regnum, cnv.u16.bytes);
- top = cnv.u16.val;
- break;
- case 1:
- collect_register (regcache, regnum, cnv.u8.bytes);
- top = cnv.u8.val;
- break;
- default:
- internal_error (__FILE__, __LINE__,
- "unhandled register size");
- }
- }
- break;
-
- case gdb_agent_op_end:
- trace_debug ("At end of expression, sp=%d, stack top cache=0x%s",
- sp, pulongest (top));
- if (rslt)
- {
- if (sp <= 0)
- {
- /* This should be an error */
- trace_debug ("Stack is empty, nothing to return");
- return expr_eval_empty_stack;
- }
- *rslt = top;
- }
- return expr_eval_no_error;
-
- case gdb_agent_op_dup:
- stack[sp++] = top;
- break;
-
- case gdb_agent_op_pop:
- if (--sp >= 0)
- top = stack[sp];
- break;
-
- case gdb_agent_op_pick:
- arg = aexpr->bytes[pc++];
- stack[sp] = top;
- top = stack[sp - arg];
- ++sp;
- break;
-
- case gdb_agent_op_rot:
- {
- ULONGEST tem = stack[sp - 1];
-
- stack[sp - 1] = stack[sp - 2];
- stack[sp - 2] = top;
- top = tem;
- }
- break;
-
- case gdb_agent_op_zero_ext:
- arg = aexpr->bytes[pc++];
- if (arg < (sizeof (LONGEST) * 8))
- top &= ((LONGEST) 1 << arg) - 1;
- break;
-
- case gdb_agent_op_swap:
- /* Interchange top two stack elements, making sure top gets
- copied back onto stack. */
- stack[sp] = top;
- top = stack[sp - 1];
- stack[sp - 1] = stack[sp];
- break;
-
- case gdb_agent_op_getv:
- /* Flush the cached stack top. */
- stack[sp++] = top;
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- top = get_trace_state_variable_value (arg);
- break;
-
- case gdb_agent_op_setv:
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- set_trace_state_variable_value (arg, top);
- /* Note that we leave the value on the stack, for the
- benefit of later/enclosing expressions. */
- break;
-
- case gdb_agent_op_tracev:
- arg = aexpr->bytes[pc++];
- arg = (arg << 8) + aexpr->bytes[pc++];
- agent_tsv_read (tframe, arg);
- break;
-
- case gdb_agent_op_printf:
- {
- void *argv;
- arg = aexpr->bytes[pc++];
- argv = (void *) (unsigned long) top;
- if (--sp >= 0)
- top = stack[sp];
-
- if (arg)
- {
- if (strstr ((char *) (aexpr->bytes + pc), "%s"))
- {
- int i;
- unsigned char buf[100];
-
- for (i = 0; i < 100; i++)
- {
- agent_mem_read (tframe, buf + i,
- (CORE_ADDR) ((unsigned long)argv + i),
- 1);
- if (!buf[i])
- break;
- }
- tp_printf ((char *) (aexpr->bytes + pc), buf);
- }
- else
- tp_printf ((char *) (aexpr->bytes + pc), argv);
- }
- else
- tp_printf ((char *) (aexpr->bytes + pc));
- pc += strlen ((char *) aexpr->bytes + pc) + 1;
- }
- 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:
- trace_debug ("Agent expression op 0x%x valid, but not handled",
- op);
- /* If ever GDB generates any of these, we don't have the
- option of ignoring. */
- return 1;
-
- default:
- trace_debug ("Agent expression op 0x%x not recognized", op);
- /* Don't struggle on, things will just get worse. */
- return expr_eval_unrecognized_opcode;
- }
-
- /* Check for stack badness. */
- if (sp >= (STACK_MAX - 1))
- {
- trace_debug ("Expression stack overflow");
- return expr_eval_stack_overflow;
- }
-
- if (sp < 0)
- {
- trace_debug ("Expression stack underflow");
- return expr_eval_stack_underflow;
- }
-
- trace_debug ("Op %s -> sp=%d, top=0x%s",
- gdb_agent_op_name (op), sp, pulongest (top));
- }
-}
-
-/* Do memory copies for bytecodes. */
-/* Do the recording of memory blocks for actions and bytecodes. */
-
-static int
-agent_mem_read (struct traceframe *tframe,
- unsigned char *to, CORE_ADDR from, ULONGEST len)
-{
- unsigned char *mspace;
- ULONGEST remaining = len;
- unsigned short blocklen;
-
- /* If a 'to' buffer is specified, use it. */
- if (to != NULL)
+ /* If a 'to' buffer is specified, use it. */
+ if (to != NULL)
{
read_inferior_memory (from, to, len);
return 0;
blocklen = (remaining > 65535 ? 65535 : remaining);
sp = 1 + sizeof (from) + sizeof (blocklen) + blocklen;
- mspace = add_traceframe_block (tframe, sp);
+ mspace = add_traceframe_block (ctx->tframe, ctx->tpoint, sp);
if (mspace == NULL)
return 1;
/* Identify block as a memory block. */
return 0;
}
+int
+agent_mem_read_string (struct eval_agent_expr_context *ctx,
+ unsigned char *to, CORE_ADDR from, ULONGEST len)
+{
+ unsigned char *buf, *mspace;
+ ULONGEST remaining = len;
+ unsigned short blocklen, i;
+
+ /* To save a bit of space, block lengths are 16-bit, so break large
+ requests into multiple blocks. Bordering on overkill for strings,
+ but it could happen that someone specifies a large max length. */
+ while (remaining > 0)
+ {
+ size_t sp;
+
+ blocklen = (remaining > 65535 ? 65535 : remaining);
+ /* We want working space to accumulate nonzero bytes, since
+ traceframes must have a predecided size (otherwise it gets
+ harder to wrap correctly for the circular case, etc). */
+ buf = (unsigned char *) xmalloc (blocklen + 1);
+ for (i = 0; i < blocklen; ++i)
+ {
+ /* Read the string one byte at a time, in case the string is
+ at the end of a valid memory area - we don't want a
+ correctly-terminated string to engender segvio
+ complaints. */
+ read_inferior_memory (from + i, buf + i, 1);
+
+ if (buf[i] == '\0')
+ {
+ blocklen = i + 1;
+ /* Make sure outer loop stops now too. */
+ remaining = blocklen;
+ break;
+ }
+ }
+ sp = 1 + sizeof (from) + sizeof (blocklen) + blocklen;
+ mspace = add_traceframe_block (ctx->tframe, ctx->tpoint, sp);
+ if (mspace == NULL)
+ {
+ xfree (buf);
+ return 1;
+ }
+ /* Identify block as a memory block. */
+ *mspace = 'M';
+ ++mspace;
+ /* Record address and size. */
+ memcpy ((void *) mspace, (void *) &from, sizeof (from));
+ mspace += sizeof (from);
+ memcpy ((void *) mspace, (void *) &blocklen, sizeof (blocklen));
+ mspace += sizeof (blocklen);
+ /* Copy the string contents. */
+ memcpy ((void *) mspace, (void *) buf, blocklen);
+ remaining -= blocklen;
+ from += blocklen;
+ xfree (buf);
+ }
+ return 0;
+}
+
/* Record the value of a trace state variable. */
-static int
-agent_tsv_read (struct traceframe *tframe, int n)
+int
+agent_tsv_read (struct eval_agent_expr_context *ctx, int n)
{
unsigned char *vspace;
LONGEST val;
- vspace = add_traceframe_block (tframe,
+ vspace = add_traceframe_block (ctx->tframe, ctx->tpoint,
1 + sizeof (n) + sizeof (LONGEST));
if (vspace == NULL)
return 1;
return NULL;
}
+/* Return the first fast tracepoint whose trampoline contains PC. */
+
+static struct tracepoint *
+fast_tracepoint_from_trampoline_address (CORE_ADDR pc)
+{
+ struct tracepoint *tpoint;
+
+ for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ {
+ if (tpoint->type == fast_tracepoint
+ && tpoint->trampoline <= pc && pc < tpoint->trampoline_end)
+ return tpoint;
+ }
+
+ return NULL;
+}
+
/* Return GDBserver's tracepoint that matches the IP Agent's
tracepoint object that lives at IPA_TPOINT_OBJ in the IP Agent's
address space. */
{
CORE_ADDR ipa_collecting;
CORE_ADDR ipa_gdb_jump_pad_buffer, ipa_gdb_jump_pad_buffer_end;
+ CORE_ADDR ipa_gdb_trampoline_buffer;
+ CORE_ADDR ipa_gdb_trampoline_buffer_end;
struct tracepoint *tpoint;
int needs_breakpoint;
&ipa_gdb_jump_pad_buffer_end))
fatal ("error extracting `gdb_jump_pad_buffer_end'");
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer,
+ &ipa_gdb_trampoline_buffer))
+ fatal ("error extracting `gdb_trampoline_buffer'");
+ if (read_inferior_data_pointer (ipa_sym_addrs.addr_gdb_trampoline_buffer_end,
+ &ipa_gdb_trampoline_buffer_end))
+ fatal ("error extracting `gdb_trampoline_buffer_end'");
+
if (ipa_gdb_jump_pad_buffer <= stop_pc
&& stop_pc < ipa_gdb_jump_pad_buffer_end)
{
&& stop_pc < tpoint->adjusted_insn_addr)
needs_breakpoint = 1;
}
+ else if (ipa_gdb_trampoline_buffer <= stop_pc
+ && stop_pc < ipa_gdb_trampoline_buffer_end)
+ {
+ /* We can tell which tracepoint(s) the thread is collecting by
+ matching the trampoline address back to the tracepoint. */
+ tpoint = fast_tracepoint_from_trampoline_address (stop_pc);
+ if (tpoint == NULL)
+ {
+ warning ("in trampoline, but no matching tpoint?");
+ return 0;
+ }
+ else
+ {
+ trace_debug ("in trampoline of tpoint (%d, %s); trampoline(%s, %s)",
+ tpoint->number, paddress (tpoint->address),
+ paddress (tpoint->trampoline),
+ paddress (tpoint->trampoline_end));
+ }
+
+ /* Have not reached jump pad yet, but treat the trampoline as a
+ part of the jump pad that is before the adjusted original
+ instruction. */
+ needs_breakpoint = 1;
+ }
else
{
collecting_t ipa_collecting_obj;
ctx.base.type = fast_tracepoint;
ctx.regs = regs;
ctx.regcache_initted = 0;
- ctx.tpoint = tpoint;
-
/* Wrap the regblock in a register cache (in the stack, we don't
want to malloc here). */
ctx.regspace = alloca (register_cache_size ());
return;
}
- /* Test the condition if present, and collect if true. */
- if (tpoint->cond == NULL
- || condition_true_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
- tpoint))
+ for (ctx.tpoint = tpoint;
+ ctx.tpoint != NULL && ctx.tpoint->address == tpoint->address;
+ ctx.tpoint = ctx.tpoint->next)
{
- collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
- tpoint->address, tpoint);
+ if (!ctx.tpoint->enabled)
+ continue;
- /* Note that this will cause original insns to be written back
- to where we jumped from, but that's OK because we're jumping
- back to the next whole instruction. This will go badly if
- instruction restoration is not atomic though. */
- if (stopping_tracepoint
- || trace_buffer_is_full
- || expr_eval_result != expr_eval_no_error)
- stop_tracing ();
- }
- else
- {
- /* If there was a condition and it evaluated to false, the only
- way we would stop tracing is if there was an error during
- condition expression evaluation. */
- if (expr_eval_result != expr_eval_no_error)
- stop_tracing ();
+ /* Multiple tracepoints of different types, such as fast tracepoint and
+ static tracepoint, can be set at the same address. */
+ if (ctx.tpoint->type != tpoint->type)
+ continue;
+
+ /* Test the condition if present, and collect if true. */
+ if (ctx.tpoint->cond == NULL
+ || condition_true_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ ctx.tpoint))
+ {
+ collect_data_at_tracepoint ((struct tracepoint_hit_ctx *) &ctx,
+ ctx.tpoint->address, ctx.tpoint);
+
+ /* Note that this will cause original insns to be written back
+ to where we jumped from, but that's OK because we're jumping
+ back to the next whole instruction. This will go badly if
+ instruction restoration is not atomic though. */
+ if (stopping_tracepoint
+ || trace_buffer_is_full
+ || expr_eval_result != expr_eval_no_error)
+ {
+ stop_tracing ();
+ break;
+ }
+ }
+ else
+ {
+ /* If there was a condition and it evaluated to false, the only
+ way we would stop tracing is if there was an error during
+ condition expression evaluation. */
+ if (expr_eval_result != expr_eval_no_error)
+ {
+ stop_tracing ();
+ break;
+ }
+ }
}
}
#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 (LONGEST 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)
+CORE_ADDR
+get_get_tsv_func_addr (void)
{
- target_emit_ops ()->emit_int_call_1 (fn, arg1);
+ return ipa_sym_addrs.addr_get_trace_state_variable_value;
}
-/* FN's prototype is `void(*fn)(int,LONGEST)'. */
-
-static void
-emit_void_call_2 (CORE_ADDR fn, int arg1)
+CORE_ADDR
+get_set_tsv_func_addr (void)
{
- target_emit_ops ()->emit_void_call_2 (fn, arg1);
+ return ipa_sym_addrs.addr_set_trace_state_variable_value;
}
-static enum eval_result_type compile_bytecodes (struct agent_expr *aexpr);
-
static void
compile_tracepoint_condition (struct tracepoint *tpoint,
CORE_ADDR *jump_entry)
*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_name (op));
- return expr_eval_unhandled_opcode;
- }
-
- trace_debug ("Op %s compiled\n", gdb_agent_op_name (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. */
return expr_addr;
}
-/* Align V up to N bits. */
-#define UALIGN(V, N) (((V) + ((N) - 1)) & ~((N) - 1))
+/* Align V up to N bits. */
+#define UALIGN(V, N) (((V) + ((N) - 1)) & ~((N) - 1))
+
+/* Sync tracepoint with IPA, but leave maintenance of linked list to caller. */
+
+static void
+download_tracepoint_1 (struct tracepoint *tpoint)
+{
+ struct tracepoint target_tracepoint;
+ CORE_ADDR tpptr = 0;
+
+ gdb_assert (tpoint->type == fast_tracepoint
+ || tpoint->type == static_tracepoint);
+
+ 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;
+
+ tpptr = target_malloc (sizeof (*tpoint));
+ tpoint->obj_addr_on_target = tpptr;
+
+ /* Write the whole object. We'll fix up its pointers in a bit.
+ Assume no next for now. This is fixed up above on the next
+ iteration, if there's any. */
+ target_tracepoint.next = NULL;
+ /* Need to clear this here too, since we're downloading the
+ tracepoints before clearing our own copy. */
+ target_tracepoint.hit_count = 0;
+
+ write_inferior_memory (tpptr, (unsigned char *) &target_tracepoint,
+ sizeof (target_tracepoint));
+
+ if (tpoint->cond)
+ write_inferior_data_ptr (tpptr + offsetof (struct tracepoint,
+ cond),
+ download_agent_expr (tpoint->cond));
+
+ if (tpoint->numactions)
+ {
+ int i;
+ CORE_ADDR actions_array;
+
+ /* The pointers array. */
+ actions_array
+ = target_malloc (sizeof (*tpoint->actions) * tpoint->numactions);
+ write_inferior_data_ptr (tpptr + offsetof (struct tracepoint,
+ actions),
+ actions_array);
+
+ /* Now for each pointer, download the action. */
+ for (i = 0; i < tpoint->numactions; i++)
+ {
+ struct tracepoint_action *action = tpoint->actions[i];
+ CORE_ADDR ipa_action = action->ops->download (action);
+
+ if (ipa_action != 0)
+ write_inferior_data_ptr
+ (actions_array + i * sizeof (sizeof (*tpoint->actions)),
+ ipa_action);
+ }
+ }
+}
+
+#define IPA_PROTO_FAST_TRACE_FLAG 0
+#define IPA_PROTO_FAST_TRACE_ADDR_ON_TARGET 2
+#define IPA_PROTO_FAST_TRACE_JUMP_PAD 10
+#define IPA_PROTO_FAST_TRACE_FJUMP_SIZE 18
+#define IPA_PROTO_FAST_TRACE_FJUMP_INSN 22
-static void
-download_tracepoints (void)
+/* Send a command to agent to download and install tracepoint TPOINT. */
+
+static int
+tracepoint_send_agent (struct tracepoint *tpoint)
{
- CORE_ADDR tpptr = 0, prev_tpptr = 0;
- struct tracepoint *tpoint;
+ char buf[IPA_CMD_BUF_SIZE];
+ char *p;
+ int i, ret;
- /* Start out empty. */
- write_inferior_data_ptr (ipa_sym_addrs.addr_tracepoints, 0);
+ p = buf;
+ strcpy (p, "FastTrace:");
+ p += 10;
- for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
+ COPY_FIELD_TO_BUF (p, tpoint, number);
+ COPY_FIELD_TO_BUF (p, tpoint, address);
+ COPY_FIELD_TO_BUF (p, tpoint, type);
+ COPY_FIELD_TO_BUF (p, tpoint, enabled);
+ COPY_FIELD_TO_BUF (p, tpoint, step_count);
+ COPY_FIELD_TO_BUF (p, tpoint, pass_count);
+ COPY_FIELD_TO_BUF (p, tpoint, numactions);
+ COPY_FIELD_TO_BUF (p, tpoint, hit_count);
+ COPY_FIELD_TO_BUF (p, tpoint, traceframe_usage);
+ COPY_FIELD_TO_BUF (p, tpoint, compiled_cond);
+ COPY_FIELD_TO_BUF (p, tpoint, orig_size);
+
+ /* condition */
+ p = agent_expr_send (p, tpoint->cond);
+
+ /* tracepoint_action */
+ for (i = 0; i < tpoint->numactions; i++)
{
- struct tracepoint target_tracepoint;
+ struct tracepoint_action *action = tpoint->actions[i];
- if (tpoint->type != fast_tracepoint
- && tpoint->type != static_tracepoint)
- continue;
+ p[0] = action->type;
+ p = action->ops->send (&p[1], action);
+ }
- /* Maybe download a compiled condition. */
- if (tpoint->cond != NULL && target_emit_ops () != NULL)
- {
- CORE_ADDR jentry, jump_entry;
+ get_jump_space_head ();
+ /* Copy the value of GDB_JUMP_PAD_HEAD to command buffer, so that
+ agent can use jump pad from it. */
+ if (tpoint->type == fast_tracepoint)
+ {
+ memcpy (p, &gdb_jump_pad_head, 8);
+ p += 8;
+ }
- jentry = jump_entry = get_jump_space_head ();
+ ret = run_inferior_command (buf, (int) (ptrdiff_t) (p - buf));
+ if (ret)
+ return ret;
- if (tpoint->cond != NULL)
- {
- /* Pad to 8-byte alignment. (needed?) */
- /* Actually this should be left for the target to
- decide. */
- jentry = UALIGN (jentry, 8);
+ if (strncmp (buf, "OK", 2) != 0)
+ return 1;
- compile_tracepoint_condition (tpoint, &jentry);
- }
+ /* The value of tracepoint's target address is stored in BUF. */
+ memcpy (&tpoint->obj_addr_on_target,
+ &buf[IPA_PROTO_FAST_TRACE_ADDR_ON_TARGET], 8);
- /* Pad to 8-byte alignment. */
- jentry = UALIGN (jentry, 8);
- claim_jump_space (jentry - jump_entry);
- }
+ if (tpoint->type == fast_tracepoint)
+ {
+ unsigned char *insn
+ = (unsigned char *) &buf[IPA_PROTO_FAST_TRACE_FJUMP_INSN];
+ int fjump_size;
- target_tracepoint = *tpoint;
+ trace_debug ("agent: read from cmd_buf 0x%x 0x%x\n",
+ (unsigned int) tpoint->obj_addr_on_target,
+ (unsigned int) gdb_jump_pad_head);
- prev_tpptr = tpptr;
- tpptr = target_malloc (sizeof (*tpoint));
- tpoint->obj_addr_on_target = tpptr;
+ memcpy (&gdb_jump_pad_head, &buf[IPA_PROTO_FAST_TRACE_JUMP_PAD], 8);
- if (tpoint == tracepoints)
- {
- /* First object in list, set the head pointer in the
- inferior. */
- write_inferior_data_ptr (ipa_sym_addrs.addr_tracepoints, tpptr);
- }
- else
- {
- write_inferior_data_ptr (prev_tpptr + offsetof (struct tracepoint,
- next),
- tpptr);
- }
+ /* This has been done in agent. We should also set up record for it. */
+ memcpy (&fjump_size, &buf[IPA_PROTO_FAST_TRACE_FJUMP_SIZE], 4);
+ /* Wire it in. */
+ tpoint->handle
+ = set_fast_tracepoint_jump (tpoint->address, insn, fjump_size);
+ }
- /* Write the whole object. We'll fix up its pointers in a bit.
- Assume no next for now. This is fixed up above on the next
- iteration, if there's any. */
- target_tracepoint.next = NULL;
- /* Need to clear this here too, since we're downloading the
- tracepoints before clearing our own copy. */
- target_tracepoint.hit_count = 0;
-
- write_inferior_memory (tpptr, (unsigned char *) &target_tracepoint,
- sizeof (target_tracepoint));
-
- if (tpoint->cond)
- write_inferior_data_ptr (tpptr + offsetof (struct tracepoint,
- cond),
- download_agent_expr (tpoint->cond));
-
- if (tpoint->numactions)
- {
- int i;
- CORE_ADDR actions_array;
-
- /* The pointers array. */
- actions_array
- = target_malloc (sizeof (*tpoint->actions) * tpoint->numactions);
- write_inferior_data_ptr (tpptr + offsetof (struct tracepoint,
- actions),
- actions_array);
-
- /* Now for each pointer, download the action. */
- for (i = 0; i < tpoint->numactions; i++)
- {
- CORE_ADDR ipa_action = 0;
- struct tracepoint_action *action = tpoint->actions[i];
+ return 0;
+}
- switch (action->type)
- {
- case 'M':
- ipa_action
- = target_malloc (sizeof (struct collect_memory_action));
- write_inferior_memory (ipa_action,
- (unsigned char *) action,
- sizeof (struct collect_memory_action));
- break;
- case 'R':
- ipa_action
- = target_malloc (sizeof (struct collect_registers_action));
- write_inferior_memory (ipa_action,
- (unsigned char *) action,
- sizeof (struct collect_registers_action));
- break;
- case 'X':
- {
- CORE_ADDR expr;
- struct eval_expr_action *eaction
- = (struct eval_expr_action *) action;
-
- ipa_action = target_malloc (sizeof (*eaction));
- write_inferior_memory (ipa_action,
- (unsigned char *) eaction,
- sizeof (*eaction));
-
- expr = download_agent_expr (eaction->expr);
- write_inferior_data_ptr
- (ipa_action + offsetof (struct eval_expr_action, expr),
- expr);
- break;
- }
- case 'L':
- ipa_action = target_malloc
- (sizeof (struct collect_static_trace_data_action));
- write_inferior_memory
- (ipa_action,
- (unsigned char *) action,
- sizeof (struct collect_static_trace_data_action));
- break;
- default:
- trace_debug ("unknown trace action '%c', ignoring",
- action->type);
- break;
- }
+static void
+download_tracepoint (struct tracepoint *tpoint)
+{
+ struct tracepoint *tp, *tp_prev;
- if (ipa_action != 0)
- write_inferior_data_ptr
- (actions_array + i * sizeof (sizeof (*tpoint->actions)),
- ipa_action);
- }
- }
+ if (tpoint->type != fast_tracepoint
+ && tpoint->type != static_tracepoint)
+ return;
+
+ download_tracepoint_1 (tpoint);
+
+ /* Find the previous entry of TPOINT, which is fast tracepoint or
+ static tracepoint. */
+ tp_prev = NULL;
+ for (tp = tracepoints; tp != tpoint; tp = tp->next)
+ {
+ if (tp->type == fast_tracepoint || tp->type == static_tracepoint)
+ tp_prev = tp;
+ }
+
+ if (tp_prev)
+ {
+ CORE_ADDR tp_prev_target_next_addr;
+
+ /* Insert TPOINT after TP_PREV in IPA. */
+ if (read_inferior_data_pointer (tp_prev->obj_addr_on_target
+ + offsetof (struct tracepoint, next),
+ &tp_prev_target_next_addr))
+ fatal ("error reading `tp_prev->next'");
+
+ /* tpoint->next = tp_prev->next */
+ write_inferior_data_ptr (tpoint->obj_addr_on_target
+ + offsetof (struct tracepoint, next),
+ tp_prev_target_next_addr);
+ /* tp_prev->next = tpoint */
+ write_inferior_data_ptr (tp_prev->obj_addr_on_target
+ + offsetof (struct tracepoint, next),
+ tpoint->obj_addr_on_target);
}
+ else
+ /* First object in list, set the head pointer in the
+ inferior. */
+ write_inferior_data_ptr (ipa_sym_addrs.addr_tracepoints,
+ tpoint->obj_addr_on_target);
+
}
static void
/* Update the token, with new counters, and the GDBserver stamp
bit. Alway reuse the current TBC index. */
- prev = ipa_trace_buffer_ctrl_curr & 0x0007ff00;
- counter = (prev + 0x100) & 0x0007ff00;
+ prev = ipa_trace_buffer_ctrl_curr & GDBSERVER_FLUSH_COUNT_MASK_CURR;
+ counter = (prev + 0x100) & GDBSERVER_FLUSH_COUNT_MASK_CURR;
- ipa_trace_buffer_ctrl_curr = (0x80000000
+ ipa_trace_buffer_ctrl_curr = (GDBSERVER_UPDATED_FLUSH_COUNT_BIT
| (prev << 12)
| counter
| curr_tbctrl_idx);
{
/* Copy the whole set of blocks in one go for now. FIXME:
split this in smaller blocks. */
- block = add_traceframe_block (tframe, ipa_tframe.data_size);
+ block = add_traceframe_block (tframe, tpoint,
+ ipa_tframe.data_size);
if (block != NULL)
{
if (read_inferior_memory (tf
#ifdef IN_PROCESS_AGENT
IP_AGENT_EXPORT int ust_loaded;
-IP_AGENT_EXPORT char cmd_buf[CMD_BUF_SIZE];
+IP_AGENT_EXPORT char cmd_buf[IPA_CMD_BUF_SIZE];
#ifdef HAVE_UST
for (tpoint = tracepoints; tpoint; tpoint = tpoint->next)
{
- if (!tpoint->enabled || tpoint->type != static_tracepoint)
+ if (tpoint->type != static_tracepoint)
continue;
if (tpoint->address == (uintptr_t) mdata->location)
return;
}
+ if (!tpoint->enabled)
+ {
+ trace_debug ("gdb_probe: tracepoint disabled");
+ return;
+ }
+
ctx.tpoint = tpoint;
trace_debug ("gdb_probe: collecting marker: "
static void
collect_ust_data_at_tracepoint (struct tracepoint_hit_ctx *ctx,
- CORE_ADDR stop_pc,
- struct tracepoint *tpoint,
struct traceframe *tframe)
{
struct static_tracepoint_ctx *umd = (struct static_tracepoint_ctx *) ctx;
trace_debug ("Want to collect ust data");
/* 'S' + size + string */
- bufspace = add_traceframe_block (tframe,
+ bufspace = add_traceframe_block (tframe, umd->tpoint,
1 + sizeof (blocklen) + size + 1);
if (bufspace == NULL)
{
#endif /* HAVE_UST */
#endif /* IN_PROCESS_AGENT */
-#ifdef HAVE_UST
-
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#ifndef UNIX_PATH_MAX
-#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) NULL)->sun_path)
-#endif
-
-/* Where we put the socked used for synchronization. */
-#define SOCK_DIR P_tmpdir
-
-#endif /* HAVE_UST */
-
#ifndef IN_PROCESS_AGENT
-#ifdef HAVE_UST
-
-static int
-gdb_ust_connect_sync_socket (int pid)
-{
- struct sockaddr_un addr;
- int res, fd;
- char path[UNIX_PATH_MAX];
-
- res = xsnprintf (path, UNIX_PATH_MAX, "%s/gdb_ust%d", SOCK_DIR, pid);
- if (res >= UNIX_PATH_MAX)
- {
- trace_debug ("string overflow allocating socket name");
- return -1;
- }
-
- res = fd = socket (PF_UNIX, SOCK_STREAM, 0);
- if (res == -1)
- {
- warning ("error opening sync socket: %s\n", strerror (errno));
- return -1;
- }
-
- addr.sun_family = AF_UNIX;
-
- res = xsnprintf (addr.sun_path, UNIX_PATH_MAX, "%s", path);
- if (res >= UNIX_PATH_MAX)
- {
- warning ("string overflow allocating socket name\n");
- close (fd);
- return -1;
- }
-
- res = connect (fd, (struct sockaddr *) &addr, sizeof (addr));
- if (res == -1)
- {
- warning ("error connecting sync socket (%s): %s. "
- "Make sure the directory exists and that it is writable.",
- path, strerror (errno));
- close (fd);
- return -1;
- }
-
- return fd;
-}
-
-/* Resume thread PTID. */
-
-static void
-resume_thread (ptid_t ptid)
-{
- struct thread_resume resume_info;
-
- resume_info.thread = ptid;
- resume_info.kind = resume_continue;
- resume_info.sig = TARGET_SIGNAL_0;
- (*the_target->resume) (&resume_info, 1);
-}
-
-/* Stop thread PTID. */
-
-static void
-stop_thread (ptid_t ptid)
-{
- struct thread_resume resume_info;
-
- resume_info.thread = ptid;
- resume_info.kind = resume_stop;
- resume_info.sig = TARGET_SIGNAL_0;
- (*the_target->resume) (&resume_info, 1);
-}
-
/* Ask the in-process agent to run a command. Since we don't want to
have to handle the IPA hitting breakpoints while running the
command, we pause all threads, remove all breakpoints, and then set
synchronization. */
static int
-run_inferior_command (char *cmd)
+run_inferior_command (char *cmd, int len)
{
int err = -1;
- int fd = -1;
- int pid = ptid_get_pid (current_inferior->entry.id);
- int tid;
- ptid_t ptid = null_ptid;
+ int pid = ptid_get_pid (current_ptid);
trace_debug ("run_inferior_command: running: %s", cmd);
pause_all (0);
uninsert_all_breakpoints ();
- if (read_inferior_integer (ipa_sym_addrs.addr_helper_thread_id, &tid))
- {
- warning ("Error reading helper thread's id in lib");
- goto out;
- }
-
- if (tid == 0)
- {
- warning ("helper thread not initialized yet");
- goto out;
- }
-
- if (write_inferior_memory (ipa_sym_addrs.addr_cmd_buf,
- (unsigned char *) cmd, strlen (cmd) + 1))
- {
- warning ("Error writing command");
- goto out;
- }
-
- ptid = ptid_build (pid, tid, 0);
-
- resume_thread (ptid);
-
- fd = gdb_ust_connect_sync_socket (pid);
- if (fd >= 0)
- {
- char buf[1] = "";
- int ret;
-
- trace_debug ("signalling helper thread");
-
- do
- {
- ret = write (fd, buf, 1);
- } while (ret == -1 && errno == EINTR);
-
- trace_debug ("waiting for helper thread's response");
-
- do
- {
- ret = read (fd, buf, 1);
- } while (ret == -1 && errno == EINTR);
-
- close (fd);
-
- trace_debug ("helper thread's response received");
- }
-
- out:
-
- /* Need to read response with the inferior stopped. */
- if (!ptid_equal (ptid, null_ptid))
- {
- int was_non_stop = non_stop;
- struct target_waitstatus status;
-
- stop_thread (ptid);
- non_stop = 1;
- mywait (ptid, &status, 0, 0);
- non_stop = was_non_stop;
- }
-
- if (fd >= 0)
- {
- if (read_inferior_memory (ipa_sym_addrs.addr_cmd_buf,
- (unsigned char *) cmd, CMD_BUF_SIZE))
- {
- warning ("Error reading command response");
- }
- else
- {
- err = 0;
- trace_debug ("run_inferior_command: response: %s", cmd);
- }
- }
+ err = agent_run_command (pid, (const char *) cmd, len);
reinsert_all_breakpoints ();
unpause_all (0);
return err;
}
-#else /* HAVE_UST */
+#else /* !IN_PROCESS_AGENT */
-static int
-run_inferior_command (char *cmd)
-{
- return -1;
-}
+#include <sys/socket.h>
+#include <sys/un.h>
-#endif /* HAVE_UST */
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) NULL)->sun_path)
+#endif
-#else /* !IN_PROCESS_AGENT */
+/* Where we put the socked used for synchronization. */
+#define SOCK_DIR P_tmpdir
/* Thread ID of the helper thread. GDBserver reads this to know which
is the help thread. This is an LWP id on Linux. */
int helper_thread_id;
-#ifdef HAVE_UST
-
static int
init_named_socket (const char *name)
{
return fd;
}
+static char agent_socket_name[UNIX_PATH_MAX];
+
static int
-gdb_ust_socket_init (void)
+gdb_agent_socket_init (void)
{
int result, fd;
- char name[UNIX_PATH_MAX];
- result = xsnprintf (name, UNIX_PATH_MAX, "%s/gdb_ust%d",
+ result = xsnprintf (agent_socket_name, UNIX_PATH_MAX, "%s/gdb_ust%d",
SOCK_DIR, getpid ());
if (result >= UNIX_PATH_MAX)
{
return -1;
}
- fd = init_named_socket (name);
+ fd = init_named_socket (agent_socket_name);
if (fd < 0)
warning ("Error initializing named socket (%s) for communication with the "
"ust helper thread. Check that directory exists and that it "
- "is writable.", name);
+ "is writable.", agent_socket_name);
return fd;
}
-/* Return an hexstr version of the STR C string, fit for sending to
- GDB. */
-
-static char *
-cstr_to_hexstr (const char *str)
-{
- int len = strlen (str);
- char *hexstr = xmalloc (len * 2 + 1);
- convert_int_to_ascii ((gdb_byte *) str, hexstr, len);
- return hexstr;
-}
+#ifdef HAVE_UST
/* The next marker to be returned on a qTsSTM command. */
static const struct marker *next_st;
return NULL;
}
+/* Return an hexstr version of the STR C string, fit for sending to
+ GDB. */
+
+static char *
+cstr_to_hexstr (const char *str)
+{
+ int len = strlen (str);
+ char *hexstr = xmalloc (len * 2 + 1);
+ convert_int_to_ascii ((gdb_byte *) str, hexstr, len);
+ return hexstr;
+}
+
/* Compose packet that is the response to the qTsSTM/qTfSTM/qTSTMat
packets. */
return -1;
}
+static void
+gdb_ust_init (void)
+{
+ if (!dlsym_ust ())
+ return;
+
+ USTF(ltt_probe_register) (&gdb_ust_probe);
+}
+
+#endif /* HAVE_UST */
+
+#include <sys/syscall.h>
+#include <stdlib.h>
+
+static void
+gdb_agent_remove_socket (void)
+{
+ unlink (agent_socket_name);
+}
+
+/* Helper thread of agent. */
+
static void *
-gdb_ust_thread (void *arg)
+gdb_agent_helper_thread (void *arg)
{
int listen_fd;
+ atexit (gdb_agent_remove_socket);
+
while (1)
{
- listen_fd = gdb_ust_socket_init ();
+ listen_fd = gdb_agent_socket_init ();
-#ifdef SYS_gettid
if (helper_thread_id == 0)
helper_thread_id = syscall (SYS_gettid);
-#endif
if (listen_fd == -1)
{
int fd;
char buf[1];
int ret;
+ int stop_loop = 0;
tmp = sizeof (sockaddr);
if (cmd_buf[0])
{
- if (strcmp ("qTfSTM", cmd_buf) == 0)
+ if (strncmp ("close", cmd_buf, 5) == 0)
+ {
+ stop_loop = 1;
+ }
+#ifdef HAVE_UST
+ else if (strcmp ("qTfSTM", cmd_buf) == 0)
{
cmd_qtfstm (cmd_buf);
}
{
cmd_qtstmat (cmd_buf);
}
- else if (strcmp (cmd_buf, "help") == 0)
- {
- strcpy (cmd_buf, "for help, press F1\n");
- }
- else
- strcpy (cmd_buf, "");
+#endif /* HAVE_UST */
}
- write (fd, buf, 1);
+ /* Fix compiler's warning: ignoring return value of 'write'. */
+ ret = write (fd, buf, 1);
close (fd);
+
+ if (stop_loop)
+ {
+ close (listen_fd);
+ unlink (agent_socket_name);
+
+ /* Sleep endlessly to wait the whole inferior stops. This
+ thread can not exit because GDB or GDBserver may still need
+ 'current_inferior' (representing this thread) to access
+ inferior memory. Otherwise, this thread exits earlier than
+ other threads, and 'current_inferior' is set to NULL. */
+ while (1)
+ sleep (10);
+ }
}
}
}
#include <signal.h>
+#include <pthread.h>
+
+IP_AGENT_EXPORT int gdb_agent_capability = AGENT_CAPA_STATIC_TRACE;
static void
-gdb_ust_init (void)
+gdb_agent_init (void)
{
int res;
pthread_t thread;
sigset_t new_mask;
sigset_t orig_mask;
- if (!dlsym_ust ())
- return;
-
/* We want the helper thread to be as transparent as possible, so
have it inherit an all-signals-blocked mask. */
res = pthread_create (&thread,
NULL,
- gdb_ust_thread,
+ gdb_agent_helper_thread,
NULL);
res = pthread_sigmask (SIG_SETMASK, &orig_mask, NULL);
while (helper_thread_id == 0)
usleep (1);
- USTF(ltt_probe_register) (&gdb_ust_probe);
+#ifdef HAVE_UST
+ gdb_ust_init ();
+#endif
}
-#endif /* HAVE_UST */
-
#include <sys/mman.h>
#include <fcntl.h>
IP_AGENT_EXPORT char *gdb_tp_heap_buffer;
IP_AGENT_EXPORT char *gdb_jump_pad_buffer;
IP_AGENT_EXPORT char *gdb_jump_pad_buffer_end;
+IP_AGENT_EXPORT char *gdb_trampoline_buffer;
+IP_AGENT_EXPORT char *gdb_trampoline_buffer_end;
+IP_AGENT_EXPORT char *gdb_trampoline_buffer_error;
+
+/* Record the result of getting buffer space for fast tracepoint
+ trampolines. Any error message is copied, since caller may not be
+ using persistent storage. */
+
+void
+set_trampoline_buffer_space (CORE_ADDR begin, CORE_ADDR end, char *errmsg)
+{
+ gdb_trampoline_buffer = (char *) (uintptr_t) begin;
+ gdb_trampoline_buffer_end = (char *) (uintptr_t) end;
+ if (errmsg)
+ strncpy (gdb_trampoline_buffer_error, errmsg, 99);
+ else
+ strcpy (gdb_trampoline_buffer_error, "no buffer passed");
+}
static void __attribute__ ((constructor))
initialize_tracepoint_ftlib (void)
{
initialize_tracepoint ();
-#ifdef HAVE_UST
- gdb_ust_init ();
-#endif
+ gdb_agent_init ();
}
#endif /* IN_PROCESS_AGENT */
+/* Return a timestamp, expressed as microseconds of the usual Unix
+ time. (As the result is a 64-bit number, it will not overflow any
+ time soon.) */
+
static LONGEST
-tsv_get_timestamp (void)
+get_timestamp (void)
{
struct timeval tv;
variable numbered 1, it will be renumbered.) */
create_trace_state_variable (1, 0);
set_trace_state_variable_name (1, "trace_timestamp");
- set_trace_state_variable_getter (1, tsv_get_timestamp);
+ set_trace_state_variable_getter (1, get_timestamp);
#ifdef IN_PROCESS_AGENT
{
+ uintptr_t addr;
int pagesize;
+
pagesize = sysconf (_SC_PAGE_SIZE);
if (pagesize == -1)
fatal ("sysconf");
gdb_tp_heap_buffer = xmalloc (5 * 1024 * 1024);
- /* Allocate scratch buffer aligned on a page boundary. */
- gdb_jump_pad_buffer = memalign (pagesize, pagesize * 20);
- gdb_jump_pad_buffer_end = gdb_jump_pad_buffer + pagesize * 20;
+#define SCRATCH_BUFFER_NPAGES 20
- /* Make it writable and executable. */
- if (mprotect (gdb_jump_pad_buffer, pagesize * 20,
- PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
+ /* Allocate scratch buffer aligned on a page boundary, at a low
+ address (close to the main executable's code). */
+ for (addr = pagesize; addr != 0; addr += pagesize)
+ {
+ gdb_jump_pad_buffer = mmap ((void *) addr, pagesize * SCRATCH_BUFFER_NPAGES,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ -1, 0);
+ if (gdb_jump_pad_buffer != MAP_FAILED)
+ break;
+ }
+
+ if (addr == 0)
fatal ("\
-initialize_tracepoint: mprotect(%p, %d, PROT_READ|PROT_EXEC) failed with %s",
- gdb_jump_pad_buffer, pagesize * 20, strerror (errno));
+initialize_tracepoint: mmap'ing jump pad buffer failed with %s",
+ strerror (errno));
+
+ gdb_jump_pad_buffer_end = gdb_jump_pad_buffer + pagesize * SCRATCH_BUFFER_NPAGES;
}
+ gdb_trampoline_buffer = gdb_trampoline_buffer_end = 0;
+
+ /* It's not a fatal error for something to go wrong with trampoline
+ buffer setup, but it can be mysterious, so create a channel to
+ report back on what went wrong, using a fixed size since we may
+ not be able to allocate space later when the problem occurs. */
+ gdb_trampoline_buffer_error = xmalloc (IPA_BUFSIZ);
+
+ strcpy (gdb_trampoline_buffer_error, "No errors reported");
+
initialize_low_tracepoint ();
#endif
}